D6005: uncommit: added interactive mode -i(issue6062)
taapas1128 (Taapas Agrawal)
phabricator at mercurial-scm.org
Mon Mar 18 14:38:10 EDT 2019
taapas1128 updated this revision to Diff 14547.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D6005?vs=14546&id=14547
REVISION DETAIL
https://phab.mercurial-scm.org/D6005
AFFECTED FILES
hgext/uncommit.py
mercurial/crecord.py
mercurial/patch.py
tests/test-uncommit-interactive.t
CHANGE DETAILS
diff --git a/tests/test-uncommit-interactive.t b/tests/test-uncommit-interactive.t
new file mode 100644
--- /dev/null
+++ b/tests/test-uncommit-interactive.t
@@ -0,0 +1,969 @@
+================================================
+|| The test for `hg uncommit --interactive` ||
+================================================
+
+Repo Setup
+============
+
+ $ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > interactive = true
+ > [experimental]
+ > evolution.createmarkers=True
+ > evolution.allowunstable=True
+ > uncommitondirtywdir = true
+ > [extensions]
+ > uncommit =
+ > amend =
+ > drawdag=$TESTDIR/drawdag.py
+ > EOF
+ $ glog() {
+ > hg log -G --template '{rev}:{node|short}@{branch}({separate("/", obsolete, phase)}) {desc|firstline}\n' "$@"
+ > }
+
+ $ hg init repo
+ $ cd repo
+
+ $ touch a
+ $ cat >> a << EOF
+ > 1
+ > 2
+ > 3
+ > 4
+ > 5
+ > EOF
+
+ $ hg add a
+ $ hg ci -m "The base commit"
+
+Make sure aborting the interactive selection does no magic
+----------------------------------------------------------
+
+ $ hg status
+ $ hg uncommit -i<<EOF
+ > q
+ > EOF
+ diff --git a/a b/a
+ new file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] q
+
+ abort: user quit
+ [255]
+ $ hg status
+
+Make a commit with multiple hunks
+---------------------------------
+
+ $ cat > a << EOF
+ > -2
+ > -1
+ > 0
+ > 1
+ > 2
+ > 3
+ > foo
+ > bar
+ > 4
+ > 5
+ > babar
+ > EOF
+
+ $ hg diff
+ diff -r 7733902a8d94 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,11 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ +babar
+
+ $ hg ci -m "another one"
+
+Not selecting anything to uncommit
+==================================
+
+ $ hg uncommit -i<<EOF
+ > y
+ > n
+ > n
+ > n
+ > EOF
+ diff --git a/a b/a
+ 3 hunks, 6 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ uncommit change 1/3 to 'a'? [Ynesfdaq?] n
+
+ @@ -1,5 +4,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ uncommit change 2/3 to 'a'? [Ynesfdaq?] n
+
+ @@ -4,2 +9,3 @@
+ 4
+ 5
+ +babar
+ uncommit change 3/3 to 'a'? [Ynesfdaq?] n
+
+ abort: nothing selected to uncommit
+ [255]
+ $ hg status
+
+Uncommit a chunk
+================
+
+ $ hg uncommit -i<<EOF
+ > y
+ > y
+ > n
+ > n
+ > EOF
+ diff --git a/a b/a
+ 3 hunks, 6 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ uncommit change 1/3 to 'a'? [Ynesfdaq?] y
+
+ @@ -1,5 +4,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ uncommit change 2/3 to 'a'? [Ynesfdaq?] n
+
+ @@ -4,2 +9,3 @@
+ 4
+ 5
+ +babar
+ uncommit change 3/3 to 'a'? [Ynesfdaq?] n
+
+
+ $ hg log -G --hidden
+ @ changeset: 3:aa6c421e9e28
+ | tag: tip
+ | parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: another one
+ |
+ | x changeset: 2:557dde779d03
+ |/ parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 3:aa6c421e9e28
+ | summary: temporary commit for uncommiting f70fb463d5bf9f0ffd38f105521d96e9f2591bc1
+ |
+ | x changeset: 1:f70fb463d5bf
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 3:aa6c421e9e28
+ | summary: another one
+ |
+ o changeset: 0:7733902a8d94
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: The base commit
+
+The unselected part should be in the diff
+-----------------------------------------
+
+ $ hg diff
+ diff -r aa6c421e9e28 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+
+The commit should contain the rest of part
+------------------------------------------
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID aa6c421e9e28164a60bdc19ec0ed4c39e5cfa65c
+ # Parent 7733902a8d94c789ca81d866bea1893d79442db6
+ another one
+
+ diff -r 7733902a8d94 -r aa6c421e9e28 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,8 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ +babar
+
+Uncommiting on dirty working directory
+======================================
+
+ $ hg status
+ M a
+ $ hg diff
+ diff -r aa6c421e9e28 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+
+ $ hg uncommit -i<<EOF
+ > y
+ > n
+ > y
+ > EOF
+ diff --git a/a b/a
+ 2 hunks, 3 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,5 +1,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ uncommit change 1/2 to 'a'? [Ynesfdaq?] n
+
+ @@ -4,2 +6,3 @@
+ 4
+ 5
+ +babar
+ uncommit change 2/2 to 'a'? [Ynesfdaq?] y
+
+ patching file a
+ Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
+
+ $ hg diff
+ diff -r 32f76305dd23 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ @@ -5,3 +8,4 @@
+ bar
+ 4
+ 5
+ +babar
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 32f76305dd23a70de61867137744bfff0abc1344
+ # Parent 7733902a8d94c789ca81d866bea1893d79442db6
+ another one
+
+ diff -r 7733902a8d94 -r 32f76305dd23 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+
+Checking the obsolescence history
+
+ $ hg log -G --hidden
+ @ changeset: 5:32f76305dd23
+ | tag: tip
+ | parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: another one
+ |
+ | x changeset: 4:c983140931c0
+ |/ parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 5:32f76305dd23
+ | summary: temporary commit for uncommiting aa6c421e9e28164a60bdc19ec0ed4c39e5cfa65c
+ |
+ | x changeset: 3:aa6c421e9e28
+ |/ parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 5:32f76305dd23
+ | summary: another one
+ |
+ | x changeset: 2:557dde779d03
+ |/ parent: 0:7733902a8d94
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 3:aa6c421e9e28
+ | summary: temporary commit for uncommiting f70fb463d5bf9f0ffd38f105521d96e9f2591bc1
+ |
+ | x changeset: 1:f70fb463d5bf
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | obsolete: rewritten using uncommit as 3:aa6c421e9e28
+ | summary: another one
+ |
+ o changeset: 0:7733902a8d94
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: The base commit
+
+Push the changes back to the commit and more commits for more testing
+
+ $ hg amend
+ $ glog
+ @ 6:c539671b1fd6 at default(draft) another one
+ |
+ o 0:7733902a8d94 at default(draft) The base commit
+
+ $ touch foo
+ $ echo "hey" >> foo
+ $ hg ci -Am "Added foo"
+ adding foo
+
+Testing uncommiting a whole changeset and also for a file addition
+==================================================================
+
+ $ hg uncommit -i<<EOF
+ > y
+ > y
+ > EOF
+ diff --git a/foo b/foo
+ new file mode 100644
+ examine changes to 'foo'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +hey
+ uncommit this change to 'foo'? [Ynesfdaq?] y
+
+
+ $ hg status
+ A foo
+ $ hg diff
+ diff -r aa21648408ea foo
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/foo Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +hey
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID aa21648408ea8a4e32d1b45cb72d70dfbfb0c24d
+ # Parent c539671b1fd601f2fb1da1141ef23d7198478c8a
+ Added foo
+
+ $ hg amend
+
+Testing to uncommit removed files completely
+============================================
+
+ $ hg rm a
+ $ hg ci -m "Removed a"
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 56f9164c2dc8359aabcc4522c7b6382d2e26ed90
+ # Parent 1888d4695f614750ec3bb4f97cdd8a9ca86f0fb3
+ Removed a
+
+ diff -r 1888d4695f61 -r 56f9164c2dc8 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,11 +0,0 @@
+ --2
+ --1
+ -0
+ -1
+ -2
+ -3
+ -foo
+ -bar
+ -4
+ -5
+ -babar
+
+Not examining the file
+----------------------
+
+ $ hg uncommit -i<<EOF
+ > n
+ > EOF
+ diff --git a/a b/a
+ deleted file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] n
+
+ abort: nothing selected to uncommit
+ [255]
+
+Examining the file
+------------------
+XXX: there is a bug in interactive selection as it is not letting to examine the
+file. Tried with curses too. In the curses UI, if you just unselect the hunks
+and the not file mod thing at the top, it will show the same "nothing unselected
+to uncommit" message which is a bug in interactive selection.
+
+ $ hg uncommit -i<<EOF
+ > y
+ > EOF
+ diff --git a/a b/a
+ deleted file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] y
+
+
+ $ hg diff
+ diff -r 8a1b6b8b3d24 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,11 +0,0 @@
+ --2
+ --1
+ -0
+ -1
+ -2
+ -3
+ -foo
+ -bar
+ -4
+ -5
+ -babar
+ $ hg status
+ R a
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 8a1b6b8b3d24c125e1c4cd3966918d26e77c9b42
+ # Parent 1888d4695f614750ec3bb4f97cdd8a9ca86f0fb3
+ Removed a
+
+
+ $ hg prune .
+ hg: unknown command 'prune'
+ (use 'hg help' for a list of commands)
+ [255]
+ $ hg revert --all
+ undeleting a
+
+ $ glog
+ @ 13:8a1b6b8b3d24 at default(draft) Removed a
+ |
+ o 10:1888d4695f61 at default(draft) Added foo
+ |
+ o 6:c539671b1fd6 at default(draft) another one
+ |
+ o 0:7733902a8d94 at default(draft) The base commit
+
+
+Testing when a new file is added in the last commit
+===================================================
+
+ $ echo "foo" >> foo
+ $ touch x
+ $ echo "abcd" >> x
+ $ hg add x
+ $ hg ci -m "Added x"
+ $ hg uncommit -i<<EOF
+ > y
+ > y
+ > y
+ > n
+ > EOF
+ diff --git a/foo b/foo
+ 1 hunks, 1 lines changed
+ examine changes to 'foo'? [Ynesfdaq?] y
+
+ @@ -1,1 +1,2 @@
+ hey
+ +foo
+ uncommit change 1/2 to 'foo'? [Ynesfdaq?] y
+
+ diff --git a/x b/x
+ new file mode 100644
+ examine changes to 'x'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +abcd
+ uncommit change 2/2 to 'x'? [Ynesfdaq?] n
+
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 039cbfbd600d0aab7f464c2eb8489af60e0385f5
+ # Parent 8a1b6b8b3d24c125e1c4cd3966918d26e77c9b42
+ Added x
+
+ diff -r 8a1b6b8b3d24 -r 039cbfbd600d x
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/x Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +abcd
+
+ $ hg diff
+ diff -r 039cbfbd600d foo
+ --- a/foo Thu Jan 01 00:00:00 1970 +0000
+ +++ b/foo Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,1 +1,2 @@
+ hey
+ +foo
+
+ $ hg status
+ M foo
+
+ $ hg revert --all
+ reverting foo
+
+Testing between the stack and with dirty working copy
+=====================================================
+
+ $ glog
+ @ 16:039cbfbd600d at default(draft) Added x
+ |
+ o 13:8a1b6b8b3d24 at default(draft) Removed a
+ |
+ o 10:1888d4695f61 at default(draft) Added foo
+ |
+ o 6:c539671b1fd6 at default(draft) another one
+ |
+ o 0:7733902a8d94 at default(draft) The base commit
+
+ $ hg up c539671b1fd6
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+
+ $ touch bar
+ $ echo "foo" >> bar
+ $ hg add bar
+ $ hg status
+ A bar
+ ? foo.orig
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID c539671b1fd601f2fb1da1141ef23d7198478c8a
+ # Parent 7733902a8d94c789ca81d866bea1893d79442db6
+ another one
+
+ diff -r 7733902a8d94 -r c539671b1fd6 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,11 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ +babar
+
+ $ hg uncommit -i<<EOF
+ > y
+ > n
+ > n
+ > y
+ > EOF
+ diff --git a/a b/a
+ 3 hunks, 6 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ uncommit change 1/3 to 'a'? [Ynesfdaq?] n
+
+ @@ -1,5 +4,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ uncommit change 2/3 to 'a'? [Ynesfdaq?] n
+
+ @@ -4,2 +9,3 @@
+ 4
+ 5
+ +babar
+ uncommit change 3/3 to 'a'? [Ynesfdaq?] y
+
+ patching file a
+ Hunk #1 succeeded at 1 with fuzz 1 (offset -1 lines).
+ 3 new orphan changesets
+
+ $ hg diff
+ diff -r a2ee0cccc073 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -8,3 +8,4 @@
+ bar
+ 4
+ 5
+ +babar
+ diff -r a2ee0cccc073 bar
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/bar Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +foo
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID a2ee0cccc0738cdcd2ba75e8b418e1b489ab6aa9
+ # Parent 7733902a8d94c789ca81d866bea1893d79442db6
+ another one
+
+ diff -r 7733902a8d94 -r a2ee0cccc073 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,10 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ $ hg status
+ M a
+ A bar
+ ? foo.orig
+
+More uncommit on the same dirty working copy
+=============================================
+
+ $ hg uncommit -i<<EOF
+ > y
+ > y
+ > n
+ > EOF
+ diff --git a/a b/a
+ 2 hunks, 5 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ uncommit change 1/2 to 'a'? [Ynesfdaq?] y
+
+ @@ -1,5 +4,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+ uncommit change 2/2 to 'a'? [Ynesfdaq?] n
+
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 975a17b4321a7bd11286bbb1ce7cc5012806fb4a
+ # Parent 7733902a8d94c789ca81d866bea1893d79442db6
+ another one
+
+ diff -r 7733902a8d94 -r 975a17b4321a a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,5 +1,7 @@
+ 1
+ 2
+ 3
+ +foo
+ +bar
+ 4
+ 5
+
+ $ hg diff
+ diff -r 975a17b4321a a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,3 +1,6 @@
+ +-2
+ +-1
+ +0
+ 1
+ 2
+ 3
+ @@ -5,3 +8,4 @@
+ bar
+ 4
+ 5
+ +babar
+ diff -r 975a17b4321a bar
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/bar Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +foo
+
+ $ hg status
+ M a
+ A bar
+ ? foo.orig
+
+Interactive uncommit with a pattern
+-----------------------------------
+
+(more setup)
+
+ $ hg ci -m 'roaming changes'
+ $ cat > b << EOF
+ > a
+ > b
+ > c
+ > d
+ > e
+ > f
+ > h
+ > EOF
+ $ hg add b
+ $ hg ci -m 'add b'
+ $ echo 'celeste' >> a
+ $ echo 'i' >> b
+ $ hg ci -m 'some more changes'
+ $ hg export
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 2cca6971430342440b2141c347d1aae43a233b52
+ # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18
+ some more changes
+
+ diff -r eb07567cdf3c -r 2cca69714303 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -9,3 +9,4 @@
+ 4
+ 5
+ babar
+ +celeste
+ diff -r eb07567cdf3c -r 2cca69714303 b
+ --- a/b Thu Jan 01 00:00:00 1970 +0000
+ +++ b/b Thu Jan 01 00:00:00 1970 +0000
+ @@ -5,3 +5,4 @@
+ e
+ f
+ h
+ +i
+
+ $ hg uncommit -i a << DONE
+ > y
+ > y
+ > DONE
+ diff --git a/a b/a
+ 1 hunks, 1 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -9,3 +9,4 @@
+ 4
+ 5
+ babar
+ +celeste
+ uncommit this change to 'a'? [Ynesfdaq?] y
+
+ $ hg status
+ M a
+ ? foo.orig
+ $ hg diff
+ diff -r 073013c9d352 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -9,3 +9,4 @@
+ 4
+ 5
+ babar
+ +celeste
+ $ hg export
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID 073013c9d352701f96f3b1fd67c95ea8ff718e6a
+ # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18
+ some more changes
+
+ diff -r eb07567cdf3c -r 073013c9d352 b
+ --- a/b Thu Jan 01 00:00:00 1970 +0000
+ +++ b/b Thu Jan 01 00:00:00 1970 +0000
+ @@ -5,3 +5,4 @@
+ e
+ f
+ h
+ +i
+
+(reset)
+
+ $ cat << EOF > a
+ > -3
+ > -2
+ > -1
+ > 0
+ > 1
+ > 2
+ > 3
+ > foo
+ > bar
+ > 4
+ > 5
+ > babar
+ > celeste
+ > EOF
+ $ hg amend
+
+Same but do not select some change in 'a'
+
+ $ hg uncommit -i a << DONE
+ > y
+ > y
+ > n
+ > DONE
+ diff --git a/a b/a
+ 2 hunks, 2 lines changed
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -1,3 +1,4 @@
+ +-3
+ -2
+ -1
+ 0
+ uncommit change 1/2 to 'a'? [Ynesfdaq?] y
+
+ @@ -9,3 +10,4 @@
+ 4
+ 5
+ babar
+ +celeste
+ uncommit change 2/2 to 'a'? [Ynesfdaq?] n
+
+ $ hg status
+ M a
+ ? foo.orig
+
+ $ hg diff
+ diff -r db62d8510875 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -1,3 +1,4 @@
+ +-3
+ -2
+ -1
+ 0
+
+ $ hg export
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID db62d8510875ea464d5287373946095971bfa056
+ # Parent eb07567cdf3c269f97ec3dcaa53bc32558016f18
+ some more changes
+
+ diff -r eb07567cdf3c -r db62d8510875 a
+ --- a/a Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -9,3 +9,4 @@
+ 4
+ 5
+ babar
+ +celeste
+ diff -r eb07567cdf3c -r db62d8510875 b
+ --- a/b Thu Jan 01 00:00:00 1970 +0000
+ +++ b/b Thu Jan 01 00:00:00 1970 +0000
+ @@ -5,3 +5,4 @@
+ e
+ f
+ h
+ +i
+
+ $ cat b
+ a
+ b
+ c
+ d
+ e
+ f
+ h
+ i
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -1012,11 +1012,13 @@
'multiple': {
'apply': _("apply change %d/%d to '%s'?"),
'discard': _("discard change %d/%d to '%s'?"),
+ 'uncommit': _("uncommit change %d/%d to '%s'?"),
'record': _("record change %d/%d to '%s'?"),
},
'single': {
'apply': _("apply this change to '%s'?"),
'discard': _("discard this change to '%s'?"),
+ 'uncommit': _("uncommit this change to '%s'?"),
'record': _("record this change to '%s'?"),
},
'help': {
@@ -1040,6 +1042,16 @@
'$$ Discard &all changes to all remaining files'
'$$ &Quit, discarding no changes'
'$$ &? (display help)'),
+ 'uncommit': _('[Ynesfdaq?]'
+ '$$ &Yes, record this change'
+ '$$ &No, skip this change'
+ '$$ &Edit this change manually'
+ '$$ &Skip remaining changes to this file'
+ '$$ Record remaining changes to this &file'
+ '$$ &Done, skip remaining changes and files'
+ '$$ Record &all changes to all remaining files'
+ '$$ &Quit, recording no changes'
+ '$$ &? (display help)'),
'record': _('[Ynesfdaq?]'
'$$ &Yes, record this change'
'$$ &No, skip this change'
diff --git a/mercurial/crecord.py b/mercurial/crecord.py
--- a/mercurial/crecord.py
+++ b/mercurial/crecord.py
@@ -566,6 +566,7 @@
_headermessages = { # {operation: text}
'apply': _('Select hunks to apply'),
'discard': _('Select hunks to discard'),
+ 'uncommit': _('Select hunks to uncommit'),
None: _('Select hunks to record'),
}
diff --git a/hgext/uncommit.py b/hgext/uncommit.py
--- a/hgext/uncommit.py
+++ b/hgext/uncommit.py
@@ -28,11 +28,14 @@
copies as copiesmod,
error,
node,
+ obsolete,
obsutil,
+ patch,
pycompat,
registrar,
rewriteutil,
scmutil,
+ util,
)
cmdtable = {}
@@ -48,6 +51,8 @@
default=False,
)
+stringio = util.stringio
+
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
@@ -139,7 +144,9 @@
ds.copy(src, dst)
@command('uncommit',
- [('', 'keep', None, _('allow an empty commit after uncommiting')),
+ [('i', 'interactive', None,
+ _('interactively select which chunks to apply (EXPERIMENTAL)')),
+ ('', 'keep', None, _('allow an empty commit after uncommiting')),
('', 'allow-dirty-working-copy', False,
_('allow uncommit with outstanding changes'))
] + commands.walkopts,
@@ -157,6 +164,7 @@
given.
"""
opts = pycompat.byteskwargs(opts)
+ interactive = opts.get('interactive')
with repo.wlock(), repo.lock():
@@ -174,13 +182,19 @@
with repo.transaction('uncommit'):
match = scmutil.match(old, pats, opts)
+ #TODO: pass keep to _interactiveuncommit() and use it there
keepcommit = pats
if not keepcommit:
if opts.get('keep') is not None:
keepcommit = opts.get('keep')
else:
keepcommit = ui.configbool('experimental', 'uncommit.keep')
- newid = _commitfiltered(repo, old, match, keepcommit)
+
+ if interactive:
+ newid = _interactiveuncommit(ui, repo, old, match)
+ else:
+ newid = _commitfiltered(repo, old, match, keepcommit)
+
if newid is None:
ui.status(_("nothing to uncommit\n"))
return 1
@@ -194,10 +208,104 @@
mapping[old.node()] = ()
with repo.dirstate.parentchange():
+ repo.dirstate.setparents(newid, node.nullid)
_fixdirstate(repo, old, repo[newid], match)
scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True)
+def _interactiveuncommit(ui, repo, old, match):
+ """Makes a temporary commit with the chunks which user selected to uncommit
+ After that the diff of the parent and that commit is applied to the working
+ directory and committed again which results in the new commit which should
+ be one after uncommitted.
+ """
+
+ # create a temporary commit with hunks user selected
+ tempnode = _createtempcommit(ui, repo, old, match)
+
+ diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
+ diffopts.nodates = True
+ diffopts.git = True
+ fp = stringio()
+ for chunk, label in patch.diffui(repo, tempnode, old.node(), None,
+ opts=diffopts):
+ fp.write(chunk)
+
+ fp.seek(0)
+ newnode = _patchtocommit(ui, repo, old, fp)
+ mapping = {tempnode: (newnode,)}
+ scmutil.cleanupnodes(repo, mapping, 'uncommit',fixphase=True)
+ return newnode
+
+def _createtempcommit(ui, repo, old, match):
+ """Creates a temporary commit for `uncommit --interative` which contains
+ the hunks which were selected by the user to uncommit.
+ """
+
+ pold = old.p1()
+ # The logic to interactively selecting something copied from
+ # cmdutil.revert()
+ diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
+ diffopts.nodates = True
+ diffopts.git = True
+ diff = patch.diff(repo, pold.node(), old.node(), match, opts=diffopts)
+ originalchunks = patch.parsepatch(diff)
+ # XXX: The interactive selection is buggy and does not let you
+ # uncommit a removed file partially.
+ # to add uncommit as an operation taking care of BC.
+ chunks, opts = cmdutil.recordfilter(repo.ui, originalchunks,
+ operation='uncommit')
+ if not chunks:
+ raise error.Abort(_("nothing selected to uncommit"))
+ fp = stringio()
+ for c in chunks:
+ c.write(fp)
+
+ fp.seek(0)
+ oldnode = old.hex()
+ message = 'temporary commit for uncommiting %s' % oldnode
+ tempnode = _patchtocommit(ui, repo, old, fp, message, oldnode)
+ return tempnode
+
+def _patchtocommit(ui, repo, old, fp, message=None, oldnode=None):
+ """Applies the patch to the working directory and make a commit whose
+ parents are same as that of old argument. The message argument tells us
+ whether to use the message of the old commit or a different message which
+ is passed. Returns the node of new commit made.
+ """
+ pold = old.p1()
+ parents = (old.p1().node(), old.p2().node())
+ date = old.date()
+ branch = old.branch()
+ user = old.user()
+ extra = old.extra()
+ if oldnode:
+ extra['uncommit_source'] = oldnode
+ if not message:
+ message = old.description()
+ store = patch.filestore()
+ try:
+ files = set()
+ try:
+ patch.patchrepo(ui, repo, pold, store, fp, 1, '',
+ files=files, eolmode=None)
+ except patch.PatchError as err:
+ raise error.Abort(str(err))
+
+ finally:
+ del fp
+
+ memctx = context.memctx(repo, parents, message, files=files,
+ filectxfn=store,
+ user=user,
+ date=date,
+ branch=branch,
+ extra=extra)
+ newcm = memctx.commit()
+ finally:
+ store.close()
+ return newcm
+
def predecessormarkers(ctx):
"""yields the obsolete markers marking the given changeset as a successor"""
for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
To: taapas1128, #hg-reviewers
Cc: ryanmce, spectral, pulkit, martinvonz, mercurial-devel
More information about the Mercurial-devel
mailing list