diff --git a/git/diff.py b/git/diff.py index d7221ac7d..c73001270 100644 --- a/git/diff.py +++ b/git/diff.py @@ -165,8 +165,9 @@ class DiffIndex(list): # A = Added # D = Deleted # R = Renamed - # M = modified - change_type = ("A", "D", "R", "M") + # M = Modified + # T = Changed in the type + change_type = ("A", "D", "R", "M", "T") def iter_change_type(self, change_type): """ @@ -179,7 +180,9 @@ def iter_change_type(self, change_type): * 'A' for added paths * 'D' for deleted paths * 'R' for renamed paths - * 'M' for paths with modified data""" + * 'M' for paths with modified data + * 'T' for changed in the type paths + """ if change_type not in self.change_type: raise ValueError("Invalid change type: %s" % change_type) @@ -251,11 +254,11 @@ class Diff(object): __slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "a_rawpath", "b_rawpath", "new_file", "deleted_file", "raw_rename_from", "raw_rename_to", - "diff", "change_type") + "diff", "change_type", "score") def __init__(self, repo, a_rawpath, b_rawpath, a_blob_id, b_blob_id, a_mode, b_mode, new_file, deleted_file, raw_rename_from, - raw_rename_to, diff, change_type): + raw_rename_to, diff, change_type, score): self.a_mode = a_mode self.b_mode = b_mode @@ -291,6 +294,7 @@ def __init__(self, repo, a_rawpath, b_rawpath, a_blob_id, b_blob_id, a_mode, self.diff = diff self.change_type = change_type + self.score = score def __eq__(self, other): for name in self.__slots__: @@ -445,7 +449,7 @@ def _index_from_patch_format(cls, repo, proc): new_file, deleted_file, rename_from, rename_to, - None, None)) + None, None, None)) previous_header = header # end for each header we parse @@ -470,7 +474,13 @@ def handle_diff_line(line): return meta, _, path = line[1:].partition('\t') - old_mode, new_mode, a_blob_id, b_blob_id, change_type = meta.split(None, 4) + 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) @@ -487,15 +497,19 @@ def handle_diff_line(line): elif change_type == 'A': a_blob_id = None new_file = True - elif change_type[0] == 'R': # parses RXXX, where XXX is a confidence value + 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, rename_from, rename_to, '', change_type) + new_file, deleted_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/git/test/fixtures/diff_change_in_type b/git/test/fixtures/diff_change_in_type new file mode 100644 index 000000000..e0ca73890 --- /dev/null +++ b/git/test/fixtures/diff_change_in_type @@ -0,0 +1,10 @@ +diff --git a/this b/this +deleted file mode 100644 +index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 +diff --git a/this b/this +new file mode 120000 +index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 +--- /dev/null ++++ b/this +@@ -0,0 +1 @@ ++that diff --git a/git/test/fixtures/diff_change_in_type_raw b/git/test/fixtures/diff_change_in_type_raw new file mode 100644 index 000000000..0793e1bbe --- /dev/null +++ b/git/test/fixtures/diff_change_in_type_raw @@ -0,0 +1 @@ +:100644 120000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 42061c01a1c70097d1e4579f29a5adf40abdec95 T this diff --git a/git/test/test_diff.py b/git/test/test_diff.py index d21dde624..e47b93317 100644 --- a/git/test/test_diff.py +++ b/git/test/test_diff.py @@ -108,8 +108,37 @@ def test_diff_with_rename(self): self.assertIsNotNone(diff.renamed) self.assertEqual(diff.rename_from, 'this') self.assertEqual(diff.rename_to, 'that') + self.assertEqual(diff.change_type, 'R') + self.assertEqual(diff.score, 100) self.assertEqual(len(list(diffs.iter_change_type('R'))), 1) + def test_diff_with_change_in_type(self): + output = StringProcessAdapter(fixture('diff_change_in_type')) + diffs = Diff._index_from_patch_format(self.rorepo, output) + self._assert_diff_format(diffs) + assert_equal(2, len(diffs)) + + diff = diffs[0] + self.assertIsNotNone(diff.deleted_file) + assert_equal(diff.a_path, 'this') + assert_equal(diff.b_path, 'this') + assert isinstance(str(diff), str) + + diff = diffs[1] + assert_equal(diff.a_path, None) + assert_equal(diff.b_path, 'this') + self.assertIsNotNone(diff.new_file) + assert isinstance(str(diff), str) + + output = StringProcessAdapter(fixture('diff_change_in_type_raw')) + diffs = Diff._index_from_raw_format(self.rorepo, output) + self.assertEqual(len(diffs), 1) + diff = diffs[0] + self.assertEqual(diff.rename_from, None) + self.assertEqual(diff.rename_to, None) + self.assertEqual(diff.change_type, 'T') + 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')) diffs = Diff._index_from_raw_format(self.rorepo, output)