Skip to content

Commit 7f2f3ff

Browse files
committed
fix: assure worktree-roots aren't pruned with pathspecs that are never meant for them.
Previously, when pathspecs were defined, the classification of the worktree-root would also be using them. This means that depending on the pathspec, worktree-roots would be pruned, which in turn makes it impossible to recurse into them. Now pathspecs are disabled when classifying the worktree-root directory.
1 parent fe24c89 commit 7f2f3ff

File tree

3 files changed

+25
-18
lines changed

3 files changed

+25
-18
lines changed

gix-dir/src/walk/classify.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ pub fn root(
2121
let mut path_buf = worktree_root.to_owned();
2222
// These initial values kick in if worktree_relative_root.is_empty();
2323
let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok();
24-
let mut out = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx)?;
24+
let pathspec_orig = std::mem::replace(
25+
ctx.pathspec,
26+
gix_pathspec::Search::from_specs(None, None, "".as_ref()).expect("empty is valid"),
27+
);
28+
let res = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx);
29+
*ctx.pathspec = pathspec_orig;
30+
let mut out = res?;
2531
let worktree_root_is_repository = out
2632
.disk_kind
2733
.map_or(false, |kind| matches!(kind, entry::Kind::Repository));
@@ -53,6 +59,9 @@ pub fn root(
5359
}
5460
last_length = Some(buf.len());
5561
}
62+
if out.pathspec_match.is_none() {
63+
out.pathspec_match = Some(PathspecMatch::Always);
64+
}
5665
Ok((out, worktree_root_is_repository))
5766
}
5867
/// The product of [`path()`] calls.

gix-dir/src/walk/function.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ pub fn walk(
6767
options,
6868
&mut ctx,
6969
)?;
70-
if !can_recurse(
70+
71+
let can_recurse = can_recurse(
7172
buf.as_bstr(),
7273
if root == worktree_root && root_info.disk_kind == Some(entry::Kind::Symlink) && current.is_dir() {
7374
classify::Outcome {
@@ -80,7 +81,8 @@ pub fn walk(
8081
options.for_deletion,
8182
worktree_root_is_repository,
8283
delegate,
83-
) {
84+
);
85+
if !can_recurse {
8486
if buf.is_empty() && !root_info.disk_kind.map_or(false, |kind| kind.is_dir()) {
8587
return Err(Error::WorktreeRootIsFile { root: root.to_owned() });
8688
}

gix-dir/tests/walk/mod.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,7 +2732,7 @@ fn worktree_root_can_be_symlink() -> crate::Result {
27322732
#[test]
27332733
fn root_may_not_go_through_dot_git() -> crate::Result {
27342734
let root = fixture("with-nested-dot-git");
2735-
for (dir, expected_pathspec) in [("", Some(Verbatim)), ("subdir", None)] {
2735+
for (dir, expected_pathspec) in [("", Verbatim), ("subdir", Always)] {
27362736
let troot = root.join("dir").join(".git").join(dir);
27372737
let ((out, _root), entries) = collect(&root, Some(&troot), |keep, ctx| {
27382738
walk(&root, ctx, options_emit_all(), keep)
@@ -2747,12 +2747,10 @@ fn root_may_not_go_through_dot_git() -> crate::Result {
27472747
);
27482748
assert_eq!(
27492749
entries,
2750-
[{
2751-
let mut e = entry("dir/.git", Pruned, Directory).with_property(DotGit);
2752-
e.0.pathspec_match = expected_pathspec;
2753-
e
2754-
}],
2755-
"no traversal happened as root passes though .git"
2750+
[entry("dir/.git", Pruned, Directory)
2751+
.with_property(DotGit)
2752+
.with_match(expected_pathspec)],
2753+
"{dir}: no traversal happened as root passes though .git"
27562754
);
27572755
}
27582756
Ok(())
@@ -3167,7 +3165,7 @@ fn root_can_be_pruned_early_with_pathspec() -> crate::Result {
31673165

31683166
assert_eq!(
31693167
entries,
3170-
[entry_nomatch("dir", Pruned, Directory)],
3168+
[entry("dir", Pruned, Directory)],
31713169
"the pathspec didn't match the root, early abort"
31723170
);
31733171
Ok(())
@@ -3929,7 +3927,7 @@ fn untracked_and_ignored_collapse_mix() {
39293927
#[test]
39303928
fn root_cannot_pass_through_case_altered_capital_dot_git_if_case_insensitive() -> crate::Result {
39313929
let root = fixture("with-nested-capitalized-dot-git");
3932-
for (dir, expected_pathspec) in [("", Some(Verbatim)), ("subdir", None)] {
3930+
for (dir, expected_pathspec) in [("", Verbatim), ("subdir", Always)] {
39333931
let troot = root.join("dir").join(".GIT").join(dir);
39343932
let ((out, _root), entries) = collect(&root, Some(&troot), |keep, ctx| {
39353933
walk(
@@ -3952,12 +3950,10 @@ fn root_cannot_pass_through_case_altered_capital_dot_git_if_case_insensitive() -
39523950
);
39533951
assert_eq!(
39543952
entries,
3955-
[{
3956-
let mut e = entry("dir/.GIT", Pruned, Directory).with_property(DotGit);
3957-
e.0.pathspec_match = expected_pathspec;
3958-
e
3959-
}],
3960-
"no traversal happened as root passes though .git, it compares in a case-insensitive fashion"
3953+
[entry("dir/.GIT", Pruned, Directory)
3954+
.with_property(DotGit)
3955+
.with_match(expected_pathspec)],
3956+
"{dir}: no traversal happened as root passes though .git, it compares in a case-insensitive fashion"
39613957
);
39623958
}
39633959

0 commit comments

Comments
 (0)