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

Side by Side Diff: checkout.py

Issue 6891003: Add post_process argument to Checkout.apply_patch(). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: post_processor Created 9 years, 8 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 | patch.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 """Manages a project checkout. 5 """Manages a project checkout.
6 6
7 Includes support for svn, git-svn and git. 7 Includes support for svn, git-svn and git.
8 """ 8 """
9 9
10 from __future__ import with_statement 10 from __future__ import with_statement
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 return get_code_review_setting(self.project_path, key) 73 return get_code_review_setting(self.project_path, key)
74 74
75 def prepare(self): 75 def prepare(self):
76 """Checks out a clean copy of the tree and removes any local modification. 76 """Checks out a clean copy of the tree and removes any local modification.
77 77
78 This function shouldn't throw unless the remote repository is inaccessible, 78 This function shouldn't throw unless the remote repository is inaccessible,
79 there is no free disk space or hard issues like that. 79 there is no free disk space or hard issues like that.
80 """ 80 """
81 raise NotImplementedError() 81 raise NotImplementedError()
82 82
83 def apply_patch(self, patches): 83 def apply_patch(self, patches, post_processor=None):
84 """Applies a patch and returns the list of modified files. 84 """Applies a patch and returns the list of modified files.
85 85
86 This function should throw patch.UnsupportedPatchFormat or 86 This function should throw patch.UnsupportedPatchFormat or
87 PatchApplicationFailed when relevant. 87 PatchApplicationFailed when relevant.
88
89 Args:
90 patches: patch.PatchSet object.
91 post_processor: list of lambda(checkout, patches) to call on each of the
92 modified files.
88 """ 93 """
89 raise NotImplementedError() 94 raise NotImplementedError()
90 95
91 def commit(self, commit_message, user): 96 def commit(self, commit_message, user):
92 """Commits the patch upstream, while impersonating 'user'.""" 97 """Commits the patch upstream, while impersonating 'user'."""
93 raise NotImplementedError() 98 raise NotImplementedError()
94 99
95 100
96 class RawCheckout(CheckoutBase): 101 class RawCheckout(CheckoutBase):
97 """Used to apply a patch locally without any intent to commit it. 102 """Used to apply a patch locally without any intent to commit it.
98 103
99 To be used by the try server. 104 To be used by the try server.
100 """ 105 """
101 def prepare(self): 106 def prepare(self):
102 """Stubbed out.""" 107 """Stubbed out."""
103 pass 108 pass
104 109
105 def apply_patch(self, patches): 110 def apply_patch(self, patches, post_processor=None):
111 """Ignores svn properties."""
112 post_processor = post_processor or []
106 for p in patches: 113 for p in patches:
107 try: 114 try:
108 stdout = '' 115 stdout = ''
109 filename = os.path.join(self.project_path, p.filename) 116 filename = os.path.join(self.project_path, p.filename)
110 if p.is_delete: 117 if p.is_delete:
111 os.remove(filename) 118 os.remove(filename)
112 else: 119 else:
113 dirname = os.path.dirname(p.filename) 120 dirname = os.path.dirname(p.filename)
114 full_dir = os.path.join(self.project_path, dirname) 121 full_dir = os.path.join(self.project_path, dirname)
115 if dirname and not os.path.isdir(full_dir): 122 if dirname and not os.path.isdir(full_dir):
116 os.makedirs(full_dir) 123 os.makedirs(full_dir)
117 if p.is_binary: 124 if p.is_binary:
118 with open(os.path.join(filename), 'wb') as f: 125 with open(os.path.join(filename), 'wb') as f:
119 f.write(p.get()) 126 f.write(p.get())
120 else: 127 else:
121 stdout = subprocess2.check_output( 128 stdout = subprocess2.check_output(
122 ['patch', '-p%s' % p.patchlevel], 129 ['patch', '-p%s' % p.patchlevel],
123 stdin=p.get(), 130 stdin=p.get(),
124 cwd=self.project_path) 131 cwd=self.project_path)
125 # Ignore p.svn_properties. 132 for post in post_processor:
133 post(self, p)
126 except OSError, e: 134 except OSError, e:
127 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) 135 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e))
128 except subprocess.CalledProcessError, e: 136 except subprocess.CalledProcessError, e:
129 raise PatchApplicationFailed( 137 raise PatchApplicationFailed(
130 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None))) 138 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None)))
131 139
132 def commit(self, commit_message, user): 140 def commit(self, commit_message, user):
133 """Stubbed out.""" 141 """Stubbed out."""
134 raise NotImplementedError('RawCheckout can\'t commit') 142 raise NotImplementedError('RawCheckout can\'t commit')
135 143
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 """Manages a subversion checkout.""" 224 """Manages a subversion checkout."""
217 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url): 225 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url):
218 super(SvnCheckout, self).__init__(root_dir, project_name) 226 super(SvnCheckout, self).__init__(root_dir, project_name)
219 self.commit_user = commit_user 227 self.commit_user = commit_user
220 self.commit_pwd = commit_pwd 228 self.commit_pwd = commit_pwd
221 self.svn_url = svn_url 229 self.svn_url = svn_url
222 assert bool(self.commit_user) >= bool(self.commit_pwd) 230 assert bool(self.commit_user) >= bool(self.commit_pwd)
223 assert self.svn_url 231 assert self.svn_url
224 232
225 def prepare(self): 233 def prepare(self):
226 """Creates the initial checkouts for the repo."""
227 # Will checkout if the directory is not present. 234 # Will checkout if the directory is not present.
228 if not os.path.isdir(self.project_path): 235 if not os.path.isdir(self.project_path):
229 logging.info('Checking out %s in %s' % 236 logging.info('Checking out %s in %s' %
230 (self.project_name, self.project_path)) 237 (self.project_name, self.project_path))
231 revision = self._revert() 238 revision = self._revert()
232 if revision != self._last_seen_revision: 239 if revision != self._last_seen_revision:
233 logging.info('Updated at revision %d' % revision) 240 logging.info('Updated at revision %d' % revision)
234 self._last_seen_revision = revision 241 self._last_seen_revision = revision
235 return revision 242 return revision
236 243
237 def apply_patch(self, patches): 244 def apply_patch(self, patches, post_processor=None):
238 """Applies a patch.""" 245 post_processor = post_processor or []
239 for p in patches: 246 for p in patches:
240 try: 247 try:
241 stdout = '' 248 stdout = ''
242 if p.is_delete: 249 if p.is_delete:
243 stdout += self._check_output_svn(['delete', p.filename, '--force']) 250 stdout += self._check_output_svn(['delete', p.filename, '--force'])
244 else: 251 else:
245 new = not os.path.exists(p.filename) 252 new = not os.path.exists(p.filename)
246 253
247 # svn add while creating directories otherwise svn add on the 254 # svn add while creating directories otherwise svn add on the
248 # contained files will silently fail. 255 # contained files will silently fail.
(...skipping 18 matching lines...) Expand all
267 cmd, stdin=p.get(), cwd=self.project_path) 274 cmd, stdin=p.get(), cwd=self.project_path)
268 if new: 275 if new:
269 stdout += self._check_output_svn(['add', p.filename, '--force']) 276 stdout += self._check_output_svn(['add', p.filename, '--force'])
270 for prop in p.svn_properties: 277 for prop in p.svn_properties:
271 stdout += self._check_output_svn( 278 stdout += self._check_output_svn(
272 ['propset', prop[0], prop[1], p.filename]) 279 ['propset', prop[0], prop[1], p.filename])
273 for prop, value in self.svn_config.auto_props.iteritems(): 280 for prop, value in self.svn_config.auto_props.iteritems():
274 if fnmatch.fnmatch(p.filename, prop): 281 if fnmatch.fnmatch(p.filename, prop):
275 stdout += self._check_output_svn( 282 stdout += self._check_output_svn(
276 ['propset'] + value.split('=', 1) + [p.filename]) 283 ['propset'] + value.split('=', 1) + [p.filename])
284 for post in post_processor:
285 post(self, p)
277 except OSError, e: 286 except OSError, e:
278 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) 287 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e))
279 except subprocess.CalledProcessError, e: 288 except subprocess.CalledProcessError, e:
280 raise PatchApplicationFailed( 289 raise PatchApplicationFailed(
281 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', ''))) 290 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', '')))
282 291
283 def commit(self, commit_message, user): 292 def commit(self, commit_message, user):
284 logging.info('Committing patch for %s' % user) 293 logging.info('Committing patch for %s' % user)
285 assert self.commit_user 294 assert self.commit_user
286 handle, commit_filename = tempfile.mkstemp(text=True) 295 handle, commit_filename = tempfile.mkstemp(text=True)
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 """ 354 """
346 assert os.path.isdir(self.project_path) 355 assert os.path.isdir(self.project_path)
347 self._check_call_git(['reset', '--hard', '--quiet']) 356 self._check_call_git(['reset', '--hard', '--quiet'])
348 branches, active = self._branches() 357 branches, active = self._branches()
349 if active != 'master': 358 if active != 'master':
350 self._check_call_git(['checkout', 'master', '--force', '--quiet']) 359 self._check_call_git(['checkout', 'master', '--force', '--quiet'])
351 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet']) 360 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet'])
352 if self.working_branch in branches: 361 if self.working_branch in branches:
353 self._call_git(['branch', '-D', self.working_branch]) 362 self._call_git(['branch', '-D', self.working_branch])
354 363
355 def apply_patch(self, patches): 364 def apply_patch(self, patches, post_processor=None):
356 """Applies a patch on 'working_branch' and switch to it.""" 365 """Applies a patch on 'working_branch' and switch to it.
366
367 Also commits the changes on the local branch.
368
369 Ignores svn properties and raise an exception on unexpected ones.
370 """
371 post_processor = post_processor or []
357 # It this throws, the checkout is corrupted. Maybe worth deleting it and 372 # It this throws, the checkout is corrupted. Maybe worth deleting it and
358 # trying again? 373 # trying again?
359 self._check_call_git( 374 self._check_call_git(
360 ['checkout', '-b', self.working_branch, 375 ['checkout', '-b', self.working_branch,
361 '%s/%s' % (self.remote, self.remote_branch)]) 376 '%s/%s' % (self.remote, self.remote_branch), '--quiet'])
362 for p in patches: 377 for p in patches:
363 try: 378 try:
364 stdout = '' 379 stdout = ''
365 if p.is_delete: 380 if p.is_delete:
366 stdout += self._check_output_git(['rm', p.filename]) 381 stdout += self._check_output_git(['rm', p.filename])
367 else: 382 else:
368 dirname = os.path.dirname(p.filename) 383 dirname = os.path.dirname(p.filename)
369 full_dir = os.path.join(self.project_path, dirname) 384 full_dir = os.path.join(self.project_path, dirname)
370 if dirname and not os.path.isdir(full_dir): 385 if dirname and not os.path.isdir(full_dir):
371 os.makedirs(full_dir) 386 os.makedirs(full_dir)
372 if p.is_binary: 387 if p.is_binary:
373 with open(os.path.join(self.project_path, p.filename), 'wb') as f: 388 with open(os.path.join(self.project_path, p.filename), 'wb') as f:
374 f.write(p.get()) 389 f.write(p.get())
375 stdout += self._check_output_git(['add', p.filename]) 390 stdout += self._check_output_git(['add', p.filename])
376 else: 391 else:
377 stdout += self._check_output_git( 392 stdout += self._check_output_git(
378 ['apply', '--index', '-p%s' % p.patchlevel], stdin=p.get()) 393 ['apply', '--index', '-p%s' % p.patchlevel], stdin=p.get())
379 for prop in p.svn_properties: 394 for prop in p.svn_properties:
380 # Ignore some known auto-props flags through .subversion/config, 395 # Ignore some known auto-props flags through .subversion/config,
381 # bails out on the other ones. 396 # bails out on the other ones.
382 # TODO(maruel): Read ~/.subversion/config and detect the rules that 397 # TODO(maruel): Read ~/.subversion/config and detect the rules that
383 # applies here to figure out if the property will be correctly 398 # applies here to figure out if the property will be correctly
384 # handled. 399 # handled.
385 if not prop[0] in ('svn:eol-style', 'svn:executable'): 400 if not prop[0] in ('svn:eol-style', 'svn:executable'):
386 raise patch.UnsupportedPatchFormat( 401 raise patch.UnsupportedPatchFormat(
387 p.filename, 402 p.filename,
388 'Cannot apply svn property %s to file %s.' % ( 403 'Cannot apply svn property %s to file %s.' % (
389 prop[0], p.filename)) 404 prop[0], p.filename))
405 for post in post_processor:
406 post(self, p)
390 except OSError, e: 407 except OSError, e:
391 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) 408 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e))
392 except subprocess.CalledProcessError, e: 409 except subprocess.CalledProcessError, e:
393 raise PatchApplicationFailed( 410 raise PatchApplicationFailed(
394 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None))) 411 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None)))
395 # Once all the patches are processed and added to the index, commit the 412 # Once all the patches are processed and added to the index, commit the
396 # index. 413 # index.
397 self._check_call_git(['commit', '-m', 'Committed patch']) 414 self._check_call_git(['commit', '-m', 'Committed patch'])
398 # TODO(maruel): Weirdly enough they don't match, need to investigate. 415 # TODO(maruel): Weirdly enough they don't match, need to investigate.
399 #found_files = self._check_output_git( 416 #found_files = self._check_output_git(
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
485 # Fix the commit message and author. It returns the git hash, which we 502 # Fix the commit message and author. It returns the git hash, which we
486 # ignore unless it's None. 503 # ignore unless it's None.
487 if not super(GitSvnCheckoutBase, self).commit(commit_message, user): 504 if not super(GitSvnCheckoutBase, self).commit(commit_message, user):
488 return None 505 return None
489 # TODO(maruel): git-svn ignores --config-dir as of git-svn version 1.7.4 and 506 # TODO(maruel): git-svn ignores --config-dir as of git-svn version 1.7.4 and
490 # doesn't support --with-revprop. 507 # doesn't support --with-revprop.
491 # Either learn perl and upstream or suck it. 508 # Either learn perl and upstream or suck it.
492 kwargs = {} 509 kwargs = {}
493 if self.commit_pwd: 510 if self.commit_pwd:
494 kwargs['stdin'] = self.commit_pwd + '\n' 511 kwargs['stdin'] = self.commit_pwd + '\n'
512 kwargs['stderr'] = subprocess2.STDOUT
495 self._check_call_git_svn( 513 self._check_call_git_svn(
496 ['dcommit', '--rmdir', '--find-copies-harder', 514 ['dcommit', '--rmdir', '--find-copies-harder',
497 '--username', self.commit_user], 515 '--username', self.commit_user],
498 **kwargs) 516 **kwargs)
499 revision = int(self._git_svn_info('revision')) 517 revision = int(self._git_svn_info('revision'))
500 return revision 518 return revision
501 519
502 def _cache_svn_auth(self): 520 def _cache_svn_auth(self):
503 """Caches the svn credentials. It is necessary since git-svn doesn't prompt 521 """Caches the svn credentials. It is necessary since git-svn doesn't prompt
504 for it.""" 522 for it."""
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
540 assert self.git_url 558 assert self.git_url
541 559
542 def prepare(self): 560 def prepare(self):
543 """Creates the initial checkout for the repo.""" 561 """Creates the initial checkout for the repo."""
544 if not os.path.isdir(self.project_path): 562 if not os.path.isdir(self.project_path):
545 logging.info('Checking out %s in %s' % 563 logging.info('Checking out %s in %s' %
546 (self.project_name, self.project_path)) 564 (self.project_name, self.project_path))
547 assert self.remote == 'origin' 565 assert self.remote == 'origin'
548 # self.project_path doesn't exist yet. 566 # self.project_path doesn't exist yet.
549 self._check_call_git( 567 self._check_call_git(
550 ['clone', self.git_url, self.project_name], 568 ['clone', self.git_url, self.project_name, '--quiet'],
551 cwd=self.root_dir) 569 cwd=self.root_dir,
570 stderr=subprocess2.STDOUT)
552 try: 571 try:
553 configured_svn_url = self._check_output_git( 572 configured_svn_url = self._check_output_git(
554 ['config', 'svn-remote.svn.url']).strip() 573 ['config', 'svn-remote.svn.url']).strip()
555 except subprocess.CalledProcessError: 574 except subprocess.CalledProcessError:
556 configured_svn_url = '' 575 configured_svn_url = ''
557 576
558 if configured_svn_url.strip() != self.svn_url: 577 if configured_svn_url.strip() != self.svn_url:
559 self._check_call_git_svn( 578 self._check_call_git_svn(
560 ['init', 579 ['init',
561 '--prefix', self.remote + '/', 580 '--prefix', self.remote + '/',
(...skipping 22 matching lines...) Expand all
584 """Creates the initial checkout for the repo.""" 603 """Creates the initial checkout for the repo."""
585 if not os.path.isdir(self.project_path): 604 if not os.path.isdir(self.project_path):
586 logging.info('Checking out %s in %s' % 605 logging.info('Checking out %s in %s' %
587 (self.project_name, self.project_path)) 606 (self.project_name, self.project_path))
588 # TODO: Create a shallow clone. 607 # TODO: Create a shallow clone.
589 # self.project_path doesn't exist yet. 608 # self.project_path doesn't exist yet.
590 self._check_call_git_svn( 609 self._check_call_git_svn(
591 ['clone', 610 ['clone',
592 '--prefix', self.remote + '/', 611 '--prefix', self.remote + '/',
593 '-T', self.trunk, 612 '-T', self.trunk,
594 self.svn_url, self.project_path], 613 self.svn_url, self.project_path,
595 cwd=self.root_dir) 614 '--quiet'],
615 cwd=self.root_dir,
616 stderr=subprocess2.STDOUT)
596 super(GitSvnCheckout, self).prepare() 617 super(GitSvnCheckout, self).prepare()
597 return self._get_revision() 618 return self._get_revision()
598 619
599 620
600 class ReadOnlyCheckout(object): 621 class ReadOnlyCheckout(object):
601 """Converts a checkout into a read-only one.""" 622 """Converts a checkout into a read-only one."""
602 def __init__(self, checkout): 623 def __init__(self, checkout):
603 self.checkout = checkout 624 self.checkout = checkout
604 625
605 def prepare(self): 626 def prepare(self):
606 return self.checkout.prepare() 627 return self.checkout.prepare()
607 628
608 def get_settings(self, key): 629 def get_settings(self, key):
609 return self.checkout.get_settings(key) 630 return self.checkout.get_settings(key)
610 631
611 def apply_patch(self, patches): 632 def apply_patch(self, patches, post_processor=None):
612 return self.checkout.apply_patch(patches) 633 return self.checkout.apply_patch(patches, post_processor)
613 634
614 def commit(self, message, user): # pylint: disable=R0201 635 def commit(self, message, user): # pylint: disable=R0201
615 logging.info('Would have committed for %s with message: %s' % ( 636 logging.info('Would have committed for %s with message: %s' % (
616 user, message)) 637 user, message))
617 return 'FAKE' 638 return 'FAKE'
618 639
619 @property 640 @property
620 def project_name(self): 641 def project_name(self):
621 return self.checkout.project_name 642 return self.checkout.project_name
622 643
623 @property 644 @property
624 def project_path(self): 645 def project_path(self):
625 return self.checkout.project_path 646 return self.checkout.project_path
OLDNEW
« no previous file with comments | « no previous file | patch.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698