changeset 12995:d90fc91c8377

subrepo: update and merge works with any git branch
author Eric Eisner <ede@mit.edu>
date Sun, 14 Nov 2010 18:31:29 -0500
parents 845c602b8635
children 3a42651b0a62
files mercurial/subrepo.py tests/test-subrepo-git.t
diffstat 2 files changed, 57 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/subrepo.py	Sun Nov 14 18:22:33 2010 -0500
+++ b/mercurial/subrepo.py	Sun Nov 14 18:31:29 2010 -0500
@@ -634,6 +634,26 @@
         out, code = self._gitdir(['cat-file', '-e', revision])
         return code == 0
 
+    def _gitbranchmap(self):
+        'returns the current branch and a map from git revision to branch[es]'
+        bm = {}
+        redirects = {}
+        current = None
+        out = self._gitcommand(['branch', '-a', '--no-color',
+                                '--verbose', '--abbrev=40'])
+        for line in out.split('\n'):
+            if not line:
+                continue
+            if line[2:].startswith('(no branch)'):
+                continue
+            branch, revision = line[2:].split()[:2]
+            if revision == '->':
+                continue # ignore remote/HEAD redirects
+            if line[0] == '*':
+                current = branch
+            bm.setdefault(revision, []).append(branch)
+        return current, bm
+
     def _fetch(self, source, revision):
         if not os.path.exists('%s/.git' % self._path):
             self._ui.status(_('cloning subrepo %s\n') % self._relpath)
@@ -658,12 +678,32 @@
     def get(self, state):
         source, revision, kind = state
         self._fetch(source, revision)
-        if self._gitstate() != revision:
+        if self._gitstate() == revision:
+            return
+        current, bm = self._gitbranchmap()
+        if revision not in bm:
+            # 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])
+            return
+        branches = bm[revision]
+        firstlocalbranch = None
+        for b in branches:
+            if b == 'master':
+                # master trumps all other branches
+                self._gitcommand(['checkout', 'master'])
+                return
+            if not firstlocalbranch and not b.startswith('remotes/'):
+                firstlocalbranch = b
+        if firstlocalbranch:
+            self._gitcommand(['checkout', firstlocalbranch])
+        else:
+            remote = branches[0]
+            local = remote.split('/')[-1]
+            self._gitcommand(['checkout', '-b', local, remote])
 
     def commit(self, text, user, date):
         cmd = ['commit', '-a', '-m', text]
@@ -692,10 +732,15 @@
         cmd = ['push']
         if force:
             cmd.append('--force')
-        # as subrepos have no notion of "where to push to" we
-        # assume origin master. This is git's default
-        self._gitcommand(cmd + ['origin', 'master', '-q'])
-        return True
+        # push the currently checked out branch
+        current, bm = self._gitbranchmap()
+        if current:
+            self._gitcommand(cmd + ['origin', current, '-q'])
+            return True
+        else:
+            self._ui.warn(_('no branch checked out in subrepo %s\n'
+                            'nothing to push') % self._relpath)
+            return False
 
 types = {
     'hg': hgsubrepo,
--- a/tests/test-subrepo-git.t	Sun Nov 14 18:22:33 2010 -0500
+++ b/tests/test-subrepo-git.t	Sun Nov 14 18:31:29 2010 -0500
@@ -40,14 +40,19 @@
    source   ../gitroot
    revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
 
-record a new commit from upstream
+record a new commit from upstream from a different branch
 
   $ cd ../gitroot
+  $ git checkout -b testing
+  Switched to a new branch 'testing'
   $ echo gg >> g
   $ git commit -q -a -m gg
 
   $ cd ../t/s
   $ git pull -q
+  $ git checkout -b testing origin/testing
+  Switched to a new branch 'testing'
+  Branch testing set up to track remote branch testing from origin.
 
   $ cd ..
   $ hg commit -m 'update git subrepo'
@@ -72,8 +77,7 @@
 update to previous substate
 
   $ hg update 1
-  checking out detached HEAD in subrepo s
-  check out a git branch if you intend to make changes
+  Switched to a new branch 'master'
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat s/g
   g