Skip to content

Commit 1232ad3

Browse files
committed
Don't require Cargo.toml to be in root of a git repo for path source walking.
1 parent 51e0c71 commit 1232ad3

File tree

3 files changed

+94
-31
lines changed

3 files changed

+94
-31
lines changed

src/cargo/sources/path.rs

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -177,40 +177,49 @@ impl<'cfg> PathSource<'cfg> {
177177
root: &Path,
178178
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
179179
) -> Option<CargoResult<Vec<PathBuf>>> {
180-
// If this package is in a Git repository, then we really do want to
181-
// query the Git repository as it takes into account items such as
182-
// `.gitignore`. We're not quite sure where the Git repository is,
183-
// however, so we do a bit of a probe.
184-
//
185-
// We walk this package's path upwards and look for a sibling
186-
// `Cargo.toml` and `.git` directory. If we find one then we assume that
187-
// we're part of that repository.
188-
let mut cur = root;
189-
loop {
190-
if cur.join("Cargo.toml").is_file() {
191-
// If we find a Git repository next to this `Cargo.toml`, we still
192-
// check to see if we are indeed part of the index. If not, then
193-
// this is likely an unrelated Git repo, so keep going.
194-
if let Ok(repo) = git2::Repository::open(cur) {
195-
let index = match repo.index() {
196-
Ok(index) => index,
197-
Err(err) => return Some(Err(err.into())),
198-
};
199-
let path = root.strip_prefix(cur).unwrap().join("Cargo.toml");
200-
if index.get_path(&path, 0).is_some() {
201-
return Some(self.list_files_git(pkg, &repo, filter));
202-
}
203-
}
180+
let repo = match git2::Repository::discover(root) {
181+
Ok(repo) => repo,
182+
Err(_) => return None,
183+
};
184+
let index = match repo.index() {
185+
Ok(index) => index,
186+
Err(err) => {
187+
let e = anyhow::Error::new(err).context(format!(
188+
"failed to open git index at {}",
189+
repo.path().display()
190+
));
191+
return Some(Err(e));
204192
}
205-
// Don't cross submodule boundaries.
206-
if cur.join(".git").is_dir() {
207-
break;
193+
};
194+
let repo_root = match repo.workdir() {
195+
Some(dir) => dir,
196+
None => {
197+
return Some(Err(anyhow::format_err!(
198+
"did not expect repo at {} to be bare",
199+
repo.path().display()
200+
)))
208201
}
209-
match cur.parent() {
210-
Some(parent) => cur = parent,
211-
None => break,
202+
};
203+
let repo_relative_path = match root.strip_prefix(repo_root) {
204+
Ok(path) => path,
205+
Err(err) => {
206+
let e = anyhow::Error::new(err).context(format!(
207+
"expected git repo {} to be parent of package {}",
208+
repo.path().display(),
209+
root.display()
210+
));
211+
return Some(Err(e));
212212
}
213+
};
214+
// Git requires forward-slashes.
215+
let repo_safe_path = repo_relative_path
216+
.join("Cargo.toml")
217+
.to_string_lossy()
218+
.replace('\\', "/");
219+
if index.get_path(Path::new(&repo_safe_path), 0).is_some() {
220+
return Some(self.list_files_git(pkg, &repo, filter));
213221
}
222+
// Package Cargo.toml is not in git, don't use git to guide our selection.
214223
None
215224
}
216225

tests/testsuite/package.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use cargo_test_support::{
66
basic_manifest, cargo_process, git, path2url, paths, project, publish::validate_crate_contents,
77
registry, symlink_supported, t,
88
};
9-
use std::fs::{read_to_string, File};
9+
use std::fs::{self, read_to_string, File};
1010
use std::path::Path;
1111

1212
#[cargo_test]
@@ -1691,3 +1691,56 @@ fn package_restricted_windows() {
16911691
)
16921692
.run();
16931693
}
1694+
1695+
#[cargo_test]
1696+
fn finds_git_in_parent() {
1697+
// Test where `Cargo.toml` is not in the root of the git repo.
1698+
let repo_path = paths::root().join("repo");
1699+
fs::create_dir(&repo_path).unwrap();
1700+
let p = project()
1701+
.at("repo/foo")
1702+
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
1703+
.file("src/lib.rs", "")
1704+
.build();
1705+
let repo = git::init(&repo_path);
1706+
git::add(&repo);
1707+
git::commit(&repo);
1708+
p.change_file("ignoreme", "");
1709+
p.change_file("ignoreme2", "");
1710+
p.cargo("package --list --allow-dirty")
1711+
.with_stdout(
1712+
"\
1713+
Cargo.toml
1714+
Cargo.toml.orig
1715+
ignoreme
1716+
ignoreme2
1717+
src/lib.rs
1718+
",
1719+
)
1720+
.run();
1721+
1722+
p.change_file(".gitignore", "ignoreme");
1723+
p.cargo("package --list --allow-dirty")
1724+
.with_stdout(
1725+
"\
1726+
.gitignore
1727+
Cargo.toml
1728+
Cargo.toml.orig
1729+
ignoreme2
1730+
src/lib.rs
1731+
",
1732+
)
1733+
.run();
1734+
1735+
fs::write(repo_path.join(".gitignore"), "ignoreme2").unwrap();
1736+
p.cargo("package --list --allow-dirty")
1737+
.with_stdout(
1738+
"\
1739+
.gitignore
1740+
Cargo.toml
1741+
Cargo.toml.orig
1742+
src/lib.rs
1743+
",
1744+
)
1745+
.run();
1746+
}

tests/testsuite/publish_lockfile.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ fn ignore_lockfile_inner() {
469469
"\
470470
[PACKAGING] bar v0.0.1 ([..])
471471
[ARCHIVING] .cargo_vcs_info.json
472+
[ARCHIVING] .gitignore
472473
[ARCHIVING] Cargo.lock
473474
[ARCHIVING] Cargo.toml
474475
[ARCHIVING] Cargo.toml.orig

0 commit comments

Comments
 (0)