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

Side by Side Diff: checkout.py

Issue 22794015: Completing implementation of GitCheckout in depot_tools (Closed) Base URL: http://src.chromium.org/svn/trunk/tools/depot_tools/
Patch Set: Created 7 years, 4 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) 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 532 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 # the order specified. 543 # the order specified.
544 try: 544 try:
545 out = self._check_output_svn( 545 out = self._check_output_svn(
546 ['log', '-q', self.svn_url, '-r', '%s:%s' % (rev1, rev2)]) 546 ['log', '-q', self.svn_url, '-r', '%s:%s' % (rev1, rev2)])
547 except subprocess.CalledProcessError: 547 except subprocess.CalledProcessError:
548 return None 548 return None
549 # Ignore the '----' lines. 549 # Ignore the '----' lines.
550 return len([l for l in out.splitlines() if l.startswith('r')]) - 1 550 return len([l for l in out.splitlines() if l.startswith('r')]) - 1
551 551
552 552
553 class GitCheckoutBase(CheckoutBase): 553 class GitCheckoutBase(CheckoutBase):
M-A Ruel 2013/08/23 17:05:10 The reason for this base class was for a git-svn s
rmistry 2013/08/26 13:08:17 Makes sense, Done.
554 """Base class for git checkout. Not to be used as-is.""" 554 """Base class for git checkout. Not to be used as-is."""
555 def __init__(self, root_dir, project_name, remote_branch, 555 def __init__(self, root_dir, project_name, remote_branch, git_url,
556 post_processors=None): 556 post_processors=None):
557 super(GitCheckoutBase, self).__init__( 557 super(GitCheckoutBase, self).__init__(
558 root_dir, project_name, post_processors) 558 root_dir, project_name, post_processors)
559 # There is no reason to not hardcode it. 559 self.git_url = git_url
560 self.remote_branch = remote_branch
561 # The working branch where patches will be applied. It will track the
562 # remote branch.
563 self.working_branch = 'working_branch'
564 # There is no reason to not hardcode origin.
560 self.remote = 'origin' 565 self.remote = 'origin'
561 self.remote_branch = remote_branch
562 self.working_branch = 'working_branch'
563 566
564 def prepare(self, revision): 567 def prepare(self, revision):
565 """Resets the git repository in a clean state. 568 """Resets the git repository in a clean state.
566 569
567 Checks it out if not present and deletes the working branch. 570 Checks it out if not present and deletes the working branch.
568 """ 571 """
569 assert self.remote_branch 572 assert self.remote_branch
570 assert os.path.isdir(self.project_path) 573
571 self._check_call_git(['reset', '--hard', '--quiet']) 574 if not os.path.isdir(self.project_path):
575 # Clone the repo if the directory is not present.
576 logging.info('Checking out %s in %s' %
577 (self.project_name, self.project_path))
578 assert self.git_url
M-A Ruel 2013/08/23 17:05:10 This assert should be in the constructor.
rmistry 2013/08/26 13:08:17 Done.
579 self._check_call_git(
580 ['clone', self.git_url, '-b', self.remote_branch, self.project_path],
581 cwd=None, timeout=FETCH_TIMEOUT)
582 else:
583 # Throw away all uncommitted changes in the existing checkout.
584 self._check_call_git(['checkout', self.remote_branch])
585 self._check_call_git(
586 ['reset', '--hard', '--quiet',
587 '%s/%s' % (self.remote, self.remote_branch)])
588
572 if revision: 589 if revision:
573 try: 590 try:
M-A Ruel 2013/08/23 17:05:10 Add a comment: # Look if the commit hash already e
rmistry 2013/08/26 13:08:17 Done.
574 revision = self._check_output_git(['rev-parse', revision]) 591 revision = self._check_output_git(['rev-parse', revision])
575 except subprocess.CalledProcessError: 592 except subprocess.CalledProcessError:
576 self._check_call_git( 593 self._check_call_git(
577 ['fetch', self.remote, self.remote_branch, '--quiet']) 594 ['fetch', self.remote, self.remote_branch, '--quiet'])
578 revision = self._check_output_git(['rev-parse', revision]) 595 revision = self._check_output_git(['rev-parse', revision])
579 self._check_call_git(['checkout', '--force', '--quiet', revision]) 596 self._check_call_git(['checkout', '--force', '--quiet', revision])
580 else: 597 else:
581 branches, active = self._branches() 598 branches, active = self._branches()
582 if active != 'master': 599 if active != 'master':
583 self._check_call_git(['checkout', '--force', '--quiet', 'master']) 600 self._check_call_git(['checkout', '--force', '--quiet', 'master'])
584 self._check_call_git(['pull', self.remote, self.remote_branch, '--quiet']) 601 self._check_call_git(['pull', '--quiet'])
M-A Ruel 2013/08/23 17:05:10 Why not keep the explicit remote?
rmistry 2013/08/26 13:08:17 There is difference in behavior here between these
M-A Ruel 2013/08/26 14:01:11 Wouha, thanks, I had never realized that. Then I'
rmistry 2013/08/26 15:16:17 Done.
Isaac (away) 2013/09/02 23:38:58 Why not "git fetch origin"? git pull is roughly a
rmistry 2013/09/03 12:40:36 The documentation on pull and fetch are a little c
585 if self.working_branch in branches: 602 if self.working_branch in branches:
586 self._call_git(['branch', '-D', self.working_branch]) 603 self._call_git(['branch', '-D', self.working_branch])
604 return self._get_revision()
605
606 def _get_revision(self):
607 """Gets the current revision from the local branch."""
608 return self._check_output_git(['rev-parse', 'HEAD']).strip()
587 609
588 def apply_patch(self, patches, post_processors=None, verbose=False): 610 def apply_patch(self, patches, post_processors=None, verbose=False):
589 """Applies a patch on 'working_branch' and switch to it. 611 """Applies a patch on 'working_branch' and switches to it.
590 612
591 Also commits the changes on the local branch. 613 Also commits the changes on the local branch.
592 614
593 Ignores svn properties and raise an exception on unexpected ones. 615 Ignores svn properties and raise an exception on unexpected ones.
594 """ 616 """
595 post_processors = post_processors or self.post_processors or [] 617 post_processors = post_processors or self.post_processors or []
596 # It this throws, the checkout is corrupted. Maybe worth deleting it and 618 # It this throws, the checkout is corrupted. Maybe worth deleting it and
597 # trying again? 619 # trying again?
598 if self.remote_branch: 620 if self.remote_branch:
599 self._check_call_git( 621 self._check_call_git(
600 ['checkout', '-b', self.working_branch, 622 ['checkout', '-b', self.working_branch, '-t', self.remote_branch,
601 '%s/%s' % (self.remote, self.remote_branch), '--quiet']) 623 '--quiet'])
624
602 for index, p in enumerate(patches): 625 for index, p in enumerate(patches):
603 stdout = [] 626 stdout = []
604 try: 627 try:
605 filepath = os.path.join(self.project_path, p.filename) 628 filepath = os.path.join(self.project_path, p.filename)
606 if p.is_delete: 629 if p.is_delete:
607 if (not os.path.exists(filepath) and 630 if (not os.path.exists(filepath) and
608 any(p1.source_filename == p.filename for p1 in patches[0:index])): 631 any(p1.source_filename == p.filename for p1 in patches[0:index])):
609 # The file was already deleted if a prior patch with file rename 632 # The file was already deleted if a prior patch with file rename
610 # was already processed because 'git apply' did it for us. 633 # was already processed because 'git apply' did it for us.
611 pass 634 pass
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
660 'While running %s;\n%s%s' % ( 683 'While running %s;\n%s%s' % (
661 ' '.join(e.cmd), 684 ' '.join(e.cmd),
662 align_stdout(stdout), 685 align_stdout(stdout),
663 align_stdout([getattr(e, 'stdout', '')]))) 686 align_stdout([getattr(e, 'stdout', '')])))
664 # Once all the patches are processed and added to the index, commit the 687 # Once all the patches are processed and added to the index, commit the
665 # index. 688 # index.
666 cmd = ['commit', '-m', 'Committed patch'] 689 cmd = ['commit', '-m', 'Committed patch']
667 if verbose: 690 if verbose:
668 cmd.append('--verbose') 691 cmd.append('--verbose')
669 self._check_call_git(cmd) 692 self._check_call_git(cmd)
670 # TODO(maruel): Weirdly enough they don't match, need to investigate. 693 found_files = self._check_output_git(
671 #found_files = self._check_output_git( 694 ['diff', '%s/%s' % (self.remote, self.remote_branch),
672 # ['diff', 'master', '--name-only']).splitlines(False) 695 '--name-only']).splitlines(False)
673 #assert sorted(patches.filenames) == sorted(found_files), ( 696 assert sorted(patches.filenames) == sorted(found_files), (
674 # sorted(out), sorted(found_files)) 697 sorted(patches.filenames), sorted(found_files))
675 698
676 def commit(self, commit_message, user): 699 def commit(self, commit_message, user):
677 """Updates the commit message. 700 """Commits and Updates the commit message.
678 701
679 Subclass needs to dcommit or push. 702 Subclass needs to dcommit or push.
680 """ 703 """
681 assert isinstance(commit_message, unicode) 704 assert isinstance(commit_message, unicode)
682 self._check_call_git(['commit', '--amend', '-m', commit_message]) 705 self._check_call_git(['commit', '--amend', '-m', commit_message])
M-A Ruel 2013/08/23 17:08:04 You also want '--author', user. You don't need any
rmistry 2013/08/26 13:08:17 I do not completely understand this. If the CQ is
M-A Ruel 2013/08/26 14:01:11 What tool output this error, git? It's interesting
rmistry 2013/08/26 15:16:17 I see what I was doing wrong. I was doing: --autho
rmistry 2013/08/27 12:44:49 Went ahead and added functionality for --author @
683 return self._check_output_git(['rev-parse', 'HEAD']).strip() 706 return self._get_revision()
684 707
685 def _check_call_git(self, args, **kwargs): 708 def _check_call_git(self, args, **kwargs):
686 kwargs.setdefault('cwd', self.project_path) 709 kwargs.setdefault('cwd', self.project_path)
687 kwargs.setdefault('stdout', self.VOID) 710 kwargs.setdefault('stdout', self.VOID)
688 kwargs.setdefault('timeout', GLOBAL_TIMEOUT) 711 kwargs.setdefault('timeout', GLOBAL_TIMEOUT)
689 return subprocess2.check_call_out(['git'] + args, **kwargs) 712 return subprocess2.check_call_out(['git'] + args, **kwargs)
690 713
691 def _call_git(self, args, **kwargs): 714 def _call_git(self, args, **kwargs):
692 """Like check_call but doesn't throw on failure.""" 715 """Like check_call but doesn't throw on failure."""
693 kwargs.setdefault('cwd', self.project_path) 716 kwargs.setdefault('cwd', self.project_path)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
726 return len(out.splitlines()) 749 return len(out.splitlines())
727 750
728 def _fetch_remote(self): 751 def _fetch_remote(self):
729 """Fetches the remote without rebasing.""" 752 """Fetches the remote without rebasing."""
730 raise NotImplementedError() 753 raise NotImplementedError()
731 754
732 755
733 class GitCheckout(GitCheckoutBase): 756 class GitCheckout(GitCheckoutBase):
734 """Git checkout implementation.""" 757 """Git checkout implementation."""
735 def _fetch_remote(self): 758 def _fetch_remote(self):
736 # git fetch is always verbose even with -q -q so redirect its output. 759 # git fetch is always verbose even with -q, so redirect its output.
737 self._check_output_git(['fetch', self.remote, self.remote_branch], 760 self._check_output_git(['fetch', self.remote, self.remote_branch],
738 timeout=FETCH_TIMEOUT) 761 timeout=FETCH_TIMEOUT)
739 762
763 def commit(self, commit_message, user):
764 """Updates the commit message and pushes."""
765 # Update the commit message and 'git commit' by calling the superclass.
766 rev = super(GitCheckout, self).commit(commit_message, user)
767 # Push to the remote repository.
768 self._check_call_git(
769 ['push', 'origin', '%s:%s' % (self.working_branch, self.remote_branch),
770 '--force', '--quiet'])
771 return rev
M-A Ruel 2013/08/23 17:05:10 Also, it should switch back to the master branch a
rmistry 2013/08/26 13:08:17 That is done in prepare step before the patches ar
M-A Ruel 2013/08/26 14:01:11 What I'm saying is that it should be done at the 2
rmistry 2013/08/26 15:16:17 I guess it does not hurt to have it in both places
772
740 773
741 class ReadOnlyCheckout(object): 774 class ReadOnlyCheckout(object):
742 """Converts a checkout into a read-only one.""" 775 """Converts a checkout into a read-only one."""
743 def __init__(self, checkout, post_processors=None): 776 def __init__(self, checkout, post_processors=None):
744 super(ReadOnlyCheckout, self).__init__() 777 super(ReadOnlyCheckout, self).__init__()
745 self.checkout = checkout 778 self.checkout = checkout
746 self.post_processors = (post_processors or []) + ( 779 self.post_processors = (post_processors or []) + (
747 self.checkout.post_processors or []) 780 self.checkout.post_processors or [])
748 781
749 def prepare(self, revision): 782 def prepare(self, revision):
(...skipping 14 matching lines...) Expand all
764 def revisions(self, rev1, rev2): 797 def revisions(self, rev1, rev2):
765 return self.checkout.revisions(rev1, rev2) 798 return self.checkout.revisions(rev1, rev2)
766 799
767 @property 800 @property
768 def project_name(self): 801 def project_name(self):
769 return self.checkout.project_name 802 return self.checkout.project_name
770 803
771 @property 804 @property
772 def project_path(self): 805 def project_path(self):
773 return self.checkout.project_path 806 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