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

Side by Side Diff: checkout.py

Issue 6995115: Make prepare() accept a revision argument. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 9 years, 6 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 | tests/checkout_test.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 self.project_path = os.path.join(self.root_dir, self.project_name) 73 self.project_path = os.path.join(self.root_dir, self.project_name)
74 # Only used for logging purposes. 74 # Only used for logging purposes.
75 self._last_seen_revision = None 75 self._last_seen_revision = None
76 self.post_processors = None 76 self.post_processors = None
77 assert self.root_dir 77 assert self.root_dir
78 assert self.project_path 78 assert self.project_path
79 79
80 def get_settings(self, key): 80 def get_settings(self, key):
81 return get_code_review_setting(self.project_path, key) 81 return get_code_review_setting(self.project_path, key)
82 82
83 def prepare(self): 83 def prepare(self, revision):
84 """Checks out a clean copy of the tree and removes any local modification. 84 """Checks out a clean copy of the tree and removes any local modification.
85 85
86 This function shouldn't throw unless the remote repository is inaccessible, 86 This function shouldn't throw unless the remote repository is inaccessible,
87 there is no free disk space or hard issues like that. 87 there is no free disk space or hard issues like that.
88
89 Args:
90 revision: The revision it should sync to, SCM specific.
88 """ 91 """
89 raise NotImplementedError() 92 raise NotImplementedError()
90 93
91 def apply_patch(self, patches): 94 def apply_patch(self, patches):
92 """Applies a patch and returns the list of modified files. 95 """Applies a patch and returns the list of modified files.
93 96
94 This function should throw patch.UnsupportedPatchFormat or 97 This function should throw patch.UnsupportedPatchFormat or
95 PatchApplicationFailed when relevant. 98 PatchApplicationFailed when relevant.
96 99
97 Args: 100 Args:
98 patches: patch.PatchSet object. 101 patches: patch.PatchSet object.
99 """ 102 """
100 raise NotImplementedError() 103 raise NotImplementedError()
101 104
102 def commit(self, commit_message, user): 105 def commit(self, commit_message, user):
103 """Commits the patch upstream, while impersonating 'user'.""" 106 """Commits the patch upstream, while impersonating 'user'."""
104 raise NotImplementedError() 107 raise NotImplementedError()
105 108
106 109
107 class RawCheckout(CheckoutBase): 110 class RawCheckout(CheckoutBase):
108 """Used to apply a patch locally without any intent to commit it. 111 """Used to apply a patch locally without any intent to commit it.
109 112
110 To be used by the try server. 113 To be used by the try server.
111 """ 114 """
112 def prepare(self): 115 def prepare(self, revision):
113 """Stubbed out.""" 116 """Stubbed out."""
114 pass 117 pass
115 118
116 def apply_patch(self, patches): 119 def apply_patch(self, patches):
117 """Ignores svn properties.""" 120 """Ignores svn properties."""
118 for p in patches: 121 for p in patches:
119 try: 122 try:
120 stdout = '' 123 stdout = ''
121 filename = os.path.join(self.project_path, p.filename) 124 filename = os.path.join(self.project_path, p.filename)
122 if p.is_delete: 125 if p.is_delete:
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 class SvnCheckout(CheckoutBase, SvnMixIn): 240 class SvnCheckout(CheckoutBase, SvnMixIn):
238 """Manages a subversion checkout.""" 241 """Manages a subversion checkout."""
239 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url, 242 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url,
240 post_processors=None): 243 post_processors=None):
241 super(SvnCheckout, self).__init__(root_dir, project_name, post_processors) 244 super(SvnCheckout, self).__init__(root_dir, project_name, post_processors)
242 self.commit_user = commit_user 245 self.commit_user = commit_user
243 self.commit_pwd = commit_pwd 246 self.commit_pwd = commit_pwd
244 self.svn_url = svn_url 247 self.svn_url = svn_url
245 assert bool(self.commit_user) >= bool(self.commit_pwd) 248 assert bool(self.commit_user) >= bool(self.commit_pwd)
246 249
247 def prepare(self): 250 def prepare(self, revision):
248 # Will checkout if the directory is not present. 251 # Will checkout if the directory is not present.
249 assert self.svn_url 252 assert self.svn_url
250 if not os.path.isdir(self.project_path): 253 if not os.path.isdir(self.project_path):
251 logging.info('Checking out %s in %s' % 254 logging.info('Checking out %s in %s' %
252 (self.project_name, self.project_path)) 255 (self.project_name, self.project_path))
253 revision = self._revert() 256 return self._revert(revision)
254 if revision != self._last_seen_revision:
255 logging.info('Updated at revision %d' % revision)
256 self._last_seen_revision = revision
257 return revision
258 257
259 def apply_patch(self, patches): 258 def apply_patch(self, patches):
260 for p in patches: 259 for p in patches:
261 try: 260 try:
262 # It is important to use credentials=False otherwise credentials could 261 # It is important to use credentials=False otherwise credentials could
263 # leak in the error message. Credentials are not necessary here for the 262 # leak in the error message. Credentials are not necessary here for the
264 # following commands anyway. 263 # following commands anyway.
265 stdout = '' 264 stdout = ''
266 if p.is_delete: 265 if p.is_delete:
267 stdout += self._check_output_svn( 266 stdout += self._check_output_svn(
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 finally: 343 finally:
345 os.remove(commit_filename) 344 os.remove(commit_filename)
346 lines = filter(None, out.splitlines()) 345 lines = filter(None, out.splitlines())
347 match = re.match(r'^Committed revision (\d+).$', lines[-1]) 346 match = re.match(r'^Committed revision (\d+).$', lines[-1])
348 if not match: 347 if not match:
349 raise PatchApplicationFailed( 348 raise PatchApplicationFailed(
350 None, 349 None,
351 'Couldn\'t make sense out of svn commit message:\n' + out) 350 'Couldn\'t make sense out of svn commit message:\n' + out)
352 return int(match.group(1)) 351 return int(match.group(1))
353 352
354 def _revert(self): 353 def _revert(self, revision):
355 """Reverts local modifications or checks out if the directory is not 354 """Reverts local modifications or checks out if the directory is not
356 present. Use depot_tools's functionality to do this. 355 present. Use depot_tools's functionality to do this.
357 """ 356 """
358 flags = ['--ignore-externals'] 357 flags = ['--ignore-externals']
358 if revision:
359 flags.extend(['--revision', str(revision)])
359 if not os.path.isdir(self.project_path): 360 if not os.path.isdir(self.project_path):
360 logging.info( 361 logging.info(
361 'Directory %s is not present, checking it out.' % self.project_path) 362 'Directory %s is not present, checking it out.' % self.project_path)
362 self._check_call_svn( 363 self._check_call_svn(
363 ['checkout', self.svn_url, self.project_path] + flags, cwd=None) 364 ['checkout', self.svn_url, self.project_path] + flags, cwd=None)
364 else: 365 else:
365 scm.SVN.Revert(self.project_path) 366 scm.SVN.Revert(self.project_path)
366 # Revive files that were deleted in scm.SVN.Revert(). 367 # Revive files that were deleted in scm.SVN.Revert().
367 self._check_call_svn(['update', '--force'] + flags) 368 self._check_call_svn(['update', '--force'] + flags)
369 return self._get_revision()
368 370
371 def _get_revision(self):
369 out = self._check_output_svn(['info', '.']) 372 out = self._check_output_svn(['info', '.'])
370 return int(self._parse_svn_info(out, 'revision')) 373 revision = int(self._parse_svn_info(out, 'revision'))
374 if revision != self._last_seen_revision:
375 logging.info('Updated at revision %d' % revision)
Dirk Pranke 2011/06/10 21:07:45 Nit: "Updated to revision" ?
376 self._last_seen_revision = revision
377 return revision
371 378
372 379
373 class GitCheckoutBase(CheckoutBase): 380 class GitCheckoutBase(CheckoutBase):
374 """Base class for git checkout. Not to be used as-is.""" 381 """Base class for git checkout. Not to be used as-is."""
375 def __init__(self, root_dir, project_name, remote_branch, 382 def __init__(self, root_dir, project_name, remote_branch,
376 post_processors=None): 383 post_processors=None):
377 super(GitCheckoutBase, self).__init__( 384 super(GitCheckoutBase, self).__init__(
378 root_dir, project_name, post_processors) 385 root_dir, project_name, post_processors)
379 # There is no reason to not hardcode it. 386 # There is no reason to not hardcode it.
380 self.remote = 'origin' 387 self.remote = 'origin'
381 self.remote_branch = remote_branch 388 self.remote_branch = remote_branch
382 self.working_branch = 'working_branch' 389 self.working_branch = 'working_branch'
383 390
384 def prepare(self): 391 def prepare(self, revision):
385 """Resets the git repository in a clean state. 392 """Resets the git repository in a clean state.
386 393
387 Checks it out if not present and deletes the working branch. 394 Checks it out if not present and deletes the working branch.
388 """ 395 """
389 assert self.remote_branch 396 assert self.remote_branch
390 assert os.path.isdir(self.project_path) 397 assert os.path.isdir(self.project_path)
391 self._check_call_git(['reset', '--hard', '--quiet']) 398 self._check_call_git(['reset', '--hard', '--quiet'])
392 branches, active = self._branches() 399 if revision:
393 if active != 'master': 400 try:
394 self._check_call_git(['checkout', 'master', '--force', '--quiet']) 401 revision = self._check_output_git(['rev-parse', revision])
395 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet']) 402 except subprocess.CalledProcessError:
396 if self.working_branch in branches: 403 self._check_call_git(
397 self._call_git(['branch', '-D', self.working_branch]) 404 ['fetch', self.remote, self.remote_branch, '--quiet'])
405 revision = self._check_output_git(['rev-parse', revision])
406 self._check_call_git(['checkout', '--force', '--quiet', revision])
407 else:
408 branches, active = self._branches()
409 if active != 'master':
410 self._check_call_git(['checkout', '--force', '--quiet', 'master'])
411 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet'])
412 if self.working_branch in branches:
413 self._call_git(['branch', '-D', self.working_branch])
398 414
399 def apply_patch(self, patches): 415 def apply_patch(self, patches):
400 """Applies a patch on 'working_branch' and switch to it. 416 """Applies a patch on 'working_branch' and switch to it.
401 417
402 Also commits the changes on the local branch. 418 Also commits the changes on the local branch.
403 419
404 Ignores svn properties and raise an exception on unexpected ones. 420 Ignores svn properties and raise an exception on unexpected ones.
405 """ 421 """
406 # It this throws, the checkout is corrupted. Maybe worth deleting it and 422 # It this throws, the checkout is corrupted. Maybe worth deleting it and
407 # trying again? 423 # trying again?
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 self.commit_user = commit_user 519 self.commit_user = commit_user
504 self.commit_pwd = commit_pwd 520 self.commit_pwd = commit_pwd
505 # svn_url in this case is the root of the svn repository. 521 # svn_url in this case is the root of the svn repository.
506 self.svn_url = svn_url 522 self.svn_url = svn_url
507 self.trunk = trunk 523 self.trunk = trunk
508 assert bool(self.commit_user) >= bool(self.commit_pwd) 524 assert bool(self.commit_user) >= bool(self.commit_pwd)
509 assert self.svn_url 525 assert self.svn_url
510 assert self.trunk 526 assert self.trunk
511 self._cache_svn_auth() 527 self._cache_svn_auth()
512 528
513 def prepare(self): 529 def prepare(self, revision):
514 """Resets the git repository in a clean state.""" 530 """Resets the git repository in a clean state."""
515 self._check_call_git(['reset', '--hard', '--quiet']) 531 self._check_call_git(['reset', '--hard', '--quiet'])
516 branches, active = self._branches() 532 if revision:
517 if active != 'master': 533 try:
518 if not 'master' in branches: 534 revision = self._check_output_git(
535 ['svn', 'find-rev', 'r%d' % revision])
536 except subprocess.CalledProcessError:
519 self._check_call_git( 537 self._check_call_git(
520 ['checkout', '--quiet', '-b', 'master', 538 ['fetch', self.remote, self.remote_branch, '--quiet'])
521 '%s/%s' % (self.remote, self.remote_branch)]) 539 revision = self._check_output_git(
522 else: 540 ['svn', 'find-rev', 'r%d' % revision])
523 self._check_call_git(['checkout', 'master', '--force', '--quiet']) 541 super(GitSvnCheckoutBase, self).prepare(revision)
524 # git svn rebase --quiet --quiet doesn't work, use two steps to silence it. 542 else:
525 self._check_call_git_svn(['fetch', '--quiet', '--quiet']) 543 branches, active = self._branches()
526 self._check_call_git( 544 if active != 'master':
527 ['rebase', '--quiet', '--quiet', 545 if not 'master' in branches:
528 '%s/%s' % (self.remote, self.remote_branch)]) 546 self._check_call_git(
529 if self.working_branch in branches: 547 ['checkout', '--quiet', '-b', 'master',
530 self._call_git(['branch', '-D', self.working_branch]) 548 '%s/%s' % (self.remote, self.remote_branch)])
531 return int(self._git_svn_info('revision')) 549 else:
550 self._check_call_git(['checkout', 'master', '--force', '--quiet'])
551 # git svn rebase --quiet --quiet doesn't work, use two steps to silence
552 # it.
553 self._check_call_git_svn(['fetch', '--quiet', '--quiet'])
554 self._check_call_git(
555 ['rebase', '--quiet', '--quiet',
556 '%s/%s' % (self.remote, self.remote_branch)])
557 if self.working_branch in branches:
558 self._call_git(['branch', '-D', self.working_branch])
559 return self._get_revision()
532 560
533 def _git_svn_info(self, key): 561 def _git_svn_info(self, key):
534 """Calls git svn info. This doesn't support nor need --config-dir.""" 562 """Calls git svn info. This doesn't support nor need --config-dir."""
535 return self._parse_svn_info(self._check_output_git(['svn', 'info']), key) 563 return self._parse_svn_info(self._check_output_git(['svn', 'info']), key)
536 564
537 def commit(self, commit_message, user): 565 def commit(self, commit_message, user):
538 """Commits a patch.""" 566 """Commits a patch."""
539 logging.info('Committing patch for %s' % user) 567 logging.info('Committing patch for %s' % user)
540 # Fix the commit message and author. It returns the git hash, which we 568 # Fix the commit message and author. It returns the git hash, which we
541 # ignore unless it's None. 569 # ignore unless it's None.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 root_dir, project_name, remote_branch, 616 root_dir, project_name, remote_branch,
589 commit_user, commit_pwd, 617 commit_user, commit_pwd,
590 svn_url, trunk, git_url, post_processors=None): 618 svn_url, trunk, git_url, post_processors=None):
591 super(GitSvnPremadeCheckout, self).__init__( 619 super(GitSvnPremadeCheckout, self).__init__(
592 root_dir, project_name, remote_branch, 620 root_dir, project_name, remote_branch,
593 commit_user, commit_pwd, 621 commit_user, commit_pwd,
594 svn_url, trunk, post_processors) 622 svn_url, trunk, post_processors)
595 self.git_url = git_url 623 self.git_url = git_url
596 assert self.git_url 624 assert self.git_url
597 625
598 def prepare(self): 626 def prepare(self, revision):
599 """Creates the initial checkout for the repo.""" 627 """Creates the initial checkout for the repo."""
600 if not os.path.isdir(self.project_path): 628 if not os.path.isdir(self.project_path):
601 logging.info('Checking out %s in %s' % 629 logging.info('Checking out %s in %s' %
602 (self.project_name, self.project_path)) 630 (self.project_name, self.project_path))
603 assert self.remote == 'origin' 631 assert self.remote == 'origin'
604 # self.project_path doesn't exist yet. 632 # self.project_path doesn't exist yet.
605 self._check_call_git( 633 self._check_call_git(
606 ['clone', self.git_url, self.project_name, '--quiet'], 634 ['clone', self.git_url, self.project_name, '--quiet'],
607 cwd=self.root_dir, 635 cwd=self.root_dir,
608 stderr=subprocess2.STDOUT) 636 stderr=subprocess2.STDOUT)
609 try: 637 try:
610 configured_svn_url = self._check_output_git( 638 configured_svn_url = self._check_output_git(
611 ['config', 'svn-remote.svn.url']).strip() 639 ['config', 'svn-remote.svn.url']).strip()
612 except subprocess.CalledProcessError: 640 except subprocess.CalledProcessError:
613 configured_svn_url = '' 641 configured_svn_url = ''
614 642
615 if configured_svn_url.strip() != self.svn_url: 643 if configured_svn_url.strip() != self.svn_url:
616 self._check_call_git_svn( 644 self._check_call_git_svn(
617 ['init', 645 ['init',
618 '--prefix', self.remote + '/', 646 '--prefix', self.remote + '/',
619 '-T', self.trunk, 647 '-T', self.trunk,
620 self.svn_url]) 648 self.svn_url])
621 self._check_call_git_svn(['fetch']) 649 self._check_call_git_svn(['fetch'])
622 super(GitSvnPremadeCheckout, self).prepare() 650 return super(GitSvnPremadeCheckout, self).prepare(revision)
623 return self._get_revision()
624 651
625 652
626 class GitSvnCheckout(GitSvnCheckoutBase): 653 class GitSvnCheckout(GitSvnCheckoutBase):
627 """Manages a git-svn clone. 654 """Manages a git-svn clone.
628 655
629 Using git-svn hides some of the complexity of using a svn checkout. 656 Using git-svn hides some of the complexity of using a svn checkout.
630 """ 657 """
631 def __init__(self, 658 def __init__(self,
632 root_dir, project_name, 659 root_dir, project_name,
633 commit_user, commit_pwd, 660 commit_user, commit_pwd,
634 svn_url, trunk, post_processors=None): 661 svn_url, trunk, post_processors=None):
635 super(GitSvnCheckout, self).__init__( 662 super(GitSvnCheckout, self).__init__(
636 root_dir, project_name, 'trunk', 663 root_dir, project_name, 'trunk',
637 commit_user, commit_pwd, 664 commit_user, commit_pwd,
638 svn_url, trunk, post_processors) 665 svn_url, trunk, post_processors)
639 666
640 def prepare(self): 667 def prepare(self, revision):
641 """Creates the initial checkout for the repo.""" 668 """Creates the initial checkout for the repo."""
669 assert not revision, 'Implement revision if necessary'
642 if not os.path.isdir(self.project_path): 670 if not os.path.isdir(self.project_path):
643 logging.info('Checking out %s in %s' % 671 logging.info('Checking out %s in %s' %
644 (self.project_name, self.project_path)) 672 (self.project_name, self.project_path))
645 # TODO: Create a shallow clone. 673 # TODO: Create a shallow clone.
646 # self.project_path doesn't exist yet. 674 # self.project_path doesn't exist yet.
647 self._check_call_git_svn( 675 self._check_call_git_svn(
648 ['clone', 676 ['clone',
649 '--prefix', self.remote + '/', 677 '--prefix', self.remote + '/',
650 '-T', self.trunk, 678 '-T', self.trunk,
651 self.svn_url, self.project_path, 679 self.svn_url, self.project_path,
652 '--quiet'], 680 '--quiet'],
653 cwd=self.root_dir, 681 cwd=self.root_dir,
654 stderr=subprocess2.STDOUT) 682 stderr=subprocess2.STDOUT)
655 super(GitSvnCheckout, self).prepare() 683 return super(GitSvnCheckout, self).prepare(revision)
656 return self._get_revision()
657 684
658 685
659 class ReadOnlyCheckout(object): 686 class ReadOnlyCheckout(object):
660 """Converts a checkout into a read-only one.""" 687 """Converts a checkout into a read-only one."""
661 def __init__(self, checkout): 688 def __init__(self, checkout):
662 self.checkout = checkout 689 self.checkout = checkout
663 690
664 def prepare(self): 691 def prepare(self, revision):
665 return self.checkout.prepare() 692 return self.checkout.prepare(revision)
666 693
667 def get_settings(self, key): 694 def get_settings(self, key):
668 return self.checkout.get_settings(key) 695 return self.checkout.get_settings(key)
669 696
670 def apply_patch(self, patches): 697 def apply_patch(self, patches):
671 return self.checkout.apply_patch(patches) 698 return self.checkout.apply_patch(patches)
672 699
673 def commit(self, message, user): # pylint: disable=R0201 700 def commit(self, message, user): # pylint: disable=R0201
674 logging.info('Would have committed for %s with message: %s' % ( 701 logging.info('Would have committed for %s with message: %s' % (
675 user, message)) 702 user, message))
676 return 'FAKE' 703 return 'FAKE'
677 704
678 @property 705 @property
679 def project_name(self): 706 def project_name(self):
680 return self.checkout.project_name 707 return self.checkout.project_name
681 708
682 @property 709 @property
683 def project_path(self): 710 def project_path(self):
684 return self.checkout.project_path 711 return self.checkout.project_path
OLDNEW
« no previous file with comments | « no previous file | tests/checkout_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698