diff --git a/git/diff.py b/git/diff.py index c1a5bd26f..b3f1b77bf 100644 --- a/git/diff.py +++ b/git/diff.py @@ -148,7 +148,8 @@ def diff( if not any(x in kwargs for x in ('find_renames', 'no_renames', 'M')): args.append("-M") - if create_patch: + name_only = 'name-only' in kwargs + if not name_only and create_patch: args.append("-p") else: args.append("--raw") @@ -170,6 +171,7 @@ def diff( elif other is NULL_TREE: args.insert(0, "-r") # recursive diff-tree args.insert(0, "--root") + Diff.is_first = True diff_cmd = self.repo.git.diff_tree elif other is not None: args.insert(0, "-r") # recursive diff-tree @@ -187,7 +189,14 @@ def diff( kwargs["as_process"] = True proc = diff_cmd(*self._process_diff_args(args), **kwargs) - diff_method = Diff._index_from_patch_format if create_patch else Diff._index_from_raw_format + cmdline = getattr(proc, 'args', '') # PY3+ only + + if '--name-only' in cmdline: + diff_method = Diff._index_from_name_only_format + elif create_patch: + diff_method = Diff._index_from_patch_format + else: + diff_method = Diff._index_from_raw_format index = diff_method(self.repo, proc) proc.wait() @@ -569,6 +578,38 @@ def _index_from_patch_format(cls, repo: "Repo", proc: Union["Popen", "Git.AutoIn return index + is_first = False + + @classmethod + def _index_from_name_only_format(cls, repo, proc): + """Create a new DiffIndex from the given text which must be in name only format + :param repo: is the repository we are operating on - it is required + :param stream: result of 'git diff' as a stream (supporting file protocol) + :return: git.DiffIndex """ + + index = DiffIndex() + + def handle_diff_line_name_only(lines): + lines = lines.decode(defenc) + + for line in lines.split('\x00'): + path = line.strip() + if len(path) == 0: + continue + if cls.is_first: + cls.is_first = False + continue + + a_path = path.encode(defenc) + b_path = path.encode(defenc) + index.append(Diff(repo, a_path, b_path, None, None, None, None, + False, False, None, None, None, + '', None, None)) + + handle_process_output(proc, handle_diff_line_name_only, None, finalize_process, decode_streams=False) + + return index + @staticmethod def _handle_diff_line(lines_bytes: bytes, repo: "Repo", index: DiffIndex) -> None: lines = lines_bytes.decode(defenc) diff --git a/test/test_diff.py b/test/test_diff.py index 504337744..c6c2ca36f 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -263,6 +263,36 @@ def test_diff_initial_commit(self): self.assertIsNotNone(diff_index[0].new_file) self.assertEqual(diff_index[0].diff, fixture("diff_initial")) + def test_diff_initial_commit_name_only(self): + initial_commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781') + + # Without creating a patch... + diff_index = initial_commit.diff(NULL_TREE, **{'name-only': True}) + self.assertEqual(diff_index[0].a_path, 'CHANGES') + self.assertEqual(diff_index[0].b_path, 'CHANGES') + + # ...and with creating a patch + diff_index = initial_commit.diff(NULL_TREE, create_patch=True, **{'name-only': True}) + self.assertEqual(diff_index[0].a_path, 'CHANGES') + self.assertEqual(diff_index[0].b_path, 'CHANGES') + + def test_diff_between_commit_name_only(self): + initial_commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781') + new_commit = self.rorepo.commit('1f66cfbbce58b4b552b041707a12d437cc5f400a') + + # Without creating a patch... + diff_index = initial_commit.diff(new_commit, **{'name-only': True}) + + files = {'lib/git_python/diff.py', 'lib/git_python/git.py', 'lib/git_python/repo.py', + 'test/git/test_repo.py', 'README'} + + for diff in diff_index: + self.assertTrue(diff.a_path in files) + self.assertTrue(diff.b_path in files) + files.remove(diff.b_path) + + self.assertTrue(len(files) == 0) + def test_diff_unsafe_paths(self): output = StringProcessAdapter(fixture("diff_patch_unsafe_paths")) res = Diff._index_from_patch_format(None, output)