diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -16,16 +16,19 @@ dispatch, error, extensions, + graphmod, hg, localrepo, lock as lockmod, obsolete, obsutil, + pycompat, registrar, revlog, revset, revsetlang, smartset, + templatekw, transaction, util, ) @@ -99,7 +102,7 @@ try: repo.vfs.makedirs('undolog') except OSError: - repo.ui.debug("can't make undolog folder in .hg") + repo.ui.debug("can't make undolog folder in .hg\n") return changes with lockmod.lock(repo.vfs, "undolog/lock", desc="undolog", timeout=2): @@ -152,10 +155,10 @@ 'workingparent': _logworkingparent(repo, tr), } try: - exsistingnodes = _readindex(repo, 0) + existingnodes = _readindex(repo, 0) except IndexError: - exsistingnodes = {} - if all(newnodes.get(x) == exsistingnodes.get(x) for x in newnodes.keys()): + existingnodes = {} + if all(newnodes.get(x) == existingnodes.get(x) for x in newnodes.keys()): # no changes to record return False else: @@ -383,6 +386,96 @@ revs = _getolddrafts(repo, reverseindex) return smartset.baseset(revs) +# Templates +keywords = {} + +templatekeyword = registrar.templatekeyword(keywords) + +@util.cachefunc +def _undonehexnodes(repo): + repo = repo.unfiltered() + reverseindex = _computerelative(repo, 1) + revstring = revsetlang.formatspec('draft() - olddraft(%d)', reverseindex) + revs = repo.revs(revstring) + tonode = repo.changelog.node + hexnodes = [repo[tonode(x)] for x in revs] + return hexnodes + +@templatekeyword("undonecommits") +def showundonecommits(repo, ctx, **args): + """String. Changectxs added by last command.""" + hexnodes = _undonehexnodes(repo) + if ctx in hexnodes: + result = ctx.hex() + else: + result = None + return result + +@util.cachefunc +def _donehexnodes(repo): + repo = repo.unfiltered() + reverseindex = _computerelative(repo, 1) + revstring = revsetlang.formatspec('olddraft(%d)', reverseindex) + revs = repo.revs(revstring) + tonode = repo.changelog.node + hexnodes = [repo[tonode(x)] for x in revs] + return hexnodes + +@templatekeyword("donecommits") +def showdonecommits(repo, ctx, **args): + """String. Changectxs one repo state ago.""" + hexnodes = _donehexnodes(repo) + if ctx in hexnodes: + result = ctx.hex() + else: + result = None + return result + +@util.cachefunc +def _oldmarks(repo): + reverseindex = _computerelative(repo, 1) + nodedict = _readindex(repo, reverseindex) + bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) + oldmarks = bookstring.split("\n") + result = [] + for mark in oldmarks: + kv = mark.rsplit(" ", 1) + if len(kv) == 2: + result.append(kv) + return result + +@templatekeyword("oldbookmarks") +def showoldbookmarks(**args): + """List of strings. Bookmarks one repo state ago.""" + args = pycompat.byteskwargs(args) + repo = args['ctx']._repo + oldmarks = _oldmarks(repo) + bookmarks = [] + for kv in oldmarks: + if repo[kv[1]] == repo[args['ctx']]: + bookmarks.append(kv[0]) + active = repo._activebookmark + makemap = lambda v: {'bookmark': v, 'active': active, 'current': active} + f = templatekw._showlist('bookmark', bookmarks, args) + return templatekw._hybrid(f, bookmarks, makemap, lambda x: x['bookmark']) + +@templatekeyword("removedbookmarks") +def showremovedbookmarks(**args): + """List of strings. Bookmarks added/moved by last command.""" + args = pycompat.byteskwargs(args) + repo = args['ctx']._repo + currentbookmarks = args['ctx'].bookmarks() + oldmarks = _oldmarks(repo) + oldbookmarks = [] + for kv in oldmarks: + if repo[kv[1]] == repo[args['ctx']]: + oldbookmarks.append(kv[0]) + bookmarks = list(set(currentbookmarks) - set(oldbookmarks)) + active = repo._activebookmark + makemap = lambda v: {'bookmark': v, 'active': active, 'current': active} + f = templatekw._showlist('bookmark', bookmarks, args) + return templatekw._hybrid(f, bookmarks, makemap, lambda x: x['bookmark']) + # Undo: @command('undo', [ @@ -391,6 +484,8 @@ ('f', 'force', False, _("undo across missing undo history (ADVANCED)")), ('k', 'keep', False, _("keep working copy changes")), ('n', 'index', 1, _("how many steps to undo back")), + ('p', 'preview', False, _("see smartlog like preview of future undo " + "state")), ]) def undo(ui, repo, *args, **opts): """perform an undo @@ -430,7 +525,7 @@ before and after states are connected. Use keep to maintain working copy changes. With keep, undo mimics hg - unamend and hg uncommit. Specifically, files that exsist currently that + unamend and hg uncommit. Specifically, files that exist currently that don't exist at the repo state we are undoing to will remain in your working copy but not in your changeset. Maintaining your working copy has primarily two downsides: firstly your new working copy won't be clean @@ -442,6 +537,11 @@ reverseindex = opts.get("index") relativeundo = not opts.get("absolute") keep = opts.get("keep") + preview = opts.get("preview") + + if preview: + _undopreview(ui, repo, **opts) + return with repo.wlock(), repo.lock(), repo.transaction("undo"): cmdutil.checkunfinished(repo) @@ -606,6 +706,46 @@ ctxts = repo.set(rev) inhibit.revive(ctxts) +def _undopreview(ui, repo, **opts): + # Print smartlog like preview of undo + # Input: + # ui: + # repo: mercurial.localrepo + # **opts: contains opts["template"] = "" + # Output: + # None + # Requires smartlog extension + try: + smartlog = extensions.find('smartlog') + except KeyError: + raise error.Abort(_('undo requires smartlog to work properly')) + reverseindex = opts.get("index") + opts["template"] = "{undopreview}" + reverseindex = _computerelative(repo, reverseindex) + revstring = revsetlang.formatspec("olddraft(%d)", reverseindex) + revs = set() + try: + revs.update(repo.revs(revstring)) + except IndexError: + raise error.Abort(_("can't undo: no data past this point")) + return + repo.filtered('visible') + revs.update(repo.revs("draft()")) + repo = repo.unfiltered() + revs = sorted(list(revs), reverse=True) + overrides = {} + if ui.config('experimental', 'graphstyle.grandparent') == '|': + overrides[('experimental', 'graphstyle.grandparent')] = '2.' + with ui.configoverride(overrides, 'smartlog'): + masterstring = opts.get('master') + masterrevset = smartlog._masterrevset(ui, repo, masterstring) + masterrev = smartlog._masterrev(repo, masterrevset) + revdag = smartlog.getdag(ui, repo, revs, masterrev) + displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) + cmdutil.displaygraph( + ui, repo, revdag, displayer, graphmod.asciiedges, None, None) + return + # Tools def _invertindex(rlog, indexorreverseindex): diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -414,3 +414,39 @@ #endif $ hg debugundohistory -l 0: -- gap in log -- + +hg undo --preview test + $ cat >> $HGRCPATH < [extensions] + > smartlog = $TESTDIR/../hgext3rd/smartlog.py + > EOF + $ touch prev1 && hg add prev1 && hg ci -m prev1 + $ hg sl + $ hg log -Gr "olddraft(1) + olddraft(0)" -T "{label(sl_label, separate('\n', separate(' ', separate(' ', label('sl.oldbook', '{if(undonecommits, shortest(undonecommits, 6))}'), label('sl.book', '{label('sl.book', '{if(donecommits, shortest(donecommits, 6))}')}')), sl_user, sl_diff, separate(' ', label('sl.book', oldbookmarks), label('sl.oldbook', removedbookmarks))), '{sl_desc}', '\n'))}" + @ c442d4 + | + o 75f633 + | + o d0fdb9 + | + o 1dafc0 master + | + o 0a3dd3 + | + | o 296fda feature2 + |/ + o 38d85b + | + o ec7553 + | + | o 49cdb4 feature1 + |/ + o b68836 + | + o df4fd6 + + $ cat >> $HGRCPATH < [extensions] + > smartlog =! + > EOF +