Patchwork [V4] revert: add an experimental config to use inverted selection

login
register
mail settings
Submitter Laurent Charignon
Date June 1, 2015, 6:17 p.m.
Message ID <c929901771de740fc7ff.1433182665@lcharignon-mbp.dhcp.thefacebook.com>
Download mbox | patch
Permalink /patch/9433/
State Superseded
Commit 69609f43c752b53deb8853370401cf29e6e6d99a
Headers show

Comments

Laurent Charignon - June 1, 2015, 6:17 p.m.
# HG changeset patch
# User Laurent Charignon <lcharignon@fb.com>
# Date 1432930312 25200
#      Fri May 29 13:11:52 2015 -0700
# Node ID c929901771de740fc7ffc8c8822dc288c119d9bb
# Parent  6084926366b979e81e5dcc84fa595965d4c07883
revert: add an experimental config to use inverted selection

We had a discussion on the list about the interactive ui for revert. This patch
adds a flag to allow people to test the second alternative (referred to as
proposition 2 on the mailing list). It effectively inverts the signs in the
Laurent Charignon - June 1, 2015, 8:17 p.m.
> On Jun 1, 2015, at 11:17 AM, Laurent Charignon <lcharignon@fb.com> wrote:
> 
> # HG changeset patch
> # User Laurent Charignon <lcharignon@fb.com>
> # Date 1432930312 25200
> #      Fri May 29 13:11:52 2015 -0700
> # Node ID c929901771de740fc7ffc8c8822dc288c119d9bb
> # Parent  6084926366b979e81e5dcc84fa595965d4c07883
> revert: add an experimental config to use inverted selection
> 
> We had a discussion on the list about the interactive ui for revert. This patch
> adds a flag to allow people to test the second alternative (referred to as
> proposition 2 on the mailing list). It effectively inverts the signs in the
> diff displayed to match the output of hg diff. A test is added to show an
> example.
> 
> By setting the following flag in the config, one can try proposition 2:
> [experimental]
> revertalternateinteractivemode=True
> 
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -3139,10 +3139,21 @@
>         diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
>         diffopts.nodates = True
>         diffopts.git = True
> -        diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
> +        reversehunks =  repo.ui.configbool('experimental',
> +                                           'revertalternateinteractivemode',
> +                                           False)
> +        if reversehunks:
> +            diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
> +        else:
> +            diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
>         originalchunks = patch.parsepatch(diff)
> +
>         try:
> +
>             chunks = recordfilter(repo.ui, originalchunks)
> +            if reversehunks:
> +                chunks = patch.reversehunks(chunks)
> +
>         except patch.PatchError, err:
>             raise util.Abort(_('error parsing patch: %s') % err)
> 
> diff --git a/mercurial/patch.py b/mercurial/patch.py
> --- a/mercurial/patch.py
> +++ b/mercurial/patch.py
> @@ -1385,6 +1385,84 @@
>             return s
>     return s[:i]
> 
> +def reversehunks(hunks):
> +    '''reverse the signs in the hunks given as argument
> +
> +    This function operates on hunks coming out of patch.filterpatch, that is
> +    a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
> +
> +    >>> rawpatch = """diff --git a/folder1/g b/folder1/g
> +    ... --- a/folder1/g
> +    ... +++ b/folder1/g
> +    ... @@ -1,7 +1,7 @@
> +    ... +firstline
> +    ...  c
> +    ...  1
> +    ...  2
> +    ... -3
> +    ... -4
> +    ...  5
> +    ...  d
> +    ... +lastline"""
> +    After parsepatch hunks look like: [header1, header2, ...]:
> +    >>> hunks = parsepatch(rawpatch)
> +
> +    After filterpatch hunks look like: [header1, hunk1, hunk2, header2...]
> +    We expand the list of headers by appending their respective hunks:
> +    >>> hunkscomingfromfilterpatch = []
> +    >>> for h in hunks:
> +    ...     hunkscomingfromfilterpatch.append(h)
> +    ...     hunkscomingfromfilterpatch.extend(h.hunks)
> +
> +    Actual hunk inversion:
> +    >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
> +
> +    Printing the hunk and checking the result:
> +    >>> fp = cStringIO.StringIO()
> +    >>> for c in reversedhunks:
> +    ...      c.write(fp)
> +    >>> fp.seek(0)
> +    >>> reversedpatch = fp.read()
> +    >>> print reversedpatch
> +    diff --git a/folder1/g b/folder1/g
> +    --- a/folder1/g
> +    +++ b/folder1/g
> +    @@ -1,4 +1,3 @@
> +    -firstline
> +     c
> +     1
> +     2
> +    @@ -1,5 +2,7 @@
> +     c
> +     1
> +     2
> +    +3
> +    +4
> +     5
> +     d
> +    @@ -6,3 +5,2 @@
> +     5
> +     d
> +    -lastline
> +
> +    '''
> +
> +    import crecord as crecordmod
> +    newhunks = []
> +    for c in hunks:
> +        if isinstance(c, crecordmod.uihunk):
> +            # curses hunks encapsulate the record hunk in _hunk
> +            c = c._hunk
> +        if isinstance(c, recordhunk):
> +            for j, line in enumerate(c.hunk):
> +                if line.startswith("-"):
> +                    c.hunk[j] = "+" + c.hunk[j][1:]
> +                elif line.startswith("+"):
> +                    c.hunk[j] = "-" + c.hunk[j][1:]
> +            c.added, c.removed = c.removed, c.added
> +        newhunks.append(c)
> +    return newhunks
> +
> def parsepatch(originalchunks):
>     """patch -> [] of headers -> [] of hunks """
>     class parser(object):
> diff --git a/tests/test-doctest.py b/tests/test-doctest.py
> --- a/tests/test-doctest.py
> +++ b/tests/test-doctest.py
> @@ -5,6 +5,7 @@
> import doctest
> 
> def testmod(name, optionflags=0, testtarget=None):
> +    sys.path.insert(1,"..")

