comparison mercurial/subrepo.py @ 13087:cca0779b4832

subrepo: lazily update git's local tracking branches This continues the strategy of separation between hg pull and hg update in git subrepos by only dealing with git's branches on an update. This behavior tries to cover the bare essentials of the semantics of git pull in the subrepo when the parent repo does hg pull and hg update.
author Eric Eisner <ede@mit.edu>
date Sun, 28 Nov 2010 17:19:23 -0500
parents 8db85e39d59c
children d0cbddfe3f4c
comparison
equal deleted inserted replaced
13086:8db85e39d59c 13087:cca0779b4832
689 current = branch 689 current = branch
690 branch2rev[branch] = revision 690 branch2rev[branch] = revision
691 rev2branch.setdefault(revision, []).append(branch) 691 rev2branch.setdefault(revision, []).append(branch)
692 return current, branch2rev, rev2branch 692 return current, branch2rev, rev2branch
693 693
694 def _gittracking(self, branches):
695 'return map of remote branch to local tracking branch'
696 # assumes no more than one local tracking branch for each remote
697 tracking = {}
698 for b in branches:
699 if b.startswith('remotes/'):
700 continue
701 remote = self._gitcommand(['config', 'branch.%s.remote' % b])
702 if remote:
703 ref = self._gitcommand(['config', 'branch.%s.merge' % b])
704 tracking['remotes/%s/%s' % (remote, ref.split('/')[-1])] = b
705 return tracking
706
694 def _fetch(self, source, revision): 707 def _fetch(self, source, revision):
695 if not os.path.exists('%s/.git' % self._path): 708 if not os.path.exists('%s/.git' % self._path):
696 self._ui.status(_('cloning subrepo %s\n') % self._relpath) 709 self._ui.status(_('cloning subrepo %s\n') % self._relpath)
697 self._gitnodir(['clone', source, self._path]) 710 self._gitnodir(['clone', source, self._path])
698 if self._githavelocally(revision): 711 if self._githavelocally(revision):
722 self._gitcommand(['reset', '--hard', 'HEAD']) 735 self._gitcommand(['reset', '--hard', 'HEAD'])
723 return 736 return
724 elif self._gitstate() == revision: 737 elif self._gitstate() == revision:
725 return 738 return
726 current, branch2rev, rev2branch = self._gitbranchmap() 739 current, branch2rev, rev2branch = self._gitbranchmap()
727 if revision not in rev2branch: 740
741 def rawcheckout():
728 # no branch to checkout, check it out with no branch 742 # no branch to checkout, check it out with no branch
729 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') % 743 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
730 self._relpath) 744 self._relpath)
731 self._ui.warn(_('check out a git branch if you intend ' 745 self._ui.warn(_('check out a git branch if you intend '
732 'to make changes\n')) 746 'to make changes\n'))
733 self._gitcommand(['checkout', '-q', revision]) 747 self._gitcommand(['checkout', '-q', revision])
748
749 if revision not in rev2branch:
750 rawcheckout()
734 return 751 return
735 branches = rev2branch[revision] 752 branches = rev2branch[revision]
736 firstlocalbranch = None 753 firstlocalbranch = None
737 for b in branches: 754 for b in branches:
738 if b == 'master': 755 if b == 'master':
741 return 758 return
742 if not firstlocalbranch and not b.startswith('remotes/'): 759 if not firstlocalbranch and not b.startswith('remotes/'):
743 firstlocalbranch = b 760 firstlocalbranch = b
744 if firstlocalbranch: 761 if firstlocalbranch:
745 self._gitcommand(['checkout', firstlocalbranch]) 762 self._gitcommand(['checkout', firstlocalbranch])
746 else: 763 return
747 remote = branches[0] 764
765 tracking = self._gittracking(branch2rev.keys())
766 # choose a remote branch already tracked if possible
767 remote = branches[0]
768 if remote not in tracking:
769 for b in branches:
770 if b in tracking:
771 remote = b
772 break
773
774 if remote not in tracking:
775 # create a new local tracking branch
748 local = remote.split('/')[-1] 776 local = remote.split('/')[-1]
749 self._gitcommand(['checkout', '-b', local, remote]) 777 self._gitcommand(['checkout', '-b', local, remote])
778 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
779 # When updating to a tracked remote branch,
780 # if the local tracking branch is downstream of it,
781 # a normal `git pull` would have performed a "fast-forward merge"
782 # which is equivalent to updating the local branch to the remote.
783 # Since we are only looking at branching at update, we need to
784 # detect this situation and perform this action lazily.
785 if tracking[remote] != current:
786 self._gitcommand(['checkout', tracking[remote]])
787 self._gitcommand(['merge', '--ff', remote])
788 else:
789 # a real merge would be required, just checkout the revision
790 rawcheckout()
750 791
751 def commit(self, text, user, date): 792 def commit(self, text, user, date):
752 cmd = ['commit', '-a', '-m', text] 793 cmd = ['commit', '-a', '-m', text]
753 if user: 794 if user:
754 cmd += ['--author', user] 795 cmd += ['--author', user]