summaryrefslogtreecommitdiff
path: root/git/diff.py
diff options
context:
space:
mode:
Diffstat (limited to 'git/diff.py')
-rw-r--r--git/diff.py50
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