Chromium Code Reviews| Index: patch.py |
| diff --git a/patch.py b/patch.py |
| index c627f48c85b79a8ab1c65da9b30e654d012d4db1..49d238354f1abc1f4b336738b5ecce0f258d7f10 100644 |
| --- a/patch.py |
| +++ b/patch.py |
| @@ -29,6 +29,7 @@ class FilePatchBase(object): |
| """ |
| is_delete = False |
| is_binary = False |
| + is_new = False |
| def __init__(self, filename): |
| self.filename = None |
| @@ -44,7 +45,7 @@ class FilePatchBase(object): |
| if self.filename.startswith(i): |
| self._fail('Filename can\'t start with \'%s\'.' % i) |
| - def get(self): |
| + def get(self): # pragma: no coverage |
| raise NotImplementedError('Nothing to grab') |
| def set_relpath(self, relpath): |
| @@ -75,10 +76,11 @@ class FilePatchBinary(FilePatchBase): |
| """Content of a new binary file.""" |
| is_binary = True |
| - def __init__(self, filename, data, svn_properties): |
| + def __init__(self, filename, data, svn_properties, is_new): |
| super(FilePatchBinary, self).__init__(filename) |
| self.data = data |
| self.svn_properties = svn_properties or [] |
| + self.is_new = is_new |
| def get(self): |
| return self.data |
| @@ -195,29 +197,40 @@ class FilePatchDiff(FilePatchBase): |
| if not old or not new: |
| self._fail('Unexpected git diff; couldn\'t find git header.') |
| - # Handle these: |
| - # new file mode \d{6} |
| - # rename from <> |
| - # rename to <> |
| - # copy from <> |
| - # copy to <> |
| - while lines: |
| - if lines[0].startswith('--- '): |
| - break |
| - line = lines.pop(0) |
| + last_line = '' |
| + |
| + def process_line(line): |
|
Dirk Pranke
2011/06/03 19:17:27
Does this really need to be a nested function?
M-A Ruel
2011/06/03 19:42:31
not anymore. so much state to keep
|
| + """Processes a single line of the header. |
| + |
| + Returns True if it should continue looping. |
| + """ |
| + # Handle these: |
| + # rename from <> |
| + # copy from <> |
| match = re.match(r'^(rename|copy) from (.+)$', line) |
| if match: |
| if old != match.group(2): |
| - self._fail('Unexpected git diff input name for %s.' % match.group(1)) |
| - if not lines: |
| - self._fail('Missing git diff output name for %s.' % match.group(1)) |
| - match = re.match(r'^(rename|copy) to (.+)$', lines.pop(0)) |
| - if not match: |
| - self._fail('Missing git diff output name for %s.' % match.group(1)) |
| + self._fail('Unexpected git diff input name for line %s.' % line) |
| + if not lines or not lines[0].startswith('%s to ' % match.group(1)): |
| + self._fail( |
| + 'Confused %s from/to git diff for line %s.' % |
| + (match.group(1), line)) |
| + return |
| + |
| + # Handle these: |
| + # rename to <> |
| + # copy to <> |
| + match = re.match(r'^(rename|copy) to (.+)$', line) |
| + if match: |
| if new != match.group(2): |
| - self._fail('Unexpected git diff output name for %s.' % match.group(1)) |
| - continue |
| - |
| + self._fail('Unexpected git diff output name for line %s.' % line) |
| + if not last_line.startswith('%s from ' % match.group(1)): |
| + self._fail( |
| + 'Confused %s from/to git diff for line %s.' % |
| + (match.group(1), line)) |
| + return |
| + |
| + # Handle "new file mode \d{6}" |
| match = re.match(r'^new file mode (\d{6})$', line) |
| if match: |
| mode = match.group(1) |
| @@ -225,22 +238,43 @@ class FilePatchDiff(FilePatchBase): |
| if bool(int(mode[4]) & 4): |
| self.svn_properties.append(('svn:executable', '*')) |
| - # Handle ---/+++ |
| + # Handle "--- " |
| + match = re.match(r'^--- (.*)$', line) |
| + if match: |
| + if last_line[:3] in ('---', '+++'): |
| + 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': |
| + 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.') |
| + return |
| + |
| + # Handle "+++ " |
| + match = re.match(r'^\+\+\+ (.*)$', line) |
| + if match: |
| + if not last_line.startswith('---'): |
| + self._fail('Unexpected git diff: --- not following +++.') |
| + # TODO(maruel): new == self.filename. |
| + if new != self.mangle(match.group(1)) and '/dev/null' != match.group(1): |
| + # TODO(maruel): Can +++ be /dev/null? If so, assert self.is_delete == |
| + # True. |
| + self._fail('Unexpected git diff: %s != %s.' % (new, match.group(1))) |
| + if lines: |
| + self._fail('Crap after +++') |
| + # We're done. |
| + return |
| + |
| while lines: |
| - match = re.match(r'^--- (.*)$', lines.pop(0)) |
| - if not match: |
| - continue |
| - if old != self.mangle(match.group(1)) and match.group(1) != '/dev/null': |
| - self._fail('Unexpected git diff: %s != %s.' % (old, match.group(1))) |
| - if not lines: |
| - self._fail('Missing git diff output name.') |
| - match = re.match(r'^\+\+\+ (.*)$', lines.pop(0)) |
| - if not match: |
| - self._fail('Unexpected git diff: --- not following +++.') |
| - if new != self.mangle(match.group(1)) and '/dev/null' != match.group(1): |
| - self._fail('Unexpected git diff: %s != %s.' % (new, match.group(1))) |
| - assert not lines, '_split_header() is broken' |
| - break |
| + line = lines.pop(0) |
| + process_line(line) |
| + last_line = line |
| + |
| + # Cheap check to make sure the file name is at least mentioned in the |
| + # 'diff' header. That the only remaining invariant. |
| + if not self.filename in self.diff_header: |
| + self._fail('Diff seems corrupted.') |
| def _verify_svn_header(self): |
| """Sanity checks the header. |
| @@ -250,31 +284,52 @@ class FilePatchDiff(FilePatchBase): |
| localized. |
| """ |
| lines = self.diff_header.splitlines() |
| + last_line = '' |
| + |
| + def process_line(line): |
| + """Processes a single line of the header. |
| + |
| + Returns True if it should continue looping. |
| + """ |
| + match = re.match(r'^--- ([^\t]+).*$', line) |
| + if match: |
| + 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 not lines or not lines[0].startswith('+++'): |
| + self._fail('Nothing after header.') |
| + return |
| + |
| + match = re.match(r'^\+\+\+ ([^\t]+).*$', line) |
| + if match: |
| + if not last_line.startswith('---'): |
| + self._fail('Unexpected diff: --- not following +++.') |
| + if (self.mangle(match.group(1)) != self.filename and |
| + match.group(1) != '/dev/null'): |
| + # TODO(maruel): Can +++ be /dev/null? If so, assert self.is_delete == |
| + # True. |
| + self._fail('Unexpected diff: %s.' % match.group(1)) |
| + if lines: |
| + self._fail('Crap after +++') |
| + # We're done. |
| + return |
| + |
| while lines: |
| - match = re.match(r'^--- ([^\t]+).*$', lines.pop(0)) |
| - if not match: |
| - continue |
| - # 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 match.group(1) not in (self.filename, '/dev/null'): |
| - # self.source_file = match.group(1) |
| - if not lines: |
| - self._fail('Nothing after header.') |
| - match = re.match(r'^\+\+\+ ([^\t]+).*$', lines.pop(0)) |
| - if not match: |
| - self._fail('Unexpected diff: --- not following +++.') |
| - if match.group(1) not in (self.filename, '/dev/null'): |
| - self._fail('Unexpected diff: %s.' % match.group(1)) |
| - assert not lines, '_split_header() is broken' |
| - break |
| - else: |
| - # Cheap check to make sure the file name is at least mentioned in the |
| - # 'diff' header. That the only remaining invariant. |
| - if not self.filename in self.diff_header: |
| - self._fail('Diff seems corrupted.') |
| + line = lines.pop(0) |
| + process_line(line) |
| + last_line = line |
| + # Cheap check to make sure the file name is at least mentioned in the |
| + # 'diff' header. That the only remaining invariant. |
| + if not self.filename in self.diff_header: |
| + self._fail('Diff seems corrupted.') |
| class PatchSet(object): |
| """A list of FilePatch* objects.""" |