[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