Skip to content

Commit 18caa61

Browse files
committed
Added test to verify binary diffs are working as well.
Related to #74
1 parent 0d9f149 commit 18caa61

File tree

4 files changed

+134
-11
lines changed

4 files changed

+134
-11
lines changed

git/diff.py

+20-10
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ class Diff(object):
183183

184184
# precompiled regex
185185
re_header = re.compile(r"""
186-
#^diff[ ]--git
187-
[ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
186+
^diff[ ]--git
187+
[ ](?:a/)?(?P<a_path>.+?)[ ](?:b/)?(?P<b_path>.+?)\n
188188
(?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
189189
^rename[ ]from[ ](?P<rename_from>\S+)\n
190190
^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
@@ -298,20 +298,30 @@ def _index_from_patch_format(cls, repo, stream):
298298
# for now, we have to bake the stream
299299
text = stream.read().decode(defenc)
300300
index = DiffIndex()
301-
302-
diff_header = cls.re_header.match
303-
for diff in ('\n' + text).split('\ndiff --git')[1:]:
304-
header = diff_header(diff)
305-
assert header is not None, "Failed to parse diff header from " % diff
306-
301+
previous_header = None
302+
for header in cls.re_header.finditer(text):
307303
a_path, b_path, similarity_index, rename_from, rename_to, \
308304
old_mode, new_mode, new_file_mode, deleted_file_mode, \
309305
a_blob_id, b_blob_id, b_mode = header.groups()
310306
new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode)
311307

308+
# Our only means to find the actual text is to see what has not been matched by our regex,
309+
# and then retro-actively assin it to our index
310+
if previous_header is not None:
311+
index[-1].diff = text[previous_header.end():header.start()]
312+
# end assign actual diff
313+
314+
# Make sure the mode is set if the path is set. Otherwise the resulting blob is invalid
315+
# We just use the one mode we should have parsed
312316
index.append(Diff(repo, a_path, b_path, a_blob_id, b_blob_id,
313-
old_mode or deleted_file_mode, new_mode or new_file_mode or b_mode,
314-
new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
317+
old_mode or deleted_file_mode or b_mode, new_mode or new_file_mode or b_mode,
318+
new_file, deleted_file, rename_from, rename_to, None))
319+
320+
previous_header = header
321+
# end for each header we parse
322+
if index:
323+
index[-1].diff = text[header.end():]
324+
# end assign last diff
315325

316326
return index
317327

git/objects/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ def _set_cache_(self, attr):
158158
if attr in IndexObject.__slots__:
159159
# they cannot be retrieved lateron ( not without searching for them )
160160
raise AttributeError(
161-
"path and mode attributes must have been set during %s object creation" % type(self).__name__)
161+
"Attribute '%s' unset: path and mode attributes must have been set during %s object creation"
162+
% (attr, type(self).__name__))
162163
else:
163164
super(IndexObject, self)._set_cache_(attr)
164165
# END hanlde slot attribute

git/test/fixtures/diff_index_patch

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
diff --git a/etc/sublime-text/git-python.sublime-project b/etc/sublime-text/git-python.sublime-project
2+
index 3dab9f6562ecb0408d9ece8dd63cc4461d280113..9c99a2cff7dc918fbbb61cd57d5d98750a1ef6c5 100644
3+
--- a/etc/sublime-text/git-python.sublime-project
4+
+++ b/etc/sublime-text/git-python.sublime-project
5+
@@ -23,7 +23,7 @@
6+
]
7+
},
8+
// GITDB
9+
- ////////
10+
+ // ////////
11+
{
12+
"follow_symlinks": true,
13+
"path": "../../git/ext/gitdb",
14+
@@ -42,8 +42,8 @@
15+
".tox",
16+
]
17+
},
18+
- // // SMMAP
19+
- // ////////
20+
+ // // // SMMAP
21+
+ // // ////////
22+
{
23+
"follow_symlinks": true,
24+
"path": "../../git/ext/gitdb/gitdb/ext/smmap",
25+
diff --git a/git/diff.py b/git/diff.py
26+
index 24e47bad9d79534d3cf474fec4f79e6fef122bb1..c1ad532e0217e293906bcfef43c523d6a8e21568 100644
27+
--- a/git/diff.py
28+
+++ b/git/diff.py
29+
@@ -302,13 +302,21 @@ class Diff(object):
30+
diff_header = cls.re_header.match
31+
for diff in ('\n' + text).split('\ndiff --git')[1:]:
32+
header = diff_header(diff)
33+
- assert header is not None, "Failed to parse diff header from " % diff
34+
+ assert header is not None, "Failed to parse diff header from '%s'" % diff
35+
36+
a_path, b_path, similarity_index, rename_from, rename_to, \
37+
old_mode, new_mode, new_file_mode, deleted_file_mode, \
38+
a_blob_id, b_blob_id, b_mode = header.groups()
39+
new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode)
40+
41+
+ # if a_path.startswith('a/'):
42+
+ # a_path = a_path[2:]
43+
+ # if b_path.startswith('b/'):
44+
+ # b_path = b_path[2:]
45+
+
46+
+ for item in (a_path, b_path, a_blob_id, b_blob_id, old_mode, deleted_file_mode, new_mode, new_file_mode, b_mode, new_file, deleted_file, rename_from, rename_to, diff[header.end():]):
47+
+ print( "####")
48+
+ print(item)
49+
index.append(Diff(repo, a_path, b_path, a_blob_id, b_blob_id,
50+
old_mode or deleted_file_mode, new_mode or new_file_mode or b_mode,
51+
new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
52+
diff --git a/git/ext/gitdb b/git/ext/gitdb
53+
index f2233fbf40f3f69309ce5cc714e99fcbdcd33ec3..a88a777df3909a61be97f1a7b1194dad6de25702 160000
54+
--- a/git/ext/gitdb
55+
+++ b/git/ext/gitdb
56+
@@ -1 +1 @@
57+
-Subproject commit f2233fbf40f3f69309ce5cc714e99fcbdcd33ec3
58+
+Subproject commit a88a777df3909a61be97f1a7b1194dad6de25702-dirty
59+
diff --git a/git/test/fixtures/diff_patch_binary b/git/test/fixtures/diff_patch_binary
60+
new file mode 100644
61+
index 0000000000000000000000000000000000000000..c92ccd6ebc92a871d38ad7cb8a48bcdb1a5dbc33
62+
--- /dev/null
63+
+++ b/git/test/fixtures/diff_patch_binary
64+
@@ -0,0 +1,3 @@
65+
+diff --git a/rps b/rps
66+
+index f4567df37451b230b1381b1bc9c2bcad76e08a3c..736bd596a36924d30b480942e9475ce0d734fa0d 100755
67+
+Binary files a/rps and b/rps differ
68+
diff --git a/git/test/fixtures/diff_raw_binary b/git/test/fixtures/diff_raw_binary
69+
new file mode 100644
70+
index 0000000000000000000000000000000000000000..d4673fa41ee8413384167fc7b9f25e4daf18a53a
71+
--- /dev/null
72+
+++ b/git/test/fixtures/diff_raw_binary
73+
@@ -0,0 +1 @@
74+
+:100755 100755 f4567df37451b230b1381b1bc9c2bcad76e08a3c 736bd596a36924d30b480942e9475ce0d734fa0d M rps
75+
diff --git a/git/test/test_diff.py b/git/test/test_diff.py
76+
index ce0f64f2261bd8de063233108caac1f26742c1fd..4de26f8884fd048ac7f10007f2bf7c7fa3fa60f4 100644
77+
--- a/git/test/test_diff.py
78+
+++ b/git/test/test_diff.py
79+
@@ -65,6 +65,21 @@ class TestDiff(TestBase):
80+
assert diff.rename_to == 'that'
81+
assert len(list(diffs.iter_change_type('R'))) == 1
82+
83+
+ def test_binary_diff(self):
84+
+ for method, file_name in ((Diff._index_from_patch_format, 'diff_patch_binary'),
85+
+ (Diff._index_from_raw_format, 'diff_raw_binary')):
86+
+ res = method(None, StringProcessAdapter(fixture(file_name)).stdout)
87+
+ assert len(res) == 1
88+
+ assert len(list(res.iter_change_type('M'))) == 1
89+
+ if res[0].diff:
90+
+ assert res[0].diff == "Binary files a/rps and b/rps differ\n", "in patch mode, we get a diff text"
91+
+ assert isinstance(str(res[0]), str), "This call should just work"
92+
+ # end for each method to test
93+
+
94+
+ def test_diff_index(self):
95+
+ res = self.rorepo.index.diff('17f5d13a7a741dcbb2a30e147bdafe929cff4697', create_patch=True)
96+
+ assert len(res) == 3
97+
+
98+
def test_diff_patch_format(self):
99+
# test all of the 'old' format diffs for completness - it should at least
100+
# be able to deal with it

git/test/test_diff.py

+12
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,20 @@ def test_binary_diff(self):
7373
assert len(list(res.iter_change_type('M'))) == 1
7474
if res[0].diff:
7575
assert res[0].diff == "Binary files a/rps and b/rps differ\n", "in patch mode, we get a diff text"
76+
assert isinstance(str(res[0]), str), "This call should just work"
7677
# end for each method to test
7778

79+
def test_diff_index(self):
80+
output = StringProcessAdapter(fixture('diff_index_patch'))
81+
res = Diff._index_from_patch_format(None, output.stdout)
82+
assert len(res) == 6
83+
for dr in res:
84+
assert dr.diff
85+
# end for each diff
86+
87+
dr = res[3]
88+
assert dr.diff.endswith("+Binary files a/rps and b/rps differ\n")
89+
7890
def test_diff_patch_format(self):
7991
# test all of the 'old' format diffs for completness - it should at least
8092
# be able to deal with it

0 commit comments

Comments
 (0)