Index: patch.py |
diff --git a/patch.py b/patch.py |
index 333cbb9f61815521ff83f1b14539f2a14a7b7a17..888e10095a7e25d68eb791f81f6141df5bceb941 100644 |
--- a/patch.py |
+++ b/patch.py |
@@ -33,6 +33,8 @@ class FilePatchBase(object): |
def __init__(self, filename): |
self.filename = self._process_filename(filename) |
+ # Set when the file is copied or moved. |
+ self.source_filename = None |
@staticmethod |
def _process_filename(filename): |
@@ -59,6 +61,9 @@ class FilePatchBase(object): |
self._fail('Relative path starts with %s' % relpath[0]) |
self.filename = self._process_filename( |
posixpath.join(relpath, self.filename)) |
+ if self.source_filename: |
+ self.source_filename = self._process_filename( |
+ posixpath.join(relpath, self.source_filename)) |
def _fail(self, msg): |
"""Shortcut function to raise UnsupportedPatchFormat.""" |
@@ -112,9 +117,21 @@ class FilePatchDiff(FilePatchBase): |
def set_relpath(self, relpath): |
old_filename = self.filename |
+ old_source_filename = self.source_filename or self.filename |
super(FilePatchDiff, self).set_relpath(relpath) |
# Update the header too. |
- self.diff_header = self.diff_header.replace(old_filename, self.filename) |
+ source_filename = self.source_filename or self.filename |
+ lines = self.diff_header.splitlines(True) |
+ for i, line in enumerate(lines): |
+ if line.startswith('diff --git'): |
+ lines[i] = line.replace( |
+ 'a/' + old_source_filename, source_filename).replace( |
+ 'b/' + old_filename, self.filename) |
+ elif re.match(r'^\w+ from .+$', line) or line.startswith('---'): |
+ lines[i] = line.replace(old_source_filename, source_filename) |
+ elif re.match(r'^\w+ to .+$', line) or line.startswith('+++'): |
+ lines[i] = line.replace(old_filename, self.filename) |
+ self.diff_header = ''.join(lines) |
def _split_header(self, diff): |
"""Splits a diff in two: the header and the hunks.""" |
@@ -186,12 +203,11 @@ class FilePatchDiff(FilePatchBase): |
match = re.match(r'^diff \-\-git (.*?) (.*)$', lines.pop(0)) |
if not match: |
continue |
- old = match.group(1).replace('\\', '/') |
- new = match.group(2).replace('\\', '/') |
- if old.startswith('a/') and new.startswith('b/'): |
+ if match.group(1).startswith('a/') and match.group(2).startswith('b/'): |
self.patchlevel = 1 |
- old = old[2:] |
- new = new[2:] |
+ old = self.mangle(match.group(1)) |
+ new = self.mangle(match.group(2)) |
+ |
# The rename is about the new file so the old file can be anything. |
if new not in (self.filename, 'dev/null'): |
self._fail('Unexpected git diff output name %s.' % new) |
@@ -202,13 +218,15 @@ class FilePatchDiff(FilePatchBase): |
if not old or not new: |
self._fail('Unexpected git diff; couldn\'t find git header.') |
+ if old not in (self.filename, 'dev/null'): |
+ # Copy or rename. |
+ self.source_filename = old |
+ |
last_line = '' |
while lines: |
line = lines.pop(0) |
- # TODO(maruel): old should be replace with self.source_file |
- # TODO(maruel): new == self.filename and remove new |
- self._verify_git_header_process_line(lines, line, last_line, old, new) |
+ self._verify_git_header_process_line(lines, line, last_line) |
last_line = line |
# Cheap check to make sure the file name is at least mentioned in the |
@@ -216,7 +234,7 @@ class FilePatchDiff(FilePatchBase): |
if not self.filename in self.diff_header: |
self._fail('Diff seems corrupted.') |
- def _verify_git_header_process_line(self, lines, line, last_line, old, new): |
+ def _verify_git_header_process_line(self, lines, line, last_line): |
"""Processes a single line of the header. |
Returns True if it should continue looping. |
@@ -225,6 +243,7 @@ class FilePatchDiff(FilePatchBase): |
http://www.kernel.org/pub/software/scm/git/docs/git-diff.html |
""" |
match = re.match(r'^(rename|copy) from (.+)$', line) |
+ old = self.source_filename or self.filename |
if match: |
if old != match.group(2): |
self._fail('Unexpected git diff input name for line %s.' % line) |
@@ -236,7 +255,7 @@ class FilePatchDiff(FilePatchBase): |
match = re.match(r'^(rename|copy) to (.+)$', line) |
if match: |
- if new != match.group(2): |
+ if self.filename != match.group(2): |
self._fail('Unexpected git diff output name for line %s.' % line) |
if not last_line.startswith('%s from ' % match.group(1)): |
self._fail( |
@@ -258,7 +277,7 @@ class FilePatchDiff(FilePatchBase): |
self._fail('--- and +++ are reversed') |
self.is_new = match.group(1) == '/dev/null' |
# TODO(maruel): Use self.source_file. |
- if old != self.mangle(match.group(1)) and match.group(1) != '/dev/null': |
+ if self.mangle(match.group(1)) not in (old, 'dev/null'): |
self._fail('Unexpected git diff: %s != %s.' % (old, match.group(1))) |
if not lines or not lines[0].startswith('+++'): |
self._fail('Missing git diff output name.') |
@@ -271,8 +290,9 @@ class FilePatchDiff(FilePatchBase): |
# TODO(maruel): new == self.filename. |
if '/dev/null' == match.group(1): |
self.is_delete = True |
- elif new != self.mangle(match.group(1)): |
- self._fail('Unexpected git diff: %s != %s.' % (new, match.group(1))) |
+ elif self.filename != self.mangle(match.group(1)): |
+ self._fail( |
+ 'Unexpected git diff: %s != %s.' % (self.filename, match.group(1))) |
if lines: |
self._fail('Crap after +++') |
# We're done. |
@@ -308,13 +328,9 @@ class FilePatchDiff(FilePatchBase): |
if last_line[:3] in ('---', '+++'): |
self._fail('--- and +++ are reversed') |
self.is_new = match.group(1) == '/dev/null' |
- # For copy and renames, it's possible that the -- line doesn't match |
- # +++, so don't check match.group(1) to match self.filename or |
- # '/dev/null', it can be anything else. |
- # TODO(maruel): Handle rename/copy explicitly. |
- # if (self.mangle(match.group(1)) != self.filename and |
- # match.group(1) != '/dev/null'): |
- # self.source_file = match.group(1) |
+ if (self.mangle(match.group(1)) != self.filename and |
+ match.group(1) != '/dev/null'): |
+ self.source_filename = match.group(1) |
if not lines or not lines[0].startswith('+++'): |
self._fail('Nothing after header.') |
return |