052026e03a86 draft — mattseddon 8 months ago
Issue 793: Fix failure in get_untracked_paths when the repository contains symlinks (#830)
3 files changed, 54 insertions(+), 13 deletions(-)

M NEWS
M dulwich/porcelain.py
M dulwich/tests/test_porcelain.py
M NEWS +5 -0
@@ 1,3 1,8 @@ 
+0.20.16	2021-01-16
+
+ * Add flag to only attempt to fetch ignored untracked files when specifically requested.
+   (Matt Seddon)
+
 0.20.15	2020-12-23
 
  * Add some functions for parsing and writing bundles.

          
M dulwich/porcelain.py +26 -9
@@ 1117,14 1117,11 @@ def status(repo=".", ignored=False):
         unstaged_changes = list(
             get_unstaged_changes(index, r.path, filter_callback)
         )
-        ignore_manager = IgnoreFilterManager.from_repo(r)
-        untracked_paths = get_untracked_paths(r.path, r.path, index)
-        if ignored:
-            untracked_changes = list(untracked_paths)
-        else:
-            untracked_changes = [
-                    p for p in untracked_paths
-                    if not ignore_manager.is_ignored(p)]
+
+        untracked_paths = get_untracked_paths(r.path, r.path, index,
+                                              exclude_ignored=not ignored)
+        untracked_changes = list(untracked_paths)
+
         return GitStatus(tracked_changes, unstaged_changes, untracked_changes)
 
 

          
@@ 1154,21 1151,41 @@ def _walk_working_dir_paths(frompath, ba
             yield filepath, False
 
 
-def get_untracked_paths(frompath, basepath, index):
+def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
     """Get untracked paths.
 
     Args:
     ;param frompath: Path to walk
       basepath: Path to compare to
       index: Index to check against
+      exclude_ignored: Whether to exclude ignored paths
     """
+    ignore_manager = _get_ignore_manager(frompath, exclude_ignored)
+
     for ap, is_dir in _walk_working_dir_paths(frompath, basepath):
+        if (exclude_ignored and
+                ignore_manager.is_ignored(os.path.relpath(ap, frompath))):
+            continue
         if not is_dir:
             ip = path_to_tree_path(basepath, ap)
             if ip not in index:
                 yield os.path.relpath(ap, frompath)
 
 
+def _get_ignore_manager(frompath, exclude_ignored):
+    """Get a repo's IgnoreFilterManager from a path if required.
+
+    Args:
+    ;param frompath: The path of the repo
+      exclude_ignored: Whether to return the IgnoreFilterManager
+    """
+    if exclude_ignored:
+        with open_repo_closing(frompath) as r:
+            ignore_manager = IgnoreFilterManager.from_repo(r)
+
+        return ignore_manager
+
+
 def get_tree_changes(repo):
     """Return add/delete/modify changes to tree by comparing index to HEAD.
 

          
M dulwich/tests/test_porcelain.py +23 -4
@@ 1444,20 1444,39 @@ class StatusTests(PorcelainTestCase):
                              .untracked))
 
     def test_get_untracked_paths_nested(self):
+        with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
+            f.write('nested/\n')
         with open(os.path.join(self.repo.path, 'notignored'), 'w') as f:
             f.write('blah\n')
+
         subrepo = Repo.init(os.path.join(self.repo.path, 'nested'), mkdir=True)
-        with open(os.path.join(subrepo.path, 'another'), 'w') as f:
-            f.write('foo\n')
+        with open(os.path.join(subrepo.path, 'ignored'), 'w') as f:
+            f.write('bleep\n')
+        with open(os.path.join(subrepo.path, 'with'), 'w') as f:
+            f.write('bloop\n')
+        with open(os.path.join(subrepo.path, 'manager'), 'w') as f:
+            f.write('blop\n')
 
         self.assertEqual(
-            set(['notignored']),
+            set(['.gitignore', 'notignored']),
             set(porcelain.get_untracked_paths(self.repo.path, self.repo.path,
                                               self.repo.open_index())))
         self.assertEqual(
-            set(['another']),
+            set(['ignored', 'with', 'manager']),
             set(porcelain.get_untracked_paths(subrepo.path, subrepo.path,
                                               subrepo.open_index())))
+        self.assertEqual(
+            set([os.path.join('nested', 'ignored'),
+                os.path.join('nested', 'with'),
+                os.path.join('nested', 'manager')]),
+            set(porcelain.get_untracked_paths(self.repo.path, subrepo.path,
+                                              self.repo.open_index(),
+                                              exclude_ignored=False)))
+        self.assertEqual(
+            set([]),
+            set(porcelain.get_untracked_paths(self.repo.path, subrepo.path,
+                                              self.repo.open_index(),
+                                              exclude_ignored=True)))
 
 
 # TODO(jelmer): Add test for dulwich.porcelain.daemon