diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -6,6 +6,7 @@ # GNU General Public License version 2 or any later version. from __future__ import absolute_import +import os import weakref from mercurial.i18n import _ @@ -145,6 +146,15 @@ revstring = "\n".join(sorted('%s %s' % (k, v) for k, v in nodes.items())) return writelog(repo, "index.i", revstring) +def _logundoredoindex(repo, reverseindex): + rlog = _getrevlog(repo, 'index.i') + hexnode = hex(rlog.node(_invertindex(rlog, reverseindex))) + return writelog(repo, "redo.i", "node " + str(hexnode)) + +def _delundoredo(repo): + path = os.path.join('undolog', 'redo.i') + repo.svfs.tryunlink(path) + # Read def _readindex(repo, reverseindex, prefetchedrevlog=None): @@ -288,6 +298,8 @@ @command('undo', [ ('n', 'index', 1, _("how many steps to undo back")), + ('a', 'absolute', False, _("absolute based on command index instead of " + "relative undo")), ]) def undo(ui, repo, *args, **opts): """ perform an undo @@ -300,16 +312,67 @@ repos can't be undone (specifically push). Undo does not preserve the working copy changes. + + To undo to a specific state use the --index and --absolute flags. + See hg debugundohistory to get a list of indeces and commands run. + By undoing to a specific index you undo to the state after that command. + For example, hg undo --index 0 --absolute won't do anything, while + hg undo -n 1 -a will bring you back to the repo state before the current + one. + + Without the --absolute flag, your undos will be relative. This means + they will behave how you expect them to. If you run hg undo twice, + you will move back two repo states from where you ran your first hg undo. + You can use this in conjunction with hg redo to move up and down repo + states. Note that as soon as you execute a different undoable command, + which isn't hg undo or hg redo, any new undos or redos will be relative to + the state after this command. When using --index with relative undos, + this is equivalent to running index many undos, except for leaving your + repo state history (hg debugundohistory) less cluttered. + + Undo states are also distinct repo states and can thereby be inspected using + debugundohistory and specifically jumped to using undo --index --absolute. """ reverseindex = opts.get("index") + relativeundo = not opts.get("absolute") if repo is not None: with repo.wlock(), repo.lock(), repo.transaction("undo"): cmdutil.checkunfinished(repo) cmdutil.bailifchanged(repo) repo = repo.unfiltered() + if relativeundo: + reverseindex = _computerelative(repo, reverseindex) _undoto(ui, repo, reverseindex) # store undo data + # for absolute undos, think of this as a reset + # for relative undos, think of this as an update + _logundoredoindex(repo, reverseindex) + +@command('redo', [ + ('n', 'index', 1, _("how many commands to redo")), +]) +def redo(ui, repo, *args, **opts): + """ perform a redo + + Performs a redo. Specifically, redo moves forward a repo state relative to + the previous undo or redo command. If you run hg undo -n 10, you can redo + each of the 10 repo states one by one all the way back to the state from + which you ran undo. You can use --index to redo across more states at once, + and you can use any number of undos/redos up to the current state or back to + when the undo extension was first active. + """ + reverseindex = -1 * abs(opts.get("index")) + reverseindex = _computerelative(repo, reverseindex) + + if repo is not None: + with repo.wlock(), repo.lock(), repo.transaction("redo"): + cmdutil.checkunfinished(repo) + cmdutil.bailifchanged(repo) + repo = repo.unfiltered() + _undoto(ui, repo, reverseindex) + _logundoredoindex(repo, reverseindex) + def _undoto(ui, repo, reverseindex): # undo to specific reverseindex try: @@ -348,6 +411,17 @@ reverseindex) revealcommits(ui, repo, removedrevs) +def _computerelative(repo, reverseindex): + # allows for relative undos using + # redo.i storage + rlog = _getrevlog(repo, 'redo.i') + if len(rlog) > 0: + redodict = _readindex(repo, 0, rlog) + rlog = _getrevlog(repo, 'index.i') + rev = rlog.rev(bin(redodict["node"])) + reverseindex = _invertindex(rlog, rev) + reverseindex + return reverseindex + # hide and reveal commits def hidecommits(repo, commit): diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -1,6 +1,9 @@ $ cat >> $HGRCPATH < [extensions] - > undo = $TESTDIR/../hgext3rd/undo.py + > undo=$TESTDIR/../hgext3rd/undo.py + > inhibit=$TESTDIR/../hgext3rd/inhibit.py + > [experimental] + > evolution=createmarkers > EOF Build up a repo @@ -62,7 +65,6 @@ workingparent fcb754f6a51eaf982f66d0637b39f3d2e6b520d5 (no-eol) $ touch a3 && hg add a3 $ hg commit --amend - saved backup bundle to $TESTTMP/repo/.hg/strip-backup/db92053d5c83-e25f6bc1-amend.hg (glob) $ hg debugdata .hg/store/undolog/command.i 11 commit\x00--amend (no-eol) (esc) @@ -149,7 +151,7 @@ Revset tests $ hg log -G -r 'draft()' --hidden | head -1 - @ changeset: 8:aa430c8afedf + @ changeset: 10:aa430c8afedf $ hg debugundohistory -n 0 command: ci -ma5 @@ -169,6 +171,93 @@ Test 'olddraft([NUM])' revset $ hg log -G -r 'olddraft(0) - olddraft(1)' --hidden -T compact - @ 8[tip][master] aa430c8afedf 1970-01-01 00:00 +0000 test + @ 10[tip][master] aa430c8afedf 1970-01-01 00:00 +0000 test | a5 ~ + +hg undo command tests + $ hg undo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg log -G -T compact | head -4 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + | + o 8:4 0a3dd3e15e65 1970-01-01 00:00 +0000 test + $ hg update 0a3dd3e15e65 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg undo + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 8[tip][master]:4 0a3dd3e15e65 1970-01-01 00:00 +0000 test + $ touch c11 && hg add c11 + $ hg commit --amend + $ hg log -G -T compact | head -1 + @ 12[tip][master]:4 55c537d18276 1970-01-01 00:00 +0000 test + $ hg undo + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg log -G -T compact | head -10 + o 7[tip][feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + | d + | + @ 4[master] 38d85b506754 1970-01-01 00:00 +0000 test + | c2 + | + o 3:1 ec7553f7b382 1970-01-01 00:00 +0000 test + | c1 + | + | o 2[feature1] 49cdb4091aca 1970-01-01 00:00 +0000 test + $ hg graft 296fda51a303 + grafting 7:296fda51a303 "d" (tip feature2) + $ hg log -G -T compact | head -4 + @ 13[tip]:4 3d8a976116c0 1970-01-01 00:00 +0000 test + | d + | + | o 7[feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + $ hg undo + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 7[tip][feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + $ hg book test + $ hg undo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg bookmarks + feature1 2:49cdb4091aca + feature2 5:db92053d5c83 + master 4:38d85b506754 + +hg redo test + $ hg redo + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 7[tip][feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + $ hg undo + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 4[tip][feature2,master] 38d85b506754 1970-01-01 00:00 +0000 test + $ hg undo -n 5 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg redo -n 5 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 4[tip][feature2,master] 38d85b506754 1970-01-01 00:00 +0000 test + $ hg redo -n 100 + abort: index out of bounds + [255] + +hg undo --absolute tests + $ hg undo -a + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg redo + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg undo -a + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg redo + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg undo -n 5 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 7[tip][feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + $ hg undo -a + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G -T compact | head -1 + @ 4[tip][feature2,master] 38d85b506754 1970-01-01 00:00 +0000 test