Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(621)

Side by Side Diff: patch.py

Issue 9167015: Fix svn delete handling (again). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | testing_support/patches_data.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # coding=utf8 1 # coding=utf8
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2011 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 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 else: 79 else:
80 out += ' ' 80 out += ' '
81 if self.is_new: 81 if self.is_new:
82 out += 'N' 82 out += 'N'
83 else: 83 else:
84 out += ' ' 84 out += ' '
85 if self.source_filename: 85 if self.source_filename:
86 out += 'R' 86 out += 'R'
87 else: 87 else:
88 out += ' ' 88 out += ' '
89 return out + ' %s->%s' % (self.source_filename, self.filename) 89 out += ' '
90 if self.source_filename:
91 out += '%s->' % self.source_filename
92 return out + str(self.filename)
90 93
91 94
92 class FilePatchDelete(FilePatchBase): 95 class FilePatchDelete(FilePatchBase):
93 """Deletes a file.""" 96 """Deletes a file."""
94 is_delete = True 97 is_delete = True
95 98
96 def __init__(self, filename, is_binary): 99 def __init__(self, filename, is_binary):
97 super(FilePatchDelete, self).__init__(filename) 100 super(FilePatchDelete, self).__init__(filename)
98 self.is_binary = is_binary 101 self.is_binary = is_binary
99 102
100 103
101 class FilePatchBinary(FilePatchBase): 104 class FilePatchBinary(FilePatchBase):
102 """Content of a new binary file.""" 105 """Content of a new binary file."""
103 is_binary = True 106 is_binary = True
104 107
105 def __init__(self, filename, data, svn_properties, is_new): 108 def __init__(self, filename, data, svn_properties, is_new):
106 super(FilePatchBinary, self).__init__(filename) 109 super(FilePatchBinary, self).__init__(filename)
107 self.data = data 110 self.data = data
108 self.svn_properties = svn_properties or [] 111 self.svn_properties = svn_properties or []
109 self.is_new = is_new 112 self.is_new = is_new
110 113
111 def get(self): 114 def get(self):
112 return self.data 115 return self.data
113 116
114 117
118 class Hunk(object):
119 """Parsed hunk data container."""
120
121 def __init__(self, start_src, lines_src, start_dst, lines_dst):
122 self.start_src = start_src
123 self.lines_src = lines_src
124 self.start_dst = start_dst
125 self.lines_dst = lines_dst
126 self.variation = self.lines_dst - self.lines_src
127 self.text = []
128
129
115 class FilePatchDiff(FilePatchBase): 130 class FilePatchDiff(FilePatchBase):
116 """Patch for a single file.""" 131 """Patch for a single file."""
117 132
118 def __init__(self, filename, diff, svn_properties): 133 def __init__(self, filename, diff, svn_properties):
119 super(FilePatchDiff, self).__init__(filename) 134 super(FilePatchDiff, self).__init__(filename)
120 if not diff: 135 if not diff:
121 self._fail('File doesn\'t have a diff.') 136 self._fail('File doesn\'t have a diff.')
122 self.diff_header, self.diff_hunks = self._split_header(diff) 137 self.diff_header, self.diff_hunks = self._split_header(diff)
123 self.svn_properties = svn_properties or [] 138 self.svn_properties = svn_properties or []
124 self.is_git_diff = self._is_git_diff_header(self.diff_header) 139 self.is_git_diff = self._is_git_diff_header(self.diff_header)
125 self.patchlevel = 0 140 self.patchlevel = 0
126 if self.is_git_diff: 141 if self.is_git_diff:
127 self._verify_git_header() 142 self._verify_git_header()
128 else: 143 else:
129 self._verify_svn_header() 144 self._verify_svn_header()
145 self.hunks = self._split_hunks()
130 if self.source_filename and not self.is_new: 146 if self.source_filename and not self.is_new:
131 self._fail('If source_filename is set, is_new must be also be set') 147 self._fail('If source_filename is set, is_new must be also be set')
132 148
133 def get(self, for_git): 149 def get(self, for_git):
134 if for_git or not self.source_filename: 150 if for_git or not self.source_filename:
135 return self.diff_header + self.diff_hunks 151 return self.diff_header + self.diff_hunks
136 else: 152 else:
137 # patch is stupid. It patches the source_filename instead so get rid of 153 # patch is stupid. It patches the source_filename instead so get rid of
138 # any source_filename reference if needed. 154 # any source_filename reference if needed.
139 return ( 155 return (
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 @staticmethod 207 @staticmethod
192 def _is_git_diff_header(diff_header): 208 def _is_git_diff_header(diff_header):
193 """Returns True if the diff for a single files was generated with git.""" 209 """Returns True if the diff for a single files was generated with git."""
194 # Delete: http://codereview.chromium.org/download/issue6368055_22_29.diff 210 # Delete: http://codereview.chromium.org/download/issue6368055_22_29.diff
195 # Rename partial change: 211 # Rename partial change:
196 # http://codereview.chromium.org/download/issue6250123_3013_6010.diff 212 # http://codereview.chromium.org/download/issue6250123_3013_6010.diff
197 # Rename no change: 213 # Rename no change:
198 # http://codereview.chromium.org/download/issue6287022_3001_4010.diff 214 # http://codereview.chromium.org/download/issue6287022_3001_4010.diff
199 return any(l.startswith('diff --git') for l in diff_header.splitlines()) 215 return any(l.startswith('diff --git') for l in diff_header.splitlines())
200 216
217 def _split_hunks(self):
218 """Splits the hunks and does verification."""
219 hunks = []
220 for line in self.diff_hunks.splitlines(True):
221 if line.startswith('@@'):
222 match = re.match(r'^@@ -(\d+),(\d+) \+([\d,]+) @@.*$', line)
223 # File add will result in "-0,0 +1" but file deletion will result in
224 # "-1,N +0,0" where N is the number of lines deleted. That's from diff
225 # and svn diff. git diff doesn't exhibit this behavior.
226 if not match:
227 self._fail('Hunk header is unparsable')
228 if ',' in match.group(3):
229 start_dst, lines_dst = map(int, match.group(3).split(',', 1))
230 else:
231 start_dst = int(match.group(3))
232 lines_dst = 0
233 new_hunk = Hunk(int(match.group(1)), int(match.group(2)),
234 start_dst, lines_dst)
235 if hunks:
236 if new_hunk.start_src <= hunks[-1].start_src:
237 self._fail('Hunks source lines are not ordered')
238 if new_hunk.start_dst <= hunks[-1].start_dst:
239 self._fail('Hunks destination lines are not ordered')
240 hunks.append(new_hunk)
241 continue
242 hunks[-1].text.append(line)
243
244 if len(hunks) == 1:
245 if hunks[0].start_src == 0 and hunks[0].lines_src == 0:
246 self.is_new = True
247 if hunks[0].start_dst == 0 and hunks[0].lines_dst == 0:
248 self.is_delete = True
249
250 if self.is_new and self.is_delete:
251 self._fail('Hunk header is all 0')
252
253 if not self.is_new and not self.is_delete:
254 for hunk in hunks:
255 variation = (
256 len([1 for i in hunk.text if i.startswith('+')]) -
257 len([1 for i in hunk.text if i.startswith('-')]))
258 if variation != hunk.variation:
259 self._fail(
260 'Hunk header is incorrect: %d vs %d' % (
261 variation, hunk.variation))
262 if not hunk.start_src:
263 self._fail(
264 'Hunk header start line is incorrect: %d' % hunk.start_src)
265 if not hunk.start_dst:
266 self._fail(
267 'Hunk header start line is incorrect: %d' % hunk.start_dst)
268 hunk.start_src -= 1
269 hunk.start_dst -= 1
270 if self.is_new and hunks:
271 hunks[0].start_dst -= 1
272 if self.is_delete and hunks:
273 hunks[0].start_src -= 1
274 return hunks
275
201 def mangle(self, string): 276 def mangle(self, string):
202 """Mangle a file path.""" 277 """Mangle a file path."""
203 return '/'.join(string.replace('\\', '/').split('/')[self.patchlevel:]) 278 return '/'.join(string.replace('\\', '/').split('/')[self.patchlevel:])
204 279
205 def _verify_git_header(self): 280 def _verify_git_header(self):
206 """Sanity checks the header. 281 """Sanity checks the header.
207 282
208 Expects the following format: 283 Expects the following format:
209 284
210 <garbagge> 285 <garbagge>
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 def __iter__(self): 489 def __iter__(self):
415 for patch in self.patches: 490 for patch in self.patches:
416 yield patch 491 yield patch
417 492
418 def __getitem__(self, key): 493 def __getitem__(self, key):
419 return self.patches[key] 494 return self.patches[key]
420 495
421 @property 496 @property
422 def filenames(self): 497 def filenames(self):
423 return [p.filename for p in self.patches] 498 return [p.filename for p in self.patches]
OLDNEW
« no previous file with comments | « no previous file | testing_support/patches_data.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698