diff mercurial/subrepo.py @ 8814:ab668c92a036

subrepo: add update/merge logic
author Matt Mackall <mpm@selenic.com>
date Mon, 15 Jun 2009 02:45:38 -0500
parents db3c1ab0e632
children e87b0fc4750b
line wrap: on
line diff
--- a/mercurial/subrepo.py	Mon Jun 15 02:45:38 2009 -0500
+++ b/mercurial/subrepo.py	Mon Jun 15 02:45:38 2009 -0500
@@ -6,8 +6,9 @@
 # GNU General Public License version 2, incorporated herein by reference.
 
 import errno, os
+from i18n import _
 import config, util, node, error
-localrepo = None
+localrepo = hg = None
 
 nullstate = ('', '')
 
@@ -43,14 +44,81 @@
                 ''.join(['%s %s\n' % (state[s][1], s)
                          for s in sorted(state)]), '')
 
+def submerge(repo, wctx, mctx, actx):
+    if mctx == actx: # backwards?
+        actx = wctx.p1()
+    s1 = wctx.substate
+    s2 = mctx.substate
+    sa = actx.substate
+    sm = {}
+
+    for s, l in s1.items():
+        a = sa.get(s, nullstate)
+        if s in s2:
+            r = s2[s]
+            if l == r or r == a: # no change or local is newer
+                sm[s] = l
+                continue
+            elif l == a: # other side changed
+                wctx.sub(s).get(r)
+                sm[s] = r
+            elif l[0] != r[0]: # sources differ
+                if repo.ui.prompt(
+                    _(' subrepository sources for %s differ\n'
+                      'use (l)ocal source (%s) or (r)emote source (%s)?'
+                      % (s, l[0], r[0]),
+                      (_('&Local'), _('&Remote')), _('l'))) == _('r'):
+                    wctx.sub(s).get(r)
+                    sm[s] = r
+            elif l[1] == a[1]: # local side is unchanged
+                wctx.sub(s).get(r)
+                sm[s] = r
+            else:
+                wctx.sub(s).merge(r)
+                sm[s] = l
+        elif l == a: # remote removed, local unchanged
+            wctx.sub(s).remove()
+        else:
+            if repo.ui.prompt(
+                _(' local changed subrepository %s which remote removed\n'
+                  'use (c)hanged version or (d)elete?' % s,
+                  (_('&Changed'), _('&Delete')), _('c'))) == _('d'):
+                wctx.sub(s).remove()
+
+    for s, r in s2.items():
+        if s in s1:
+            continue
+        elif s not in sa:
+            wctx.sub(s).get(r)
+            sm[s] = r
+        elif r != sa[s]:
+            if repo.ui.prompt(
+                _(' remote changed subrepository %s which local removed\n'
+                  'use (c)hanged version or (d)elete?' % s,
+                  (_('&Changed'), _('&Delete')), _('c'))) == _('c'):
+                wctx.sub(s).get(r)
+                sm[s] = r
+
+    # record merged .hgsubstate
+    writestate(repo, sm)
+
+def _abssource(repo):
+    if hasattr(repo, '_subparent'):
+        source = repo._subsource
+        if source.startswith('/') or '://' in source:
+            return source
+        return os.path.join(_abssource(repo._subparent), repo._subsource)
+    return repo.ui.config('paths', 'default', repo.root)
+
 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
+    global localrepo, hg
+    import localrepo as l, hg as h
     localrepo = l
+    hg = h
 
     state = ctx.substate.get(path, nullstate)
     if state[0].startswith('['): # future expansion
@@ -64,7 +132,13 @@
         self._state = state
         r = ctx._repo
         root = r.wjoin(path)
-        self._repo = localrepo.localrepository(r.ui, root)
+        if os.path.exists(os.path.join(root, '.hg')):
+            self._repo = localrepo.localrepository(r.ui, root)
+        else:
+            util.makedirs(root)
+            self._repo = localrepo.localrepository(r.ui, root, create=True)
+        self._repo._subparent = r
+        self._repo._subsource = state[0]
 
     def dirty(self):
         r = self._state[1]
@@ -80,3 +154,25 @@
         if not n:
             return self._repo['.'].hex() # different version checked out
         return node.hex(n)
+
+    def remove(self):
+        # we can't fully delete the repository as it may contain
+        # local-only history
+        self._repo.ui.note(_('removing subrepo %s\n') % self._path)
+        hg.clean(self._repo, node.nullid, False)
+
+    def get(self, state):
+        source, revision = state
+        try:
+            self._repo.lookup(revision)
+        except error.RepoError:
+            self._repo._subsource = source
+            self._repo.ui.status(_('pulling subrepo %s\n') % self._path)
+            srcurl = _abssource(self._repo)
+            other = hg.repository(self._repo.ui, srcurl)
+            self._repo.pull(other)
+
+        hg.clean(self._repo, revision, False)
+
+    def merge(self, state):
+        hg.merge(self._repo, state[1], remind=False)