Please discard this hunk, it needs a proper fix and it was a temporary hack to test locally.
Without it I was not able to run the doc test against the local mercurial, does anyone have a workaround/tip for this?

>     __import__(name)
>     mod = sys.modules[name]
>     if testtarget is not None:
> diff --git a/tests/test-revert-interactive.t b/tests/test-revert-interactive.t
> --- a/tests/test-revert-interactive.t
> +++ b/tests/test-revert-interactive.t
> @@ -320,3 +320,78 @@
>   4
>   5
>   b
> +
> +Check the experimental config to invert the selection:
> +  $ cat <<EOF >> $HGRCPATH
> +  > [experimental]
> +  > revertalternateinteractivemode=True
> +  > EOF
> +
> +
> +  $ hg up -C .
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  $ cat folder1/g > tmp
> +  $ echo 'firstline' > folder1/g
> +  $ cat tmp | egrep -v "3|4" >> folder1/g
> +  $ echo 'lastline' >> folder1/g
> +  $ hg diff --nodates
> +  diff -r 5a858e056dc0 folder1/g
> +  --- a/folder1/g
> +  +++ b/folder1/g
> +  @@ -1,7 +1,7 @@
> +  +firstline
> +   c
> +   1
> +   2
> +  -3
> +  -4
> +   5
> +   d
> +  +lastline
> +  $ hg revert -i <<EOF
> +  > y
> +  > y
> +  > n
> +  > n
> +  > EOF
> +  reverting folder1/g (glob)
> +  diff --git a/folder1/g b/folder1/g
> +  3 hunks, 4 lines changed
> +  examine changes to 'folder1/g'? [Ynesfdaq?] y
> +  
> +  @@ -1,3 +1,4 @@
> +  +firstline
> +   c
> +   1
> +   2
> +  record change 1/3 to 'folder1/g'? [Ynesfdaq?] y
> +  
> +  @@ -1,7 +2,5 @@
> +   c
> +   1
> +   2
> +  -3
> +  -4
> +   5
> +   d
> +  record change 2/3 to 'folder1/g'? [Ynesfdaq?] n
> +  
> +  @@ -6,2 +5,3 @@
> +   5
> +   d
> +  +lastline
> +  record change 3/3 to 'folder1/g'? [Ynesfdaq?] n
> +  
> +  $ hg diff --nodates
> +  diff -r 5a858e056dc0 folder1/g
> +  --- a/folder1/g
> +  +++ b/folder1/g
> +  @@ -1,7 +1,6 @@
> +   c
> +   1
> +   2
> +  -3
> +  -4
> +   5
> +   d
> +  +lastline
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
Pierre-Yves David - June 1, 2015, 8:25 p.m.
On 06/01/2015 11:17 AM, Laurent Charignon wrote:
> # HG changeset patch
> # User Laurent Charignon <lcharignon@fb.com>
> # Date 1432930312 25200
> #      Fri May 29 13:11:52 2015 -0700
> # Node ID c929901771de740fc7ffc8c8822dc288c119d9bb
> # Parent  6084926366b979e81e5dcc84fa595965d4c07883
> revert: add an experimental config to use inverted selection
>
> We had a discussion on the list about the interactive ui for revert. This patch
> adds a flag to allow people to test the second alternative (referred to as
> proposition 2 on the mailing list). It effectively inverts the signs in the
> diff displayed to match the output of hg diff. A test is added to show an
> example.
>
> By setting the following flag in the config, one can try proposition 2:
> [experimental]
>   revertalternateinteractivemode=True
>
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -3139,10 +3139,21 @@
>           diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
>           diffopts.nodates = True
>           diffopts.git = True
> -        diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
> +        reversehunks =  repo.ui.configbool('experimental',
> +                                           'revertalternateinteractivemode',
> +                                           False)
> +        if reversehunks:
> +            diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
> +        else:
> +            diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
>           originalchunks = patch.parsepatch(diff)
> +
>           try:
> +
>               chunks = recordfilter(repo.ui, originalchunks)
> +            if reversehunks:
> +                chunks = patch.reversehunks(chunks)
> +
>           except patch.PatchError, err:
>               raise util.Abort(_('error parsing patch: %s') % err)
>
> diff --git a/mercurial/patch.py b/mercurial/patch.py
> --- a/mercurial/patch.py
> +++ b/mercurial/patch.py
> @@ -1385,6 +1385,84 @@
>               return s
>       return s[:i]
>
> +def reversehunks(hunks):
> +    '''reverse the signs in the hunks given as argument
> +
> +    This function operates on hunks coming out of patch.filterpatch, that is
> +    a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
> +
> +    >>> rawpatch = """diff --git a/folder1/g b/folder1/g
> +    ... --- a/folder1/g
> +    ... +++ b/folder1/g
> +    ... @@ -1,7 +1,7 @@
> +    ... +firstline
> +    ...  c
> +    ...  1
> +    ...  2
> +    ... -3
> +    ... -4
> +    ...  5
> +    ...  d
> +    ... +lastline"""

You are still lacking tests for the most common for on patch:

- some removed line
+ some added line

(Testing it manually show that the inversion changes the sign in place 
leading to the unusual:

+ some removed line
- some added line

This is okay as long as we do not display this to the user. but we 
should probably document that.)


> +    After parsepatch hunks look like: [header1, header2, ...]:

This comment is seen as output by doctest and makes it fails.

As pointed in a previous review, I think you should drop most of them. 
If any comment is dimmed necessary you can use '>>> # blah' for it.

> +    >>> hunks = parsepatch(rawpatch)
> +
> +    After filterpatch hunks look like: [header1, hunk1, hunk2, header2...]
> +    We expand the list of headers by appending their respective hunks:
> +    >>> hunkscomingfromfilterpatch = []
> +    >>> for h in hunks:
> +    ...     hunkscomingfromfilterpatch.append(h)
> +    ...     hunkscomingfromfilterpatch.extend(h.hunks)
> +
> +    Actual hunk inversion:
> +    >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
> +
> +    Printing the hunk and checking the result:
> +    >>> fp = cStringIO.StringIO()
> +    >>> for c in reversedhunks:
> +    ...      c.write(fp)
> +    >>> fp.seek(0)
> +    >>> reversedpatch = fp.read()
> +    >>> print reversedpatch
> +    diff --git a/folder1/g b/folder1/g
> +    --- a/folder1/g
> +    +++ b/folder1/g
> +    @@ -1,4 +1,3 @@
> +    -firstline
> +     c
> +     1
> +     2
> +    @@ -1,5 +2,7 @@
> +     c
> +     1
> +     2
> +    +3
> +    +4
> +     5
> +     d
> +    @@ -6,3 +5,2 @@
> +     5
> +     d
> +    -lastline
> +
> +    '''

