From patchwork Sun Mar 3 21:05:45 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [1,of,5,V2] pull: add --subrepos flag From: Angel Ezquerra X-Patchwork-Id: 1075 Message-Id: To: mercurial-devel@selenic.com Date: Sun, 03 Mar 2013 22:05:45 +0100 # HG changeset patch # User Angel Ezquerra # Date 1360519226 -3600 # Node ID c5e164d3282a4a2fb54171e6246b3b2e8a520080 # Parent a07be895373394be66ba38b1ff111e26aca03ac8 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 was already 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. Pulls are recursive (i.e. subrepos withing subrepos will be also pulled as needed), but clones are not recursive yet (it requires adding a similar --subrepos flag to clone, which will be done on another patch). 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. diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -4654,6 +4654,8 @@ ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')), ('b', 'branch', [], _('a specific branch you would like to pull'), _('BRANCH')), + ('S', 'subrepos', None, + _('pull current and incoming subrepos recursively')), ] + remoteopts, _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')) def pull(ui, repo, source="default", **opts): @@ -4698,7 +4700,9 @@ "so a rev cannot be specified.") raise util.Abort(err) - modheads = repo.pull(other, heads=revs, force=opts.get('force')) + modheads = repo.pull(other, heads=revs, force=opts.get('force'), + subrepos=opts.get('subrepos')) + bookmarks.updatefromremote(ui, repo, other, source) if checkout: checkout = str(repo.changelog.rev(other.lookup(checkout))) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -10,12 +10,14 @@ import changelog, dirstate, filelog, manifest, context, bookmarks, phases import lock, transaction, store, encoding, base85 import scmutil, util, extensions, hook, error, revset +import config import match as matchmod import merge as mergemod import tags as tagsmod from lock import release import weakref, errno, os, time, inspect import branchmap +import itertools propertycache = util.propertycache filecache = scmutil.filecache @@ -1646,11 +1648,13 @@ return r - def pull(self, remote, heads=None, force=False): + def pull(self, remote, heads=None, force=False, subrepos=False): # don't open transaction for nothing or you break future useful # rollback call tr = None trname = 'pull\n' + util.hidepassword(remote.url()) + if subrepos: + oldtip = len(self) lock = self.lock() try: tmp = discovery.findcommonincoming(self, remote, heads=heads, @@ -1730,8 +1734,43 @@ tr.release() lock.release() + # update current and new subrepos + if subrepos: + # pull (or clone) the subrepos that are referenced by the + # current revision or by any of the incoming revisions + revstocheck = itertools.chain(['.'], self.changelog.revs(oldtip)) + self.getsubrepos(revs=revstocheck, source=remote.url()) + return result + def getsubrepos(self, revs=None, source=None): + """Get (clone or pull) the subrepos that are referenced + on any of the revisions on the given revision list + """ + if revs is None: + # check all revisions + revs = self.changelog.revs() + # use a sortdict to make sure that we get the subrepos + # in the order they are found + substopull = config.sortdict() + for rev in revs: + ctx = self[rev] + # read the substate items in alphabetical order to ensure + # that we always process the subrepos in the same order + for sname in sorted(ctx.substate): + sinfo = ctx.substate[sname] + substopull[(sname, sinfo[0], sinfo[2])] = ctx + try: + self._subtoppath = source + for (sname, ssource, stype), ctx in substopull.items(): + try: + ctx.sub(sname).pull(ssource) + except (error.RepoError, urllib2.HTTPError), ex: + self.ui.warn(_('could not pull subrepo %s from %s (%s)\n') + % (sname, ssource, str(ex))) + finally: + del self._subtoppath + def checkpush(self, force, revs): """Extensions can override this function if additional checks have to be performed before pushing, or call it if they override push 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,8 +533,13 @@ 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 + subrepos = revision is None if revision not in self._repo: self._repo._subsource = source srcurl = _abssource(self._repo) @@ -547,7 +557,7 @@ else: self._repo.ui.status(_('pulling subrepo %s from %s\n') % (subrelpath(self), srcurl)) - self._repo.pull(other) + self._repo.pull(other, subrepos=subrepos) bookmarks.updatefromremote(self._repo.ui, self._repo, other, srcurl) 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