diff options
Diffstat (limited to 'git/diff.py')
-rw-r--r-- | git/diff.py | 50 |
1 files changed, 37 insertions, 13 deletions
diff --git a/git/diff.py b/git/diff.py index d4affd30..76426940 100644 --- a/git/diff.py +++ b/git/diff.py @@ -22,6 +22,23 @@ __all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE') NULL_TREE = object() +def decode_path(path, has_ab_prefix=True): + if path == b'/dev/null': + return None + + if path.startswith(b'"') and path.endswith(b'"'): + path = (path[1:-1].replace(b'\\n', b'\n') + .replace(b'\\t', b'\t') + .replace(b'\\"', b'"') + .replace(b'\\\\', b'\\')) + + if has_ab_prefix: + assert path.startswith(b'a/') or path.startswith(b'b/') + path = path[2:] + + return path + + class Diffable(object): """Common interface for all object that can be diffed against another object of compatible type. @@ -196,9 +213,9 @@ class Diff(object): be different to the version in the index or tree, and hence has been modified.""" # precompiled regex - re_header = re.compile(r""" + re_header = re.compile(br""" ^diff[ ]--git - [ ](?:a/)?(?P<a_path_fallback>.+?)[ ](?:b/)?(?P<b_path_fallback>.+?)\n + [ ](?P<a_path_fallback>"?a/.+?"?)[ ](?P<b_path_fallback>"?b/.+?"?)\n (?:^old[ ]mode[ ](?P<old_mode>\d+)\n ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))? (?:^similarity[ ]index[ ]\d+%\n @@ -208,9 +225,9 @@ class Diff(object): (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))? (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+) \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))? - (?:^---[ ](?:a/)?(?P<a_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))? - (?:^\+\+\+[ ](?:b/)?(?P<b_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))? - """.encode('ascii'), re.VERBOSE | re.MULTILINE) + (?:^---[ ](?P<a_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))? + (?:^\+\+\+[ ](?P<b_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))? + """, re.VERBOSE | re.MULTILINE) # can be used for comparisons NULL_HEX_SHA = "0" * 40 NULL_BIN_SHA = b"\0" * 20 @@ -320,6 +337,19 @@ class Diff(object): return self.rename_from != self.rename_to @classmethod + def _pick_best_path(cls, path_match, rename_match, path_fallback_match): + if path_match: + return decode_path(path_match) + + if rename_match: + return decode_path(rename_match, has_ab_prefix=False) + + if path_fallback_match: + return decode_path(path_fallback_match) + + return None + + @classmethod def _index_from_patch_format(cls, repo, stream): """Create a new DiffIndex from the given text which must be in patch format :param repo: is the repository we are operating on - it is required @@ -338,14 +368,8 @@ class Diff(object): a_path, b_path = header.groups() new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode) - a_path = a_path or rename_from or a_path_fallback - b_path = b_path or rename_to or b_path_fallback - - if a_path == b'/dev/null': - a_path = None - - if b_path == b'/dev/null': - b_path = None + a_path = cls._pick_best_path(a_path, rename_from, a_path_fallback) + b_path = cls._pick_best_path(b_path, rename_to, b_path_fallback) # Our only means to find the actual text is to see what has not been matched by our regex, # and then retro-actively assin it to our index |