Skip to content

Commit db741ac

Browse files
committed
Auto merge of #9186 - weihanglo:issue-9054, r=alexcrichton
Respect Cargo.toml `[package.exclude]` even not in a git repo. May resolves #9054 This bug (or feature?) has been lingering for a while. #7680 fixed the `cargo package` part but `cargo vendor` is still affected by the heuristic rule of ignoring dotfiles. ~~I propose to drop the rule and include dotfiles by default even if the package is not under git-controlled~~. See below. ## Updated: Changes Summary `cargo vendor` vendors dependencies without git-controlled but `cargo package` often runs under a VCS like git. [These lines](https://github.com/rust-lang/cargo/blob/1ca930b/src/cargo/sources/path.rs#L161-L168) are where they diverges: `fn list_files_walk_except_dot_files_and_dirs` builds [its own ignore instance], which cannot merge with other filter rules from `[package.exclude]`. This causes some patterns to not work as expected, such as re-including file after ignoring dotfiles `[.*, !negated_file]`. To make re-include (negate) rule works, this patch adds the excluding dotfiles rule directly into the `package.exclude` ignore instance if **_no include option nor git repo exists_**. Other old behaviors should not change in this patch. [its own ignore instance]: https://github.com/rust-lang/cargo/blob/1ca930b6/src/cargo/sources/path.rs#L364-L366
2 parents 468314f + 153146e commit db741ac

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

src/cargo/sources/path.rs

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,17 @@ impl<'cfg> PathSource<'cfg> {
108108
fn _list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
109109
let root = pkg.root();
110110
let no_include_option = pkg.manifest().include().is_empty();
111+
let git_repo = if no_include_option {
112+
self.discover_git_repo(root)?
113+
} else {
114+
None
115+
};
111116

112117
let mut exclude_builder = GitignoreBuilder::new(root);
118+
if no_include_option && git_repo.is_none() {
119+
// no include option and not git repo discovered (see rust-lang/cargo#7183).
120+
exclude_builder.add_line(None, ".*")?;
121+
}
113122
for rule in pkg.manifest().exclude() {
114123
exclude_builder.add_line(None, rule)?;
115124
}
@@ -161,23 +170,16 @@ impl<'cfg> PathSource<'cfg> {
161170

162171
// Attempt Git-prepopulate only if no `include` (see rust-lang/cargo#4135).
163172
if no_include_option {
164-
if let Some(result) = self.discover_git_and_list_files(pkg, root, &mut filter)? {
165-
return Ok(result);
173+
if let Some(repo) = git_repo {
174+
return self.list_files_git(pkg, &repo, &mut filter);
166175
}
167-
// no include option and not git repo discovered (see rust-lang/cargo#7183).
168-
return self.list_files_walk_except_dot_files_and_dirs(pkg, &mut filter);
169176
}
170177
self.list_files_walk(pkg, &mut filter)
171178
}
172179

173-
// Returns `Some(_)` if found sibling `Cargo.toml` and `.git` directory;
174-
// otherwise, caller should fall back on full file list.
175-
fn discover_git_and_list_files(
176-
&self,
177-
pkg: &Package,
178-
root: &Path,
179-
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
180-
) -> CargoResult<Option<Vec<PathBuf>>> {
180+
/// Returns `Some(git2::Repository)` if found sibling `Cargo.toml` and `.git`
181+
/// directory; otherwise, caller should fall back on full file list.
182+
fn discover_git_repo(&self, root: &Path) -> CargoResult<Option<git2::Repository>> {
181183
let repo = match git2::Repository::discover(root) {
182184
Ok(repo) => repo,
183185
Err(e) => {
@@ -212,7 +214,7 @@ impl<'cfg> PathSource<'cfg> {
212214
};
213215
let manifest_path = repo_relative_path.join("Cargo.toml");
214216
if index.get_path(&manifest_path, 0).is_some() {
215-
return Ok(Some(self.list_files_git(pkg, &repo, filter)?));
217+
return Ok(Some(repo));
216218
}
217219
// Package Cargo.toml is not in git, don't use git to guide our selection.
218220
Ok(None)
@@ -356,27 +358,6 @@ impl<'cfg> PathSource<'cfg> {
356358
}
357359
}
358360

359-
fn list_files_walk_except_dot_files_and_dirs(
360-
&self,
361-
pkg: &Package,
362-
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
363-
) -> CargoResult<Vec<PathBuf>> {
364-
let root = pkg.root();
365-
let mut exclude_dot_files_dir_builder = GitignoreBuilder::new(root);
366-
exclude_dot_files_dir_builder.add_line(None, ".*")?;
367-
let ignore_dot_files_and_dirs = exclude_dot_files_dir_builder.build()?;
368-
369-
let mut filter_ignore_dot_files_and_dirs =
370-
|path: &Path, is_dir: bool| -> CargoResult<bool> {
371-
let relative_path = path.strip_prefix(root)?;
372-
match ignore_dot_files_and_dirs.matched_path_or_any_parents(relative_path, is_dir) {
373-
Match::Ignore(_) => Ok(false),
374-
_ => filter(path, is_dir),
375-
}
376-
};
377-
self.list_files_walk(pkg, &mut filter_ignore_dot_files_and_dirs)
378-
}
379-
380361
fn list_files_walk(
381362
&self,
382363
pkg: &Package,

tests/testsuite/vendor.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,50 @@ fn add_vendor_config(p: &Project) {
5050
);
5151
}
5252

53+
#[cargo_test]
54+
fn package_exclude() {
55+
let p = project()
56+
.file(
57+
"Cargo.toml",
58+
r#"
59+
[package]
60+
name = "foo"
61+
version = "0.1.0"
62+
63+
[dependencies]
64+
bar = "0.1.0"
65+
"#,
66+
)
67+
.file("src/lib.rs", "")
68+
.build();
69+
70+
Package::new("bar", "0.1.0")
71+
.file(
72+
"Cargo.toml",
73+
r#"
74+
[package]
75+
name = "bar"
76+
version = "0.1.0"
77+
exclude = [".*", "!.include", "!.dotdir/include"]
78+
"#,
79+
)
80+
.file("src/lib.rs", "")
81+
.file(".exclude", "")
82+
.file(".include", "")
83+
.file(".dotdir/exclude", "")
84+
.file(".dotdir/include", "")
85+
.publish();
86+
87+
p.cargo("vendor --respect-source-config").run();
88+
let csum = dbg!(p.read_file("vendor/bar/.cargo-checksum.json"));
89+
assert!(csum.contains(".include"));
90+
assert!(!csum.contains(".exclude"));
91+
assert!(!csum.contains(".dotdir/exclude"));
92+
// Gitignore doesn't re-include a file in an excluded parent directory,
93+
// even if negating it explicitly.
94+
assert!(!csum.contains(".dotdir/include"));
95+
}
96+
5397
#[cargo_test]
5498
fn two_versions() {
5599
let p = project()

0 commit comments

Comments
 (0)