discovery: introduce outgoing object for result of findcommonoutgoing
authorPierre-Yves David <pierre-yves.david@ens-lyon.org>
Mon, 09 Jan 2012 03:47:16 +0100
changeset 15837 cd956049fc14
parent 15836 926c9ee8d4be
child 15838 7299e09a85a2
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.
mercurial/commands.py
mercurial/discovery.py
mercurial/hg.py
mercurial/localrepo.py
mercurial/revset.py
--- a/mercurial/commands.py	Mon Jan 09 04:16:00 2012 +0100
+++ b/mercurial/commands.py	Mon Jan 09 03:47:16 2012 +0100
@@ -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'):
--- a/mercurial/discovery.py	Mon Jan 09 04:16:00 2012 +0100
+++ b/mercurial/discovery.py	Mon Jan 09 03:47:16 2012 +0100
@@ -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
--- a/mercurial/hg.py	Mon Jan 09 04:16:00 2012 +0100
+++ b/mercurial/hg.py	Mon Jan 09 03:47:16 2012 +0100
@@ -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
--- a/mercurial/localrepo.py	Mon Jan 09 04:16:00 2012 +0100
+++ b/mercurial/localrepo.py	Mon Jan 09 03:47:16 2012 +0100
@@ -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):
 
--- a/mercurial/revset.py	Mon Jan 09 04:16:00 2012 +0100
+++ b/mercurial/revset.py	Mon Jan 09 03:47:16 2012 +0100
@@ -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):