Mercurial > public > mercurial-scm > hg-stable
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] |