OLD | NEW |
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 Loading... |
559 out = self._check_output_git(['branch']).splitlines(False) | 559 out = self._check_output_git(['branch']).splitlines(False) |
560 branches = [l[2:] for l in out] | 560 branches = [l[2:] for l in out] |
561 active = None | 561 active = None |
562 for l in out: | 562 for l in out: |
563 if l.startswith('*'): | 563 if l.startswith('*'): |
564 active = l[2:] | 564 active = l[2:] |
565 break | 565 break |
566 return branches, active | 566 return branches, active |
567 | 567 |
568 | 568 |
569 class GitSvnCheckoutBase(GitCheckoutBase, SvnMixIn): | |
570 """Base class for git-svn checkout. Not to be used as-is.""" | |
571 def __init__(self, | |
572 root_dir, project_name, remote_branch, | |
573 commit_user, commit_pwd, | |
574 svn_url, trunk, post_processors=None): | |
575 """trunk is optional.""" | |
576 GitCheckoutBase.__init__( | |
577 self, root_dir, project_name + '.git', remote_branch, post_processors) | |
578 SvnMixIn.__init__(self) | |
579 self.commit_user = commit_user | |
580 self.commit_pwd = commit_pwd | |
581 # svn_url in this case is the root of the svn repository. | |
582 self.svn_url = svn_url | |
583 self.trunk = trunk | |
584 assert bool(self.commit_user) >= bool(self.commit_pwd) | |
585 assert self.svn_url | |
586 assert self.trunk | |
587 self._cache_svn_auth() | |
588 | |
589 def prepare(self, revision): | |
590 """Resets the git repository in a clean state.""" | |
591 self._check_call_git(['reset', '--hard', '--quiet']) | |
592 if revision: | |
593 try: | |
594 revision = self._check_output_git( | |
595 ['svn', 'find-rev', 'r%d' % revision]) | |
596 except subprocess.CalledProcessError: | |
597 self._check_call_git( | |
598 ['fetch', self.remote, self.remote_branch, '--quiet']) | |
599 revision = self._check_output_git( | |
600 ['svn', 'find-rev', 'r%d' % revision]) | |
601 super(GitSvnCheckoutBase, self).prepare(revision) | |
602 else: | |
603 branches, active = self._branches() | |
604 if active != 'master': | |
605 if not 'master' in branches: | |
606 self._check_call_git( | |
607 ['checkout', '--quiet', '-b', 'master', | |
608 '%s/%s' % (self.remote, self.remote_branch)]) | |
609 else: | |
610 self._check_call_git(['checkout', 'master', '--force', '--quiet']) | |
611 # git svn rebase --quiet --quiet doesn't work, use two steps to silence | |
612 # it. | |
613 self._check_call_git_svn(['fetch', '--quiet', '--quiet']) | |
614 self._check_call_git( | |
615 ['rebase', '--quiet', '--quiet', | |
616 '%s/%s' % (self.remote, self.remote_branch)]) | |
617 if self.working_branch in branches: | |
618 self._call_git(['branch', '-D', self.working_branch]) | |
619 return self._get_revision() | |
620 | |
621 def _git_svn_info(self, key): | |
622 """Calls git svn info. This doesn't support nor need --config-dir.""" | |
623 return self._parse_svn_info(self._check_output_git(['svn', 'info']), key) | |
624 | |
625 def commit(self, commit_message, user): | |
626 """Commits a patch.""" | |
627 logging.info('Committing patch for %s' % user) | |
628 # Fix the commit message and author. It returns the git hash, which we | |
629 # ignore unless it's None. | |
630 if not super(GitSvnCheckoutBase, self).commit(commit_message, user): | |
631 return None | |
632 # TODO(maruel): git-svn ignores --config-dir as of git-svn version 1.7.4 and | |
633 # doesn't support --with-revprop. | |
634 # Either learn perl and upstream or suck it. | |
635 kwargs = {} | |
636 if self.commit_pwd: | |
637 kwargs['stdin'] = self.commit_pwd + '\n' | |
638 kwargs['stderr'] = subprocess2.STDOUT | |
639 self._check_call_git_svn( | |
640 ['dcommit', '--rmdir', '--find-copies-harder', | |
641 '--username', self.commit_user], | |
642 **kwargs) | |
643 revision = int(self._git_svn_info('revision')) | |
644 return revision | |
645 | |
646 def _cache_svn_auth(self): | |
647 """Caches the svn credentials. It is necessary since git-svn doesn't prompt | |
648 for it.""" | |
649 if not self.commit_user or not self.commit_pwd: | |
650 return | |
651 # Use capture to lower noise in logs. | |
652 self._check_output_svn(['ls', self.svn_url], cwd=None) | |
653 | |
654 def _check_call_git_svn(self, args, **kwargs): | |
655 """Handles svn authentication while calling git svn.""" | |
656 args = ['svn'] + args | |
657 if not self.svn_config.default: | |
658 args.extend(['--config-dir', self.svn_config.svn_config_dir]) | |
659 return self._check_call_git(args, **kwargs) | |
660 | |
661 def _get_revision(self): | |
662 revision = int(self._git_svn_info('revision')) | |
663 if revision != self._last_seen_revision: | |
664 logging.info('Updated to revision %d' % revision) | |
665 self._last_seen_revision = revision | |
666 return revision | |
667 | |
668 | |
669 class GitSvnPremadeCheckout(GitSvnCheckoutBase): | |
670 """Manages a git-svn clone made out from an initial git-svn seed. | |
671 | |
672 This class is very similar to GitSvnCheckout but is faster to bootstrap | |
673 because it starts right off with an existing git-svn clone. | |
674 """ | |
675 def __init__(self, | |
676 root_dir, project_name, remote_branch, | |
677 commit_user, commit_pwd, | |
678 svn_url, trunk, git_url, post_processors=None): | |
679 super(GitSvnPremadeCheckout, self).__init__( | |
680 root_dir, project_name, remote_branch, | |
681 commit_user, commit_pwd, | |
682 svn_url, trunk, post_processors) | |
683 self.git_url = git_url | |
684 assert self.git_url | |
685 | |
686 def prepare(self, revision): | |
687 """Creates the initial checkout for the repo.""" | |
688 if not os.path.isdir(self.project_path): | |
689 logging.info('Checking out %s in %s' % | |
690 (self.project_name, self.project_path)) | |
691 assert self.remote == 'origin' | |
692 # self.project_path doesn't exist yet. | |
693 self._check_call_git( | |
694 ['clone', self.git_url, self.project_name, '--quiet'], | |
695 cwd=self.root_dir, | |
696 stderr=subprocess2.STDOUT) | |
697 try: | |
698 configured_svn_url = self._check_output_git( | |
699 ['config', 'svn-remote.svn.url']).strip() | |
700 except subprocess.CalledProcessError: | |
701 configured_svn_url = '' | |
702 | |
703 if configured_svn_url.strip() != self.svn_url: | |
704 self._check_call_git_svn( | |
705 ['init', | |
706 '--prefix', self.remote + '/', | |
707 '-T', self.trunk, | |
708 self.svn_url]) | |
709 self._check_call_git_svn(['fetch']) | |
710 return super(GitSvnPremadeCheckout, self).prepare(revision) | |
711 | |
712 | |
713 class GitSvnCheckout(GitSvnCheckoutBase): | |
714 """Manages a git-svn clone. | |
715 | |
716 Using git-svn hides some of the complexity of using a svn checkout. | |
717 """ | |
718 def __init__(self, | |
719 root_dir, project_name, | |
720 commit_user, commit_pwd, | |
721 svn_url, trunk, post_processors=None): | |
722 super(GitSvnCheckout, self).__init__( | |
723 root_dir, project_name, 'trunk', | |
724 commit_user, commit_pwd, | |
725 svn_url, trunk, post_processors) | |
726 | |
727 def prepare(self, revision): | |
728 """Creates the initial checkout for the repo.""" | |
729 assert not revision, 'Implement revision if necessary' | |
730 if not os.path.isdir(self.project_path): | |
731 logging.info('Checking out %s in %s' % | |
732 (self.project_name, self.project_path)) | |
733 # TODO: Create a shallow clone. | |
734 # self.project_path doesn't exist yet. | |
735 self._check_call_git_svn( | |
736 ['clone', | |
737 '--prefix', self.remote + '/', | |
738 '-T', self.trunk, | |
739 self.svn_url, self.project_path, | |
740 '--quiet'], | |
741 cwd=self.root_dir, | |
742 stderr=subprocess2.STDOUT) | |
743 return super(GitSvnCheckout, self).prepare(revision) | |
744 | |
745 | |
746 class ReadOnlyCheckout(object): | 569 class ReadOnlyCheckout(object): |
747 """Converts a checkout into a read-only one.""" | 570 """Converts a checkout into a read-only one.""" |
748 def __init__(self, checkout, post_processors=None): | 571 def __init__(self, checkout, post_processors=None): |
749 super(ReadOnlyCheckout, self).__init__() | 572 super(ReadOnlyCheckout, self).__init__() |
750 self.checkout = checkout | 573 self.checkout = checkout |
751 self.post_processors = (post_processors or []) + ( | 574 self.post_processors = (post_processors or []) + ( |
752 self.checkout.post_processors or []) | 575 self.checkout.post_processors or []) |
753 | 576 |
754 def prepare(self, revision): | 577 def prepare(self, revision): |
755 return self.checkout.prepare(revision) | 578 return self.checkout.prepare(revision) |
(...skipping 10 matching lines...) Expand all Loading... |
766 user, message)) | 589 user, message)) |
767 return 'FAKE' | 590 return 'FAKE' |
768 | 591 |
769 @property | 592 @property |
770 def project_name(self): | 593 def project_name(self): |
771 return self.checkout.project_name | 594 return self.checkout.project_name |
772 | 595 |
773 @property | 596 @property |
774 def project_path(self): | 597 def project_path(self): |
775 return self.checkout.project_path | 598 return self.checkout.project_path |
OLD | NEW |