setdiscover: allow to ignore part of the local graph
authorBoris Feld <boris.feld@octobus.net>
Wed, 06 Dec 2017 22:44:51 +0100
changeset 35304 f77121b6bf1b
parent 35303 67b7e39b441b
child 35305 483b5dd0f1aa
setdiscover: allow to ignore part of the local graph Currently, the push discovery first determines the full set of common nodes before looking into what changesets are outgoing. When pushing a specific subset, this can lead to pathological situations where we search for the status of thousand of local heads that are unrelated to the requested pushes. To fix this, we need to teach the discovery to ignores part of the graph. Most of the necessary pieces were already in place. This changeset just makes them available to higher level API and tests them. Change actually impacting pushes are coming in a later changeset.
mercurial/dagutil.py
mercurial/debugcommands.py
mercurial/setdiscovery.py
tests/test-completion.t
tests/test-setdiscovery.t
--- a/mercurial/dagutil.py	Thu Dec 07 01:53:14 2017 +0100
+++ b/mercurial/dagutil.py	Wed Dec 06 22:44:51 2017 +0100
@@ -154,8 +154,9 @@
 class revlogdag(revlogbaseddag):
     '''dag interface to a revlog'''
 
-    def __init__(self, revlog):
+    def __init__(self, revlog, localsubset=None):
         revlogbaseddag.__init__(self, revlog, set(revlog))
+        self._heads = localsubset
 
     def _getheads(self):
         return [r for r in self._revlog.headrevs() if r != nullrev]
--- a/mercurial/debugcommands.py	Thu Dec 07 01:53:14 2017 +0100
+++ b/mercurial/debugcommands.py	Wed Dec 06 22:44:51 2017 +0100
@@ -734,6 +734,7 @@
     [('', 'old', None, _('use old-style discovery')),
     ('', 'nonheads', None,
      _('use old-style discovery with non-heads included')),
+    ('', 'rev', [], 'restrict discovery to this set of revs'),
     ] + cmdutil.remoteopts,
     _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
 def debugdiscovery(ui, repo, remoteurl="default", **opts):
@@ -747,11 +748,8 @@
     # make sure tests are repeatable
     random.seed(12323)
 
-    def doit(localheads, remoteheads, remote=remote):
+    def doit(pushedrevs, remoteheads, remote=remote):
         if opts.get('old'):
-            if localheads:
-                raise error.Abort('cannot use localheads with old style '
-                                 'discovery')
             if not util.safehasattr(remote, 'branches'):
                 # enable in-client legacy support
                 remote = localrepo.locallegacypeer(remote.local())
@@ -765,7 +763,12 @@
                 all = dag.ancestorset(dag.internalizeall(common))
                 common = dag.externalizeall(dag.headsetofconnecteds(all))
         else:
-            common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
+            nodes = None
+            if pushedrevs:
+                revs = scmutil.revrange(repo, pushedrevs)
+                nodes = [repo[r].node() for r in revs]
+            common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
+                                                            ancestorsof=nodes)
         common = set(common)
         rheads = set(hds)
         lheads = set(repo.heads())
@@ -794,7 +797,7 @@
     else:
         remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
                                                  opts.get('remote_head'))
-        localrevs = opts.get('local_head')
+        localrevs = opts.get('rev')
         doit(localrevs, remoterevs)
 
 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
--- a/mercurial/setdiscovery.py	Thu Dec 07 01:53:14 2017 +0100
+++ b/mercurial/setdiscovery.py	Wed Dec 06 22:44:51 2017 +0100
@@ -133,7 +133,8 @@
 def findcommonheads(ui, local, remote,
                     initialsamplesize=100,
                     fullsamplesize=200,
-                    abortwhenunrelated=True):
+                    abortwhenunrelated=True,
+                    ancestorsof=None):
     '''Return a tuple (common, anyincoming, remoteheads) used to identify
     missing nodes from or in remote.
     '''
@@ -141,7 +142,11 @@
 
     roundtrips = 0
     cl = local.changelog
-    dag = dagutil.revlogdag(cl)
+    localsubset = None
+    if ancestorsof is not None:
+        rev = local.changelog.rev
+        localsubset = [rev(n) for n in ancestorsof]
+    dag = dagutil.revlogdag(cl, localsubset=localsubset)
 
     # early exit if we know all the specified remote heads already
     ui.debug("query 1; heads\n")
--- a/tests/test-completion.t	Thu Dec 07 01:53:14 2017 +0100
+++ b/tests/test-completion.t	Wed Dec 06 22:44:51 2017 +0100
@@ -261,7 +261,7 @@
   debugdate: extended
   debugdeltachain: changelog, manifest, dir, template
   debugdirstate: nodates, datesort
-  debugdiscovery: old, nonheads, ssh, remotecmd, insecure
+  debugdiscovery: old, nonheads, rev, ssh, remotecmd, insecure
   debugextensions: template
   debugfileset: rev
   debugfsinfo: 
