mercurial/subrepo.py
changeset 13087 cca0779b4832
parent 13086 8db85e39d59c
child 13093 d0cbddfe3f4c
--- a/mercurial/subrepo.py	Sun Nov 28 15:21:23 2010 -0500
+++ b/mercurial/subrepo.py	Sun Nov 28 17:19:23 2010 -0500
@@ -691,6 +691,19 @@
             rev2branch.setdefault(revision, []).append(branch)
         return current, branch2rev, rev2branch
 
+    def _gittracking(self, branches):
+        'return map of remote branch to local tracking branch'
+        # assumes no more than one local tracking branch for each remote
+        tracking = {}
+        for b in branches:
+            if b.startswith('remotes/'):
+                continue
+            remote = self._gitcommand(['config', 'branch.%s.remote' % b])
+            if remote:
+                ref = self._gitcommand(['config', 'branch.%s.merge' % b])
+                tracking['remotes/%s/%s' % (remote, ref.split('/')[-1])] = b
+        return tracking
+
     def _fetch(self, source, revision):
         if not os.path.exists('%s/.git' % self._path):
             self._ui.status(_('cloning subrepo %s\n') % self._relpath)
@@ -724,13 +737,17 @@
         elif self._gitstate() == revision:
             return
         current, branch2rev, rev2branch = self._gitbranchmap()
-        if revision not in rev2branch:
+
+        def rawcheckout():
             # no branch to checkout, check it out with no branch
             self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
                           self._relpath)
             self._ui.warn(_('check out a git branch if you intend '
                             'to make changes\n'))
             self._gitcommand(['checkout', '-q', revision])
+
+        if revision not in rev2branch:
+            rawcheckout()
             return
         branches = rev2branch[revision]
         firstlocalbranch = None
@@ -743,10 +760,34 @@
                 firstlocalbranch = b
         if firstlocalbranch:
             self._gitcommand(['checkout', firstlocalbranch])
-        else:
-            remote = branches[0]
+            return
+
+        tracking = self._gittracking(branch2rev.keys())
+        # choose a remote branch already tracked if possible
+        remote = branches[0]
+        if remote not in tracking:
+            for b in branches:
+                if b in tracking:
+                    remote = b
+                    break
+
+        if remote not in tracking:
+            # create a new local tracking branch
             local = remote.split('/')[-1]
             self._gitcommand(['checkout', '-b', local, remote])
+        elif self._gitisancestor(branch2rev[tracking[remote]], remote):
+            # When updating to a tracked remote branch,
+            # if the local tracking branch is downstream of it,
+            # a normal `git pull` would have performed a "fast-forward merge"
+            # which is equivalent to updating the local branch to the remote.
+            # Since we are only looking at branching at update, we need to
+            # detect this situation and perform this action lazily.
+            if tracking[remote] != current:
+                self._gitcommand(['checkout', tracking[remote]])
+            self._gitcommand(['merge', '--ff', remote])
+        else:
+            # a real merge would be required, just checkout the revision
+            rawcheckout()
 
     def commit(self, text, user, date):
         cmd = ['commit', '-a', '-m', text]