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
6969 )?;
7070 if !can_recurse(
7171 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+ },
7380 options.for_deletion,
74− worktree_root_is_repository, /* is root */
81+ worktree_root_is_repository,
7582 delegate,
7683 ) {
7784 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
147154 rela_path: &BStr,
148155 info: classify::Outcome,
149156 for_deletion: Option<ForDeletionMode>,
150− is_root: bool,
157+ worktree_root_is_repository: bool,
151158 delegate: &mut dyn Delegate,
152159 ) -> 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 {
154162 return false;
155163 }
156164 delegate.can_recurse(
157165 EntryRef::from_outcome(Cow::Borrowed(rela_path), info),
158166 for_deletion,
159− is_root,
167+ worktree_root_is_repository,
160168 )
161169 }
162170
gix-dir/tests/fixtures/many-symlinks.sh+2−0
@@ -41,3 +41,5 @@ EOF · 40 unmodified lines
4141 ln -s src/file file1
4242 ln -s src/file file2
4343 )
44+
45+ln -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
118118 Ok(())
119119 }
120120
121+#[test]
122+#[cfg_attr(windows, ignore = "symlinks the way they are organized don't yet work on windows")]
123+fn 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+
121154 #[test]
122155 fn empty_root() -> crate::Result {
123156 let root = fixture("empty");