[PATCH evolve-ext] evolve: evolve --rev should warn of unsolvable case and not crash

Pierre-Yves David pierre-yves.david at ens-lyon.org
Mon Jun 1 08:41:27 UTC 2015



On 05/29/2015 03:32 PM, Laurent Charignon wrote:
> # HG changeset patch
> # User Laurent Charignon <lcharignon at fb.com>
> # Date 1432938715 25200
> #      Fri May 29 15:31:55 2015 -0700
> # Node ID 2750ff8ab0779cd4f478522f9d7781900b91a231
> # Parent  69e5de3e6129185469c2cbf98383ac6d58260d0c
> evolve: evolve --rev should warn of unsolvable case and not crash
>
> Before this patch, evolve --rev was crashing when the given set of troubles
> was impossible to solve. This patch fixes the crash by adding a new function
> to validate that the troubles are solvable before processing to resolution.

Actually, this changeset does not "warn" it aborts

1) abort should be done with "raise Abort(...)" cf Jordi comment in your 
fold message for details (especially regarding error message length)

2) We should probably just skip such revision with a warning.

3) detecting such situation may be done at run time, when we try to 
solve the revision trouble and realise we cannot. This will probably be 
cheaper and result in simpler code.

(3) Would also remove the crash that happen when such situation is 
triggered without --rev, and give you proper "right trouble with right 
situation" detection (as explained later in this review)

It is also a good practice to provide an example of the message in the 
commit description. This allow discussing it earlier.

> diff --git a/hgext/evolve.py b/hgext/evolve.py
> --- a/hgext/evolve.py
> +++ b/hgext/evolve.py
> @@ -1232,6 +1232,44 @@
>       if repo['.'] != startnode:
>           ui.status(_('working directory is now at %s\n') % repo['.'])
>
> +def unsolvable(repo, revs, troubles):
> +    """ Returns a string describing an issue in case revs cannot be solved
> +
> +    When a user tries to resolve the troubles in his repository, he can make
> +    a selection that is too narrow.
> +    Consider the following example:
> +
> +    O 5
> +    |
> +    O 4
> +    |
> +    O-----X
> +    .     .
> +    .     .
> +    .     .
> +
> +    hg evolve --rev 5 cannot be solved but hg evolve --rev 4:: can be
> +    This is because 5 is based on the troubled revision 4.

I think this unsolvability should focus on instability. It is probably 
possible to resolve some trouble (like divergence) with your parent 
troubles. It is instability that you cannot solve it you inherit if from 
your parent instead of behind the first old to be affected because your 
parent are obsolete.

> +    """
> +    sortedrevs = sorted(revs)

> +    for r in sortedrevs:
> +        p = repo[r].parents()
> +        p1 = p[0].rev() if p[0] is not None else -1
> +        p2 = p[1].rev() if not len(p) == 1 and p[1] is not None else -1

I know we can use these (if else) now but we should probably refrain to 
use it if the conditional in non trivial (as it is the case for p2)

Also, using changelog.parentrevs (and nullid exclusion) will likely be 
more efficient (This probably do not matter for this code)

> +        if p1 in sortedrevs or p2 in sortedrevs:

Sorted provides you a list, p1 in sortedrevs() will do a 
O(len(sortedrevs)) lookup. You want to stick with a set like object, use 
p1 in _revs.

> +            # At least one of the parent in revs. During the troubles
> +            # resolution, the parent's troubles will be resolved before we
> +            # reach this rev: it is therefore solvable, skip it.
> +            continue
> +        if p1 in troubles or p2 in troubles:
> +            # None of the parents are in revs but at least one of them is
> +            # troubled. During the troubles resolution we won't deal with that
> +            # parent before reaching this rev and we won't be able to solve it.
> +            return "%d cannot be solved because one of its parents is troubled\
> + but is not one of the revisions specified with --rev"%(r)

cf comment about "'troubles' are not the set you are looking for".

> +    return None
> +
>   @command('^evolve|stabilize|solve',
>       [('n', 'dry-run', False,
>           'do not perform actions, just print what would be done'),
> @@ -1305,6 +1343,11 @@
>           if not _revs:
>               ui.write_err("No troubled changes in the specified revisions\n")
>           else:
> +            issue = unsolvable(repo, _revs, troubled)
> +            if issue is not None:
> +                ui.write_err("Cannot solve troubles in the specified list.\n",
> +                             "%s\n" %(issue))
> +                return 1

cf above comment about using Abort

>               # For the progress bar to show
>               count = len(_revs)
>               for rev in _revs:
> diff --git a/tests/test-evolve.t b/tests/test-evolve.t
> --- a/tests/test-evolve.t
> +++ b/tests/test-evolve.t
> @@ -1027,5 +1027,44 @@
>     |
>     o  0	: a0 - test
>
> +Check hg evolve --rev on singled out commit
>
>
> +  $ hg up 19 -C
> +  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  $ mkcommit j1
> +  $ mkcommit j2
> +  $ mkcommit j3
> +  $ hg up .^^
> +  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
> +  $ echo "hello" > j4
> +  $ hg add j4
> +  $ hg amend
> +  2 new unstable changesets
> +  $ hg glog
> +  @  25	: add j1 - test
> +  |
> +  | o  23	: add j3 - test
> +  | |
> +  | o  22	: add j2 - test
> +  | |
> +  | x  21	: add j1 - test
> +  |/
> +  | o  20	: add gh - test
> +  | |
> +  o |  19	: add gg - test
> +  |/
> +  o  18	: a3 - test
> +  |
> +  o  13	: bumped update to f37ed7a60f43: - test
> +  |
> +  o  11	: a2 - test
> +  |
> +  o  10	testbookmark: a1__ - test
> +  |
> +  o  0	: a0 - test
> +
> +  $ hg evolve --rev 23
> +  Cannot solve  troubles in the specified list.
> +  23 cannot be solved because one of its parents is troubled but is not one of the revisions specified with --rev

cf Jordi Feedback about gigantic message.

-- 
Pierre-Yves David


More information about the Mercurial-devel mailing list