diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -14,6 +14,7 @@ dispatch, error, extensions, + hg, localrepo, lock as lockmod, obsolete, @@ -87,6 +88,9 @@ tr = lighttransaction(repo) with tr: changes = log(repo.filtered('visible'), command, tr) + if changes and not ("undo" == command[0] or "redo" == + command[0]): + _delundoredo(repo) return changes def lighttransaction(repo): @@ -173,6 +177,16 @@ revstring = "\n".join(sorted('%s %s' % (k, v) for k, v in nodes.items())) return writelog(repo, tr, "index.i", revstring) +def _logundoredoindex(repo, tr, reverseindex): + rlog = _getrevlog(repo, 'index.i') + hexnode = hex(rlog.node(_invertindex(rlog, reverseindex))) + #return writelog(repo, tr, "redo.i", "node " + str(hexnode)) + return repo.svfs.write("undolog/redo.i", str(hexnode)) + +def _delundoredo(repo): + path = 'undolog' + '/' + 'redo.i' + repo.svfs.tryunlink(path) + # Read def _readindex(repo, reverseindex, prefetchedrevlog=None): @@ -316,6 +330,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 @@ -328,15 +344,65 @@ public 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") 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, repo.currenttransaction(), 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) + + with repo.wlock(), repo.lock(), repo.transaction("redo"): + cmdutil.checkunfinished(repo) + cmdutil.bailifchanged(repo) + repo = repo.unfiltered() + _undoto(ui, repo, reverseindex) + _logundoredoindex(repo, repo.currenttransaction(), reverseindex) def _undoto(ui, repo, reverseindex): # undo to specific reverseindex @@ -366,7 +432,8 @@ workingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) revealcommits(repo, workingcopyparent) - commands.update(ui, repo, rev=workingcopyparent) + hg.updatetotally(ui, repo, workingcopyparent, workingcopyparent, + clean=False, updatecheck='abort') # visible changesets addedrevs = revsetlang.formatspec('olddraft(0) - olddraft(%d)', @@ -377,6 +444,19 @@ reverseindex) revealcommits(repo, removedrevs) +def _computerelative(repo, reverseindex): + # allows for relative undos using + # redo.i storage + try: + hexnode = repo.svfs.read("undolog/redo.i") + rlog = _getrevlog(repo, 'index.i') + rev = rlog.rev(bin(hexnode)) + reverseindex = _invertindex(rlog, rev) + reverseindex + except IOError: + # return input index + pass + return reverseindex + # hide and reveal commits def hidecommits(repo, rev): diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -1,8 +1,11 @@ $ cat >> $HGRCPATH < [extensions] > undo = $TESTDIR/../hgext3rd/undo.py + > inhibit=$TESTDIR/../hgext3rd/inhibit.py > [undo] > _duringundologlock=1 + > [experimental] + > evolution=createmarkers > EOF Build up a repo @@ -169,7 +172,7 @@ 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 ~ @@ -178,3 +181,114 @@ $ sleep 0.1 $ hg st --time time: real [1-9]*\..* (re) + +hg undo command tests + $ hg undo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + (leaving bookmark master) + $ hg log -G -T compact -l2 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + | + o 8:4 0a3dd3e15e65 1970-01-01 00:00 +0000 test + | words + ~ + $ hg update 0a3dd3e15e65 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg undo + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ touch c11 && hg add c11 + $ hg commit --amend + $ hg log -G -T compact -l1 + @ 12[tip][master]:8 2dca609174c2 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg undo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg log -G -T compact -l4 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + | + o 8:4 0a3dd3e15e65 1970-01-01 00:00 +0000 test + | words + | + | o 7[feature2]:4 296fda51a303 1970-01-01 00:00 +0000 test + |/ d + | + o 4 38d85b506754 1970-01-01 00:00 +0000 test + | c2 + ~ + $ hg graft 296fda51a303 + grafting 7:296fda51a303 "d" (feature2) + $ hg log -G -T compact -l2 + @ 13[tip]:9 f007a7cf4c3d 1970-01-01 00:00 +0000 test + | d + | + o 9[master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg undo + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg book test + $ hg undo + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + (leaving bookmark test) + $ hg bookmarks + feature1 2:49cdb4091aca + feature2 7:296fda51a303 + master 9:1dafc0b43612 + +hg redo test + $ hg redo + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master,test] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg undo + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg undo -n 5 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg redo -n 5 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg redo -n 100 + abort: index out of bounds + [255] + +hg undo --absolute tests + $ hg undo -a + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg redo + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg undo -a + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg redo + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg undo -n 5 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master,test] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~ + $ hg undo -a + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T compact -l1 + @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test + | cmiss + ~