[PATCH evolve-ext] evolve: add a command to split commits

Pierre-Yves David pierre-yves.david at ens-lyon.org
Fri Aug 7 13:09:22 CDT 2015



On 08/07/2015 10:53 AM, Laurent Charignon wrote:
> # HG changeset patch
> # User Laurent Charignon <lcharignon at fb.com>
> # Date 1434671333 25200
> #      Thu Jun 18 16:48:53 2015 -0700
> # Node ID ec33aec0415556a456660e09ad61094c34aacb04
> # Parent  8e6de39b724d854cb92d2de3a0472ffb03627034
> 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.
>
> diff --git a/hgext/evolve.py b/hgext/evolve.py
> --- a/hgext/evolve.py
> +++ b/hgext/evolve.py
> @@ -2554,6 +2554,75 @@ def commitwrapper(orig, ui, repo, *arg,
>       finally:
>           lockmod.release(lock, wlock)
>
> + at command('^split',
> +    [('r', 'rev', [], _("revision to fold")),
> +    ] + commitopts + commitopts2,
> +    _('hg split [OPTION]... [-r] REV'))
> +def split(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
> +    cmdutil.bailifchanged(repo)
> +    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()
> +        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)

I would be happier if we could do all the diffing, recording and 
patching directly in memory. But I'm fine with this as a first step. 
However, if you do an update here, you have to check if the working copy 
has any change or this can result in some butchery.

cf mercurial.cmdutil.bailifchanged


> +
> +        commands.revert(ui, repo, rev=r, all=True)
> +        def haschanges():
> +            return len(list(patch.diff(repo))) != 0

We usually use status for such check.

cf mercurial.cmdutil.bailifchanged

> +        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")),
> diff --git a/tests/test-split.t b/tests/test-split.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-split.t
> @@ -0,0 +1,184 @@
> +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"

You probably want to document (or some pointeur) to the NUM logic.



-- 
Pierre-Yves David


More information about the Mercurial-devel mailing list