| 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
|
|
|