History

fix: allow traversals to start from a symlink that points to a directory

Now symlinked repositories can be traversed as well.

Sebastian Thiel committed 2 years ago

Commit e7e91cf1 parent b05af9f3 files · +48 −5Browse files

Unified · Split

3 files
  1. gix-dir/src/walk/function.rs +13 −5
  2. gix-dir/tests/fixtures/many-symlinks.sh +2 −0
  3. gix-dir/tests/walk/mod.rs +33 −0
gix-dir/src/walk/function.rs+13−5
@@ -69,9 +69,16 @@ pub fn walk( · 68 unmodified lines
69 )?;69 )?;
70 if !can_recurse(70 if !can_recurse(
71 buf.as_bstr(),71 buf.as_bstr(),
72 root_info,72 if root == worktree_root && root_info.disk_kind == Some(entry::Kind::Symlink) && current.is_dir() {
73 classify::Outcome {
74 disk_kind: Some(entry::Kind::Directory),
75 ..root_info
76 }
77 } else {
78 root_info
79 },
73 options.for_deletion,80 options.for_deletion,
74 worktree_root_is_repository, /* is root */81 worktree_root_is_repository,
75 delegate,82 delegate,
76 ) {83 ) {
77 if buf.is_empty() && !root_info.disk_kind.map_or(false, |kind| kind.is_dir()) {84 if buf.is_empty() && !root_info.disk_kind.map_or(false, |kind| kind.is_dir()) {
@@ -147,16 +154,17 @@ pub(super) fn can_recurse( · 69 unmodified lines
147 rela_path: &BStr,154 rela_path: &BStr,
148 info: classify::Outcome,155 info: classify::Outcome,
149 for_deletion: Option<ForDeletionMode>,156 for_deletion: Option<ForDeletionMode>,
150 is_root: bool,157 worktree_root_is_repository: bool,
151 delegate: &mut dyn Delegate,158 delegate: &mut dyn Delegate,
152) -> bool {159) -> bool {
153 if info.disk_kind.map_or(true, |k| !k.is_dir()) {160 let is_dir = info.disk_kind.map_or(false, |k| k.is_dir());
161 if !is_dir {
154 return false;162 return false;
155 }163 }
156 delegate.can_recurse(164 delegate.can_recurse(
157 EntryRef::from_outcome(Cow::Borrowed(rela_path), info),165 EntryRef::from_outcome(Cow::Borrowed(rela_path), info),
158 for_deletion,166 for_deletion,
159 is_root,167 worktree_root_is_repository,
160 )168 )
161}169}
162 170
gix-dir/tests/fixtures/many-symlinks.sh+2−0
@@ -41,3 +41,5 @@ EOF · 40 unmodified lines
41 ln -s src/file file141 ln -s src/file file1
42 ln -s src/file file242 ln -s src/file file2
43)43)
44
45ln -s excluded-symlinks-to-dir worktree-root-is-symlink
gix-dir/tests/walk/mod.rs+33−0
@@ -118,6 +118,39 @@ fn root_may_not_lead_through_symlinks() -> crate::Result { · 117 unmodified lines
118 Ok(())118 Ok(())
119}119}
120 120
121#[test]
122#[cfg_attr(windows, ignore = "symlinks the way they are organized don't yet work on windows")]
123fn root_may_be_a_symlink_if_it_is_the_worktree() -> crate::Result {
124 let root = fixture_in("many-symlinks", "worktree-root-is-symlink");
125 let ((_out, _root), entries) = collect(&root, None, |keep, ctx| {
126 walk(
127 &root,
128 ctx,
129 gix_dir::walk::Options {
130 emit_ignored: Some(Matching),
131 symlinks_to_directories_are_ignored_like_directories: true,
132 ..options()
133 },
134 keep,
135 )
136 });
137
138 assert_eq!(
139 entries,
140 &[
141 entry("file1", Ignored(Expendable), Symlink),
142 entry("file2", Untracked, Symlink),
143 entry("ignored", Ignored(Expendable), Directory),
144 entry("ignored-must-be-dir", Ignored(Expendable), Directory),
145 entry("src/file", Untracked, File),
146 entry("src1", Ignored(Expendable), Symlink),
147 entry("src2", Ignored(Expendable), Symlink), /* marked as src2/ in .gitignore */
148 ],
149 "it traversed the directory normally - without this capability, symlinked repos can't be traversed"
150 );
151 Ok(())
152}
153
121#[test]154#[test]
122fn empty_root() -> crate::Result {155fn empty_root() -> crate::Result {
123 let root = fixture("empty");156 let root = fixture("empty");