diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -55,7 +55,9 @@ # Check wether undolog is consistent # ie check wether the undo ext was # off before this command - safelog(repo, [""]) + changes = safelog(repo, [""]) + if changes: + _recordnewgap(repo) result = orig(lui, repo, cmd, fullargs, *args) @@ -186,6 +188,14 @@ path = 'undolog' + '/' + 'redonode' repo.svfs.tryunlink(path) +def _recordnewgap(repo, absoluteindex=None): + path = os.path.join('undolog', 'gap') + if absoluteindex is None: + rlog = _getrevlog(repo, 'index.i') + repo.svfs.write(path, str(len(rlog) - 1)) + else: + repo.svfs.write(path, str(absoluteindex)) + # Read def _readindex(repo, reverseindex, prefetchedrevlog=None): @@ -208,6 +218,29 @@ rlog = _getrevlog(repo, filename) return rlog.revision(bin(hexnode)) +def _gapcheck(repo, reverseindex): + rlog = _getrevlog(repo, 'index.i') + absoluteindex = _invertindex(rlog, reverseindex) + path = os.path.join('undolog', "gap") + try: + result = absoluteindex >= int(repo.svfs.read(path)) + except IOError: + # recreate file + repo.ui.debug("failed to read gap file in %s, attempting recreation\n" + % path) + rlog = _getrevlog(repo, 'index.i') + i = 0 + while i < (len(rlog)): + indexdict = _readindex(repo, i, rlog) + if "" == _readnode(repo, "command.i", indexdict["command"]): + break + i += 1 + # defaults to before oldest command + _recordnewgap(repo, _invertindex(rlog, i)) + result = absoluteindex >= _invertindex(rlog, i) + finally: + return result + # Visualize """debug commands and instrumentation for the undo extension @@ -329,6 +362,7 @@ @command('undo', [ ('n', 'index', 1, _("how many steps to undo back")), + ('f', 'force', False, _("undo across missing undo history (ADVANCED)")), ('a', 'absolute', False, _("absolute based on command index instead of " "relative undo")), ]) @@ -363,6 +397,11 @@ Undo states are also distinct repo states and can thereby be inspected using debugundohistory and specifically jumped to using undo --index --absolute. + + If the undo extension was turned off and on again, you might loose the + ability to undo to certain repo states. Undoing to repo states before the + missing ones can be forced, but isn't advised unless its known how the + before and after states are connected. """ reverseindex = opts.get("index") @@ -374,6 +413,9 @@ repo = repo.unfiltered() if relativeundo: reverseindex = _computerelative(repo, reverseindex) + if not (opts.get("force") or _gapcheck(repo, reverseindex)): + raise error.Abort(_("attempted risky undo across" + " missing history")) _undoto(ui, repo, reverseindex) # store undo data # for absolute undos, think of this as a reset diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -292,3 +292,18 @@ @ 9[tip][master] 1dafc0b43612 1970-01-01 00:00 +0000 test | cmiss ~ + +hg undo --force tests + $ hg debugundohistory -l 18 + 18: undo + 19: ci -ma5 + 20: -- gap in log -- + 21: commit -m words + 22: update master + $ hg undo -a -n 25 + abort: attempted risky undo across missing history + [255] + $ hg undo -a -n 25 -f + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg undo -a + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved