Patchwork [4,of,4] localrepo: move the addchangegroup method in changegroup module

login
register
mail settings
Submitter Pierre-Yves David
Date April 3, 2014, 5:45 p.m.
Message ID <acb06cef54f4723627b5.1396547115@marginatus.alto.octopoid.net>
Download mbox | patch
Permalink /patch/4210/
State Accepted
Commit d3775db748a06450b69e4fd5beb52cb5e9ef07b0
Headers show

Comments

Pierre-Yves David - April 3, 2014, 5:45 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@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.
Matt Mackall - April 3, 2014, 8:16 p.m.
On Thu, 2014-04-03 at 10:45 -0700, pierre-yves.david@ens-lyon.org wrote:
> # HG changeset patch
> # User Pierre-Yves David <pierre-yves.david@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

These are queued for default, thanks.

Patch

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@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)