commit 744fcc280f5a4e23f14598fb4630d1c76f33389b from: Jelmer Vernooij via: GitHub date: Mon May 16 16:49:36 2022 UTC Merge pull request #964 from dtrifiro/status-untracked-no porcelain: rename walk_untracked kwarg to untracked_file commit - 699fde009ad0ee86b1ef7b1a619548f2006cc356 commit + 744fcc280f5a4e23f14598fb4630d1c76f33389b blob - cb4bcad46b5bdbdaa150ea20aebef169cce2be60 blob + 1c1d8c4cb574c330838b5e118156315ec1e2ec44 --- dulwich/porcelain.py +++ dulwich/porcelain.py @@ -1156,15 +1156,19 @@ def pull( _import_remote_refs(r.refs, remote_name, fetch_result.refs) -def status(repo=".", ignored=False, walk_untracked=True): +def status(repo=".", ignored=False, untracked_files="all"): """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). + untracked_files: How to handle untracked files, defaults to "all": + "no": do not return untracked files + "all": include all files in untracked directories + Using `untracked_files="no"` can be faster than "all" when the worktreee + contains many untracked files/directories. + + Note: `untracked_files="normal" (`git`'s default) is not implemented. Returns: GitStatus tuple, staged - dict with lists of staged paths (diff index/HEAD) @@ -1181,7 +1185,11 @@ def status(repo=".", ignored=False, walk_untracked=Tru 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, walk=walk_untracked + r.path, + r.path, + index, + exclude_ignored=not ignored, + untracked_files=untracked_files, ) untracked_changes = list(untracked_paths) @@ -1221,11 +1229,7 @@ def _walk_working_dir_paths(frompath, basepath, prune_ def get_untracked_paths( - frompath, - basepath, - index, - exclude_ignored=False, - walk=True, + frompath, basepath, index, exclude_ignored=False, untracked_files="all" ): """Get untracked paths. @@ -1234,17 +1238,28 @@ def get_untracked_paths( basepath: Path to compare to index: Index to check against exclude_ignored: Whether to exclude ignored paths - walk: Whether to walk untracked paths. + untracked_files: How to handle untracked files: + - "no": return an empty list + - "all": return all files in untracked directories + - "normal": Not implemented Note: ignored directories will never be walked for performance reasons. If exclude_ignored is False, only the path to an ignored directory will be yielded, no files inside the directory will be returned """ + if untracked_files == "normal": + raise NotImplementedError("normal is not yet supported") + + if untracked_files not in ("no", "all"): + raise ValueError("untracked_files must be one of (no, all)") + + if untracked_files == "no": + return + with open_repo_closing(basepath) as r: ignore_manager = IgnoreFilterManager.from_repo(r) ignored_dirs = [] - untracked_dirs = [] def prune_dirnames(dirpath, dirnames): for i in range(len(dirnames) - 1, -1, -1): @@ -1256,15 +1271,6 @@ def get_untracked_paths( 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( @@ -1279,7 +1285,6 @@ def get_untracked_paths( yield os.path.relpath(ap, frompath) yield from ignored_dirs - yield from untracked_dirs def get_tree_changes(repo): blob - f70a6d678b3cc811898a69a67764ddb5039df79d blob + 201669ebff9d296ddd2ea89404ca02dfa5debb6e --- dulwich/tests/test_porcelain.py +++ dulwich/tests/test_porcelain.py @@ -1849,18 +1849,13 @@ 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, "untracked_file") - os.mkdir(os.path.join(self.repo.path, "untracked_dir")) - ut_subfile_path = os.path.join(self.repo.path, "untracked_dir/file") - + ut_path = os.path.join(self.repo.path, "blyat") 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, @@ -1886,16 +1881,13 @@ class StatusTests(PorcelainTestCase): results.staged, ) self.assertListEqual(results.unstaged, [b"blye"]) - self.assertListEqual( - results.untracked, [ - "untracked_file", os.path.join("untracked_dir", "file")] - ) - results_no_walk = porcelain.status(self.repo.path, walk_untracked=False) - self.assertListEqual( - results_no_walk.untracked, - ["untracked_file", "untracked_dir" + os.path.sep] - ) + results_no_untracked = porcelain.status(self.repo.path, untracked_files="no") + self.assertListEqual(results_no_untracked.untracked, []) + def test_status_wrong_untracked_files_value(self): + with self.assertRaises(ValueError): + porcelain.status(self.repo.path, untracked_files="antani") + def test_status_crlf_mismatch(self): # First make a commit as if the file has been added on a Linux system # or with core.autocrlf=True @@ -2187,35 +2179,25 @@ 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" + os.path.sep}, - set( + def test_get_untracked_paths_invalid_untracked_files(self): + with self.assertRaises(ValueError): + list( porcelain.get_untracked_paths( - self.repo.path, self.repo.path, self.repo.open_index(), walk=False - ) - ), - ) - self.assertEqual( - set([os.path.join("dir", "file"), - os.path.join("dir", "subdir", "subfile")]), - set( - porcelain.get_untracked_paths( - self.repo.path, self.repo.path, self.repo.open_index(), walk=True + self.repo.path, + self.repo.path, + self.repo.open_index(), + untracked_files="invalid_value", ) - ), - ) + ) + def test_get_untracked_paths_normal(self): + with self.assertRaises(NotImplementedError): + _, _, _ = porcelain.status( + repo=self.repo.path, untracked_files="normal" + ) # TODO(jelmer): Add test for dulwich.porcelain.daemon