Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7fbc182

Browse files
committed
Fix diff patch parser for paths with unsafe chars
This specifically covers the cases where unsafe chars occur in pathnames, and git-diff -p will escape those.From the git-diff-tree manpage:> 3. TAB, LF, double quote and backslash characters in pathnames are> represented as \t, \n, \" and \\, respectively. If there is need> for such substitution then the whole pathname is put in double> quotes.This patch checks whether or not this has happened and will unescapethose paths accordingly.One thing to note here is that, depending on the position in the patchformat, those paths may be prefixed with an a/ or b/. I've specificallymade sure to never interpret a path that actually starts with a/ or b/incorrectly.Example of that subtlety below. Here, the actual file path is"b/normal". On the diff file that gets encoded as "b/b/normal". diff --git a/b/normal b/b/normal new file mode 100644 index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 --- /dev/null +++ b/b/normal @@ -0,0 +1 @@ +dummy contentHere, we prefer the "---" and "+++" lines' values. Note that thesepaths start with a/ or b/. The only exception is the value "/dev/null",which is handled as a special case.Suppose now the file gets moved "b/moved", the output of that diff wouldthen be this: diff --git a/b/normal b/b/moved similarity index 100% rename from b/normal rename to b/movedWe prefer the "rename" lines' values in this case (the "diff" line isalways a last resort). Take note that those lines are not prefixed witha/ or b/, but the ones in the "diff" line are (just like the ones in"---" or "+++" lines).
1 parent504870e commit7fbc182

File tree

3 files changed

+136
-15
lines changed

3 files changed

+136
-15
lines changed

‎git/diff.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@
2222
NULL_TREE=object()
2323

2424

25+
defdecode_path(path,has_ab_prefix=True):
26+
ifpath==b'/dev/null':
27+
returnNone
28+
29+
ifpath.startswith(b'"')andpath.endswith(b'"'):
30+
path=path[1:-1].decode('string_escape')
31+
32+
ifhas_ab_prefix:
33+
assertpath.startswith(b'a/')orpath.startswith(b'b/')
34+
path=path[2:]
35+
36+
returnpath
37+
38+
2539
classDiffable(object):
2640

2741
"""Common interface for all object that can be diffed against another object of compatible type.
@@ -196,9 +210,9 @@ class Diff(object):
196210
be different to the version in the index or tree, and hence has been modified."""
197211