--- a/tests/test-setdiscovery.t	Thu Dec 07 01:53:14 2017 +0100
+++ b/tests/test-setdiscovery.t	Wed Dec 06 22:44:51 2017 +0100
@@ -16,11 +16,17 @@
   >     echo "% -- a -> b set"
   >     hg -R a debugdiscovery b --verbose --debug --config progress.debug=true
   >     echo
+  >     echo "% -- a -> b set (tip only)"
+  >     hg -R a debugdiscovery b --verbose --debug --config progress.debug=true --rev tip
+  >     echo
   >     echo "% -- b -> a tree"
   >     hg -R b debugdiscovery a --verbose --old
   >     echo
   >     echo "% -- b -> a set"
   >     hg -R b debugdiscovery a --verbose --debug --config progress.debug=true
+  >     echo
+  >     echo "% -- b -> a set (tip only)"
+  >     hg -R b debugdiscovery a --verbose --debug --config progress.debug=true --rev tip
   >     cd ..
   > }
 
@@ -48,6 +54,13 @@
   common heads: 01241442b3c2 b5714e113bc0
   local is subset
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  all local heads known remotely
+  common heads: b5714e113bc0
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -62,6 +75,14 @@
   all remote heads known locally
   common heads: 01241442b3c2 b5714e113bc0
   remote is subset
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  all remote heads known locally
+  common heads: 01241442b3c2 b5714e113bc0
+  remote is subset
 
 
 Many new:
@@ -86,6 +107,16 @@
   2 total queries in *.????s (glob)
   common heads: bebd167eb94d
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 31, sample size is: 31
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -101,6 +132,16 @@
   query 2; still undecided: 2, sample size is: 2
   2 total queries in *.????s (glob)
   common heads: bebd167eb94d
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  taking initial sample
+  searching: 2 queries
+  query 2; still undecided: 2, sample size is: 2
+  2 total queries in *.????s (glob)
+  common heads: bebd167eb94d
 
 Both sides many new with stub:
 
@@ -124,6 +165,16 @@
   2 total queries in *.????s (glob)
   common heads: 2dc09a01254d
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 31, sample size is: 31
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -139,6 +190,16 @@
   query 2; still undecided: 29, sample size is: 29
   2 total queries in *.????s (glob)
   common heads: 2dc09a01254d
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  taking initial sample
+  searching: 2 queries
+  query 2; still undecided: 29, sample size is: 29
+  2 total queries in *.????s (glob)
+  common heads: 2dc09a01254d
 
 
 Both many new:
@@ -163,6 +224,16 @@
   2 total queries in *.????s (glob)
   common heads: 66f7d451a68b
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 31, sample size is: 31
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -178,6 +249,16 @@
   query 2; still undecided: 31, sample size is: 31
   2 total queries in *.????s (glob)
   common heads: 66f7d451a68b
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 31, sample size is: 31
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
 
 
 Both many new skewed:
@@ -202,6 +283,16 @@
   2 total queries in *.????s (glob)
   common heads: 66f7d451a68b
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 51, sample size is: 51
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -217,6 +308,16 @@
   query 2; still undecided: 31, sample size is: 31
   2 total queries in *.????s (glob)
   common heads: 66f7d451a68b
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 31, sample size is: 31
+  2 total queries in *.????s (glob)
+  common heads: 66f7d451a68b
 
 
 Both many new on top of long history:
@@ -244,6 +345,19 @@
   3 total queries in *.????s (glob)
   common heads: 7ead0cba2838
   
+  % -- a -> b set (tip only)
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 1049, sample size is: 11
+  sampling from both directions
+  searching: 3 queries
+  query 3; still undecided: 31, sample size is: 31
+  3 total queries in *.????s (glob)
+  common heads: 7ead0cba2838
+  
   % -- b -> a tree
   comparing with a
   searching for changes
@@ -262,6 +376,19 @@
   query 3; still undecided: 15, sample size is: 15
   3 total queries in *.????s (glob)
   common heads: 7ead0cba2838
+  
+  % -- b -> a set (tip only)
+  comparing with a
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 1029, sample size is: 11
+  sampling from both directions
+  searching: 3 queries
+  query 3; still undecided: 15, sample size is: 15
+  3 total queries in *.????s (glob)
+  common heads: 7ead0cba2838
 
 
 One with >200 heads, which used to use up all of the sample:
@@ -327,6 +454,18 @@
   query 6; still undecided: \d+, sample size is: \d+ (re)
   6 total queries in *.????s (glob)
   common heads: 3ee37d65064a
+  $ hg -R a debugdiscovery b --debug --verbose --config progress.debug=true --rev tip
+  comparing with b
+  query 1; heads
+  searching for changes
+  taking quick initial sample
+  searching: 2 queries
+  query 2; still undecided: 303, sample size is: 9
+  sampling from both directions
+  searching: 3 queries
+  query 3; still undecided: 3, sample size is: 3
+  3 total queries in *.????s (glob)
+  common heads: 3ee37d65064a
 
 Test actual protocol when pulling one new head in addition to common heads