Mercurial > public > mercurial-scm > hg
comparison mercurial/subrepo.py @ 13150:8617b8b74fae
subrepo: use low-level git-for-each-ref command in branchmap
This command's output doesn't change across versions, and it also
disambiguates cases where there are slashes in local branch names.
author | Eric Eisner <ede@mit.edu> |
---|---|
date | Tue, 14 Dec 2010 21:53:40 -0500 |
parents | aae2d5cbde64 |
children | 519ac79d680b |
comparison
equal
deleted
inserted
replaced
13149:735dd8e8a208 | 13150:8617b8b74fae |
---|---|
647 p.wait() | 647 p.wait() |
648 | 648 |
649 if p.returncode != 0 and p.returncode != 1: | 649 if p.returncode != 0 and p.returncode != 1: |
650 # there are certain error codes that are ok | 650 # there are certain error codes that are ok |
651 command = commands[0] | 651 command = commands[0] |
652 if command == 'cat-file': | 652 if command in ('cat-file', 'symbolic-ref'): |
653 return retdata, p.returncode | 653 return retdata, p.returncode |
654 # for all others, abort | 654 # for all others, abort |
655 raise util.Abort('git %s error %d in %s' % | 655 raise util.Abort('git %s error %d in %s' % |
656 (command, p.returncode, self._relpath)) | 656 (command, p.returncode, self._relpath)) |
657 | 657 |
673 the current branch, | 673 the current branch, |
674 a map from git branch to revision | 674 a map from git branch to revision |
675 a map from revision to branches''' | 675 a map from revision to branches''' |
676 branch2rev = {} | 676 branch2rev = {} |
677 rev2branch = {} | 677 rev2branch = {} |
678 current = None | 678 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet']) |
679 out = self._gitcommand(['branch', '-a', '--no-color', | 679 if err: |
680 '--verbose', '--no-abbrev']) | 680 current = None |
681 | |
682 out = self._gitcommand(['for-each-ref', '--format', | |
683 '%(objectname) %(refname)']) | |
681 for line in out.split('\n'): | 684 for line in out.split('\n'): |
682 if line[2:].startswith('(no branch)'): | 685 revision, ref = line.split(' ') |
686 if ref.startswith('refs/tags/'): | |
683 continue | 687 continue |
684 branch, revision = line[2:].split()[:2] | 688 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'): |
685 if revision == '->' or branch.endswith('/HEAD'): | |
686 continue # ignore remote/HEAD redirects | 689 continue # ignore remote/HEAD redirects |
687 if '/' in branch and not branch.startswith('remotes/'): | 690 branch2rev[ref] = revision |
688 # old git compatability | 691 rev2branch.setdefault(revision, []).append(ref) |
689 branch = 'remotes/' + branch | |
690 if line[0] == '*': | |
691 current = branch | |
692 branch2rev[branch] = revision | |
693 rev2branch.setdefault(revision, []).append(branch) | |
694 return current, branch2rev, rev2branch | 692 return current, branch2rev, rev2branch |
695 | 693 |
696 def _gittracking(self, branches): | 694 def _gittracking(self, branches): |
697 'return map of remote branch to local tracking branch' | 695 'return map of remote branch to local tracking branch' |
698 # assumes no more than one local tracking branch for each remote | 696 # assumes no more than one local tracking branch for each remote |
699 tracking = {} | 697 tracking = {} |
700 for b in branches: | 698 for b in branches: |
701 if b.startswith('remotes/'): | 699 if b.startswith('refs/remotes/'): |
702 continue | 700 continue |
703 remote = self._gitcommand(['config', 'branch.%s.remote' % b]) | 701 remote = self._gitcommand(['config', 'branch.%s.remote' % b]) |
704 if remote: | 702 if remote: |
705 ref = self._gitcommand(['config', 'branch.%s.merge' % b]) | 703 ref = self._gitcommand(['config', 'branch.%s.merge' % b]) |
706 tracking['remotes/%s/%s' % (remote, ref.split('/')[-1])] = b | 704 tracking['refs/remotes/%s/%s' % |
705 (remote, ref.split('/', 2)[2])] = b | |
707 return tracking | 706 return tracking |
708 | 707 |
709 def _fetch(self, source, revision): | 708 def _fetch(self, source, revision): |
710 if not os.path.exists('%s/.git' % self._path): | 709 if not os.path.exists('%s/.git' % self._path): |
711 self._ui.status(_('cloning subrepo %s\n') % self._relpath) | 710 self._ui.status(_('cloning subrepo %s\n') % self._relpath) |
756 rawcheckout() | 755 rawcheckout() |
757 return | 756 return |
758 branches = rev2branch[revision] | 757 branches = rev2branch[revision] |
759 firstlocalbranch = None | 758 firstlocalbranch = None |
760 for b in branches: | 759 for b in branches: |
761 if b == 'master': | 760 if b == 'refs/heads/master': |
762 # master trumps all other branches | 761 # master trumps all other branches |
763 self._gitcommand(['checkout', 'master']) | 762 self._gitcommand(['checkout', 'refs/heads/master']) |
764 return | 763 return |
765 if not firstlocalbranch and not b.startswith('remotes/'): | 764 if not firstlocalbranch and not b.startswith('refs/remotes/'): |
766 firstlocalbranch = b | 765 firstlocalbranch = b |
767 if firstlocalbranch: | 766 if firstlocalbranch: |
768 self._gitcommand(['checkout', firstlocalbranch]) | 767 self._gitcommand(['checkout', firstlocalbranch]) |
769 return | 768 return |
770 | 769 |
777 remote = b | 776 remote = b |
778 break | 777 break |
779 | 778 |
780 if remote not in tracking: | 779 if remote not in tracking: |
781 # create a new local tracking branch | 780 # create a new local tracking branch |
782 local = remote.split('/')[-1] | 781 local = remote.split('/', 2)[2] |
783 self._gitcommand(['checkout', '-b', local, remote]) | 782 self._gitcommand(['checkout', '-b', local, remote]) |
784 elif self._gitisancestor(branch2rev[tracking[remote]], remote): | 783 elif self._gitisancestor(branch2rev[tracking[remote]], remote): |
785 # When updating to a tracked remote branch, | 784 # When updating to a tracked remote branch, |
786 # if the local tracking branch is downstream of it, | 785 # if the local tracking branch is downstream of it, |
787 # a normal `git pull` would have performed a "fast-forward merge" | 786 # a normal `git pull` would have performed a "fast-forward merge" |
822 def push(self, force): | 821 def push(self, force): |
823 # if a branch in origin contains the revision, nothing to do | 822 # if a branch in origin contains the revision, nothing to do |
824 current, branch2rev, rev2branch = self._gitbranchmap() | 823 current, branch2rev, rev2branch = self._gitbranchmap() |
825 if self._state[1] in rev2branch: | 824 if self._state[1] in rev2branch: |
826 for b in rev2branch[self._state[1]]: | 825 for b in rev2branch[self._state[1]]: |
827 if b.startswith('remotes/origin/'): | 826 if b.startswith('refs/remotes/origin/'): |
828 return True | 827 return True |
829 for b, revision in branch2rev.iteritems(): | 828 for b, revision in branch2rev.iteritems(): |
830 if b.startswith('remotes/origin/'): | 829 if b.startswith('refs/remotes/origin/'): |
831 if self._gitisancestor(self._state[1], revision): | 830 if self._gitisancestor(self._state[1], revision): |
832 return True | 831 return True |
833 # otherwise, try to push the currently checked out branch | 832 # otherwise, try to push the currently checked out branch |
834 cmd = ['push'] | 833 cmd = ['push'] |
835 if force: | 834 if force: |
839 if not self._gitisancestor(self._state[1], current): | 838 if not self._gitisancestor(self._state[1], current): |
840 self._ui.warn(_('unrelated git branch checked out ' | 839 self._ui.warn(_('unrelated git branch checked out ' |
841 'in subrepo %s\n') % self._relpath) | 840 'in subrepo %s\n') % self._relpath) |
842 return False | 841 return False |
843 self._ui.status(_('pushing branch %s of subrepo %s\n') % | 842 self._ui.status(_('pushing branch %s of subrepo %s\n') % |
844 (current, self._relpath)) | 843 (current.split('/', 2)[2], self._relpath)) |
845 self._gitcommand(cmd + ['origin', current]) | 844 self._gitcommand(cmd + ['origin', current]) |
846 return True | 845 return True |
847 else: | 846 else: |
848 self._ui.warn(_('no branch checked out in subrepo %s\n' | 847 self._ui.warn(_('no branch checked out in subrepo %s\n' |
849 'cannot push revision %s') % | 848 'cannot push revision %s') % |