diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -402,6 +402,8 @@ @command('undo', [ ('a', 'absolute', False, _("absolute based on command index instead of " "relative undo")), + ('b', 'branch', "", _("single branch undo, accepts commit hash " + "(EXPERIMENTAL)")), ('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")), @@ -456,6 +458,7 @@ reverseindex = opts.get("index") relativeundo = not opts.get("absolute") keep = opts.get("keep") + branch = opts.get("branch") with repo.wlock(), repo.lock(), repo.transaction("undo"): cmdutil.checkunfinished(repo) @@ -466,7 +469,8 @@ if not (opts.get("force") or _gapcheck(repo, reverseindex)): raise error.Abort(_("attempted risky undo across" " missing history")) - _undoto(ui, repo, reverseindex, keep=keep) + _undoto(ui, repo, reverseindex, keep=keep, branch=branch) + # store undo data # for absolute undos, think of this as a reset # for relative undos, think of this as an update @@ -495,9 +499,11 @@ _undoto(ui, repo, reverseindex) _logundoredoindex(repo, repo.currenttransaction(), reverseindex) -def _undoto(ui, repo, reverseindex, keep=False): +def _undoto(ui, repo, reverseindex, keep=False, branch=None): # undo to specific reverseindex # requires inhibit extension + # branch is a changectx hash (potentially short form) + # which identifies its branch via localbranch revset if repo != repo.unfiltered(): raise error.ProgrammingError(_("_undoto expects unfilterd repo")) try: @@ -508,27 +514,38 @@ # bookmarks bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) booklist = bookstring.split("\n") + if branch: + revs = repo.revs(revsetlang.formatspec('localbranch(%s)', branch)) + tonode = repo.changelog.node + branchcommits = [tonode(x) for x in revs] + else: + branchcommits = False + # copy implementation for bookmarks itercopy = [] for mark in repo._bookmarks.iteritems(): itercopy.append(mark) - bmchanges = [(mark[0], None) for mark in itercopy] + bmchanges = [] + for mark in itercopy: + if not branchcommits or mark[1] in branchcommits: + bmchanges.append((mark[0], None)) repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmchanges) bmchanges = [] for mark in booklist: if mark: kv = mark.rsplit(" ", 1) - bmchanges.append((kv[0], bin(kv[1]))) + if not branchcommits or bin(kv[1]) in branchcommits: + bmchanges.append((kv[0], bin(kv[1]))) repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmchanges) # working copy parent workingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) if not keep: - #revealcommits(repo, workingcopyparent) - hg.updatetotally(ui, repo, workingcopyparent, workingcopyparent, - clean=False, updatecheck='abort') - else: + if not branchcommits or bin(workingcopyparent) in branchcommits: + hg.updatetotally(ui, repo, workingcopyparent, workingcopyparent, + clean=False, updatecheck='abort') + elif not branchcommits or workingcopyparent in branchcommits: # keeps working copy files precnode = bin(workingcopyparent) precctx = repo[precnode] @@ -556,8 +573,19 @@ reverseindex) removedrevs = revsetlang.formatspec('olddraft(%d) - olddraft(0)', reverseindex) - smarthide(repo, addedrevs, removedrevs) - revealcommits(repo, removedrevs) + if not branch: + smarthide(repo, addedrevs, removedrevs) + revealcommits(repo, removedrevs) + else: + localadds = revsetlang.formatspec('(olddraft(0) - olddraft(%d)) and' + ' localbranch(%s)', + reverseindex, branch) + localremoves = revsetlang.formatspec('(olddraft(%d) - olddraft(0)) and' + ' localbranch(%s)', + reverseindex, branch) + smarthide(repo, localadds, removedrevs) + smarthide(repo, addedrevs, localremoves, local=True) + revealcommits(repo, localremoves) def _computerelative(repo, reverseindex): # allows for relative undos using @@ -573,11 +601,13 @@ return reverseindex # hide and reveal commits -def smarthide(repo, revhide, revshow): +def smarthide(repo, revhide, revshow, local=False): '''hides changecontexts and reveals some commits tries to connect related hides and shows with obs marker when reasonable and correct + + use local to not hide revhides without corresponding revshows ''' hidectxs = repo.set(revhide) showctxs = repo.set(revshow) @@ -605,8 +635,11 @@ # correct solution for complex edge case if len(destinations) == 1: hidecommits(repo, ctx, destinations) - else: + elif len(destinations) > 1:# split hidecommits(repo, ctx, []) + elif len(destinations) == 0: + if not local: + hidecommits(repo, ctx, []) def hidecommits(repo, curctx, precctxs): obsolete.createmarkers(repo, [(curctx, precctxs)], operation='undo') diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -540,3 +540,52 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: newfiles + +localbranch undos +Make changes on two branches (old and new) +Undo only changes in one branch (old) +Includes commit and book changes + $ hg book "oldbook" + $ touch oldbranch + $ hg add oldbranch && hg ci -moldbranch + $ hg update null + 0 files updated, 0 files merged, 8 files removed, 0 files unresolved + (leaving bookmark oldbook) + $ touch newbranch + $ hg add newbranch && hg ci -mnewbranch + created new head + $ hg book "newbook" + $ hg log -l 2 + changeset: 20:805791ba4bcd + bookmark: newbook + tag: tip + parent: -1:000000000000 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: newbranch + + changeset: 19:7b0ef4f2a1ae + bookmark: oldbook + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: oldbranch + + $ hg up 75f63379f12b + 7 files updated, 0 files merged, 1 files removed, 0 files unresolved + (leaving bookmark newbook) + $ hg undo -b 75f63379f12b -n 6 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -l 2 + changeset: 20:805791ba4bcd + bookmark: newbook + tag: tip + parent: -1:000000000000 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: newbranch + + changeset: 18:75f63379f12b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: newfiles +