Prechádzať zdrojové kódy

add walk_untracked kwarg to porcelain.status

keep old behaviour by having this default to True.
Status calls can be quite slow when large untracked
directories are present. Calling status with
walk_untracked=False will speed up the status() call
considerably in these cases. This is similar to
`git status` default. Using `walk_untracked=False`
behaves like `git status -uall`.
Daniele Trifirò 3 rokov pred
rodič
commit
3b5b9d34fb
2 zmenil súbory, kde vykonal 66 pridanie a 11 odobranie
  1. 27 8
      dulwich/porcelain.py
  2. 39 3
      dulwich/tests/test_porcelain.py

+ 27 - 8
dulwich/porcelain.py

@@ -1156,12 +1156,16 @@ def pull(
             _import_remote_refs(r.refs, remote_name, fetch_result.refs)
 
 
-def status(repo=".", ignored=False):
+def status(repo=".", ignored=False, walk_untracked=True):
     """Returns staged, unstaged, and untracked changes relative to the HEAD.
 
     Args:
       repo: Path to repository or repository object
       ignored: Whether to include ignored files in untracked
+      walk_untracked: Whether to walk untracked directories
+            When True, behaves like `git status -uall`.
+            When False, behaves like to `git status -unormal` (git default).
+
     Returns: GitStatus tuple,
         staged -  dict with lists of staged paths (diff index/HEAD)
         unstaged -  list of unstaged paths (diff index/working-tree)
@@ -1177,7 +1181,7 @@ def status(repo=".", ignored=False):
         unstaged_changes = list(get_unstaged_changes(index, r.path, filter_callback))
 
         untracked_paths = get_untracked_paths(
-            r.path, r.path, index, exclude_ignored=not ignored
+            r.path, r.path, index, exclude_ignored=not ignored, walk=walk_untracked
         )
         untracked_changes = list(untracked_paths)
 
@@ -1216,7 +1220,13 @@ def _walk_working_dir_paths(frompath, basepath, prune_dirnames=None):
             dirnames[:] = prune_dirnames(dirpath, dirnames)
 
 
-def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
+def get_untracked_paths(
+    frompath,
+    basepath,
+    index,
+    exclude_ignored=False,
+    walk=True,
+):
     """Get untracked paths.
 
     Args:
@@ -1224,6 +1234,7 @@ def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
       basepath: Path to compare to
       index: Index to check against
       exclude_ignored: Whether to exclude ignored paths
+      walk: Whether to walk untracked paths.
 
     Note: ignored directories will never be walked for performance reasons.
       If exclude_ignored is False, only the path to an ignored directory will
@@ -1233,6 +1244,7 @@ def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
         ignore_manager = IgnoreFilterManager.from_repo(r)
 
     ignored_dirs = []
+    untracked_dirs = []
 
     def prune_dirnames(dirpath, dirnames):
         for i in range(len(dirnames) - 1, -1, -1):
@@ -1244,6 +1256,15 @@ def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
                         os.path.join(os.path.relpath(path, frompath), "")
                     )
                 del dirnames[i]
+            elif not walk:
+                if not any(
+                    os.fsdecode(index_path).startswith(dirnames[i])
+                    for index_path, _, in index.items()
+                ):
+                    untracked_dirs.append(
+                        os.path.join(os.path.relpath(path, frompath)) + os.sep
+                    )
+                    del dirnames[i]
         return dirnames
 
     for ap, is_dir in _walk_working_dir_paths(
@@ -1252,15 +1273,13 @@ def get_untracked_paths(frompath, basepath, index, exclude_ignored=False):
         if not is_dir:
             ip = path_to_tree_path(basepath, ap)
             if ip not in index:
-                if (
-                    not exclude_ignored
-                    or not ignore_manager.is_ignored(
-                        os.path.relpath(ap, basepath)
-                    )
+                if not exclude_ignored or not ignore_manager.is_ignored(
+                    os.path.relpath(ap, basepath)
                 ):
                     yield os.path.relpath(ap, frompath)
 
     yield from ignored_dirs
+    yield from untracked_dirs
 
 
 def get_tree_changes(repo):

+ 39 - 3
dulwich/tests/test_porcelain.py

@@ -1842,13 +1842,18 @@ class StatusTests(PorcelainTestCase):
         mod_path = os.path.join(self.repo.path, "bar")
         add_path = os.path.join(self.repo.path, "baz")
         us_path = os.path.join(self.repo.path, "blye")
-        ut_path = os.path.join(self.repo.path, "blyat")
+        ut_path = os.path.join(self.repo.path, "untracked_file")
+        os.mkdir(os.path.join(self.repo.path, "untracked_dir"))
+        ut_subfile_path = os.path.join(self.repo.path, "untracked_dir/file")
+
         with open(del_path, "w") as f:
             f.write("origstuff")
         with open(mod_path, "w") as f:
             f.write("origstuff")
         with open(us_path, "w") as f:
             f.write("origstuff")
+        with open(ut_subfile_path, "w") as f:
+            f.write("origstuff")
         porcelain.add(repo=self.repo.path, paths=[del_path, mod_path, us_path])
         porcelain.commit(
             repo=self.repo.path,
@@ -1874,7 +1879,13 @@ class StatusTests(PorcelainTestCase):
             results.staged,
         )
         self.assertListEqual(results.unstaged, [b"blye"])
-        self.assertListEqual(results.untracked, ["blyat"])
+        self.assertListEqual(
+            results.untracked, ["untracked_file", "untracked_dir/file"]
+        )
+        results_no_walk = porcelain.status(self.repo.path, walk_untracked=False)
+        self.assertListEqual(
+            results_no_walk.untracked, ["untracked_file", "untracked_dir/"]
+        )
 
     def test_status_crlf_mismatch(self):
         # First make a commit as if the file has been added on a Linux system
@@ -2167,7 +2178,32 @@ class StatusTests(PorcelainTestCase):
                     self.repo.open_index(),
                     exclude_ignored=True,
                 )
-            )
+            ),
+        )
+
+    def test_get_untracked_paths_walk(self):
+        os.mkdir(os.path.join(self.repo.path, "dir"))
+        os.mkdir(os.path.join(self.repo.path, "dir/subdir"))
+        with open(os.path.join(self.repo.path, "dir", "file"), "w") as f:
+            f.write("foo")
+        with open(os.path.join(self.repo.path, "dir/subdir/subfile"), "w") as f:
+            f.write("foo")
+
+        self.assertEqual(
+            {"dir/"},
+            set(
+                porcelain.get_untracked_paths(
+                    self.repo.path, self.repo.path, self.repo.open_index(), walk=False
+                )
+            ),
+        )
+        self.assertEqual(
+            set(["dir/file", "dir/subdir/subfile"]),
+            set(
+                porcelain.get_untracked_paths(
+                    self.repo.path, self.repo.path, self.repo.open_index(), walk=True
+                )
+            ),
         )