comparison mercurial/subrepo.py @ 13152:70d80907e4b8

subrepo: defer determination of git's current branch
author Eric Eisner <ede@mit.edu>
date Tue, 14 Dec 2010 21:56:43 -0500
parents 519ac79d680b
children dca5488f0e4f
comparison
equal deleted inserted replaced
13151:519ac79d680b 13152:70d80907e4b8
658 return retdata, p.returncode 658 return retdata, p.returncode
659 659
660 def _gitstate(self): 660 def _gitstate(self):
661 return self._gitcommand(['rev-parse', 'HEAD']) 661 return self._gitcommand(['rev-parse', 'HEAD'])
662 662
663 def _gitcurrentbranch(self):
664 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
665 if err:
666 current = None
667 return current
668
663 def _githavelocally(self, revision): 669 def _githavelocally(self, revision):
664 out, code = self._gitdir(['cat-file', '-e', revision]) 670 out, code = self._gitdir(['cat-file', '-e', revision])
665 return code == 0 671 return code == 0
666 672
667 def _gitisancestor(self, r1, r2): 673 def _gitisancestor(self, r1, r2):
668 base = self._gitcommand(['merge-base', r1, r2]) 674 base = self._gitcommand(['merge-base', r1, r2])
669 return base == r1 675 return base == r1
670 676
671 def _gitbranchmap(self): 677 def _gitbranchmap(self):
672 '''returns 3 things: 678 '''returns 3 things:
673 the current branch,
674 a map from git branch to revision 679 a map from git branch to revision
675 a map from revision to branches''' 680 a map from revision to branches
681 a map from remote branch to local tracking branch'''
676 branch2rev = {} 682 branch2rev = {}
677 rev2branch = {} 683 rev2branch = {}
678 tracking = {} 684 tracking = {}
679 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
680 if err:
681 current = None
682
683 out = self._gitcommand(['for-each-ref', '--format', 685 out = self._gitcommand(['for-each-ref', '--format',
684 '%(objectname) %(refname) %(upstream) end']) 686 '%(objectname) %(refname) %(upstream) end'])
685 for line in out.split('\n'): 687 for line in out.split('\n'):
686 revision, ref, upstream = line.split(' ')[:3] 688 revision, ref, upstream = line.split(' ')[:3]
687 if ref.startswith('refs/tags/'): 689 if ref.startswith('refs/tags/'):
691 branch2rev[ref] = revision 693 branch2rev[ref] = revision
692 rev2branch.setdefault(revision, []).append(ref) 694 rev2branch.setdefault(revision, []).append(ref)
693 if upstream: 695 if upstream:
694 # assumes no more than one local tracking branch for a remote 696 # assumes no more than one local tracking branch for a remote
695 tracking[upstream] = ref 697 tracking[upstream] = ref
696 return current, branch2rev, rev2branch, tracking 698 return branch2rev, rev2branch, tracking
697 699
698 def _fetch(self, source, revision): 700 def _fetch(self, source, revision):
699 if not os.path.exists('%s/.git' % self._path): 701 if not os.path.exists('%s/.git' % self._path):
700 self._ui.status(_('cloning subrepo %s\n') % self._relpath) 702 self._ui.status(_('cloning subrepo %s\n') % self._relpath)
701 self._gitnodir(['clone', source, self._path]) 703 self._gitnodir(['clone', source, self._path])
729 if self._gitstate() == revision: 731 if self._gitstate() == revision:
730 self._gitcommand(['reset', '--hard', 'HEAD']) 732 self._gitcommand(['reset', '--hard', 'HEAD'])
731 return 733 return
732 elif self._gitstate() == revision: 734 elif self._gitstate() == revision:
733 return 735 return
734 current, branch2rev, rev2branch, tracking = self._gitbranchmap() 736 branch2rev, rev2branch, tracking = self._gitbranchmap()
735 737
736 def rawcheckout(): 738 def rawcheckout():
737 # no branch to checkout, check it out with no branch 739 # no branch to checkout, check it out with no branch
738 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') % 740 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
739 self._relpath) 741 self._relpath)
774 # if the local tracking branch is downstream of it, 776 # if the local tracking branch is downstream of it,
775 # a normal `git pull` would have performed a "fast-forward merge" 777 # a normal `git pull` would have performed a "fast-forward merge"
776 # which is equivalent to updating the local branch to the remote. 778 # which is equivalent to updating the local branch to the remote.
777 # Since we are only looking at branching at update, we need to 779 # Since we are only looking at branching at update, we need to
778 # detect this situation and perform this action lazily. 780 # detect this situation and perform this action lazily.
779 if tracking[remote] != current: 781 if tracking[remote] != self._gitcurrentbranch():
780 self._gitcommand(['checkout', tracking[remote]]) 782 self._gitcommand(['checkout', tracking[remote]])
781 self._gitcommand(['merge', '--ff', remote]) 783 self._gitcommand(['merge', '--ff', remote])
782 else: 784 else:
783 # a real merge would be required, just checkout the revision 785 # a real merge would be required, just checkout the revision
784 rawcheckout() 786 rawcheckout()
807 elif base != self._state[1]: 809 elif base != self._state[1]:
808 self._gitcommand(['merge', '--no-commit', revision]) 810 self._gitcommand(['merge', '--no-commit', revision])
809 811
810 def push(self, force): 812 def push(self, force):
811 # if a branch in origin contains the revision, nothing to do 813 # if a branch in origin contains the revision, nothing to do
812 current, branch2rev, rev2branch, tracking = self._gitbranchmap() 814 branch2rev, rev2branch, tracking = self._gitbranchmap()
813 if self._state[1] in rev2branch: 815 if self._state[1] in rev2branch:
814 for b in rev2branch[self._state[1]]: 816 for b in rev2branch[self._state[1]]:
815 if b.startswith('refs/remotes/origin/'): 817 if b.startswith('refs/remotes/origin/'):
816 return True 818 return True
817 for b, revision in branch2rev.iteritems(): 819 for b, revision in branch2rev.iteritems():
820 return True 822 return True
821 # otherwise, try to push the currently checked out branch 823 # otherwise, try to push the currently checked out branch
822 cmd = ['push'] 824 cmd = ['push']
823 if force: 825 if force:
824 cmd.append('--force') 826 cmd.append('--force')
827
828 current = self._gitcurrentbranch()
825 if current: 829 if current:
826 # determine if the current branch is even useful 830 # determine if the current branch is even useful
827 if not self._gitisancestor(self._state[1], current): 831 if not self._gitisancestor(self._state[1], current):
828 self._ui.warn(_('unrelated git branch checked out ' 832 self._ui.warn(_('unrelated git branch checked out '
829 'in subrepo %s\n') % self._relpath) 833 'in subrepo %s\n') % self._relpath)