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

Side by Side Diff: checkout.py

Issue 175873003: Fix GitCheckout.prepare to be sane and use git-cache (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Rely on default origin refspec Created 6 years, 9 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 | no next file » | 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) 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 """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 import ConfigParser 10 import ConfigParser
(...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 commit_user, post_processors=None, base_ref=None): 559 commit_user, post_processors=None, base_ref=None):
560 super(GitCheckout, self).__init__(root_dir, project_name, post_processors) 560 super(GitCheckout, self).__init__(root_dir, project_name, post_processors)
561 self.base_ref = base_ref 561 self.base_ref = base_ref
562 self.git_url = git_url 562 self.git_url = git_url
563 self.commit_user = commit_user 563 self.commit_user = commit_user
564 self.remote_branch = remote_branch 564 self.remote_branch = remote_branch
565 # The working branch where patches will be applied. It will track the 565 # The working branch where patches will be applied. It will track the
566 # remote branch. 566 # remote branch.
567 self.working_branch = 'working_branch' 567 self.working_branch = 'working_branch'
568 # There is no reason to not hardcode origin. 568 # There is no reason to not hardcode origin.
569 self.remote = 'origin' 569 self.pull_remote = 'origin'
570 # There is no reason to not hardcode master. 570 self.push_remote = 'upstream'
571 self.master_branch = 'master'
572 571
573 def prepare(self, revision): 572 def prepare(self, revision):
574 """Resets the git repository in a clean state. 573 """Resets the git repository in a clean state.
575 574
576 Checks it out if not present and deletes the working branch. 575 Checks it out if not present and deletes the working branch.
577 """ 576 """
577 assert self.git_url
578 assert self.remote_branch 578 assert self.remote_branch
579 assert self.git_url 579
580 self._check_call_git(
581 ['cache', 'populate', self.git_url], timeout=FETCH_TIMEOUT, cwd=None)
582 cache_path = self._check_output_git(
583 ['cache', 'exists', self.git_url], cwd=None).strip()
580 584
581 if not os.path.isdir(self.project_path): 585 if not os.path.isdir(self.project_path):
582 # Clone the repo if the directory is not present. 586 # Clone the repo if the directory is not present.
583 logging.info(
584 'Checking out %s in %s', self.project_name, self.project_path)
585 self._check_call_git( 587 self._check_call_git(
586 ['clone', self.git_url, '-b', self.remote_branch, self.project_path], 588 ['clone', '--shared', cache_path, self.project_path],
587 cwd=None, timeout=FETCH_TIMEOUT) 589 cwd=None, timeout=FETCH_TIMEOUT)
588 else: 590 self._call_git(
589 # Throw away all uncommitted changes in the existing checkout. 591 ['config', 'remote.%s.url' % self.push_remote, self.git_url],
590 self._check_call_git(['checkout', self.remote_branch]) 592 cwd=self.project_path)
591 self._check_call_git(
592 ['reset', '--hard', '--quiet',
593 '%s/%s' % (self.remote, self.remote_branch)])
594 593
595 if revision: 594 if not revision:
596 try: 595 revision = self.remote_branch
597 # Look if the commit hash already exist. If so, we can skip a
598 # 'git fetch' call.
599 revision = self._check_output_git(['rev-parse', revision])
600 except subprocess.CalledProcessError:
601 self._check_call_git(
602 ['fetch', self.remote, self.remote_branch, '--quiet'])
603 revision = self._check_output_git(['rev-parse', revision])
604 self._check_call_git(['checkout', '--force', '--quiet', revision])
605 else:
606 branches, active = self._branches()
607 if active != self.master_branch:
608 self._check_call_git(
609 ['checkout', '--force', '--quiet', self.master_branch])
610 self._sync_remote_branch()
611 596
612 if self.working_branch in branches: 597 if not re.match(r'[0-9a-f]{40}$', revision, flags=re.IGNORECASE):
613 self._call_git(['branch', '-D', self.working_branch]) 598 self._check_call_git(['fetch', self.pull_remote, revision])
599 revision = self.pull_remote + '/' + revision
600
601 self._check_call_git(['checkout', '--force', '--quiet', revision])
602 self._call_git(['clean', '-fdx'])
603
604 branches, _ = self._branches()
605 if self.working_branch in branches:
606 self._call_git(['branch', '-D', self.working_branch])
607
614 return self._get_head_commit_hash() 608 return self._get_head_commit_hash()
615 609
616 def _sync_remote_branch(self):
617 """Syncs the remote branch."""
618 # We do a 'git pull origin master:refs/remotes/origin/master' instead of
619 # 'git pull origin master' because from the manpage for git-pull:
620 # A parameter <ref> without a colon is equivalent to <ref>: when
621 # pulling/fetching, so it merges <ref> into the current branch without
622 # storing the remote branch anywhere locally.
623 remote_tracked_path = 'refs/remotes/%s/%s' % (
624 self.remote, self.remote_branch)
625 self._check_call_git(
626 ['pull', self.remote,
627 '%s:%s' % (self.remote_branch, remote_tracked_path),
628 '--quiet'])
629
630 def _get_head_commit_hash(self): 610 def _get_head_commit_hash(self):
631 """Gets the current revision (in unicode) from the local branch.""" 611 """Gets the current revision (in unicode) from the local branch."""
632 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip()) 612 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip())
633 613
634 def apply_patch(self, patches, post_processors=None, verbose=False, 614 def apply_patch(self, patches, post_processors=None, verbose=False,
635 name=None, email=None): 615 name=None, email=None):
636 """Applies a patch on 'working_branch' and switches to it. 616 """Applies a patch on 'working_branch' and switches to it.
637 617
638 Also commits the changes on the local branch. 618 Also commits the changes on the local branch.
639 619
640 Ignores svn properties and raise an exception on unexpected ones. 620 Ignores svn properties and raise an exception on unexpected ones.
641 """ 621 """
642 post_processors = post_processors or self.post_processors or [] 622 post_processors = post_processors or self.post_processors or []
643 # It this throws, the checkout is corrupted. Maybe worth deleting it and 623 # It this throws, the checkout is corrupted. Maybe worth deleting it and
644 # trying again? 624 # trying again?
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
713 # index. 693 # index.
714 cmd = ['commit', '-m', 'Committed patch'] 694 cmd = ['commit', '-m', 'Committed patch']
715 if name and email: 695 if name and email:
716 cmd = ['-c', 'user.email=%s' % email, '-c', 'user.name=%s' % name] + cmd 696 cmd = ['-c', 'user.email=%s' % email, '-c', 'user.name=%s' % name] + cmd
717 if verbose: 697 if verbose:
718 cmd.append('--verbose') 698 cmd.append('--verbose')
719 self._check_call_git(cmd) 699 self._check_call_git(cmd)
720 if self.base_ref: 700 if self.base_ref:
721 base_ref = self.base_ref 701 base_ref = self.base_ref
722 else: 702 else:
723 base_ref = '%s/%s' % (self.remote, 703 base_ref = '%s/%s' % (self.pull_remote, self.remote_branch)
724 self.remote_branch or self.master_branch)
725 found_files = self._check_output_git( 704 found_files = self._check_output_git(
726 ['diff', base_ref, 705 ['diff', base_ref,
727 '--name-only']).splitlines(False) 706 '--name-only']).splitlines(False)
728 assert sorted(patches.filenames) == sorted(found_files), ( 707 assert sorted(patches.filenames) == sorted(found_files), (
729 sorted(patches.filenames), sorted(found_files)) 708 sorted(patches.filenames), sorted(found_files))
730 709
731 def commit(self, commit_message, user): 710 def commit(self, commit_message, user):
732 """Commits, updates the commit message and pushes.""" 711 """Commits, updates the commit message and pushes."""
733 assert self.commit_user 712 assert self.commit_user
734 assert isinstance(commit_message, unicode) 713 assert isinstance(commit_message, unicode)
735 current_branch = self._check_output_git( 714 current_branch = self._check_output_git(
736 ['rev-parse', '--abbrev-ref', 'HEAD']).strip() 715 ['rev-parse', '--abbrev-ref', 'HEAD']).strip()
737 assert current_branch == self.working_branch 716 assert current_branch == self.working_branch
738 717
739 commit_cmd = ['commit', '--amend', '-m', commit_message] 718 commit_cmd = ['commit', '--amend', '-m', commit_message]
740 if user and user != self.commit_user: 719 if user and user != self.commit_user:
741 # We do not have the first or last name of the user, grab the username 720 # We do not have the first or last name of the user, grab the username
742 # from the email and call it the original author's name. 721 # from the email and call it the original author's name.
743 # TODO(rmistry): Do not need the below if user is already in 722 # TODO(rmistry): Do not need the below if user is already in
744 # "Name <email>" format. 723 # "Name <email>" format.
745 name = user.split('@')[0] 724 name = user.split('@')[0]
746 commit_cmd.extend(['--author', '%s <%s>' % (name, user)]) 725 commit_cmd.extend(['--author', '%s <%s>' % (name, user)])
747 self._check_call_git(commit_cmd) 726 self._check_call_git(commit_cmd)
748 727
749 # Push to the remote repository. 728 # Push to the remote repository.
750 self._check_call_git( 729 self._check_call_git(
751 ['push', 'origin', '%s:%s' % (self.working_branch, self.remote_branch), 730 ['push', self.push_remote,
731 '%s:%s' % (self.working_branch, self.remote_branch),
752 '--force', '--quiet']) 732 '--force', '--quiet'])
753 # Get the revision after the push. 733 # Get the revision after the push.
754 revision = self._get_head_commit_hash() 734 revision = self._get_head_commit_hash()
755 # Switch back to the remote_branch and sync it. 735 # Switch back to the remote_branch.
756 self._check_call_git(['checkout', self.remote_branch]) 736 self._check_call_git(['cache', 'populate', self.git_url])
757 self._sync_remote_branch() 737 self._check_call_git(['fetch', self.pull_remote])
738 self._check_call_git(['checkout', '--force', '--quiet',
739 '%s/%s' % (self.pull_remote, self.remote_branch)])
758 # Delete the working branch since we are done with it. 740 # Delete the working branch since we are done with it.
759 self._check_call_git(['branch', '-D', self.working_branch]) 741 self._check_call_git(['branch', '-D', self.working_branch])
760 742
761 return revision 743 return revision
762 744
763 def _check_call_git(self, args, **kwargs): 745 def _check_call_git(self, args, **kwargs):
764 kwargs.setdefault('cwd', self.project_path) 746 kwargs.setdefault('cwd', self.project_path)
765 kwargs.setdefault('stdout', self.VOID) 747 kwargs.setdefault('stdout', self.VOID)
766 kwargs.setdefault('timeout', GLOBAL_TIMEOUT) 748 kwargs.setdefault('timeout', GLOBAL_TIMEOUT)
767 return subprocess2.check_call_out(['git'] + args, **kwargs) 749 return subprocess2.check_call_out(['git'] + args, **kwargs)
(...skipping 19 matching lines...) Expand all
787 for l in out: 769 for l in out:
788 if l.startswith('*'): 770 if l.startswith('*'):
789 active = l[2:] 771 active = l[2:]
790 break 772 break
791 return branches, active 773 return branches, active
792 774
793 def revisions(self, rev1, rev2): 775 def revisions(self, rev1, rev2):
794 """Returns the number of actual commits between both hash.""" 776 """Returns the number of actual commits between both hash."""
795 self._fetch_remote() 777 self._fetch_remote()
796 778
797 rev2 = rev2 or '%s/%s' % (self.remote, self.remote_branch) 779 rev2 = rev2 or '%s/%s' % (self.pull_remote, self.remote_branch)
798 # Revision range is ]rev1, rev2] and ordering matters. 780 # Revision range is ]rev1, rev2] and ordering matters.
799 try: 781 try:
800 out = self._check_output_git( 782 out = self._check_output_git(
801 ['log', '--format="%H"' , '%s..%s' % (rev1, rev2)]) 783 ['log', '--format="%H"' , '%s..%s' % (rev1, rev2)])
802 except subprocess.CalledProcessError: 784 except subprocess.CalledProcessError:
803 return None 785 return None
804 return len(out.splitlines()) 786 return len(out.splitlines())
805 787
806 def _fetch_remote(self): 788 def _fetch_remote(self):
807 """Fetches the remote without rebasing.""" 789 """Fetches the remote without rebasing."""
808 # git fetch is always verbose even with -q, so redirect its output. 790 # git fetch is always verbose even with -q, so redirect its output.
809 self._check_output_git(['fetch', self.remote, self.remote_branch], 791 self._check_output_git(['fetch', self.pull_remote, self.remote_branch],
810 timeout=FETCH_TIMEOUT) 792 timeout=FETCH_TIMEOUT)
811 793
812 794
813 class ReadOnlyCheckout(object): 795 class ReadOnlyCheckout(object):
814 """Converts a checkout into a read-only one.""" 796 """Converts a checkout into a read-only one."""
815 def __init__(self, checkout, post_processors=None): 797 def __init__(self, checkout, post_processors=None):
816 super(ReadOnlyCheckout, self).__init__() 798 super(ReadOnlyCheckout, self).__init__()
817 self.checkout = checkout 799 self.checkout = checkout
818 self.post_processors = (post_processors or []) + ( 800 self.post_processors = (post_processors or []) + (
819 self.checkout.post_processors or []) 801 self.checkout.post_processors or [])
(...skipping 17 matching lines...) Expand all
837 def revisions(self, rev1, rev2): 819 def revisions(self, rev1, rev2):
838 return self.checkout.revisions(rev1, rev2) 820 return self.checkout.revisions(rev1, rev2)
839 821
840 @property 822 @property
841 def project_name(self): 823 def project_name(self):
842 return self.checkout.project_name 824 return self.checkout.project_name
843 825
844 @property 826 @property
845 def project_path(self): 827 def project_path(self):
846 return self.checkout.project_path 828 return self.checkout.project_path
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698