[PATCH 3 of 4 phases] discovery: introduce outgoing object for result of findcommonoutgoing

Pierre-Yves David pierre-yves.david at ens-lyon.org
Tue Jan 10 17:31:22 CST 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1326077236 -3600
# Node ID 7d93456f321f510481b62becaae977d171a54392
# Parent  d9bfbcac14abf9ad31cb671b7a99c60033bedb76
discovery: introduce outgoing object for result of findcommonoutgoing

Simplifies client logic in multiple places since it encapsulates the
computation of the common and, more importantly, the missing node lists.

This also allows an upcomping patch to communicate precomputed versions of
these lists to clients.

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -977,18 +977,18 @@
             raise util.Abort(_("--base is incompatible with specifying "
                                "a destination"))
         common = [repo.lookup(rev) for rev in base]
-        outheads = revs and map(repo.lookup, revs) or revs
+        heads = revs and map(repo.lookup, revs) or revs
+        cg = repo.getbundle('bundle', heads=heads, common=common)
     else:
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest, opts.get('branch'))
         other = hg.peer(repo, opts, dest)
         revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
         heads = revs and map(repo.lookup, revs) or revs
-        common, outheads = discovery.findcommonoutgoing(repo, other,
-                                                        onlyheads=heads,
-                                                        force=opts.get('force'))
-
-    cg = repo.getbundle('bundle', common=common, heads=outheads)
+        outgoing = discovery.findcommonoutgoing(repo, other,
+                                                onlyheads=heads,
+                                                force=opts.get('force'))
+        cg = repo.getlocalbundle('bundle', outgoing)
     if not cg:
         ui.status(_("no changes found\n"))
         return 1
@@ -5436,10 +5436,10 @@
             commoninc = None
             ui.debug('comparing with %s\n' % util.hidepassword(dest))
         repo.ui.pushbuffer()
-        common, outheads = discovery.findcommonoutgoing(repo, other,
-                                                        commoninc=commoninc)
+        outgoing = discovery.findcommonoutgoing(repo, other,
+                                                commoninc=commoninc)
         repo.ui.popbuffer()
-        o = repo.changelog.findmissing(common=common, heads=outheads)
+        o = outgoing.missing
         if o:
             t.append(_('%d outgoing') % len(o))
         if 'bookmarks' in other.listkeys('namespaces'):
diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -46,20 +46,56 @@
     common, anyinc, srvheads = res
     return (list(common), anyinc, heads or list(srvheads))
 
+class outgoing(object):
+    '''Represents the set of nodes present in a local repo but not in a
+    (possibly) remote one.
+
+    Members:
+
+      missing is a list of all nodes present in local but not in remote.
+      common is a list of all nodes shared between the two repos.
+      missingheads is the list of heads of missing.
+      commonheads is the list of heads of common.
+
+    The sets are computed on demand from the heads, unless provided upfront
+    by discovery.'''
+
+    def __init__(self, revlog, commonheads, missingheads):
+        self.commonheads = commonheads
+        self.missingheads = missingheads
+        self._revlog = revlog
+        self._common = None
+        self._missing = None
+
+    def _computecommonmissing(self):
+        sets = self._revlog.findcommonmissing(self.commonheads,
+                                              self.missingheads)
+        self._common, self._missing = sets
+
+    @util.propertycache
+    def common(self):
+        if self._common is None:
+            self._computecommonmissing()
+        return self._common
+
+    @util.propertycache
+    def missing(self):
+        if self._missing is None:
+            self._computecommonmissing()
+        return self._missing
+
 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
-    '''Return a tuple (common, anyoutgoing, heads) used to identify the set
-    of nodes present in repo but not in other.
+    '''Return an outgoing instance to identify the nodes present in repo but
+    not in other.
 
     If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
     are included. If you already know the local repo's heads, passing them in
     onlyheads is faster than letting them be recomputed here.
 
     If commoninc is given, it must the the result of a prior call to
-    findcommonincoming(repo, other, force) to avoid recomputing it here.
-
-    The returned tuple is meant to be passed to changelog.findmissing.'''
+    findcommonincoming(repo, other, force) to avoid recomputing it here.'''
     common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
-    return (common, onlyheads or repo.heads())
+    return outgoing(repo.changelog, common, onlyheads or repo.heads())
 
 def prepush(repo, remote, force, revs, newbranch):
     '''Analyze the local and remote repositories and determine which
@@ -80,12 +116,13 @@
     be after push completion.
     '''
     commoninc = findcommonincoming(repo, remote, force=force)
-    common, revs = findcommonoutgoing(repo, remote, onlyheads=revs,
+    outgoing = findcommonoutgoing(repo, remote, onlyheads=revs,
                                       commoninc=commoninc, force=force)
     _common, inc, remoteheads = commoninc
 
     cl = repo.changelog
-    alloutg = cl.findmissing(common, revs)
+    alloutg = outgoing.missing
+    common = outgoing.commonheads
     outg = []
     secret = []
     for o in alloutg:
@@ -208,7 +245,7 @@
         # use the fast path, no race possible on push
         cg = repo._changegroup(outg, 'push')
     else:
-        cg = repo.getbundle('push', heads=revs, common=common)
+        cg = repo.getlocalbundle('push', outgoing)
     # no need to compute outg ancestor. All node in outg have either:
     # - parents in outg
     # - parents in common
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -507,9 +507,9 @@
         revs = [repo.lookup(rev) for rev in revs]
 
     other = peer(repo, opts, dest)
-    common, outheads = discovery.findcommonoutgoing(repo, other, revs,
-                                                    force=opts.get('force'))
-    o = repo.changelog.findmissing(common, outheads)
+    outgoing = discovery.findcommonoutgoing(repo, other, revs,
+                                            force=opts.get('force'))
+    o = outgoing.missing
     if not o:
         ui.status(_("no changes found\n"))
         return None
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1739,6 +1739,18 @@
         common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
         return self._changegroupsubset(common, csets, heads, source)
 
+    def getlocalbundle(self, source, outgoing):
+        """Like getbundle, but taking a discovery.outgoing as an argument.
+
+        This is only implemented for local repos and reuses potentially
+        precomputed sets in outgoing."""
+        if not outgoing.missing:
+            return None
+        return self._changegroupsubset(outgoing.common,
+                                       outgoing.missing,
+                                       outgoing.missingheads,
+                                       source)
+
     def getbundle(self, source, heads=None, common=None):
         """Like changegroupsubset, but returns the set difference between the
         ancestors of heads and the ancestors common.
@@ -1756,10 +1768,8 @@
             common = [nullid]
         if not heads:
             heads = cl.heads()
-        common, missing = cl.findcommonmissing(common, heads)
-        if not missing:
-            return None
-        return self._changegroupsubset(common, missing, heads, source)
+        return self.getlocalbundle(source,
+                                   discovery.outgoing(cl, common, heads))
 
     def _changegroupsubset(self, commonrevs, csets, heads, source):
 
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -644,10 +644,10 @@
         revs = [repo.lookup(rev) for rev in revs]
     other = hg.peer(repo, {}, dest)
     repo.ui.pushbuffer()
-    common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
+    outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
     repo.ui.popbuffer()
     cl = repo.changelog
-    o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
+    o = set([cl.rev(r) for r in outgoing.missing])
     return [r for r in subset if r in o]
 
 def p1(repo, subset, x):


More information about the Mercurial-devel mailing list