commit: recurse into subrepositories
authorMatt Mackall <mpm@selenic.com>
Mon, 15 Jun 2009 02:45:38 -0500
changeset 8813 db3c1ab0e632
parent 8812 859f841937d0
child 8814 ab668c92a036
commit: recurse into subrepositories
mercurial/context.py
mercurial/localrepo.py
mercurial/subrepo.py
--- a/mercurial/context.py	Mon Jun 15 02:45:38 2009 -0500
+++ b/mercurial/context.py	Mon Jun 15 02:45:38 2009 -0500
@@ -184,6 +184,9 @@
             if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
                 yield fn
 
+    def sub(self, path):
+        return subrepo.subrepo(self, path)
+
 class filectx(object):
     """A filecontext object makes access to data related to a particular
        filerevision convenient."""
--- a/mercurial/localrepo.py	Mon Jun 15 02:45:38 2009 -0500
+++ b/mercurial/localrepo.py	Mon Jun 15 02:45:38 2009 -0500
@@ -7,7 +7,7 @@
 
 from node import bin, hex, nullid, nullrev, short
 from i18n import _
-import repo, changegroup
+import repo, changegroup, subrepo
 import changelog, dirstate, filelog, manifest, context
 import lock, transaction, store, encoding
 import util, extensions, hook, error
@@ -807,6 +807,7 @@
         wlock = self.wlock()
         try:
             p1, p2 = self.dirstate.parents()
+            wctx = self[None]
 
             if (not force and p2 != nullid and match and
                 (match.files() or match.anypats())):
@@ -817,12 +818,20 @@
             if force:
                 changes[0].extend(changes[6]) # mq may commit unchanged files
 
+            # check subrepos
+            subs = []
+            for s in wctx.substate:
+                if match(s) and wctx.sub(s).dirty():
+                    subs.append(s)
+            if subs and '.hgsubstate' not in changes[0]:
+                changes[0].insert(0, '.hgsubstate')
+
             # make sure all explicit patterns are matched
             if not force and match.files():
                 matched = set(changes[0] + changes[1] + changes[2])
 
                 for f in match.files():
-                    if f == '.' or f in matched: # matched
+                    if f == '.' or f in matched or f in wctx.substate:
                         continue
                     if f in changes[3]: # missing
                         fail(f, _('file not found!'))
@@ -852,6 +861,16 @@
                                       extra, changes)
             if editor:
                 cctx._text = editor(self, cctx)
+
+            # commit subs
+            if subs:
+                state = wctx.substate.copy()
+                for s in subs:
+                    self.ui.status(_('committing subrepository %s\n') % s)
+                    sr = wctx.sub(s).commit(cctx._text, user, date)
+                    state[s] = (state[s][0], sr)
+                subrepo.writestate(self, state)
+
             ret = self.commitctx(cctx, True)
 
             # update dirstate and mergestate
--- a/mercurial/subrepo.py	Mon Jun 15 02:45:38 2009 -0500
+++ b/mercurial/subrepo.py	Mon Jun 15 02:45:38 2009 -0500
@@ -5,7 +5,11 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
-import config, util, errno
+import errno, os
+import config, util, node, error
+localrepo = None
+
+nullstate = ('', '')
 
 def state(ctx):
     p = config.config()
@@ -33,3 +37,46 @@
         state[path] = (src, rev.get(path, ''))
 
     return state
+
+def writestate(repo, state):
+    repo.wwrite('.hgsubstate',
+                ''.join(['%s %s\n' % (state[s][1], s)
+                         for s in sorted(state)]), '')
+
+def subrepo(ctx, path):
+    # subrepo inherently violates our import layering rules
+    # because it wants to make repo objects from deep inside the stack
+    # so we manually delay the circular imports to not break
+    # scripts that don't use our demand-loading
+    global localrepo
+    import localrepo as l
+    localrepo = l
+
+    state = ctx.substate.get(path, nullstate)
+    if state[0].startswith('['): # future expansion
+        raise error.Abort('unknown subrepo source %s' % state[0])
+    return hgsubrepo(ctx, path, state)
+
+class hgsubrepo(object):
+    def __init__(self, ctx, path, state):
+        self._parent = ctx
+        self._path = path
+        self._state = state
+        r = ctx._repo
+        root = r.wjoin(path)
+        self._repo = localrepo.localrepository(r.ui, root)
+
+    def dirty(self):
+        r = self._state[1]
+        if r == '':
+            return True
+        w = self._repo[None]
+        if w.p1() != self._repo[r]: # version checked out changed
+            return True
+        return w.dirty() # working directory changed
+
+    def commit(self, text, user, date):
+        n = self._repo.commit(text, user, date)
+        if not n:
+            return self._repo['.'].hex() # different version checked out
+        return node.hex(n)