I'm not sure why this single hunk get splitted into 3 with over lapping 
context. This is probably okay as long as we do not present that to the 
user, but we should document it.
Augie Fackler - June 2, 2015, 12:03 a.m.
On Mon, Jun 01, 2015 at 08:17:21PM +0000, Laurent Charignon wrote:
>
> > On Jun 1, 2015, at 11:17 AM, Laurent Charignon <lcharignon@fb.com> wrote:

> > diff --git a/tests/test-doctest.py b/tests/test-doctest.py
> > --- a/tests/test-doctest.py
> > +++ b/tests/test-doctest.py
> > @@ -5,6 +5,7 @@
> > import doctest
> >
> > def testmod(name, optionflags=0, testtarget=None):
> > +    sys.path.insert(1,"..")
>
> Please discard this hunk, it needs a proper fix and it was a
> temporary hack to test locally.  Without it I was not able to run
> the doc test against the local mercurial, does anyone have a
> workaround/tip for this?

Huh, that's interesting. What output do you see if you try and run
doctests against a local hg?

(This is probably worth filing a tracking bug on bz.selenic for, but
I'm happy to discuss here as the problem may be easy for me to fix.)

>
> >     __import__(name)
> >     mod = sys.modules[name]
> >     if testtarget is not None:
Laurent Charignon - June 3, 2015, 7:46 p.m.
I am sending a V5 taking into account your comments, thanks.
> On Jun 1, 2015, at 1:25 PM, Pierre-Yves David <pierre-yves.david@ens-lyon.org> wrote:
> 
> 
> 
> On 06/01/2015 11:17 AM, Laurent Charignon wrote:
>> # HG changeset patch
>> # User Laurent Charignon <lcharignon@fb.com>
>> # Date 1432930312 25200
>> #      Fri May 29 13:11:52 2015 -0700
>> # Node ID c929901771de740fc7ffc8c8822dc288c119d9bb
>> # Parent  6084926366b979e81e5dcc84fa595965d4c07883
>> revert: add an experimental config to use inverted selection
>> 
>> We had a discussion on the list about the interactive ui for revert. This patch
>> adds a flag to allow people to test the second alternative (referred to as
>> proposition 2 on the mailing list). It effectively inverts the signs in the
>> diff displayed to match the output of hg diff. A test is added to show an
>> example.
>> 
>> By setting the following flag in the config, one can try proposition 2:
>> [experimental]
>>  revertalternateinteractivemode=True
>> 
>> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
>> --- a/mercurial/cmdutil.py
>> +++ b/mercurial/cmdutil.py
>> @@ -3139,10 +3139,21 @@
>>          diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
>>          diffopts.nodates = True
>>          diffopts.git = True
>> -        diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
>> +        reversehunks =  repo.ui.configbool('experimental',
>> +                                           'revertalternateinteractivemode',
>> +                                           False)
>> +        if reversehunks:
>> +            diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
>> +        else:
>> +            diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
>>          originalchunks = patch.parsepatch(diff)
>> +
>>          try:
>> +
>>              chunks = recordfilter(repo.ui, originalchunks)
>> +            if reversehunks:
>> +                chunks = patch.reversehunks(chunks)
>> +
>>          except patch.PatchError, err:
>>              raise util.Abort(_('error parsing patch: %s') % err)
>> 
>> diff --git a/mercurial/patch.py b/mercurial/patch.py
>> --- a/mercurial/patch.py
>> +++ b/mercurial/patch.py
>> @@ -1385,6 +1385,84 @@
>>              return s
>>      return s[:i]
>> 
>> +def reversehunks(hunks):
>> +    '''reverse the signs in the hunks given as argument
>> +
>> +    This function operates on hunks coming out of patch.filterpatch, that is
>> +    a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
>> +
>> +    >>> rawpatch = """diff --git a/folder1/g b/folder1/g
>> +    ... --- a/folder1/g
>> +    ... +++ b/folder1/g
>> +    ... @@ -1,7 +1,7 @@
>> +    ... +firstline
>> +    ...  c
>> +    ...  1
>> +    ...  2
>> +    ... -3
>> +    ... -4
>> +    ...  5
>> +    ...  d
>> +    ... +lastline"""
> 
> You are still lacking tests for the most common for on patch:
> 
> - some removed line
> + some added line
> 
> (Testing it manually show that the inversion changes the sign in place leading to the unusual:
> 
> + some removed line
> - some added line

By testing manually, I see that:
- some removed line
+ some added line
leads as expected to
+some removed line
- some added line

I will add this test case to the test and the doctest

> 
> This is okay as long as we do not display this to the user. but we should probably document that.)
> 
> 
>> +    After parsepatch hunks look like: [header1, header2, ...]:
> 
> This comment is seen as output by doctest and makes it fails.
OK, good catch
> 
> As pointed in a previous review, I think you should drop most of them. If any comment is dimmed necessary you can use '>>> # blah' for it.
OK
> 
>> +    >>> hunks = parsepatch(rawpatch)
>> +
>> +    After filterpatch hunks look like: [header1, hunk1, hunk2, header2...]
>> +    We expand the list of headers by appending their respective hunks:
>> +    >>> hunkscomingfromfilterpatch = []
>> +    >>> for h in hunks:
>> +    ...     hunkscomingfromfilterpatch.append(h)
>> +    ...     hunkscomingfromfilterpatch.extend(h.hunks)
>> +
>> +    Actual hunk inversion:
>> +    >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
>> +
>> +    Printing the hunk and checking the result:
>> +    >>> fp = cStringIO.StringIO()
>> +    >>> for c in reversedhunks:
>> +    ...      c.write(fp)
>> +    >>> fp.seek(0)
>> +    >>> reversedpatch = fp.read()
>> +    >>> print reversedpatch
>> +    diff --git a/folder1/g b/folder1/g
>> +    --- a/folder1/g
>> +    +++ b/folder1/g
>> +    @@ -1,4 +1,3 @@
>> +    -firstline
>> +     c
>> +     1
>> +     2
>> +    @@ -1,5 +2,7 @@
>> +     c
>> +     1
>> +     2
>> +    +3
>> +    +4
>> +     5
>> +     d
>> +    @@ -6,3 +5,2 @@
>> +     5
>> +     d
>> +    -lastline
>> +
>> +    '''
> 
> I'm not sure why this single hunk get splitted into 3 with over lapping context. This is probably okay as long as we do not present that to the user, but we should document it.

We present it to the user and it is the default behavior.
Minimal example:
$ echo "a\nb\nc" > a ; hg add a ; hg commit -m "base"
$ echo "1\na\n2\nb\n3\nc\n4" > a ;  hg commit -im "change" 
Will show you four hunks with overlapping context.
My understanding is that one line of context in between changes defines a hunks. What do you think is wrong here, the number of hunks? The amount of context?


Laurent
> 
> -- 
> Pierre-Yves David

Patch

diff displayed to match the output of hg diff. A test is added to show an
example.

By setting the following flag in the config, one can try proposition 2:
[experimental]
 revertalternateinteractivemode=True

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -3139,10 +3139,21 @@ 
         diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
         diffopts.nodates = True
         diffopts.git = True
-        diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
+        reversehunks =  repo.ui.configbool('experimental',
+                                           'revertalternateinteractivemode',
+                                           False)
+        if reversehunks:
+            diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
+        else:
+            diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
         originalchunks = patch.parsepatch(diff)
+
         try:
+
             chunks = recordfilter(repo.ui, originalchunks)
+            if reversehunks:
+                chunks = patch.reversehunks(chunks)
+
         except patch.PatchError, err:
             raise util.Abort(_('error parsing patch: %s') % err)
 
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -1385,6 +1385,84 @@ 
             return s
     return s[:i]
 
+def reversehunks(hunks):
+    '''reverse the signs in the hunks given as argument
+
+    This function operates on hunks coming out of patch.filterpatch, that is
+    a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
+
+    >>> rawpatch = """diff --git a/folder1/g b/folder1/g
+    ... --- a/folder1/g
+    ... +++ b/folder1/g
+    ... @@ -1,7 +1,7 @@
+    ... +firstline
+    ...  c
+    ...  1
+    ...  2
+    ... -3
+    ... -4
+    ...  5
+    ...  d
+    ... +lastline"""
+    After parsepatch hunks look like: [header1, header2, ...]:
+    >>> hunks = parsepatch(rawpatch)
+
+    After filterpatch hunks look like: [header1, hunk1, hunk2, header2...]
+    We expand the list of headers by appending their respective hunks:
+    >>> hunkscomingfromfilterpatch = []
+    >>> for h in hunks:
+    ...     hunkscomingfromfilterpatch.append(h)
+    ...     hunkscomingfromfilterpatch.extend(h.hunks)
+
+    Actual hunk inversion:
+    >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
+
+    Printing the hunk and checking the result:
+    >>> fp = cStringIO.StringIO()
+    >>> for c in reversedhunks:
+    ...      c.write(fp)
+    >>> fp.seek(0)
+    >>> reversedpatch = fp.read()
+    >>> print reversedpatch
+    diff --git a/folder1/g b/folder1/g
+    --- a/folder1/g
+    +++ b/folder1/g
+    @@ -1,4 +1,3 @@
+    -firstline
+     c
+     1
+     2
+    @@ -1,5 +2,7 @@
+     c
+     1
+     2
+    +3
+    +4
+     5
+     d
+    @@ -6,3 +5,2 @@
+     5
+     d
+    -lastline
+
+    '''
+
+    import crecord as crecordmod
+    newhunks = []
+    for c in hunks:
+        if isinstance(c, crecordmod.uihunk):
+            # curses hunks encapsulate the record hunk in _hunk
+            c = c._hunk
+        if isinstance(c, recordhunk):
+            for j, line in enumerate(c.hunk):
+                if line.startswith("-"):
+                    c.hunk[j] = "+" + c.hunk[j][1:]
+                elif line.startswith("+"):
+                    c.hunk[j] = "-" + c.hunk[j][1:]
+            c.added, c.removed = c.removed, c.added
+        newhunks.append(c)
+    return newhunks
+
 def parsepatch(originalchunks):
     """patch -> [] of headers -> [] of hunks """
     class parser(object):
diff --git a/tests/test-doctest.py b/tests/test-doctest.py
--- a/tests/test-doctest.py
+++ b/tests/test-doctest.py
@@ -5,6 +5,7 @@ 
 import doctest
 
 def testmod(name, optionflags=0, testtarget=None):
+    sys.path.insert(1,"..")
     __import__(name)
     mod = sys.modules[name]
     if testtarget is not None:
diff --git a/tests/test-revert-interactive.t b/tests/test-revert-interactive.t
--- a/tests/test-revert-interactive.t
+++ b/tests/test-revert-interactive.t
@@ -320,3 +320,78 @@ 
   4
   5
   b
+
+Check the experimental config to invert the selection:
+  $ cat <<EOF >> $HGRCPATH
+  > [experimental]
+  > revertalternateinteractivemode=True
+  > EOF
+
+
+  $ hg up -C .
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat folder1/g > tmp
+  $ echo 'firstline' > folder1/g
+  $ cat tmp | egrep -v "3|4" >> folder1/g
+  $ echo 'lastline' >> folder1/g
+  $ hg diff --nodates
+  diff -r 5a858e056dc0 folder1/g
+  --- a/folder1/g
+  +++ b/folder1/g
+  @@ -1,7 +1,7 @@
+  +firstline
+   c
+   1
+   2
+  -3
+  -4
+   5
+   d
+  +lastline
+  $ hg revert -i <<EOF
+  > y
+  > y
+  > n
+  > n
+  > EOF
+  reverting folder1/g (glob)
+  diff --git a/folder1/g b/folder1/g
+  3 hunks, 4 lines changed
+  examine changes to 'folder1/g'? [Ynesfdaq?] y
+  
+  @@ -1,3 +1,4 @@
+  +firstline
+   c
+   1
+   2
+  record change 1/3 to 'folder1/g'? [Ynesfdaq?] y
+  
+  @@ -1,7 +2,5 @@
+   c
+   1
+   2
+  -3
+  -4
+   5
+   d
+  record change 2/3 to 'folder1/g'? [Ynesfdaq?] n
+  
+  @@ -6,2 +5,3 @@
+   5
+   d
+  +lastline
+  record change 3/3 to 'folder1/g'? [Ynesfdaq?] n
+  
+  $ hg diff --nodates
+  diff -r 5a858e056dc0 folder1/g
+  --- a/folder1/g
+  +++ b/folder1/g
+  @@ -1,7 +1,6 @@
+   c
+   1
+   2
+  -3
+  -4
+   5
+   d
+  +lastline