[PATCH 2 of 3] subrepo: allow git subrepos to push and merge

Eric Eisner ede at MIT.EDU
Thu Nov 11 13:43:26 CST 2010


# HG changeset patch
# User Eric Eisner <ede at mit.edu>
# Date 1289503448 18000
# Node ID 6485c19a9601d37ff5a55385e0769ab93cf471cf
# Parent  ccc59ab604a8160d44847754f8b48dedb25d2f16
subrepo: allow git subrepos to push and merge

(master branch only)

gitsubrepo based on patch from David Soria Parra:
http://bitbucket.org/segv/davids-poor-git-subrepo-attempt/

diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -612,7 +612,10 @@ class gitsubrepo(object):
         err = err.read().strip()
 
         if err:
-            raise util.Abort(err)
+            # TODO: differentiate between failing and succeeding cases
+            # merge fetch and push all print to stderr normally...
+            if 'merge' not in commands:
+                raise util.Abort(err)
         # we have to wait for the child to exit to avoid race condition.
         p.wait()
         return retdata, p.returncode
@@ -626,19 +629,21 @@ class gitsubrepo(object):
 
     def _fetch(self, source, revision):
         if not os.path.exists('%s/.git' % self._path):
+            self._ui.status(_('cloning subrepo %s\n') % self._path)
             self._gitnodir(['clone', source, self._path])
         if self._githavelocally(revision):
             return
         self._ui.status(_('pulling subrepo %s\n') % self._path)
-        self._gitcommand(['fetch', '--all', source])
+        self._gitcommand(['fetch', '--all', '-q'])
         if not self._githavelocally(revision):
             raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
-                               (revision, source))
+                               (revision, self._path))
 
     def dirty(self):
         if self._state[1] != self._gitstate(): # version checked out changed?
             return True
         # check for staged changes or modified files; ignore untracked files
+        # docs say --porcelain flag is future-proof format
         changed = self._gitcommand(['status', '--porcelain',
                                     '--untracked-files=no'])
         return bool(changed.strip())
@@ -653,14 +658,41 @@ class gitsubrepo(object):
         cmd = ['commit', '-a', '-m', text]
         if user:
             cmd += ['--author', user]
-        # TODO: git's date parser silently ignores when seconds < 1e9
         if date:
-            cmd += ['--date', '%s %s' % date]
+            # git's date parser silently ignores when seconds < 1e9
+            # convert to ISO8601
+            cmd += ['--date', util.datestr(date, '%Y-%m-%dT%H:%M:%S %1%2')]
         self._gitcommand(cmd)
         # make sure commit works otherwise HEAD might not exist under certain
         # circumstances
         return self._gitstate()
 
+    def merge(self, state):
+        source, revision, kind = state
+        self._fetch(source, revision)
+        base = self._gitcommand(['merge-base', revision, self._state[1]])
+        if base.startswith(revision):
+            self.get(state)
+        elif not base.startswith(self._state[1]):
+            self._gitcommand(['merge', '--no-commit', revision])
+
+    def remove(self):
+        if self.dirty():
+            self._ui.warn(_('not removing repo %s because '
+                            'it has changes.\n') % self._path)
+            return
+        self._ui.note(_('removing subrepo %s\n') % self._path)
+        shutil.rmtree(self._ctx.repo.join(self._path))
+
+    def push(self, force):
+        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
+
 types = {
     'hg': hgsubrepo,
     'svn': svnsubrepo,
diff --git a/tests/test-subrepo-git.t b/tests/test-subrepo-git.t
--- a/tests/test-subrepo-git.t
+++ b/tests/test-subrepo-git.t
@@ -57,3 +57,101 @@ record a new commit from upstream
   path s
    source   ../gitroot
    revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
+
+make $GITROOT pushable, by replacing it with a clone with nothing checked out
+
+  $ cd ..
+  $ git clone gitroot gitrootbare --bare -q
+  $ rm -rf gitroot
+  $ mv gitrootbare gitroot
+
+clone root, make local change
+
+  $ cd t
+  $ hg clone . ../ta
+  updating to branch default
+  cloning subrepo $TESTTMP/ta/s
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd ../ta
+  $ hg debugsub
+  path s
+   source   ../gitroot
+   revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
+
+  $ echo ggg >> s/g
+  $ hg commit -m ggg
+  committing subrepository $TESTTMP/ta/s
+  $ hg debugsub
+  path s
+   source   ../gitroot
+   revision 79695940086840c99328513acbe35f90fcd55e57
+
+clone root separately, make different local change
+
+  $ cd ../t
+  $ hg clone . ../tb
+  updating to branch default
+  cloning subrepo $TESTTMP/tb/s
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd ../tb
+  $ hg debugsub
+  path s
+   source   ../gitroot
+   revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
+
+  $ cd s
+  $ echo f > f
+  $ git add f
+  $ cd ..
+
+  $ hg commit -m f
+  committing subrepository $TESTTMP/tb/s
+  $ hg debugsub
+  path s
+   source   ../gitroot
+   revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
+
+user b push changes
+
+  $ hg push
+  pushing to $TESTTMP/t
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+user a pulls, merges, commits
+
+  $ cd ../ta
+  $ hg pull
+  pulling from $TESTTMP/t
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg merge
+  pulling subrepo $TESTTMP/ta/s
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ cat s/f
+  f
+  $ cat s/g
+  g
+  gg
+  ggg
+  $ hg commit -m 'merge'
+  committing subrepository $TESTTMP/ta/s
+  $ hg debugsub
+  path s
+   source   ../gitroot
+   revision f47b465e1bce645dbf37232a00574aa1546ca8d3
+  $ hg push
+  pushing to $TESTTMP/t
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files


More information about the Mercurial-devel mailing list