diff --git a/git/diff.py b/git/diff.py index 0fc30b9eb..a9dc4b572 100644 --- a/git/diff.py +++ b/git/diff.py @@ -108,6 +108,7 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs): args.append("-p") else: args.append("--raw") + args.append("-z") # in any way, assure we don't see colored output, # fixes https://github.com/gitpython-developers/GitPython/issues/172 @@ -478,55 +479,55 @@ def _index_from_raw_format(cls, repo, proc): index = DiffIndex() - def handle_diff_line(line): - line = line.decode(defenc) - if not line.startswith(":"): - return - - meta, _, path = line[1:].partition('\t') - old_mode, new_mode, a_blob_id, b_blob_id, _change_type = meta.split(None, 4) - # Change type can be R100 - # R: status letter - # 100: score (in case of copy and rename) - change_type = _change_type[0] - score_str = ''.join(_change_type[1:]) - score = int(score_str) if score_str.isdigit() else None - path = path.strip() - a_path = path.encode(defenc) - b_path = path.encode(defenc) - deleted_file = False - new_file = False - copied_file = False - rename_from = None - rename_to = None - - # NOTE: We cannot conclude from the existence of a blob to change type - # as diffs with the working do not have blobs yet - if change_type == 'D': - b_blob_id = None - deleted_file = True - elif change_type == 'A': - a_blob_id = None - new_file = True - elif change_type == 'C': - copied_file = True - a_path, b_path = path.split('\t', 1) - a_path = a_path.encode(defenc) - b_path = b_path.encode(defenc) - elif change_type == 'R': - a_path, b_path = path.split('\t', 1) - a_path = a_path.encode(defenc) - b_path = b_path.encode(defenc) - rename_from, rename_to = a_path, b_path - elif change_type == 'T': - # Nothing to do - pass - # END add/remove handling - - diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode, - new_file, deleted_file, copied_file, rename_from, rename_to, - '', change_type, score) - index.append(diff) + def handle_diff_line(lines): + lines = lines.decode(defenc) + + for line in lines.split(':')[1:]: + meta, _, path = line.partition('\x00') + path = path.rstrip('\x00') + old_mode, new_mode, a_blob_id, b_blob_id, _change_type = meta.split(None, 4) + # Change type can be R100 + # R: status letter + # 100: score (in case of copy and rename) + change_type = _change_type[0] + score_str = ''.join(_change_type[1:]) + score = int(score_str) if score_str.isdigit() else None + path = path.strip() + a_path = path.encode(defenc) + b_path = path.encode(defenc) + deleted_file = False + new_file = False + copied_file = False + rename_from = None + rename_to = None + + # NOTE: We cannot conclude from the existence of a blob to change type + # as diffs with the working do not have blobs yet + if change_type == 'D': + b_blob_id = None + deleted_file = True + elif change_type == 'A': + a_blob_id = None + new_file = True + elif change_type == 'C': + copied_file = True + a_path, b_path = path.split('\x00', 1) + a_path = a_path.encode(defenc) + b_path = b_path.encode(defenc) + elif change_type == 'R': + a_path, b_path = path.split('\x00', 1) + a_path = a_path.encode(defenc) + b_path = b_path.encode(defenc) + rename_from, rename_to = a_path, b_path + elif change_type == 'T': + # Nothing to do + pass + # END add/remove handling + + diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode, + new_file, deleted_file, copied_file, rename_from, rename_to, + '', change_type, score) + index.append(diff) handle_process_output(proc, handle_diff_line, None, finalize_process, decode_streams=False) diff --git a/test/test_diff.py b/test/test_diff.py index 378a58de5..c6c9b67a0 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -26,6 +26,10 @@ import os.path as osp +def to_raw(input): + return input.replace(b'\t', b'\x00') + + @ddt.ddt class TestDiff(TestBase): @@ -112,7 +116,7 @@ def test_diff_with_rename(self): self.assertEqual(diff.raw_rename_to, b'm\xc3\xbcller') assert isinstance(str(diff), str) - output = StringProcessAdapter(fixture('diff_rename_raw')) + output = StringProcessAdapter(to_raw(fixture('diff_rename_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] @@ -137,7 +141,7 @@ def test_diff_with_copied_file(self): self.assertTrue(diff.b_path, 'test2.txt') assert isinstance(str(diff), str) - output = StringProcessAdapter(fixture('diff_copied_mode_raw')) + output = StringProcessAdapter(to_raw(fixture('diff_copied_mode_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] @@ -165,7 +169,7 @@ def test_diff_with_change_in_type(self): self.assertIsNotNone(diff.new_file) assert isinstance(str(diff), str) - output = StringProcessAdapter(fixture('diff_change_in_type_raw')) + output = StringProcessAdapter(to_raw(fixture('diff_change_in_type_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] @@ -175,7 +179,7 @@ def test_diff_with_change_in_type(self): self.assertEqual(len(list(diffs.iter_change_type('T'))), 1) def test_diff_of_modified_files_not_added_to_the_index(self): - output = StringProcessAdapter(fixture('diff_abbrev-40_full-index_M_raw_no-color')) + output = StringProcessAdapter(to_raw(fixture('diff_abbrev-40_full-index_M_raw_no-color'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1, 'one modification')
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: