diff --git a/hgext/uncommit.py b/hgext/uncommit.py --- a/hgext/uncommit.py +++ b/hgext/uncommit.py @@ -28,11 +28,14 @@ copies as copiesmod, error, node, + obsolete, obsutil, + patch, pycompat, registrar, rewriteutil, scmutil, + util, ) cmdtable = {} @@ -48,6 +51,8 @@ default=False, ) +stringio = util.stringio + # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should # be specifying the version(s) of Mercurial they are tested with, or @@ -139,7 +144,9 @@ ds.copy(src, dst) @command('uncommit', - [('', 'keep', None, _('allow an empty commit after uncommiting')), + [('i', 'interactive', None, + _('interactively select which chunks to apply (EXPERIMENTAL)')), + ('', 'keep', None, _('allow an empty commit after uncommiting')), ('', 'allow-dirty-working-copy', False, _('allow uncommit with outstanding changes')) ] + commands.walkopts, @@ -157,6 +164,7 @@ given. """ opts = pycompat.byteskwargs(opts) + interactive = opts.get('interactive') with repo.wlock(), repo.lock(): @@ -174,13 +182,19 @@ with repo.transaction('uncommit'): match = scmutil.match(old, pats, opts) + #TODO: pass keep to _interactiveuncommit() and use it there keepcommit = pats if not keepcommit: if opts.get('keep') is not None: keepcommit = opts.get('keep') else: keepcommit = ui.configbool('experimental', 'uncommit.keep') - newid = _commitfiltered(repo, old, match, keepcommit) + + if interactive: + newid = _interactiveuncommit(ui, repo, old, match) + else: + newid = _commitfiltered(repo, old, match, keepcommit) + if newid is None: ui.status(_("nothing to uncommit\n")) return 1 @@ -194,10 +208,104 @@ mapping[old.node()] = () with repo.dirstate.parentchange(): + repo.dirstate.setparents(newid, node.nullid) _fixdirstate(repo, old, repo[newid], match) scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True) +def _interactiveuncommit(ui, repo, old, match): + """Makes a temporary commit with the chunks which user selected to uncommit + After that the diff of the parent and that commit is applied to the working + directory and committed again which results in the new commit which should + be one after uncommitted. + """ + + # create a temporary commit with hunks user selected + tempnode = _createtempcommit(ui, repo, old, match) + + diffopts = patch.difffeatureopts(repo.ui, whitespace=True) + diffopts.nodates = True + diffopts.git = True + fp = stringio() + for chunk, label in patch.diffui(repo, tempnode, old.node(), None, + opts=diffopts): + fp.write(chunk) + + fp.seek(0) + newnode = _patchtocommit(ui, repo, old, fp) + mapping = {tempnode: (newnode,)} + scmutil.cleanupnodes(repo, mapping, 'uncommit',fixphase=True) + return newnode + +def _createtempcommit(ui, repo, old, match): + """Creates a temporary commit for `uncommit --interative` which contains + the hunks which were selected by the user to uncommit. + """ + + pold = old.p1() + # The logic to interactively selecting something copied from + # cmdutil.revert() + diffopts = patch.difffeatureopts(repo.ui, whitespace=True) + diffopts.nodates = True + diffopts.git = True + diff = patch.diff(repo, pold.node(), old.node(), match, opts=diffopts) + originalchunks = patch.parsepatch(diff) + # XXX: The interactive selection is buggy and does not let you + # uncommit a removed file partially. + # to add uncommit as an operation taking care of BC. + chunks, opts = cmdutil.recordfilter(repo.ui, originalchunks, + operation='uncommit') + if not chunks: + raise error.Abort(_("nothing selected to uncommit")) + fp = stringio() + for c in chunks: + c.write(fp) + + fp.seek(0) + oldnode = old.hex() + message = 'temporary commit for uncommiting %s' % oldnode + tempnode = _patchtocommit(ui, repo, old, fp, message, oldnode) + return tempnode + +def _patchtocommit(ui, repo, old, fp, message=None, oldnode=None): + """Applies the patch to the working directory and make a commit whose + parents are same as that of old argument. The message argument tells us + whether to use the message of the old commit or a different message which + is passed. Returns the node of new commit made. + """ + pold = old.p1() + parents = (old.p1().node(), old.p2().node()) + date = old.date() + branch = old.branch() + user = old.user() + extra = old.extra() + if oldnode: + extra['uncommit_source'] = oldnode + if not message: + message = old.description() + store = patch.filestore() + try: + files = set() + try: + patch.patchrepo(ui, repo, pold, store, fp, 1, '', + files=files, eolmode=None) + except patch.PatchError as err: + raise error.Abort(str(err)) + + finally: + del fp + + memctx = context.memctx(repo, parents, message, files=files, + filectxfn=store, + user=user, + date=date, + branch=branch, + extra=extra) + newcm = memctx.commit() + finally: + store.close() + return newcm + def predecessormarkers(ctx): """yields the obsolete markers marking the given changeset as a successor""" for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()): diff --git a/mercurial/crecord.py b/mercurial/crecord.py --- a/mercurial/crecord.py +++ b/mercurial/crecord.py @@ -566,6 +566,7 @@ _headermessages = { # {operation: text} 'apply': _('Select hunks to apply'), 'discard': _('Select hunks to discard'), + 'uncommit': _('Select hunks to uncommit'), None: _('Select hunks to record'), } diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -1012,11 +1012,13 @@ 'multiple': { 'apply': _("apply change %d/%d to '%s'?"), 'discard': _("discard change %d/%d to '%s'?"), + 'uncommit': _("discard change %d/%d to '%s'?"), 'record': _("record change %d/%d to '%s'?"), }, 'single': { 'apply': _("apply this change to '%s'?"), 'discard': _("discard this change to '%s'?"), + 'uncommit': _("uncommit this change to '%s'?"), 'record': _("record this change to '%s'?"), }, 'help': { @@ -1040,6 +1042,16 @@ '$$ Discard &all changes to all remaining files' '$$ &Quit, discarding no changes' '$$ &? (display help)'), + 'uncommit': _('[Ynesfdaq?]' + '$$ &Yes, record this change' + '$$ &No, skip this change' + '$$ &Edit this change manually' + '$$ &Skip remaining changes to this file' + '$$ Record remaining changes to this &file' + '$$ &Done, skip remaining changes and files' + '$$ Record &all changes to all remaining files' + '$$ &Quit, recording no changes' + '$$ &? (display help)'), 'record': _('[Ynesfdaq?]' '$$ &Yes, record this change' '$$ &No, skip this change' diff --git a/tests/test-uncommit-interactive.t b/tests/test-uncommit-interactive.t new file mode 100644 --- /dev/null +++ b/tests/test-uncommit-interactive.t @@ -0,0 +1,969 @@ +================================================ +|| The test for `hg uncommit --interactive` || +================================================ + +Repo Setup +============ + + $ cat >> $HGRCPATH < [ui] + > interactive = true + > [experimental] + > evolution.createmarkers=True + > evolution.allowunstable=True + > uncommitondirtywdir = true + > [extensions] + > uncommit = + > amend = + > drawdag=$TESTDIR/drawdag.py + > EOF + $ glog() { + > hg log -G --template '{rev}:{node|short}@{branch}({separate("/", obsolete, phase)}) {desc|firstline}\n' "$@" + > } + + $ hg init repo + $ cd repo + + $ touch a + $ cat >> a << EOF + > 1 + > 2 + > 3 + > 4 + > 5 + > EOF + + $ hg add a + $ hg ci -m "The base commit" + +Make sure aborting the interactive selection does no magic +---------------------------------------------------------- + + $ hg status + $ hg uncommit -i< q + > EOF + diff --git a/a b/a + new file mode 100644 + examine changes to 'a'? [Ynesfdaq?] q + + abort: user quit + [255] + $ hg status + +Make a commit with multiple hunks +--------------------------------- + + $ cat > a << EOF + > -2 + > -1 + > 0 + > 1 + > 2 + > 3 + > foo + > bar + > 4 + > 5 + > babar + > EOF + + $ hg diff + diff -r 7733902a8d94 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,11 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + +foo + +bar + 4 + 5 + +babar + + $ hg ci -m "another one" + +Not selecting anything to uncommit +================================== + + $ hg uncommit -i< y + > n + > n + > n + > EOF + diff --git a/a b/a + 3 hunks, 6 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + discard change 1/3 to 'a'? [Ynesfdaq?] n + + @@ -1,5 +4,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + discard change 2/3 to 'a'? [Ynesfdaq?] n + + @@ -4,2 +9,3 @@ + 4 + 5 + +babar + discard change 3/3 to 'a'? [Ynesfdaq?] n + + abort: nothing selected to uncommit + [255] + $ hg status + +Uncommit a chunk +================ + + $ hg uncommit -i< y + > y + > n + > n + > EOF + diff --git a/a b/a + 3 hunks, 6 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + discard change 1/3 to 'a'? [Ynesfdaq?] y + + @@ -1,5 +4,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + discard change 2/3 to 'a'? [Ynesfdaq?] n + + @@ -4,2 +9,3 @@ + 4 + 5 + +babar + discard change 3/3 to 'a'? [Ynesfdaq?] n + + + $ hg log -G --hidden + @ changeset: 3:aa6c421e9e28 + | tag: tip + | parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: another one + | + | x changeset: 2:557dde779d03 + |/ parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 3:aa6c421e9e28 + | summary: temporary commit for uncommiting f70fb463d5bf9f0ffd38f105521d96e9f2591bc1 + | + | x changeset: 1:f70fb463d5bf + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 3:aa6c421e9e28 + | summary: another one + | + o changeset: 0:7733902a8d94 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: The base commit + +The unselected part should be in the diff +----------------------------------------- + + $ hg diff + diff -r aa6c421e9e28 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + +The commit should contain the rest of part +------------------------------------------ + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID aa6c421e9e28164a60bdc19ec0ed4c39e5cfa65c + # Parent 7733902a8d94c789ca81d866bea1893d79442db6 + another one + + diff -r 7733902a8d94 -r aa6c421e9e28 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,8 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + +babar + +Uncommiting on dirty working directory +====================================== + + $ hg status + M a + $ hg diff + diff -r aa6c421e9e28 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + + $ hg uncommit -i< y + > n + > y + > EOF + diff --git a/a b/a + 2 hunks, 3 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,5 +1,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + discard change 1/2 to 'a'? [Ynesfdaq?] n + + @@ -4,2 +6,3 @@ + 4 + 5 + +babar + discard change 2/2 to 'a'? [Ynesfdaq?] y + + patching file a + Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines). + + $ hg diff + diff -r 32f76305dd23 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + @@ -5,3 +8,4 @@ + bar + 4 + 5 + +babar + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 32f76305dd23a70de61867137744bfff0abc1344 + # Parent 7733902a8d94c789ca81d866bea1893d79442db6 + another one + + diff -r 7733902a8d94 -r 32f76305dd23 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + +Checking the obsolescence history + + $ hg log -G --hidden + @ changeset: 5:32f76305dd23 + | tag: tip + | parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: another one + | + | x changeset: 4:c983140931c0 + |/ parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 5:32f76305dd23 + | summary: temporary commit for uncommiting aa6c421e9e28164a60bdc19ec0ed4c39e5cfa65c + | + | x changeset: 3:aa6c421e9e28 + |/ parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 5:32f76305dd23 + | summary: another one + | + | x changeset: 2:557dde779d03 + |/ parent: 0:7733902a8d94 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 3:aa6c421e9e28 + | summary: temporary commit for uncommiting f70fb463d5bf9f0ffd38f105521d96e9f2591bc1 + | + | x changeset: 1:f70fb463d5bf + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: rewritten using uncommit as 3:aa6c421e9e28 + | summary: another one + | + o changeset: 0:7733902a8d94 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: The base commit + +Push the changes back to the commit and more commits for more testing + + $ hg amend + $ glog + @ 6:c539671b1fd6@default(draft) another one + | + o 0:7733902a8d94@default(draft) The base commit + + $ touch foo + $ echo "hey" >> foo + $ hg ci -Am "Added foo" + adding foo + +Testing uncommiting a whole changeset and also for a file addition +================================================================== + + $ hg uncommit -i< y + > y + > EOF + diff --git a/foo b/foo + new file mode 100644 + examine changes to 'foo'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +hey + uncommit this change to 'foo'? [Ynesfdaq?] y + + + $ hg status + A foo + $ hg diff + diff -r aa21648408ea foo + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/foo Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +hey + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID aa21648408ea8a4e32d1b45cb72d70dfbfb0c24d + # Parent c539671b1fd601f2fb1da1141ef23d7198478c8a + Added foo + + $ hg amend + +Testing to uncommit removed files completely +============================================ + + $ hg rm a + $ hg ci -m "Removed a" + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 56f9164c2dc8359aabcc4522c7b6382d2e26ed90 + # Parent 1888d4695f614750ec3bb4f97cdd8a9ca86f0fb3 + Removed a + + diff -r 1888d4695f61 -r 56f9164c2dc8 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,11 +0,0 @@ + --2 + --1 + -0 + -1 + -2 + -3 + -foo + -bar + -4 + -5 + -babar + +Not examining the file +---------------------- + + $ hg uncommit -i< n + > EOF + diff --git a/a b/a + deleted file mode 100644 + examine changes to 'a'? [Ynesfdaq?] n + + abort: nothing selected to uncommit + [255] + +Examining the file +------------------ +XXX: there is a bug in interactive selection as it is not letting to examine the +file. Tried with curses too. In the curses UI, if you just unselect the hunks +and the not file mod thing at the top, it will show the same "nothing unselected +to uncommit" message which is a bug in interactive selection. + + $ hg uncommit -i< y + > EOF + diff --git a/a b/a + deleted file mode 100644 + examine changes to 'a'? [Ynesfdaq?] y + + + $ hg diff + diff -r 8a1b6b8b3d24 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 + @@ -1,11 +0,0 @@ + --2 + --1 + -0 + -1 + -2 + -3 + -foo + -bar + -4 + -5 + -babar + $ hg status + R a + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 8a1b6b8b3d24c125e1c4cd3966918d26e77c9b42 + # Parent 1888d4695f614750ec3bb4f97cdd8a9ca86f0fb3 + Removed a + + + $ hg prune . + hg: unknown command 'prune' + (use 'hg help' for a list of commands) + [255] + $ hg revert --all + undeleting a + + $ glog + @ 13:8a1b6b8b3d24@default(draft) Removed a + | + o 10:1888d4695f61@default(draft) Added foo + | + o 6:c539671b1fd6@default(draft) another one + | + o 0:7733902a8d94@default(draft) The base commit + + +Testing when a new file is added in the last commit +=================================================== + + $ echo "foo" >> foo + $ touch x + $ echo "abcd" >> x + $ hg add x + $ hg ci -m "Added x" + $ hg uncommit -i< y + > y + > y + > n + > EOF + diff --git a/foo b/foo + 1 hunks, 1 lines changed + examine changes to 'foo'? [Ynesfdaq?] y + + @@ -1,1 +1,2 @@ + hey + +foo + discard change 1/2 to 'foo'? [Ynesfdaq?] y + + diff --git a/x b/x + new file mode 100644 + examine changes to 'x'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +abcd + discard change 2/2 to 'x'? [Ynesfdaq?] n + + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 039cbfbd600d0aab7f464c2eb8489af60e0385f5 + # Parent 8a1b6b8b3d24c125e1c4cd3966918d26e77c9b42 + Added x + + diff -r 8a1b6b8b3d24 -r 039cbfbd600d x + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/x Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +abcd + + $ hg diff + diff -r 039cbfbd600d foo + --- a/foo Thu Jan 01 00:00:00 1970 +0000 + +++ b/foo Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,2 @@ + hey + +foo + + $ hg status + M foo + + $ hg revert --all + reverting foo + +Testing between the stack and with dirty working copy +===================================================== + + $ glog + @ 16:039cbfbd600d@default(draft) Added x + | + o 13:8a1b6b8b3d24@default(draft) Removed a + | + o 10:1888d4695f61@default(draft) Added foo + | + o 6:c539671b1fd6@default(draft) another one + | + o 0:7733902a8d94@default(draft) The base commit + + $ hg up c539671b1fd6 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + + $ touch bar + $ echo "foo" >> bar + $ hg add bar + $ hg status + A bar + ? foo.orig + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID c539671b1fd601f2fb1da1141ef23d7198478c8a + # Parent 7733902a8d94c789ca81d866bea1893d79442db6 + another one + + diff -r 7733902a8d94 -r c539671b1fd6 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,11 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + +foo + +bar + 4 + 5 + +babar + + $ hg uncommit -i< y + > n + > n + > y + > EOF + diff --git a/a b/a + 3 hunks, 6 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + discard change 1/3 to 'a'? [Ynesfdaq?] n + + @@ -1,5 +4,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + discard change 2/3 to 'a'? [Ynesfdaq?] n + + @@ -4,2 +9,3 @@ + 4 + 5 + +babar + discard change 3/3 to 'a'? [Ynesfdaq?] y + + patching file a + Hunk #1 succeeded at 1 with fuzz 1 (offset -1 lines). + 3 new orphan changesets + + $ hg diff + diff -r a2ee0cccc073 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -8,3 +8,4 @@ + bar + 4 + 5 + +babar + diff -r a2ee0cccc073 bar + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/bar Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +foo + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID a2ee0cccc0738cdcd2ba75e8b418e1b489ab6aa9 + # Parent 7733902a8d94c789ca81d866bea1893d79442db6 + another one + + diff -r 7733902a8d94 -r a2ee0cccc073 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,10 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + +foo + +bar + 4 + 5 + $ hg status + M a + A bar + ? foo.orig + +More uncommit on the same dirty working copy +============================================= + + $ hg uncommit -i< y + > y + > n + > EOF + diff --git a/a b/a + 2 hunks, 5 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + discard change 1/2 to 'a'? [Ynesfdaq?] y + + @@ -1,5 +4,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + discard change 2/2 to 'a'? [Ynesfdaq?] n + + + $ hg exp + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 975a17b4321a7bd11286bbb1ce7cc5012806fb4a + # Parent 7733902a8d94c789ca81d866bea1893d79442db6 + another one + + diff -r 7733902a8d94 -r 975a17b4321a a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,5 +1,7 @@ + 1 + 2 + 3 + +foo + +bar + 4 + 5 + + $ hg diff + diff -r 975a17b4321a a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,6 @@ + +-2 + +-1 + +0 + 1 + 2 + 3 + @@ -5,3 +8,4 @@ + bar + 4 + 5 + +babar + diff -r 975a17b4321a bar + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/bar Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +foo + + $ hg status + M a + A bar + ? foo.orig + +Interactive uncommit with a pattern +----------------------------------- + +(more setup) + + $ hg ci -m 'roaming changes' + $ cat > b << EOF + > a + > b + > c + > d + > e + > f + > h + > EOF + $ hg add b + $ hg ci -m 'add b' + $ echo 'celeste' >> a + $ echo 'i' >> b + $ hg ci -m 'some more changes' + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 2cca6971430342440b2141c347d1aae43a233b52 + # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18 + some more changes + + diff -r eb07567cdf3c -r 2cca69714303 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -9,3 +9,4 @@ + 4 + 5 + babar + +celeste + diff -r eb07567cdf3c -r 2cca69714303 b + --- a/b Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -5,3 +5,4 @@ + e + f + h + +i + + $ hg uncommit -i a << DONE + > y + > y + > DONE + diff --git a/a b/a + 1 hunks, 1 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -9,3 +9,4 @@ + 4 + 5 + babar + +celeste + uncommit this change to 'a'? [Ynesfdaq?] y + + $ hg status + M a + ? foo.orig + $ hg diff + diff -r 073013c9d352 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -9,3 +9,4 @@ + 4 + 5 + babar + +celeste + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 073013c9d352701f96f3b1fd67c95ea8ff718e6a + # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18 + some more changes + + diff -r eb07567cdf3c -r 073013c9d352 b + --- a/b Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -5,3 +5,4 @@ + e + f + h + +i + +(reset) + + $ cat << EOF > a + > -3 + > -2 + > -1 + > 0 + > 1 + > 2 + > 3 + > foo + > bar + > 4 + > 5 + > babar + > celeste + > EOF + $ hg amend + +Same but do not select some change in 'a' + + $ hg uncommit -i a << DONE + > y + > y + > n + > DONE + diff --git a/a b/a + 2 hunks, 2 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,3 +1,4 @@ + +-3 + -2 + -1 + 0 + discard change 1/2 to 'a'? [Ynesfdaq?] y + + @@ -9,3 +10,4 @@ + 4 + 5 + babar + +celeste + discard change 2/2 to 'a'? [Ynesfdaq?] n + + $ hg status + M a + ? foo.orig + + $ hg diff + diff -r db62d8510875 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,4 @@ + +-3 + -2 + -1 + 0 + + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID db62d8510875ea464d5287373946095971bfa056 + # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18 + some more changes + + diff -r eb07567cdf3c -r db62d8510875 a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -9,3 +9,4 @@ + 4 + 5 + babar + +celeste + diff -r eb07567cdf3c -r db62d8510875 b + --- a/b Thu Jan 01 00:00:00 1970 +0000 + +++ b/b Thu Jan 01 00:00:00 1970 +0000 + @@ -5,3 +5,4 @@ + e + f + h + +i + + $ cat b + a + b + c + d + e + f + h + i