[PATCH 4 of 4] localrepo: move the addchangegroup method in changegroup module
pierre-yves.david at ens-lyon.org
pierre-yves.david at ens-lyon.org
Thu Apr 3 17:45:15 UTC 2014
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1396391273 25200
# Tue Apr 01 15:27:53 2014 -0700
# Node ID acb06cef54f4723627b593ed2b70d98c2b8210b0
# Parent b0211a6be1866eeaed28de856df4ebacd415a2e8
localrepo: move the addchangegroup method in changegroup module
This is a gratuitous code move aimed at reducing the localrepo bloatness.
The method had few callers, not enough to be kept in local repo.
diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -559,11 +559,12 @@ def unshelve(ui, repo, *shelved, **opts)
try:
ui.quiet = True
fp = shelvedfile(repo, basename, 'hg').opener()
gen = changegroup.readbundle(fp, fp.name)
- repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
+ changegroup.addchangegroup(repo, gen, 'unshelve',
+ 'bundle:' + fp.name)
nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
phases.retractboundary(repo, phases.secret, nodes)
finally:
fp.close()
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -3,15 +3,16 @@
# Copyright 2006 Matt Mackall <mpm at selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
+import weakref
from i18n import _
-from node import nullrev, nullid, hex
+from node import nullrev, nullid, hex, short
import mdiff, util, dagutil
import struct, os, bz2, zlib, tempfile
-import discovery, error
+import discovery, error, phases, branchmap
_BUNDLE10_DELTA_HEADER = "20s20s20s20s"
def readexactly(stream, n):
'''read n bytes from stream.read and abort if less was available'''
@@ -553,5 +554,186 @@ def addchangegroupfiles(repo, source, re
raise util.Abort(
_('missing file data for %s:%s - run hg verify') %
(f, hex(n)))
return revisions, files
+
+def addchangegroup(repo, source, srctype, url, emptyok=False):
+ """Add the changegroup returned by source.read() to this repo.
+ srctype is a string like 'push', 'pull', or 'unbundle'. url is
+ the URL of the repo where this changegroup is coming from.
+
+ Return an integer summarizing the change to this repo:
+ - nothing changed or no source: 0
+ - more heads than before: 1+added heads (2..n)
+ - fewer heads than before: -1-removed heads (-2..-n)
+ - number of heads stays the same: 1
+ """
+ repo = repo.unfiltered()
+ def csmap(x):
+ repo.ui.debug("add changeset %s\n" % short(x))
+ return len(cl)
+
+ def revmap(x):
+ return cl.rev(x)
+
+ if not source:
+ return 0
+
+ repo.hook('prechangegroup', throw=True, source=srctype, url=url)
+
+ changesets = files = revisions = 0
+ efiles = set()
+
+ # write changelog data to temp files so concurrent readers will not see
+ # inconsistent view
+ cl = repo.changelog
+ cl.delayupdate()
+ oldheads = cl.heads()
+
+ tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
+ try:
+ trp = weakref.proxy(tr)
+ # pull off the changeset group
+ repo.ui.status(_("adding changesets\n"))
+ clstart = len(cl)
+ class prog(object):
+ step = _('changesets')
+ count = 1
+ ui = repo.ui
+ total = None
+ def __call__(repo):
+ repo.ui.progress(repo.step, repo.count, unit=_('chunks'),
+ total=repo.total)
+ repo.count += 1
+ pr = prog()
+ source.callback = pr
+
+ source.changelogheader()
+ srccontent = cl.addgroup(source, csmap, trp)
+ if not (srccontent or emptyok):
+ raise util.Abort(_("received changelog group is empty"))
+ clend = len(cl)
+ changesets = clend - clstart
+ for c in xrange(clstart, clend):
+ efiles.update(repo[c].files())
+ efiles = len(efiles)
+ repo.ui.progress(_('changesets'), None)
+
+ # pull off the manifest group
+ repo.ui.status(_("adding manifests\n"))
+ pr.step = _('manifests')
+ pr.count = 1
+ pr.total = changesets # manifests <= changesets
+ # no need to check for empty manifest group here:
+ # if the result of the merge of 1 and 2 is the same in 3 and 4,
+ # no new manifest will be created and the manifest group will
+ # be empty during the pull
+ source.manifestheader()
+ repo.manifest.addgroup(source, revmap, trp)
+ repo.ui.progress(_('manifests'), None)
+
+ needfiles = {}
+ if repo.ui.configbool('server', 'validate', default=False):
+ # validate incoming csets have their manifests
+ for cset in xrange(clstart, clend):
+ mfest = repo.changelog.read(repo.changelog.node(cset))[0]
+ mfest = repo.manifest.readdelta(mfest)
+ # store file nodes we must see
+ for f, n in mfest.iteritems():
+ needfiles.setdefault(f, set()).add(n)
+
+ # process the files
+ repo.ui.status(_("adding file changes\n"))
+ pr.step = _('files')
+ pr.count = 1
+ pr.total = efiles
+ source.callback = None
+
+ newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
+ needfiles)
+ revisions += newrevs
+ files += newfiles
+
+ dh = 0
+ if oldheads:
+ heads = cl.heads()
+ dh = len(heads) - len(oldheads)
+ for h in heads:
+ if h not in oldheads and repo[h].closesbranch():
+ dh -= 1
+ htext = ""
+ if dh:
+ htext = _(" (%+d heads)") % dh
+
+ repo.ui.status(_("added %d changesets"
+ " with %d changes to %d files%s\n")
+ % (changesets, revisions, files, htext))
+ repo.invalidatevolatilesets()
+
+ if changesets > 0:
+ p = lambda: cl.writepending() and repo.root or ""
+ repo.hook('pretxnchangegroup', throw=True,
+ node=hex(cl.node(clstart)), source=srctype,
+ url=url, pending=p)
+
+ added = [cl.node(r) for r in xrange(clstart, clend)]
+ publishing = repo.ui.configbool('phases', 'publish', True)
+ if srctype == 'push':
+ # Old servers can not push the boundary themselves.
+ # New servers won't push the boundary if changeset already
+ # exists locally as secret
+ #
+ # We should not use added here but the list of all change in
+ # the bundle
+ if publishing:
+ phases.advanceboundary(repo, phases.public, srccontent)
+ else:
+ phases.advanceboundary(repo, phases.draft, srccontent)
+ phases.retractboundary(repo, phases.draft, added)
+ elif srctype != 'strip':
+ # publishing only alter behavior during push
+ #
+ # strip should not touch boundary at all
+ phases.retractboundary(repo, phases.draft, added)
+
+ # make changelog see real files again
+ cl.finalize(trp)
+
+ tr.close()
+
+ if changesets > 0:
+ if srctype != 'strip':
+ # During strip, branchcache is invalid but coming call to
+ # `destroyed` will repair it.
+ # In other case we can safely update cache on disk.
+ branchmap.updatecache(repo.filtered('served'))
+ def runhooks():
+ # These hooks run when the lock releases, not when the
+ # transaction closes. So it's possible for the changelog
+ # to have changed since we last saw it.
+ if clstart >= len(repo):
+ return
+
+ # forcefully update the on-disk branch cache
+ repo.ui.debug("updating the branch cache\n")
+ repo.hook("changegroup", node=hex(cl.node(clstart)),
+ source=srctype, url=url)
+
+ for n in added:
+ repo.hook("incoming", node=hex(n), source=srctype,
+ url=url)
+
+ newheads = [h for h in repo.heads() if h not in oldheads]
+ repo.ui.log("incoming",
+ "%s incoming changes - new heads: %s\n",
+ len(added),
+ ', '.join([hex(c[:6]) for c in newheads]))
+ repo._afterlock(runhooks)
+
+ finally:
+ tr.release()
+ # never return 0 here:
+ if dh < 0:
+ return dh - 1
+ else:
+ return dh + 1
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5778,11 +5778,12 @@ def unbundle(ui, repo, fname1, *fnames,
wc = repo['.']
try:
for fname in fnames:
f = hg.openpath(ui, fname)
gen = changegroup.readbundle(f, fname)
- modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
+ modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
+ 'bundle:' + fname)
finally:
lock.release()
bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
return postincoming(ui, repo, modheads, opts.get('update'), None)
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -506,11 +506,11 @@ def _pullchangeset(pullop):
raise util.Abort(_("partial pull cannot be done because "
"other repository doesn't support "
"changegroupsubset."))
else:
cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
- pullop.cgresult = pullop.repo.addchangegroup(cg, 'pull',
+ pullop.cgresult = changegroup.addchangegroup(pullop.repo, cg, 'pull',
pullop.remote.url())
def _pullphase(pullop):
# Get remote phases data from remote
pullop.todosteps.remove('phases')
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -112,11 +112,11 @@ class localpeer(peer.peerrepository):
def lock(self):
return self._repo.lock()
def addchangegroup(self, cg, source, url):
- return self._repo.addchangegroup(cg, source, url)
+ return changegroup.addchangegroup(self._repo, cg, source, url)
def pushkey(self, namespace, key, old, new):
return self._repo.pushkey(namespace, key, old, new)
def listkeys(self, namespace):
@@ -1681,196 +1681,10 @@ class localrepository(object):
pass
def push(self, remote, force=False, revs=None, newbranch=False):
return exchange.push(self, remote, force, revs, newbranch)
- @unfilteredmethod
- def addchangegroup(self, source, srctype, url, emptyok=False):
- """Add the changegroup returned by source.read() to this repo.
- srctype is a string like 'push', 'pull', or 'unbundle'. url is
- the URL of the repo where this changegroup is coming from.
-
- Return an integer summarizing the change to this repo:
- - nothing changed or no source: 0
- - more heads than before: 1+added heads (2..n)
- - fewer heads than before: -1-removed heads (-2..-n)
- - number of heads stays the same: 1
- """
- def csmap(x):
- self.ui.debug("add changeset %s\n" % short(x))
- return len(cl)
-
- def revmap(x):
- return cl.rev(x)
-
- if not source:
- return 0
-
- self.hook('prechangegroup', throw=True, source=srctype, url=url)
-
- changesets = files = revisions = 0
- efiles = set()
-
- # write changelog data to temp files so concurrent readers will not see
- # inconsistent view
- cl = self.changelog
- cl.delayupdate()
- oldheads = cl.heads()
-
- tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
- try:
- trp = weakref.proxy(tr)
- # pull off the changeset group
- self.ui.status(_("adding changesets\n"))
- clstart = len(cl)
- class prog(object):
- step = _('changesets')
- count = 1
- ui = self.ui
- total = None
- def __call__(self):
- self.ui.progress(self.step, self.count, unit=_('chunks'),
- total=self.total)
- self.count += 1
- pr = prog()
- source.callback = pr
-
- source.changelogheader()
- srccontent = cl.addgroup(source, csmap, trp)
- if not (srccontent or emptyok):
- raise util.Abort(_("received changelog group is empty"))
- clend = len(cl)
- changesets = clend - clstart
- for c in xrange(clstart, clend):
- efiles.update(self[c].files())
- efiles = len(efiles)
- self.ui.progress(_('changesets'), None)
-
- # pull off the manifest group
- self.ui.status(_("adding manifests\n"))
- pr.step = _('manifests')
- pr.count = 1
- pr.total = changesets # manifests <= changesets
- # no need to check for empty manifest group here:
- # if the result of the merge of 1 and 2 is the same in 3 and 4,
- # no new manifest will be created and the manifest group will
- # be empty during the pull
- source.manifestheader()
- self.manifest.addgroup(source, revmap, trp)
- self.ui.progress(_('manifests'), None)
-
- needfiles = {}
- if self.ui.configbool('server', 'validate', default=False):
- # validate incoming csets have their manifests
- for cset in xrange(clstart, clend):
- mfest = self.changelog.read(self.changelog.node(cset))[0]
- mfest = self.manifest.readdelta(mfest)
- # store file nodes we must see
- for f, n in mfest.iteritems():
- needfiles.setdefault(f, set()).add(n)
-
- # process the files
- self.ui.status(_("adding file changes\n"))
- pr.step = _('files')
- pr.count = 1
- pr.total = efiles
- source.callback = None
-
- newrevs, newfiles = changegroup.addchangegroupfiles(self,
- source,
- revmap,
- trp,
- pr,
- needfiles)
- revisions += newrevs
- files += newfiles
-
- dh = 0
- if oldheads:
- heads = cl.heads()
- dh = len(heads) - len(oldheads)
- for h in heads:
- if h not in oldheads and self[h].closesbranch():
- dh -= 1
- htext = ""
- if dh:
- htext = _(" (%+d heads)") % dh
-
- self.ui.status(_("added %d changesets"
- " with %d changes to %d files%s\n")
- % (changesets, revisions, files, htext))
- self.invalidatevolatilesets()
-
- if changesets > 0:
- p = lambda: cl.writepending() and self.root or ""
- self.hook('pretxnchangegroup', throw=True,
- node=hex(cl.node(clstart)), source=srctype,
- url=url, pending=p)
-
- added = [cl.node(r) for r in xrange(clstart, clend)]
- publishing = self.ui.configbool('phases', 'publish', True)
- if srctype == 'push':
- # Old servers can not push the boundary themselves.
- # New servers won't push the boundary if changeset already
- # exists locally as secret
- #
- # We should not use added here but the list of all change in
- # the bundle
- if publishing:
- phases.advanceboundary(self, phases.public, srccontent)
- else:
- phases.advanceboundary(self, phases.draft, srccontent)
- phases.retractboundary(self, phases.draft, added)
- elif srctype != 'strip':
- # publishing only alter behavior during push
- #
- # strip should not touch boundary at all
- phases.retractboundary(self, phases.draft, added)
-
- # make changelog see real files again
- cl.finalize(trp)
-
- tr.close()
-
- if changesets > 0:
- if srctype != 'strip':
- # During strip, branchcache is invalid but coming call to
- # `destroyed` will repair it.
- # In other case we can safely update cache on disk.
- branchmap.updatecache(self.filtered('served'))
- def runhooks():
- # These hooks run when the lock releases, not when the
- # transaction closes. So it's possible for the changelog
- # to have changed since we last saw it.
- if clstart >= len(self):
- return
-
- # forcefully update the on-disk branch cache
- self.ui.debug("updating the branch cache\n")
- self.hook("changegroup", node=hex(cl.node(clstart)),
- source=srctype, url=url)
-
- for n in added:
- self.hook("incoming", node=hex(n), source=srctype,
- url=url)
-
- newheads = [h for h in self.heads() if h not in oldheads]
- self.ui.log("incoming",
- "%s incoming changes - new heads: %s\n",
- len(added),
- ', '.join([hex(c[:6]) for c in newheads]))
- self._afterlock(runhooks)
-
- finally:
- tr.release()
- # never return 0 here:
- if dh < 0:
- return dh - 1
- else:
- return dh + 1
-
-
def stream_in(self, remote, requirements):
lock = self.lock()
try:
# Save remote branchmap. We will use it later
# to speed up branchcache creation
diff --git a/mercurial/repair.py b/mercurial/repair.py
--- a/mercurial/repair.py
+++ b/mercurial/repair.py
@@ -146,11 +146,12 @@ def strip(ui, repo, nodelist, backup="al
f = open(chgrpfile, "rb")
gen = changegroup.readbundle(f, chgrpfile)
if not repo.ui.verbose:
# silence internal shuffling chatter
repo.ui.pushbuffer()
- repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
+ changegroup.addchangegroup(repo, gen, 'strip',
+ 'bundle:' + chgrpfile, True)
if not repo.ui.verbose:
repo.ui.popbuffer()
f.close()
if not keeppartialbundle:
os.unlink(chgrpfile)
diff --git a/mercurial/sshserver.py b/mercurial/sshserver.py
--- a/mercurial/sshserver.py
+++ b/mercurial/sshserver.py
@@ -141,11 +141,11 @@ class sshserver(object):
self.sendresponse("not locked")
return
self.sendresponse("")
cg = changegroup.unbundle10(self.fin, "UN")
- r = self.repo.addchangegroup(cg, 'serve', self._client())
+ r = changegroup.addchangegroup(self.repo, cg, 'serve', self._client())
self.lock.release()
return str(r)
def _client(self):
client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -646,11 +646,12 @@ def unbundle(repo, proto, heads):
# push can proceed
fp.seek(0)
gen = changegroupmod.readbundle(fp, None)
try:
- r = repo.addchangegroup(gen, 'serve', proto._client())
+ r = changegroupmod.addchangegroup(repo, gen, 'serve',
+ proto._client())
except util.Abort, inst:
sys.stderr.write("abort: %s\n" % inst)
finally:
lock.release()
return pushres(r)
More information about the Mercurial-devel
mailing list