[PATCH 2 of 2 STABLE] changegroup: use unfiltered ancestor when common node is filtered (issue4982)
Pierre-Yves David
pierre-yves.david at ens-lyon.org
Sun Dec 6 00:37:56 CST 2015
On 12/02/2015 02:20 PM, Gregory Szorc wrote:
> # 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 n is unknown (buggy client, strip, etc). This will raise an exception
and crash the operation. We should use ucl.hasnode too.
> + if rev in cl:
> + node = cl.node(rev)
> + if node not in newcommon:
> + newcommon.append(node)
> + break
This will mis-behave in case of merge. heads(::COMMON - unserved()) can
contains multiple entry. This code would only catch the top most one.
--
Pierre-Yves David
More information about the Mercurial-devel
mailing list