diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -407,6 +407,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")), @@ -461,6 +463,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) @@ -471,7 +474,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 @@ -500,9 +504,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: @@ -513,27 +519,40 @@ # 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] - repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmchanges) + bmremove = [] + for mark in itercopy: + if not branchcommits or mark[1] in branchcommits: + bmremove.append((mark[0], None)) + repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmremove) 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 or\ + (kv[0], None) in bmremove: + 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] @@ -561,8 +580,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 @@ -578,11 +608,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) @@ -610,8 +642,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 @@ -541,3 +541,95 @@ 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 + +Check rebase local undos of rebases +Make sure bookmarks and commits are not lost +and commits are not duplicated + $ cat >> $HGRCPATH < [extensions] + > rebase = + > EOF + $ hg rebase -s 8057 -d 75f6 + rebasing 20:805791ba4bcd "newbranch" (tip newbook) + $ hg log -l 2 + changeset: 21:35324a911c0d + bookmark: newbook + tag: tip + parent: 18:75f63379f12b + 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 + + $ hg undo -b 3532 + 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 + + $ cat >> $HGRCPATH < [extensions] + > rebase = ! + > EOF