[PATCH 1 of 3] pull: add --subrepos flag

Angel Ezquerra angel.ezquerra at gmail.com
Sun Feb 17 06:19:16 CST 2013

# HG changeset patch
# User Angel Ezquerra <angel.ezquerra at gmail.com>
# Date 1360519226 -3600
# Node ID abbd26cca35280fb8f784b3f2c02eef71696c47b
# Parent  55b9b294b7544a6a144f627f71f4b770907d5a98
pull: add --subrepos flag

The purpose of this new flag is to ensure that you are able to update to any
incoming revision without requiring any network access. The idea is to make sure
that the repository is self-contained after doing hg pull --subrepos, as long as
it already was self-contained before the pull).

When the --subrepos flag is enabled, pull will also pull (or clone) all subrepos
that are present on the current revision and those that are referenced by any of
the incoming revisions.

If the incoming revisions refer to subrepos that are not on the working
directory yet they will be cloned. If any of the subrepositories changes its
pull source (as defined on the .hgsub file) it will be pulled from the current
and the new source.

This first patch only supports mercurial subrepos (a NotImplementedError
exception will be raised for all other subrepo types). Future patches will add
support for other subrepo types.

This requires some tests (I will add them after review). I ran the whole test
suite and all non skipped tests passed (# Ran 426 tests, 29 skipped, 0 failed.).

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -18,6 +18,7 @@
 import dagparser, context, simplemerge, graphmod
 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
 import phases, obsolete
+import itertools
 table = {}
@@ -4694,6 +4695,7 @@
     ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
     ('b', 'branch', [], _('a specific branch you would like to pull'),
+    ('S', 'subrepos', None, _('pull current and incoming subrepos')),
     ] + remoteopts,
     _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
 def pull(ui, repo, source="default", **opts):
@@ -4738,7 +4740,25 @@
                     "so a rev cannot be specified.")
             raise util.Abort(err)
+    oldtip = len(repo)
     modheads = repo.pull(other, heads=revs, force=opts.get('force'))
+    # update current and new subrepos
+    if opts.get('subrepos'):
+        # pull (or clone) the subrepos that are referenced by the
+        # current revision or by any of the incoming revisions
+        substopull = {}
+        revstocheck = itertools.chain(['.'], repo.changelog.revs(oldtip))
+        for rev in revstocheck:
+            ctx = repo[rev]
+            for sname, sinfo in ctx.substate.items():
+                substopull[(sname, sinfo,)] = ctx
+        # note that we must pull the subrepos before calling postincoming
+        # to avoid pulling them again if --update is also set
+        for (sname, sinfo), ctx in substopull.items():
+            ctx.sub(sname).pull(sinfo[0])
     bookmarks.updatefromremote(ui, repo, other, source)
     if checkout:
         checkout = str(repo.changelog.rev(other.lookup(checkout)))
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -330,6 +330,11 @@
         raise NotImplementedError
+    def pull(self, source):
+        """pull from the given parent repository source
+        """
+        raise NotImplementedError
     def get(self, state, overwrite=False):
         """run whatever commands are needed to put the subrepo into
         this state
@@ -528,6 +533,10 @@
         self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
         hg.clean(self._repo, node.nullid, False)
+    def pull(self, source):
+        state = (source, None, 'hg')
+        return self._get(state)
     def _get(self, state):
         source, revision, kind = state
         if revision not in self._repo:
diff --git a/tests/test-debugcomplete.t b/tests/test-debugcomplete.t
--- a/tests/test-debugcomplete.t
+++ b/tests/test-debugcomplete.t
@@ -204,7 +204,7 @@
   init: ssh, remotecmd, insecure
   log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
   merge: force, rev, preview, tool
-  pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
+  pull: update, force, rev, bookmark, branch, subrepos, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
   remove: after, force, include, exclude
   serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate

More information about the Mercurial-devel mailing list