D821: unamend: move fb extension unamend to core
pulkit (Pulkit Goyal)
phabricator at mercurial-scm.org
Sat Nov 11 09:11:56 EST 2017
pulkit updated this revision to Diff 3421.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D821?vs=2493&id=3421
REVISION DETAIL
https://phab.mercurial-scm.org/D821
AFFECTED FILES
hgext/uncommit.py
tests/test-unamend.t
CHANGE DETAILS
diff --git a/tests/test-unamend.t b/tests/test-unamend.t
new file mode 100644
--- /dev/null
+++ b/tests/test-unamend.t
@@ -0,0 +1,362 @@
+Test for command `hg unamend` which lives in uncommit extension
+===============================================================
+
+ $ cat >> $HGRCPATH << EOF
+ > [alias]
+ > glog = log -G -T '{rev}:{node|short} {desc}'
+ > [experimental]
+ > evolution = createmarkers, allowunstable
+ > [extensions]
+ > rebase =
+ > amend =
+ > uncommit =
+ > EOF
+
+Repo Setup
+---------
+
+ $ hg init repo
+ $ cd repo
+ $ for ch in {a..h}; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
+
+ $ hg glog
+ @ 7:ec2426147f0e Added h
+ |
+ o 6:87d6d6676308 Added g
+ |
+ o 5:825660c69f0c Added f
+ |
+ o 4:aa98ab95a928 Added e
+ |
+ o 3:62615734edd5 Added d
+ |
+ o 2:28ad74487de9 Added c
+ |
+ o 1:29becc82797a Added b
+ |
+ o 0:18d04c59bb5d Added a
+
+
+Trying to unamend when there was no amend done
+----------------------------------------------
+
+ $ hg unamend
+ abort: changeset must have one predecessor, found 0 predecessors
+ [255]
+
+Unamend on clean wdir and tip
+------------------------------
+
+ $ echo "bar" >> h
+ $ hg amend
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
+ # Parent 87d6d66763085b629e6d7ed56778c79827273022
+ Added h
+
+ diff -r 87d6d6676308 -r c9fa1a715c1b h
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/h Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,2 @@
+ +foo
+ +bar
+
+ $ hg glog --hidden
+ @ 8:c9fa1a715c1b Added h
+ |
+ | x 7:ec2426147f0e Added h
+ |/
+ o 6:87d6d6676308 Added g
+ |
+ o 5:825660c69f0c Added f
+ |
+ o 4:aa98ab95a928 Added e
+ |
+ o 3:62615734edd5 Added d
+ |
+ o 2:28ad74487de9 Added c
+ |
+ o 1:29becc82797a Added b
+ |
+ o 0:18d04c59bb5d Added a
+
+ $ hg unamend
+ $ hg glog --hidden
+ @ 9:8da14a1fd653 Added h
+ |
+ | x 8:c9fa1a715c1b Added h
+ |/
+ | x 7:ec2426147f0e Added h
+ |/
+ o 6:87d6d6676308 Added g
+ |
+ o 5:825660c69f0c Added f
+ |
+ o 4:aa98ab95a928 Added e
+ |
+ o 3:62615734edd5 Added d
+ |
+ o 2:28ad74487de9 Added c
+ |
+ o 1:29becc82797a Added b
+ |
+ o 0:18d04c59bb5d Added a
+
+ $ hg diff
+ diff -r 8da14a1fd653 h
+ --- a/h Thu Jan 01 00:00:00 1970 +0000
+ +++ b/h Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,1 +1,2 @@
+ foo
+ +bar
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 8da14a1fd653c3f07fdad5760511c9e12652a306
+ # Parent 87d6d66763085b629e6d7ed56778c79827273022
+ Added h
+
+ diff -r 87d6d6676308 -r 8da14a1fd653 h
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/h Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +foo
+
+ $ hg status
+ M h
+
+ $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
+ branch=default
+ unamend_source=\xc9\xfa\x1aq\\\x1bva\xc0\xfa\xfb6*\x9f0\xbdu\x87\x8d}
+
+Unamend on a dirty working directory
+------------------------------------
+
+ $ hg ci -m "Added bar to h"
+ $ echo "bar" >> a
+ $ hg amend
+ $ echo "foobar" >> a
+ $ echo "bar" >> b
+ $ hg status
+ M a
+ M b
+
+ $ hg unamend
+
+ $ hg status
+ M a
+ M b
+
+ $ hg diff
+ diff -r 49f03646f7a0 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,1 +1,3 @@
+ foo
+ +bar
+ +foobar
+ diff -r 49f03646f7a0 b
+ --- a/b Thu Jan 01 00:00:00 1970 +0000
+ +++ b/b Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,1 +1,2 @@
+ foo
+ +bar
+
+Unamending an added file
+------------------------
+
+ $ hg ci -m "Added things to a and b"
+ $ echo foo > bar
+ $ hg add bar
+ $ hg amend
+
+ $ hg unamend
+ $ hg status
+ A bar
+
+ $ hg revert --all
+ forgetting bar
+
+Unamending a removed file
+-------------------------
+
+ $ hg remove a
+ $ hg amend
+
+ $ hg unamend
+ $ hg status
+ R a
+ ? bar
+
+ $ hg revert --all
+ undeleting a
+
+Unamending an added file with dirty wdir status
+-----------------------------------------------
+
+ $ hg add bar
+ $ hg amend
+ $ echo bar >> bar
+ $ hg status
+ M bar
+
+ $ hg unamend
+ $ hg status
+ A bar
+ $ hg diff
+ diff -r 738d71740456 bar
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/bar Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,2 @@
+ +foo
+ +bar
+
+ $ hg revert --all
+ forgetting bar
+
+Unamending in middle of a stack
+-------------------------------
+
+ $ hg glog
+ @ 19:738d71740456 Added things to a and b
+ |
+ o 12:49f03646f7a0 Added bar to h
+ |
+ o 9:8da14a1fd653 Added h
+ |
+ o 6:87d6d6676308 Added g
+ |
+ o 5:825660c69f0c Added f
+ |
+ o 4:aa98ab95a928 Added e
+ |
+ o 3:62615734edd5 Added d
+ |
+ o 2:28ad74487de9 Added c
+ |
+ o 1:29becc82797a Added b
+ |
+ o 0:18d04c59bb5d Added a
+
+ $ hg up 5
+ 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ echo bar >> f
+ $ hg amend
+ $ hg rebase -s 6 -d . -q
+
+ $ hg glog
+ o 24:947f189a0dfc Added things to a and b
+ |
+ o 23:8c216e653148 Added bar to h
+ |
+ o 22:533b8ea407b4 Added h
+ |
+ o 21:49635b68477e Added g
+ |
+ @ 20:93f0e8ffab32 Added f
+ |
+ o 4:aa98ab95a928 Added e
+ |
+ o 3:62615734edd5 Added d
+ |
+ o 2:28ad74487de9 Added c
+ |
+ o 1:29becc82797a Added b
+ |
+ o 0:18d04c59bb5d Added a
+
+
+ $ hg unamend
+ abort: cannot unamend in the middle of a stack
+ [255]
+
+Trying to unamend a public changeset
+------------------------------------
+
+ $ hg up
+ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg phase -r . -p
+ $ hg unamend
+ abort: cannot unamend public changesets
+ [255]
+
+Testing whether unamend retains copies or not
+---------------------------------------------
+
+ $ hg status
+ ? bar
+
+ $ hg mv a foo
+
+ $ hg ci -m "Moved a to foo"
+ $ hg exp --git
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID bb7fb18b1747d280f5b6e69c2b534e35c027a761
+ # Parent 947f189a0dfc1ba98770b529559caaa9189ab661
+ Moved a to foo
+
+ diff --git a/a b/foo
+ rename from a
+ rename to foo
+
+ $ hg mv b foobar
+ $ hg diff --git
+ diff --git a/b b/foobar
+ rename from b
+ rename to foobar
+ $ hg amend
+
+ $ hg exp --git
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 6f7abde89c82902bd296e4f1da3188fa446064f8
+ # Parent 947f189a0dfc1ba98770b529559caaa9189ab661
+ Moved a to foo
+
+ diff --git a/a b/foo
+ rename from a
+ rename to foo
+ diff --git a/b b/foobar
+ rename from b
+ rename to foobar
+
+ $ hg mv c wat
+ $ hg unamend
+
+Retained copies in new prdecessor commit
+
+ $ hg exp --git
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 4aebcb77823896c71017dbd19d8b680461dbca93
+ # Parent 947f189a0dfc1ba98770b529559caaa9189ab661
+ Moved a to foo
+
+ diff --git a/a b/foo
+ rename from a
+ rename to foo
+
+Retained copies in working directoy
+
+ $ hg diff --git
+ diff --git a/b b/foobar
+ rename from b
+ rename to foobar
+ diff --git a/c b/wat
+ rename from c
+ rename to wat
diff --git a/hgext/uncommit.py b/hgext/uncommit.py
--- a/hgext/uncommit.py
+++ b/hgext/uncommit.py
@@ -29,6 +29,7 @@
error,
node,
obsolete,
+ obsutil,
pycompat,
registrar,
scmutil,
@@ -194,3 +195,121 @@
with repo.dirstate.parentchange():
repo.dirstate.setparents(newid, node.nullid)
_uncommitdirstate(repo, old, match)
+
+def predecessormarkers(ctx):
+ """yields the obsolete markers marking the given changeset as a successor"""
+ for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
+ yield obsutil.marker(ctx.repo(), data)
+
+def _unamenddirstate(repo, predctx, curctx):
+ """"""
+
+ s = repo.status(predctx, curctx)
+ ds = repo.dirstate
+ copies = dict(ds.copies())
+ for f in s.modified:
+ if ds[f] == 'r':
+ # modified + removed -> removed
+ continue
+ ds.normallookup(f)
+
+ for f in s.added:
+ if ds[f] == 'r':
+ # added + removed -> unknown
+ ds.drop(f)
+ elif ds[f] != 'a':
+ ds.add(f)
+
+ for f in s.removed:
+ if ds[f] == 'a':
+ # removed + added -> normal
+ ds.normallookup(f)
+ elif ds[f] != 'r':
+ ds.remove(f)
+
+ # Merge old parent and old working dir copies
+ oldcopies = {}
+ for f in (s.modified + s.added):
+ src = curctx[f].renamed()
+ if src:
+ oldcopies[f] = src[0]
+ oldcopies.update(copies)
+ copies = dict((dst, oldcopies.get(src, src))
+ for dst, src in oldcopies.iteritems())
+ # Adjust the dirstate copies
+ for dst, src in copies.iteritems():
+ if (src not in predctx or dst in predctx or ds[dst] != 'a'):
+ src = None
+ ds.copy(src, dst)
+
+ at command('^unamend', [])
+def unamend(ui, repo, **opts):
+ """undo the amend operation on a current changeset
+
+ This command will roll back to the previous version of a changeset,
+ leaving working directory in state in which it was before running
+ `hg amend` (e.g. files modified as part of an amend will be
+ marked as modified `hg status`)"""
+
+ unfi = repo.unfiltered()
+
+ # identify the commit from which to unamend
+ curctx = repo['.']
+
+ with repo.wlock(), repo.lock(), repo.transaction('unamend'):
+ if not curctx.mutable():
+ raise error.Abort(_('cannot unamend public changesets'))
+
+ # identify the commit to which to unamend
+ markers = list(predecessormarkers(curctx))
+ if len(markers) != 1:
+ e = _("changeset must have one predecessor, found %i predecessors")
+ raise error.Abort(e % len(markers))
+
+ prednode = markers[0].prednode()
+ predctx = unfi[prednode]
+
+ if curctx.children():
+ raise error.Abort(_("cannot unamend in the middle of a stack"))
+
+ # add an extra so that we get a new hash
+ extras = predctx.extra()
+ extras['unamend_source'] = curctx.node()
+
+ def filectxfn(repo, ctx_, path):
+ try:
+ return predctx.filectx(path)
+ except KeyError:
+ return None
+
+ # Make a new commit same as predctx
+ newctx = context.memctx(repo,
+ parents=(predctx.p1(), predctx.p2()),
+ text=predctx.description(),
+ files=predctx.files(),
+ filectxfn=filectxfn,
+ user=predctx.user(),
+ date=predctx.date(),
+ extra=extras)
+ # phase handling
+ commitphase = curctx.phase()
+ overrides = {('phases', 'new-commit'): commitphase}
+ with repo.ui.configoverride(overrides, 'uncommit'):
+ newprednode = repo.commitctx(newctx)
+
+ newpredctx = repo[newprednode]
+
+ changedfiles = []
+ wctx = repo[None]
+ wm = wctx.manifest()
+ cm = newpredctx.manifest()
+ dirstate = repo.dirstate
+ diff = cm.diff(wm)
+ changedfiles.extend(diff.iterkeys())
+
+ with dirstate.parentchange():
+ dirstate.setparents(newprednode, node.nullid)
+ _unamenddirstate(repo, newpredctx, curctx)
+
+ mapping = {curctx.node(): (newprednode,)}
+ scmutil.cleanupnodes(repo, mapping, 'unamend')
To: pulkit, #hg-reviewers, durham
Cc: ryanmce, singhsrb, durham, mercurial-devel
More information about the Mercurial-devel
mailing list