| OLD | NEW |
| 1 # coding=utf8 | 1 # coding=utf8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Utility functions to handle patches.""" | 5 """Utility functions to handle patches.""" |
| 6 | 6 |
| 7 import posixpath | 7 import posixpath |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 | 10 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 """Parsed hunk data container.""" | 128 """Parsed hunk data container.""" |
| 129 | 129 |
| 130 def __init__(self, start_src, lines_src, start_dst, lines_dst): | 130 def __init__(self, start_src, lines_src, start_dst, lines_dst): |
| 131 self.start_src = start_src | 131 self.start_src = start_src |
| 132 self.lines_src = lines_src | 132 self.lines_src = lines_src |
| 133 self.start_dst = start_dst | 133 self.start_dst = start_dst |
| 134 self.lines_dst = lines_dst | 134 self.lines_dst = lines_dst |
| 135 self.variation = self.lines_dst - self.lines_src | 135 self.variation = self.lines_dst - self.lines_src |
| 136 self.text = [] | 136 self.text = [] |
| 137 | 137 |
| 138 def __repr__(self): |
| 139 return '%s<(%d, %d) to (%d, %d)>' % ( |
| 140 self.__class__.__name__, |
| 141 self.start_src, self.lines_src, self.start_dst, self.lines_dst) |
| 142 |
| 138 | 143 |
| 139 class FilePatchDiff(FilePatchBase): | 144 class FilePatchDiff(FilePatchBase): |
| 140 """Patch for a single file.""" | 145 """Patch for a single file.""" |
| 141 | 146 |
| 142 def __init__(self, filename, diff, svn_properties): | 147 def __init__(self, filename, diff, svn_properties): |
| 143 super(FilePatchDiff, self).__init__(filename) | 148 super(FilePatchDiff, self).__init__(filename) |
| 144 if not diff: | 149 if not diff: |
| 145 self._fail('File doesn\'t have a diff.') | 150 self._fail('File doesn\'t have a diff.') |
| 146 self.diff_header, self.diff_hunks = self._split_header(diff) | 151 self.diff_header, self.diff_hunks = self._split_header(diff) |
| 147 self.svn_properties = svn_properties or [] | 152 self.svn_properties = svn_properties or [] |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 def _split_hunks(self): | 233 def _split_hunks(self): |
| 229 """Splits the hunks and does verification.""" | 234 """Splits the hunks and does verification.""" |
| 230 hunks = [] | 235 hunks = [] |
| 231 for line in self.diff_hunks.splitlines(True): | 236 for line in self.diff_hunks.splitlines(True): |
| 232 if line.startswith('@@'): | 237 if line.startswith('@@'): |
| 233 match = re.match(r'^@@ -([\d,]+) \+([\d,]+) @@.*$', line) | 238 match = re.match(r'^@@ -([\d,]+) \+([\d,]+) @@.*$', line) |
| 234 # File add will result in "-0,0 +1" but file deletion will result in | 239 # File add will result in "-0,0 +1" but file deletion will result in |
| 235 # "-1,N +0,0" where N is the number of lines deleted. That's from diff | 240 # "-1,N +0,0" where N is the number of lines deleted. That's from diff |
| 236 # and svn diff. git diff doesn't exhibit this behavior. | 241 # and svn diff. git diff doesn't exhibit this behavior. |
| 237 # svn diff for a single line file rewrite "@@ -1 +1 @@". Fun. | 242 # svn diff for a single line file rewrite "@@ -1 +1 @@". Fun. |
| 243 # "@@ -1 +1,N @@" is also valid where N is the length of the new file. |
| 238 if not match: | 244 if not match: |
| 239 self._fail('Hunk header is unparsable') | 245 self._fail('Hunk header is unparsable') |
| 240 if ',' in match.group(1): | 246 count = match.group(1).count(',') |
| 247 if not count: |
| 248 start_src = int(match.group(1)) |
| 249 lines_src = 1 |
| 250 elif count == 1: |
| 241 start_src, lines_src = map(int, match.group(1).split(',', 1)) | 251 start_src, lines_src = map(int, match.group(1).split(',', 1)) |
| 242 else: | 252 else: |
| 243 start_src = int(match.group(1)) | 253 self._fail('Hunk header is malformed') |
| 244 lines_src = 0 | 254 |
| 245 if ',' in match.group(2): | 255 count = match.group(2).count(',') |
| 256 if not count: |
| 257 start_dst = int(match.group(2)) |
| 258 lines_dst = 1 |
| 259 elif count == 1: |
| 246 start_dst, lines_dst = map(int, match.group(2).split(',', 1)) | 260 start_dst, lines_dst = map(int, match.group(2).split(',', 1)) |
| 247 else: | 261 else: |
| 248 start_dst = int(match.group(2)) | 262 self._fail('Hunk header is malformed') |
| 249 lines_dst = 0 | |
| 250 new_hunk = Hunk(start_src, lines_src, start_dst, lines_dst) | 263 new_hunk = Hunk(start_src, lines_src, start_dst, lines_dst) |
| 251 if hunks: | 264 if hunks: |
| 252 if new_hunk.start_src <= hunks[-1].start_src: | 265 if new_hunk.start_src <= hunks[-1].start_src: |
| 253 self._fail('Hunks source lines are not ordered') | 266 self._fail('Hunks source lines are not ordered') |
| 254 if new_hunk.start_dst <= hunks[-1].start_dst: | 267 if new_hunk.start_dst <= hunks[-1].start_dst: |
| 255 self._fail('Hunks destination lines are not ordered') | 268 self._fail('Hunks destination lines are not ordered') |
| 256 hunks.append(new_hunk) | 269 hunks.append(new_hunk) |
| 257 continue | 270 continue |
| 258 hunks[-1].text.append(line) | 271 hunks[-1].text.append(line) |
| 259 | 272 |
| 260 if len(hunks) == 1: | 273 if len(hunks) == 1: |
| 261 if hunks[0].start_src == 0 and hunks[0].lines_src == 0: | 274 if hunks[0].start_src == 0 and hunks[0].lines_src == 0: |
| 262 self.is_new = True | 275 self.is_new = True |
| 263 if hunks[0].start_dst == 0 and hunks[0].lines_dst == 0: | 276 if hunks[0].start_dst == 0 and hunks[0].lines_dst == 0: |
| 264 self.is_delete = True | 277 self.is_delete = True |
| 265 | 278 |
| 266 if self.is_new and self.is_delete: | 279 if self.is_new and self.is_delete: |
| 267 self._fail('Hunk header is all 0') | 280 self._fail('Hunk header is all 0') |
| 268 | 281 |
| 269 if not self.is_new and not self.is_delete: | 282 if not self.is_new and not self.is_delete: |
| 270 for hunk in hunks: | 283 for hunk in hunks: |
| 271 variation = ( | 284 variation = ( |
| 272 len([1 for i in hunk.text if i.startswith('+')]) - | 285 len([1 for i in hunk.text if i.startswith('+')]) - |
| 273 len([1 for i in hunk.text if i.startswith('-')])) | 286 len([1 for i in hunk.text if i.startswith('-')])) |
| 274 if variation != hunk.variation: | 287 if variation != hunk.variation: |
| 275 self._fail( | 288 self._fail( |
| 276 'Hunk header is incorrect: %d vs %d' % ( | 289 'Hunk header is incorrect: %d vs %d; %r' % ( |
| 277 variation, hunk.variation)) | 290 variation, hunk.variation, hunk)) |
| 278 if not hunk.start_src: | 291 if not hunk.start_src: |
| 279 self._fail( | 292 self._fail( |
| 280 'Hunk header start line is incorrect: %d' % hunk.start_src) | 293 'Hunk header start line is incorrect: %d' % hunk.start_src) |
| 281 if not hunk.start_dst: | 294 if not hunk.start_dst: |
| 282 self._fail( | 295 self._fail( |
| 283 'Hunk header start line is incorrect: %d' % hunk.start_dst) | 296 'Hunk header start line is incorrect: %d' % hunk.start_dst) |
| 284 hunk.start_src -= 1 | 297 hunk.start_src -= 1 |
| 285 hunk.start_dst -= 1 | 298 hunk.start_dst -= 1 |
| 286 if self.is_new and hunks: | 299 if self.is_new and hunks: |
| 287 hunks[0].start_dst -= 1 | 300 hunks[0].start_dst -= 1 |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 def __iter__(self): | 518 def __iter__(self): |
| 506 for patch in self.patches: | 519 for patch in self.patches: |
| 507 yield patch | 520 yield patch |
| 508 | 521 |
| 509 def __getitem__(self, key): | 522 def __getitem__(self, key): |
| 510 return self.patches[key] | 523 return self.patches[key] |
| 511 | 524 |
| 512 @property | 525 @property |
| 513 def filenames(self): | 526 def filenames(self): |
| 514 return [p.filename for p in self.patches] | 527 return [p.filename for p in self.patches] |
| OLD | NEW |