diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -3139,10 +3139,21 @@ def _performrevert(repo, parents, ctx, a 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,77 @@ def parsefilename(str): 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""" + >>> hunks = parsepatch(rawpatch) + >>> hunkscomingfromfilterpatch = [] + >>> for h in hunks: + ... hunkscomingfromfilterpatch.append(h) + ... hunkscomingfromfilterpatch.extend(h.hunks) + + >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch) + >>> 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,6 +2,6 @@ + c + 1 + 2 + - 3 + +4 + 5 + d + @@ -5,3 +6,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-revert-interactive.t b/tests/test-revert-interactive.t --- a/tests/test-revert-interactive.t +++ b/tests/test-revert-interactive.t @@ -320,3 +320,74 @@ 4) Use interactive revert to recover f a 4 5 b + +Check the experimental config to invert the selection: + $ cat <> $HGRCPATH + > [experimental] + > revertalternateinteractivemode=True + > EOF + + + $ hg up -C . + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ printf 'firstline\nc\n1\n2\n3\n 3\n5\nd\nlastline\n' > folder1/g + $ hg diff --nodates + diff -r 5a858e056dc0 folder1/g + --- a/folder1/g + +++ b/folder1/g + @@ -1,7 +1,9 @@ + +firstline + c + 1 + 2 + 3 + -4 + + 3 + 5 + d + +lastline + $ hg revert -i < y + > y + > y + > n + > EOF + reverting folder1/g (glob) + diff --git a/folder1/g b/folder1/g + 3 hunks, 3 lines changed + examine changes to 'folder1/g'? [Ynesfdaq?] y + + @@ -1,4 +1,5 @@ + +firstline + c + 1 + 2 + 3 + record change 1/3 to 'folder1/g'? [Ynesfdaq?] y + + @@ -1,7 +2,7 @@ + c + 1 + 2 + 3 + -4 + + 3 + 5 + d + record change 2/3 to 'folder1/g'? [Ynesfdaq?] y + + @@ -6,2 +7,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 + @@ -5,3 +5,4 @@ + 4 + 5 + d + +lastline