evolve: add a command to split commits
Before this patch, to split commit one had to use prune. This patch adds a
new command called split that prompts the user interactively to split a given
changeset with record/crecord.
--- a/README Mon Aug 03 11:29:27 2015 -0700
+++ b/README Thu Jun 18 16:48:53 2015 -0700
@@ -51,6 +51,10 @@
Changelog
=========
+5.2.0 --
+
+- split: add a new command to split changesets
+
5.2.0 -- 2015-06-25
- evolve: gain a --rev option to control what revisions to evolve (issue4391)
--- a/hgext/evolve.py Mon Aug 03 11:29:27 2015 -0700
+++ b/hgext/evolve.py Thu Jun 18 16:48:53 2015 -0700
@@ -2554,6 +2554,76 @@
finally:
lockmod.release(lock, wlock)
+@command('^split',
+ [('r', 'rev', [], _("revision to fold")),
+ ] + commitopts + commitopts2,
+ _('hg split [OPTION]... [-r] REV'))
+def cmdsplit(ui, repo, *revs, **opts):
+ """Split the current commit using interactive selection
+
+ By default, split the current revision by prompting for all its hunk to be
+ redistributed into new changesets.
+
+ Use --rev for splitting a given changeset instead.
+ """
+ tr = wlock = lock = None
+ newcommits = []
+
+ revopt = opts.get('rev')
+ if revopt:
+ revs = scmutil.revrange(repo, revopt)
+ if len(revs) != 1:
+ raise util.Abort(_("you can only specify one revision to split"))
+ else:
+ rev = list(revs)[0]
+ commands.update(ui, repo, rev)
+ else:
+ rev = '.'
+
+ try:
+ wlock = repo.wlock()
+ lock = repo.lock()
+ cmdutil.bailifchanged(repo)
+ tr = repo.transaction('split')
+ ctx = repo[rev]
+ r = ctx.rev()
+ disallowunstable = not obsolete.isenabled(repo,
+ obsolete.allowunstableopt)
+ if disallowunstable:
+ # XXX We should check head revs
+ if repo.revs("(%d::) - %d", rev, rev):
+ raise util.Abort(_("cannot split commit: %s not a head" % ctx))
+
+ if len(ctx.parents()) > 1:
+ raise util.Abort(_("cannot split merge commits"))
+ prev = ctx.p1()
+ hg.update(repo, prev)
+
+ commands.revert(ui, repo, rev=r, all=True)
+ def haschanges():
+ modified, added, removed, deleted = repo.status()[:4]
+ return modified or added or removed or deleted
+ while haschanges():
+ pats = ()
+ cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
+ cmdutil.recordfilter, *pats, **opts)
+ # TODO: Does no seem like the best way to do this
+ # We should make dorecord return the newly created commit
+ newcommits.append(repo['.'])
+ if haschanges():
+ if ui.prompt('Done splitting? [yN]', default='n') == 'y':
+ commands.commit(ui, repo, **opts)
+ newcommits.append(repo['.'])
+ break
+ else:
+ ui.status("no more change to split\n")
+
+ obsolete.createmarkers(repo, [(repo[r], newcommits)])
+ tr.close()
+ finally:
+ lockmod.release(tr, lock, wlock)
+
+
@eh.wrapcommand('strip', extension='strip', opts=[
('', 'bundle', None, _("delete the commit entirely and move it to a "
"backup bundle")),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-split.t Thu Jun 18 16:48:53 2015 -0700
@@ -0,0 +1,193 @@
+test of the split command
+-----------------------
+
+ $ cat >> $HGRCPATH <<EOF
+ > [defaults]
+ > amend=-d "0 0"
+ > fold=-d "0 0"
+ > split=-d "0 0"
+ > amend=-d "0 0"
+ > [web]
+ > push_ssl = false
+ > allow_push = *
+ > [phases]
+ > publish = False
+ > [diff]
+ > git = 1
+ > unified = 0
+ > [ui]
+ > interactive = true
+ > [extensions]
+ > hgext.graphlog=
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+ $ mkcommit() {
+ > echo "$1" > "$1"
+ > hg add "$1"
+ > hg ci -m "add $1"
+ > }
+
+
+Basic case, split a head
+ $ hg init testsplit
+ $ cd testsplit
+ $ mkcommit _a
+ $ mkcommit _b
+ $ mkcommit _c
+ $ mkcommit _d
+ $ echo "change to a" >> _a
+ $ hg amend
+ $ hg debugobsolete
+ 9e84a109b8eb081ad754681ee4b1380d17a3741f aa8f656bb307022172d2648be6fb65322f801225 0 (*) {'user': 'test'} (glob)
+ f002b57772d7f09b180c407213ae16d92996a988 0 {9e84a109b8eb081ad754681ee4b1380d17a3741f} (*) {'user': 'test'} (glob)
+
+To create commits with the number of split
+ $ export NUM=0
+ $ export HGEDITOR="NUM=$((NUM+1)); echo split$NUM > $1"
+ $ hg split << EOF
+ > y
+ > y
+ > y
+ > n
+ > N
+ > y
+ > y
+ > EOF
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ reverting _a
+ adding _d
+ diff --git a/_a b/_a
+ 1 hunks, 1 lines changed
+ examine changes to '_a'? [Ynesfdaq?] y
+
+ @@ -1,0 +2,1 @@
+ +change to a
+ record change 1/2 to '_a'? [Ynesfdaq?] y
+
+ diff --git a/_d b/_d
+ new file mode 100644
+ examine changes to '_d'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +_d
+ record change 2/2 to '_d'? [Ynesfdaq?] n
+
+ created new head
+ Done splitting? [yN] N
+ diff --git a/_d b/_d
+ new file mode 100644
+ examine changes to '_d'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +_d
+ record this change to '_d'? [Ynesfdaq?] y
+
+ no more change to split
+
+ $ hg debugobsolete
+ 9e84a109b8eb081ad754681ee4b1380d17a3741f aa8f656bb307022172d2648be6fb65322f801225 0 (*) {'user': 'test'} (glob)
+ f002b57772d7f09b180c407213ae16d92996a988 0 {9e84a109b8eb081ad754681ee4b1380d17a3741f} (*) {'user': 'test'} (glob)
+ aa8f656bb307022172d2648be6fb65322f801225 8a76f55839e6badd47ed8338803d8bc16f578d68 1e105584671a463974ee2122f95979ce5e507f1a 0 (*) {'user': 'test'} (glob)
+
+Cannot split a commit with uncommited changes
+ $ hg up "desc(_c)"
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo "_cd" > _c
+ $ hg split
+ abort: uncommitted changes
+ [255]
+
+Split a revision specified with -r
+ $ hg up "desc(_c)" -C
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo "change to b" >> _b
+ $ hg amend -m "_cprim"
+ 2 new unstable changesets
+ $ hg evolve --all
+ move:[6] split0
+ atop:[9] _cprim
+ move:[7] split0
+ atop:[10] split0
+ working directory is now at * (glob)
+ $ hg log -r "desc(_cprim)" -v -p
+ changeset: 9:719157b217ac
+ parent: 1:37445b16603b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ files: _b _c
+ description:
+ _cprim
+
+
+ diff --git a/_b b/_b
+ --- a/_b
+ +++ b/_b
+ @@ -1,0 +2,1 @@
+ +change to b
+ diff --git a/_c b/_c
+ new file mode 100644
+ --- /dev/null
+ +++ b/_c
+ @@ -0,0 +1,1 @@
+ +_c
+
+ $ hg split -r "desc(_cprim)" <<EOF
+ > y
+ > y
+ > y
+ > n
+ > y
+ > EOF
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ reverting _b
+ adding _c
+ diff --git a/_b b/_b
+ 1 hunks, 1 lines changed
+ examine changes to '_b'? [Ynesfdaq?] y
+
+ @@ -1,0 +2,1 @@
+ +change to b
+ record change 1/2 to '_b'? [Ynesfdaq?] y
+
+ diff --git a/_c b/_c
+ new file mode 100644
+ examine changes to '_c'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +_c
+ record change 2/2 to '_c'? [Ynesfdaq?] n
+
+ created new head
+ Done splitting? [yN] y
+
+Stop before splitting the commit completely creates a commit with all the
+remaining changes
+
+ $ hg debugobsolete
+ 9e84a109b8eb081ad754681ee4b1380d17a3741f aa8f656bb307022172d2648be6fb65322f801225 0 (*) {'user': 'test'} (glob)
+ f002b57772d7f09b180c407213ae16d92996a988 0 {9e84a109b8eb081ad754681ee4b1380d17a3741f} (*) {'user': 'test'} (glob)
+ aa8f656bb307022172d2648be6fb65322f801225 8a76f55839e6badd47ed8338803d8bc16f578d68 1e105584671a463974ee2122f95979ce5e507f1a 0 (*) {'user': 'test'} (glob)
+ 10200229058723ce8d67f6612c1f6b4f73b1fe73 719157b217acc43d397369a448824ed4c7a302f2 0 (*) {'user': 'test'} (glob)
+ 5d0c8b0f2d3e5e1ff95f93d7da2ba06650605ab5 0 {10200229058723ce8d67f6612c1f6b4f73b1fe73} (*) {'user': 'test'} (glob)
+ 8a76f55839e6badd47ed8338803d8bc16f578d68 0ea1d0d23e674ea8a6affe760741c82bb8e380f7 0 (*) {'user': 'test'} (glob)
+ 1e105584671a463974ee2122f95979ce5e507f1a b6099ccb49cae181af7c59ed5603a1dfca632445 0 (*) {'user': 'test'} (glob)
+ 719157b217acc43d397369a448824ed4c7a302f2 8eb71353cb2c70fc1154be3af79c0ce98898ae88 ead904640c8543606f72490c6ae10955fb11fff0 0 (*) {'user': 'test'} (glob)
+
+Cannot specify multiple revisions with -r
+ $ hg split -r "desc(_a)::"
+ abort: you can only specify one revision to split
+ [255]
+
+Cannot split a commit that is not a head if instability is not allowed
+ $ cat >> $HGRCPATH <<EOF
+ > [experimental]
+ > evolution=createmarkers
+ > evolutioncommands=split
+ > EOF
+ $ hg split -r "desc(_c)"
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ abort: cannot split commit: 719157b217ac not a head
+ [255]
+
+