| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 504870e commit 7fbc182
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,20 @@ | |||
| 22 | 22 | NULL_TREE = object() | |
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + def decode_path(path, has_ab_prefix=True): | ||
| 26 | + if path == b'/dev/null': | ||
| 27 | + return None | ||
| 28 | + | ||
| 29 | + if path.startswith(b'"') and path.endswith(b'"'): | ||
| 30 | + path = path[1:-1].decode('string_escape') | ||
| 31 | + | ||
| 32 | + if has_ab_prefix: | ||
| 33 | + assert path.startswith(b'a/') or path.startswith(b'b/') | ||
| 34 | + path = path[2:] | ||
| 35 | + | ||
| 36 | + return path | ||
| 37 | + | ||
| 38 | + | ||
| 25 | 39 | class Diffable(object): | |
| 26 | 40 | ||
| 27 | 41 | """Common interface for all object that can be diffed against another object of compatible type. | |
@@ -196,9 +210,9 @@ class Diff(object): | |||
| 196 | 210 | be different to the version in the index or tree, and hence has been modified.""" | |
| 197 | 211 | ||
| 198 | 212 | # precompiled regex | |
| 199 | - re_header = re.compile(r""" | ||
| 213 | + re_header = re.compile(br""" | ||
| 200 | 214 | ^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 | ||
| 202 | 216 | (?:^old[ ]mode[ ](?P<old_mode>\d+)\n | |
| 203 | 217 | ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))? | |
| 204 | 218 | (?:^similarity[ ]index[ ]\d+%\n | |
@@ -208,9 +222,9 @@ class Diff(object): | |||
| 208 | 222 | (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))? | |
| 209 | 223 | (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+) | |
| 210 | 224 | \.\.(?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) | ||
| 214 | 228 | # can be used for comparisons | |
| 215 | 229 | NULL_HEX_SHA = "0" * 40 | |
| 216 | 230 | NULL_BIN_SHA = b"\0" * 20 | |
@@ -319,6 +333,19 @@ def renamed(self): | |||
| 319 | 333 | """:returns: True if the blob of our diff has been renamed""" | |
| 320 | 334 | return self.rename_from != self.rename_to | |
| 321 | 335 | ||
| 336 | + @classmethod | ||
| 337 | + def _pick_best_path(cls, path_match, rename_match, path_fallback_match): | ||
| 338 | + if path_match: | ||
| 339 | + return decode_path(path_match) | ||
| 340 | + | ||
| 341 | + if rename_match: | ||
| 342 | + return decode_path(rename_match, has_ab_prefix=False) | ||
| 343 | + | ||
| 344 | + if path_fallback_match: | ||
| 345 | + return decode_path(path_fallback_match) | ||
| 346 | + | ||
| 347 | + return None | ||
| 348 | + | ||
| 322 | 349 | @classmethod | |
| 323 | 350 | def _index_from_patch_format(cls, repo, stream): | |
| 324 | 351 | """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): | |||
| 338 | 365 | a_path, b_path = header.groups() | |
| 339 | 366 | new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode) | |
| 340 | 367 | ||
| 341 | - a_path = a_path or rename_from or a_path_fallback | ||
| 342 | - b_path = b_path or rename_to or b_path_fallback | ||
| 343 | - | ||
| 344 | - if a_path == b'/dev/null': | ||
| 345 | - a_path = None | ||
| 346 | - | ||
| 347 | - if b_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) | ||
| 349 | 370 | ||
| 350 | 371 | # Our only means to find the actual text is to see what has not been matched by our regex, | |
| 351 | 372 | # and then retro-actively assin it to our index | |
| Original file line number | Diff line number | Diff 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\"" | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - #-*-coding:utf-8-*- | ||
| 1 | + # coding: utf-8 | ||
| 2 | 2 | # test_diff.py | |
| 3 | 3 | # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors | |
| 4 | 4 | # | |
@@ -145,12 +145,37 @@ def test_diff_initial_commit(self): | |||
| 145 | 145 | assert diff_index[0].new_file | |
| 146 | 146 | assert diff_index[0].diff == fixture('diff_initial') | |
| 147 | 147 | ||
| 148 | + def test_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 | + | ||
| 148 | 173 | def test_diff_patch_format(self): | |
| 149 | 174 | # test all of the 'old' format diffs for completness - it should at least | |
| 150 | 175 | # be able to deal with it | |
| 151 | 176 | fixtures = ("diff_2", "diff_2f", "diff_f", "diff_i", "diff_mode_only", | |
| 152 | 177 | "diff_new_mode", "diff_numstat", "diff_p", "diff_rename", | |
| 153 | - "diff_tree_numstat_root") | ||
| 178 | + "diff_tree_numstat_root", "diff_patch_unsafe_paths") | ||
| 154 | 179 | ||
| 155 | 180 | for fixture_name in fixtures: | |
| 156 | 181 | diff_proc = StringProcessAdapter(fixture(fixture_name)) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments