[PATCH 2 of 2 STABLE] changegroup: use unfiltered ancestor when common node is filtered (issue4982)

Gregory Szorc gregory.szorc at gmail.com
Wed Dec 2 16:20:36 CST 2015


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1449094789 28800
#      Wed Dec 02 14:19:49 2015 -0800
# Branch stable
# Node ID baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
# Parent  36b3a5ae03ae1df7a5a03e21f2f715da3116ea7c
changegroup: use unfiltered ancestor when common node is filtered (issue4982)

The current behavior of computeoutgoing() is to prune common nodes that
are unknown. This check is performed against the current repository's
view/filter, which for many cases (including hgweb) will be the
"visible" filter, which excludes hidden changesets.

If a hidden changeset is dropped from the common node list, every
unhidden changeset in that DAG head that isn't captured by another
entry in the common node list will be part of the outgoing changegroup.
In the worst case scenario, the resulting common node list will be
empty (all passed in nodes are filtered) and all unfiltered changesets
and their corresponding data will be included in the changegroup! This
could result in `hg pull` fetching what is effectivelly a full repo
bundle if the client sends a single common node which is hidden.

This patch teaches computeoutgoing() to be filtering aware. When we
encounter a filtered node, we trace its ancestors back to the first
unfiltered node and substitute that as the common node. All ancestors of
the first unfiltered node are thus excluded from the generated
changegroup.

diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -874,12 +874,35 @@ def computeoutgoing(repo, heads, common)
     the logic.
 
     Returns a discovery.outgoing object.
     """
+
+    # If a common node is filtered, we trace back to the first unfiltered
+    # ancestor node and use that as a common node. If we don't do this,
+    # we'd have to send a nodes from that filtered nodes DAG head that
+    # aren't also in the common set. In the worst case, there could be
+    # a single filtered node in common and the resulting empty list of
+    # common nodes would result in *all* nodes being marked as outgoing.
+
     cl = repo.changelog
+    ucl = repo.unfiltered().changelog
+
     if common:
         hasnode = cl.hasnode
-        common = [n for n in common if hasnode(n)]
+        newcommon = []
+        for n in common:
+            if hasnode(n):
+                newcommon.append(n)
+                continue
+
+            for rev in ucl.ancestors([ucl.rev(n)]):
+                if rev in cl:
+                    node = cl.node(rev)
+                    if node not in newcommon:
+                        newcommon.append(node)
+                    break
+
+        common = newcommon
     else:
         common = [nullid]
     if not heads:
         heads = cl.heads()
diff --git a/tests/test-obsolete-changeset-exchange.t b/tests/test-obsolete-changeset-exchange.t
--- a/tests/test-obsolete-changeset-exchange.t
+++ b/tests/test-obsolete-changeset-exchange.t
@@ -126,12 +126,10 @@ commands ever stop saying they have hidd
   pulling from $TESTTMP/pull-hidden-common
   query 1; heads
   searching for changes
   all local heads known remotely
-  3 changesets found
+  1 changesets found
   list of changesets:
-  96ee1d7354c4ad7372047672c36a1f561e3a6a4c
-  a33779fdfc23063680fc31e9ff637dff6876d3d2
   bec0734cd68e84477ba7fc1d13e6cff53ab70129
   listing keys for "phase"
   listing keys for "bookmarks"
   bundle2-output-bundle: "HG20", 3 parts total
@@ -140,16 +138,14 @@ commands ever stop saying they have hidd
   bundle2-output-part: "listkeys" (params: 1 mandatory) empty payload
   bundle2-input-bundle: with-transaction
   bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
   adding changesets
-  add changeset 96ee1d7354c4
-  add changeset a33779fdfc23
   add changeset bec0734cd68e
   adding manifests
   adding file changes
   adding foo revisions
   added 1 changesets with 1 changes to 1 files (+1 heads)
-  bundle2-input-part: total payload size 1378
+  bundle2-input-part: total payload size 474
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-part: "listkeys" (params: 1 mandatory) supported
   bundle2-input-bundle: 2 parts total
   checking for updated bookmarks


More information about the Mercurial-devel mailing list