diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -11,13 +11,17 @@ from mercurial.i18n import _ from mercurial import ( + cmdutil, + commands, dispatch, error, extensions, + obsolete, registrar, revlog, revset, revsetlang, + scmutil, smartset, transaction, util, @@ -29,6 +33,10 @@ nullid, ) +from hgext3rd import ( + inhibit, +) + cmdtable = {} command = registrar.command(cmdtable) @@ -276,6 +284,80 @@ revs = _getolddrafts(repo, reverseindex) return smartset.baseset(revs) +# Undo: + +@command('undo', [ + ('n', 'index', 1, _("how many steps to undo back")), +]) +def undo(ui, repo, *args, **opts): + """ perform an undo + + Undoes an undoable command. An undoable command is one that changed atleast + one of the following three: bookmarks, working copy parent or changesets. + Note that this specifically does not include commands like log. It will + include update if update changes the working copy parent (you update to a + changeset that isn't the current one). Note that commands that edit public + repos can't be undone (specifically push). + + Undo does not preserve the working copy changes. + """ + reverseindex = opts.get("index") + + if repo is not None: + with repo.wlock(), repo.lock(), repo.transaction("undo"): + cmdutil.checkunfinished(repo) + cmdutil.bailifchanged(repo) + repo = repo.unfiltered() + _undoto(ui, repo, reverseindex) + # store undo data +def _undoto(ui, repo, reverseindex): + # undo to specific reverseindex + try: + nodedict = _readindex(repo, reverseindex) + except IndexError: + raise error.Abort(_("index out of bounds")) + + # bookmarks + bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) + booklist = bookstring.split("\n") + # copy implementation for bookmarks + itercopy = [] + for mark in repo._bookmarks.iteritems(): + itercopy.append(mark) + for mark in itercopy: + commands.bookmark(ui, repo, mark[0], delete=True) + for mark in booklist: + if mark is not None and mark != "": + kv = mark.rsplit(" ", 1) + commands.bookmark(ui, repo, kv[0], rev=kv[1]) + + # working copy parent + workingcopyparent = _readnode(repo, "workingparent.i", + nodedict["workingparent"]) + revealcommits(ui, repo, workingcopyparent) + commands.update(ui, repo, rev=workingcopyparent) + + # visible changesets + addedrevs = revsetlang.formatspec('olddraft(0) - olddraft(%d)', + reverseindex) + revs = repo.revs(addedrevs) + with repo.lock(): + for r in revs: + hidecommits(repo, repo.changectx(r)) + removedrevs = revsetlang.formatspec('olddraft(%d) - olddraft(0)', + reverseindex) + revealcommits(ui, repo, removedrevs) + +# hide and reveal commits + +def hidecommits(repo, commit): + obsolete.createmarkers(repo, [[commit,[]]]) + +def revealcommits(ui, repo, rev): + revr = scmutil.revrange(repo, [rev]) + contexts = (repo.changectx(repo[r].node()) for r in revr) + inhibit.revive(contexts) + # Tools def _invertindex(rlog, indexorreverseindex):