diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -182,7 +182,7 @@ repo.svfs.tryunlink(path) def _recordnewgap(repo, absoluteindex=None): - path = os.path.join('undolog', 'gap') + path = 'undolog' + '/' + 'gap' if absoluteindex is None: rlog = _getrevlog(repo, 'index.i') repo.svfs.write(path, str(len(rlog) - 1)) @@ -214,7 +214,7 @@ def _gapcheck(repo, reverseindex): rlog = _getrevlog(repo, 'index.i') absoluteindex = _invertindex(rlog, reverseindex) - path = os.path.join('undolog', "gap") + path = 'undolog' + '/' + 'gap' try: result = absoluteindex >= int(repo.svfs.read(path)) except IOError: @@ -354,10 +354,11 @@ # Undo: @command('undo', [ - ('n', 'index', 1, _("how many steps to undo back")), - ('f', 'force', False, _("undo across a gap in memory (ADVANCED)")), ('a', 'absolute', False, _("absolute based on command index instead of " "relative undo")), + ('f', 'force', False, _("undo across a gap in memory (ADVANCED)")), + ('k', 'keep', False, _("keep working copy changes")), + ('n', 'index', 1, _("how many steps to undo back")), ]) def undo(ui, repo, *args, **opts): """perform an undo @@ -398,10 +399,15 @@ repo or another command breaks something. Undoing across this "gap" can be forced, but isn't advised unless its known how the before and after states are connected. Undoing up to a gap is safe. + + Use keep to maintain working copy changes. With keep, undo mimics hg + unamend and hg uncommit. Specifcally, files that exsist currently that + don't exsist at the repo state we are undoing to will remain in your + workingcopy but not in your changeset. """ - reverseindex = opts.get("index") relativeundo = not opts.get("absolute") + keep = opts.get("keep") with repo.wlock(), repo.lock(), repo.transaction("undo"): cmdutil.checkunfinished(repo) @@ -412,7 +418,7 @@ if not (opts.get("force") or _gapcheck(repo, reverseindex)): raise error.Abort(_("attempted risky undo across" " missing history")) - _undoto(ui, repo, reverseindex) + _undoto(ui, repo, reverseindex, keep=keep) # store undo data # for absolute undos, think of this as a reset # for relative undos, think of this as an update @@ -441,7 +447,7 @@ _undoto(ui, repo, reverseindex) _logundoredoindex(repo, repo.currenttransaction(), reverseindex) -def _undoto(ui, repo, reverseindex): +def _undoto(ui, repo, reverseindex, keep=False): # undo to specific reverseindex # requires inhibit extension try: @@ -467,9 +473,34 @@ # working copy parent workingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) - revealcommits(repo, workingcopyparent) - hg.updatetotally(ui, repo, workingcopyparent, workingcopyparent, - clean=False, updatecheck='abort') + if not keep: + revealcommits(repo, workingcopyparent) + hg.updatetotally(ui, repo, workingcopyparent, workingcopyparent, + clean=False, updatecheck='abort') + else: + # keeps working copy files + curctx = repo['.'] + precnode = bin(workingcopyparent) + precctx = repo[precnode] + + changedfiles = [] + wctx = repo[None] + wctxmanifest = wctx.manifest() + precctxmanifest = precctx.manifest() + dirstate = repo.dirstate + diff = precctxmanifest.diff(wctxmanifest) + changedfiles.extend(diff.iterkeys()) + + with dirstate.parentchange(): + dirstate.rebuild(precnode, precctxmanifest, changedfiles) + # we want added and removed files to be shown + # properly, not with ? and ! prefixes + for filename, data in diff.iteritems(): + if data[0][0] is None: + dirstate.add(filename) + if data[1][0] is None: + dirstate.remove(filename) + obsolete.createmarkers(repo, [(curctx, (precctx,))]) # visible changesets addedrevs = revsetlang.formatspec('olddraft(0) - olddraft(%d)', diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -299,3 +299,13 @@ 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 + +hg undo --keep tests + $ touch kfl1 && hg add kfl1 + $ hg st + A kfl1 + $ hg commit --amend + $ hg st + $ hg undo --keep + $ hg st + A kfl1