198212
# precompiled regex
199-
re_header=re.compile(r"""
213+
re_header=re.compile(br"""
200214
^diff[ ]--git
201-
[ ](?:a/)?(?P<a_path_fallback>.+?)[ ](?:b/)?(?P<b_path_fallback>.+?)\n
215+
[ ](?P<a_path_fallback>"?a/.+?"?)[ ](?P<b_path_fallback>"?b/.+?"?)\n
202216
(?:^old[ ]mode[ ](?P<old_mode>\d+)\n
203217
^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
204218
(?:^similarity[ ]index[ ]\d+%\n
@@ -208,9 +222,9 @@ class Diff(object):
208222
(?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
209223
(?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
210224
\.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
211-
(?:^---[ ](?:a/)?(?P<a_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))?
212-
(?:^\+\+\+[ ](?:b/)?(?P<b_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))?
213-
""".encode('ascii'),re.VERBOSE|re.MULTILINE)
225+
(?:^---[ ](?P<a_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))?
226+
(?:^\+\+\+[ ](?P<b_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))?
227+
""",re.VERBOSE|re.MULTILINE)
214228
# can be used for comparisons
215229
NULL_HEX_SHA="0"*40
216230
NULL_BIN_SHA=b"\0"*20
@@ -319,6 +333,19 @@ def renamed(self):
319333
""":returns: True if the blob of our diff has been renamed"""
320334
returnself.rename_from!=self.rename_to
321335

336+
@classmethod
337+
def_pick_best_path(cls,path_match,rename_match,path_fallback_match):
338+
ifpath_match:
339+
returndecode_path(path_match)
340+
341+
ifrename_match:
342+
returndecode_path(rename_match,has_ab_prefix=False)
343+
344+
ifpath_fallback_match:
345+
returndecode_path(path_fallback_match)
346+
347+
returnNone
348+
322349
@classmethod
323350
def_index_from_patch_format(cls,repo,stream):
324351
"""Create a new DiffIndex from the given text which must be in patch format
@@ -338,14 +365,8 @@ def _index_from_patch_format(cls, repo, stream):
338365
a_path,b_path=header.groups()
339366
new_file,deleted_file=bool(new_file_mode),bool(deleted_file_mode)
340367

341-
a_path=a_pathorrename_fromora_path_fallback
342-
b_path=b_pathorrename_toorb_path_fallback
343-
344-
ifa_path==b'/dev/null':
345-
a_path=None
346-
347-
ifb_path==b'/dev/null':
348-
b_path=None
368+
a_path=cls._pick_best_path(a_path,rename_from,a_path_fallback)
369+
b_path=cls._pick_best_path(b_path,rename_to,b_path_fallback)
349370

350371
# Our only means to find the actual text is to see what has not been matched by our regex,
351372
# and then retro-actively assin it to our index
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
diff --git a/path/ starting with a space b/path/ starting with a space
2+
new file mode 100644
3+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
4+
--- /dev/null
5+
+++ b/path/ starting with a space
6+
@@ -0,0 +1 @@
7+
+dummy content
8+
diff --git "a/path/\"with-quotes\"" "b/path/\"with-quotes\""
9+
new file mode 100644
10+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
11+
--- /dev/null
12+
+++ "b/path/\"with-quotes\""
13+
@@ -0,0 +1 @@
14+
+dummy content
15+
diff --git a/path/'with-single-quotes' b/path/'with-single-quotes'
16+
new file mode 100644
17+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
18+
--- /dev/null
19+
+++ b/path/'with-single-quotes'
20+
@@ -0,0 +1 @@
21+
+dummy content
22+
diff --git a/path/ending in a space b/path/ending in a space
23+
new file mode 100644
24+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
25+
--- /dev/null
26+
+++ b/path/ending in a space
27+
@@ -0,0 +1 @@
28+
+dummy content
29+
diff --git "a/path/with\ttab" "b/path/with\ttab"
30+
new file mode 100644
31+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
32+
--- /dev/null
33+
+++ "b/path/with\ttab"
34+
@@ -0,0 +1 @@
35+
+dummy content
36+
diff --git "a/path/with\nnewline" "b/path/with\nnewline"
37+
new file mode 100644
38+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
39+
--- /dev/null
40+
+++ "b/path/with\nnewline"
41+
@@ -0,0 +1 @@
42+
+dummy content
43+
diff --git a/path/with spaces b/path/with spaces
44+
new file mode 100644
45+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
46+
--- /dev/null
47+
+++ b/path/with spaces
48+
@@ -0,0 +1 @@
49+
+dummy content
50+
diff --git a/path/with-question-mark? b/path/with-question-mark?
51+
new file mode 100644
52+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
53+
--- /dev/null
54+
+++ b/path/with-question-mark?
55+
@@ -0,0 +1 @@
56+
+dummy content
57+
diff --git "a/path/¯\\_(ツ)_|¯" "b/path/¯\\_(ツ)_|¯"
58+
new file mode 100644
59+
index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54
60+
--- /dev/null
61+
+++ "b/path/¯\\_(ツ)_|¯"
62+
@@ -0,0 +1 @@
63+
+dummy content
64+
diff --git a/a/with spaces b/b/with some spaces
65+
similarity index 100%
66+
rename from a/with spaces
67+
rename to b/with some spaces
68+
diff --git a/a/ending in a space b/b/ending with space
69+
similarity index 100%
70+
rename from a/ending in a space
71+
rename to b/ending with space
72+
diff --git "a/a/\"with-quotes\"" "b/b/\"with even more quotes\""
73+
similarity index 100%
74+
rename from "a/\"with-quotes\""
75+
rename to "b/\"with even more quotes\""

‎git/test/test_diff.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#-*-coding:utf-8-*-
1+
#coding:utf-8
22
# test_diff.py
33
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
44
#
@@ -145,12 +145,37 @@ def test_diff_initial_commit(self):
145145
assertdiff_index[0].new_file
146146
assertdiff_index[0].diff==fixture('diff_initial')
147147

148+
deftest_diff_unsafe_paths(self):
149+
output=StringProcessAdapter(fixture('diff_patch_unsafe_paths'))
150+
res=Diff._index_from_patch_format(None,output.stdout)
151+
152+
# The "Additions"
153+
self.assertEqual(res[0].b_path,u'path/ starting with a space')
154+
self.assertEqual(res[1].b_path,u'path/"with-quotes"')
155+
self.assertEqual(res[2].b_path,u"path/'with-single-quotes'")
156+
self.assertEqual(res[3].b_path,u'path/ending in a space ')
157+
self.assertEqual(res[4].b_path,u'path/with\ttab')
158+
self.assertEqual(res[5].b_path,u'path/with\nnewline')
159+
self.assertEqual(res[6].b_path,u'path/with spaces')
160+
self.assertEqual(res[7].b_path,u'path/with-question-mark?')
161+
self.assertEqual(res[8].b_path,ur'path/¯\_(ツ)_|¯')
162+
163+
# The "Moves"
164+
# NOTE: The path prefixes a/ and b/ here are legit! We're actually
165+
# verifying that it's not "a/a/" that shows up, see the fixture data.
166+
self.assertEqual(res[9].a_path,u'a/with spaces')# NOTE: path a/ here legit!
167+
self.assertEqual(res[9].b_path,u'b/with some spaces')# NOTE: path b/ here legit!
168+
self.assertEqual(res[10].a_path,u'a/ending in a space ')
169+
self.assertEqual(res[10].b_path,u'b/ending with space ')
170+
self.assertEqual(res[11].a_path,u'a/"with-quotes"')
171+
self.assertEqual(res[11].b_path,u'b/"with even more quotes"')
172+
148173
deftest_diff_patch_format(self):
149174
# test all of the 'old' format diffs for completness - it should at least
150175
# be able to deal with it
151176
fixtures= ("diff_2","diff_2f","diff_f","diff_i","diff_mode_only",
152177
"diff_new_mode","diff_numstat","diff_p","diff_rename",
153-
"diff_tree_numstat_root")
178+
"diff_tree_numstat_root","diff_patch_unsafe_paths")
154179

155180
forfixture_nameinfixtures:
156181
diff_proc=StringProcessAdapter(fixture(fixture_name))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp