Skip to content

Commit b747054

Browse files
committed
Auto merge of #9645 - ehuss:git-package-wt_deleted, r=alexcrichton
Handle git deleted files with dirty worktree. When listing git files for things like `cargo package`, it was including unstaged deleted files. This is because the file is still in the index, so it was included in the list. `cargo package --allow-dirty` would then fail with a confusing "file not found" error. This fixes it by keeping a set of deleted files, and skipping those. This allows `cargo package --allow-dirty` to work. Closes #9580
2 parents b0e2cc5 + 97a1350 commit b747054

File tree

2 files changed

+60
-8
lines changed

2 files changed

+60
-8
lines changed

src/cargo/sources/path.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashSet;
12
use std::fmt::{self, Debug, Formatter};
23
use std::fs;
34
use std::path::{Path, PathBuf};
@@ -261,19 +262,36 @@ impl<'cfg> PathSource<'cfg> {
261262
opts.pathspec(suffix);
262263
}
263264
let statuses = repo.statuses(Some(&mut opts))?;
264-
let untracked = statuses.iter().filter_map(|entry| match entry.status() {
265-
// Don't include Cargo.lock if it is untracked. Packaging will
266-
// generate a new one as needed.
267-
git2::Status::WT_NEW if entry.path() != Some("Cargo.lock") => {
268-
Some((join(root, entry.path_bytes()), None))
269-
}
270-
_ => None,
271-
});
265+
let mut skip_paths = HashSet::new();
266+
let untracked: Vec<_> = statuses
267+
.iter()
268+
.filter_map(|entry| {
269+
match entry.status() {
270+
// Don't include Cargo.lock if it is untracked. Packaging will
271+
// generate a new one as needed.
272+
git2::Status::WT_NEW if entry.path() != Some("Cargo.lock") => {
273+
Some(Ok((join(root, entry.path_bytes()), None)))
274+
}
275+
git2::Status::WT_DELETED => {
276+
let path = match join(root, entry.path_bytes()) {
277+
Ok(p) => p,
278+
Err(e) => return Some(Err(e)),
279+
};
280+
skip_paths.insert(path);
281+
None
282+
}
283+
_ => None,
284+
}
285+
})
286+
.collect::<CargoResult<_>>()?;
272287

273288
let mut subpackages_found = Vec::new();
274289

275290
for (file_path, is_dir) in index_files.chain(untracked) {
276291
let file_path = file_path?;
292+
if skip_paths.contains(&file_path) {
293+
continue;
294+
}
277295

278296
// Filter out files blatantly outside this package. This is helped a
279297
// bit above via the `pathspec` function call, but we need to filter

tests/testsuite/package.rs

+34
Original file line numberDiff line numberDiff line change
@@ -2071,3 +2071,37 @@ fn package_with_resolver_and_metadata() {
20712071

20722072
p.cargo("package").run();
20732073
}
2074+
2075+
#[cargo_test]
2076+
fn deleted_git_working_tree() {
2077+
// When deleting a file, but not staged, cargo should ignore the file.
2078+
let (p, repo) = git::new_repo("foo", |p| {
2079+
p.file("src/lib.rs", "").file("src/main.rs", "fn main() {}")
2080+
});
2081+
p.root().join("src/lib.rs").rm_rf();
2082+
p.cargo("package --allow-dirty --list")
2083+
.with_stdout(
2084+
"\
2085+
Cargo.lock
2086+
Cargo.toml
2087+
Cargo.toml.orig
2088+
src/main.rs
2089+
",
2090+
)
2091+
.run();
2092+
p.cargo("package --allow-dirty").run();
2093+
let mut index = t!(repo.index());
2094+
t!(index.remove(Path::new("src/lib.rs"), 0));
2095+
t!(index.write());
2096+
p.cargo("package --allow-dirty --list")
2097+
.with_stdout(
2098+
"\
2099+
Cargo.lock
2100+
Cargo.toml
2101+
Cargo.toml.orig
2102+
src/main.rs
2103+
",
2104+
)
2105+
.run();
2106+
p.cargo("package --allow-dirty").run();
2107+
}

0 commit comments

Comments
 (0)