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 |