[PATCH 3 of 3 evolve-ext RFC] commands: introduce a new command to edit commit metadata

Laurent Charignon lcharignon at fb.com
Fri Mar 11 16:51:07 EST 2016


Hi,

I put two comments inline for things we could do differently. Let me know
what you think.

It took me a while to understand the purpose of this series.
Maybe we could add some context to the commit message, for example:

"This patch introduces metaedit, a command to metadata of a set of
revisions without updating working copy.
It also supports folding a stack in the same operation.
This is particularily relevant for repository where changing the parent of
the working copy is time-consumming.
We could add more stack manipulation operations to metaedit in the future."

Thanks!


Laurent

On 3/10/16, 1:24 AM, "Mercurial-devel on behalf of Siddharth Agarwal"
<mercurial-devel-bounces at mercurial-scm.org on behalf of sid0 at fb.com> wrote:

># HG changeset patch
># User Siddharth Agarwal <sid0 at fb.com>
># Date 1457601780 28800
>#      Thu Mar 10 01:23:00 2016 -0800
># Branch stable
># Node ID baf6b4129735a703fd356077e0c73c49c0d13f20
># Parent  acb48e8d739cec3b824a088843d8c432a34d2fd6
>commands: introduce a new command to edit commit metadata
>
>The eventual goal of this command is to allow editing one or more
>changesets'
>metadata without being on the particular changeset.
>
>This implementation is by no means complete, but it does allow folding
>multiple
>commits into one. Crucially, it is different from 'hg fold --exact' in
>that it
>also allows 'folding' a single commit and rewriting its metadata. This is
>really useful to have as a single logical operation, for example while
>preparing a series of multiple local changesets that will need to be
>pushed as
>a single changeset.
>
>diff --git a/hgext/evolve.py b/hgext/evolve.py
>--- a/hgext/evolve.py
>+++ b/hgext/evolve.py
>@@ -2997,6 +2997,127 @@ def fold(ui, repo, *revs, **opts):
>     finally:
>         lockmod.release(lock, wlock)
> 
>+ at command('^metaedit',
>+         [('r', 'rev', [], _("revisions to edit")),
>+          ('', 'fold', None, _("also fold specified revisions into one"))
>+         ] + commitopts + commitopts2,
>+         _('hg metaedit [OPTION]... [-r] [REV]'))
>+def metaedit(ui, repo, *revs, **opts):
>+    """edit commit information
>+
>+    Edits the commit information for the specified revisions. By
>default, edits
>+    commit information for the working directory parent.
>+
>+    With --fold, also folds multiple revisions into one if necessary. In
>this
>+    case, the given revisions must form a linear unbroken chain.
>+
>+    .. container:: verbose
>+
>+     Some examples:
>+
>+     - Edit the commit message for the working directory parent::
>+
>+         hg metaedit
>+
>+     - Change the username for the working directory parent::
>+
>+         hg metaedit --user 'New User <new-email at example.com>'
>+
>+     - Combine all draft revisions that are ancestors of foo but not of
>@ into
>+       one::
>+
>+         hg metaedit --fold 'draft() and only(foo,@)'
>+
>+       See :hg:`help phases` for more about draft revisions and
>+       :hg:`help revsets` for more about the `draft()` and `only()`
>keywords
>+    """
>+    revs = list(revs)
>+    revs.extend(opts['rev'])
>+    if not revs:
>+        if opts['fold']:
>+            raise error.Abort(_('revisions must be specified with
>--fold'))
>+        revs = ['.']
>+
>+    revs = scmutil.revrange(repo, revs)
>+    if not opts['fold'] and len(revs) > 1:
>+        # TODO: handle the non-fold case with multiple revisions. This is
>+        # somewhat tricky because if we want to edit a series of commits:
>+        #
>+        #   a ---- b ---- c
>+        #
>+        # we need to rewrite a first, then directly rewrite b on top of
>the new
>+        # a, then rewrite c on top of the new b. So we need to handle
>revisions
>+        # in topological order.
>+        raise error.Abort(_('editing multiple revisions without --fold
>is not '
>+                            'currently supported'))
>+
>+    if opts['fold']:
>+        root, head = _foldcheck(repo, revs)
>+    else:
>+        if _willcreatedisallowedunstable(repo, revs):
>+            raise error.Abort(_('cannot edit commit information in the
>middle '
>+                                'of a stack'))
>+        # check above ensures only one revision
>+        root = head = repo[revs.first()]
>+        if root.phase() == phases.public:

Let's use mutable here:

 if not root.mutable():

>+            raise error.Abort(_('cannot edit commit information for
>public '
>+                                'revisions'))
>+
>+    wlock = lock = None
>+    try:
>+        wlock = repo.wlock()
>+        lock = repo.lock()
>+        wctx = repo[None]
>+        p1, p2 = wctx.p1(), wctx.p2()
>+        tr = repo.transaction('metaedit')
>+        newp1 = None
>+        try:
>+            commitopts = opts.copy()
>+            allctx = [repo[r] for r in revs]
>+            targetphase = max(c.phase() for c in allctx)
>+
>+            if commitopts.get('message') or commitopts.get('logfile'):
>+                commitopts['edit'] = False
>+            else:
>+                if opts['fold']:
>+                    msgs = ["HG: This is a fold of %d changesets." %
>len(allctx)]
>+                    msgs += ["HG: Commit message of changeset
>%s.\n\n%s\n" %
>+                             (c.rev(), c.description()) for c in allctx]
>+                else:
>+                    msgs = [head.description()]
>+                commitopts['message'] =  "\n".join(msgs)
>+                commitopts['edit'] = True
>+
>+            # TODO: don't create a new commit if there are no non-trivial
>+            # changes

What do you mean?

>+            newid, created = rewrite(repo, root, allctx, head,
>+                                     [root.p1().node(),
>root.p2().node()],
>+                                     commitopts=commitopts)
>+            # Optimization: if the working copy parent is a *head* (not
>root,
>+            # not in between) of a commit or commit series that got
>rewritten,
>+            # just use localrepo.setparents and avoid any working copy
>+            # updates. It's easier to do this if we don't also have to
>worry
>+            # about p2.
>+            if not p2 and head == p1:
>+                newp1 = newid
>+            if created:
>+                phases.retractboundary(repo, tr, targetphase, [newid])
>+                obsolete.createmarkers(repo, [(ctx, (repo[newid],))
>+                                              for ctx in allctx])
>+            else:
>+                ui.status(_("nothing changed\n"))
>+            tr.close()
>+        finally:
>+            tr.release()
>+        if opts['fold']:
>+            ui.status('%i changesets folded\n' % len(revs))
>+        if newp1 is not None:
>+            repo.setparents(newp1)
>+        elif p1.rev() in revs:
>+            hg.update(repo, newid)
>+    finally:
>+        lockmod.release(lock, wlock)
>+
> def _foldcheck(repo, revs):
>     roots = repo.revs('roots(%ld)', revs)
>     if len(roots) > 1:
>diff --git a/tests/test-evolve.t b/tests/test-evolve.t
>--- a/tests/test-evolve.t
>+++ b/tests/test-evolve.t
>@@ -2,6 +2,7 @@
>   > [defaults]
>   > amend=-d "0 0"
>   > fold=-d "0 0"
>+  > metaedit=-d "0 0"
>   > [web]
>   > push_ssl = false
>   > allow_push = *
>@@ -1449,3 +1450,113 @@ Check that dirstate changes are kept at
> 
>   $ hg status newlyadded
>   A newlyadded
>+
>+hg metaedit
>+-----------
>+
>+deliberately leave the working copy with dirty merges so that we know
>there are
>+no updates going on
>+  $ hg update .
>+  abort: outstanding merge conflicts
>+  [255]
>+check that metaedit respects allowunstable
>+  $ hg metaedit '36 + 42' --fold
>+  abort: cannot fold non-linear revisions (multiple roots given)
>+  [255]
>+  $ hg metaedit '36::39 + 41' --fold
>+  abort: cannot fold non-linear revisions (multiple heads given)
>+  [255]
>+  $ hg metaedit -r 0
>+  abort: cannot edit commit information for public revisions
>+  [255]
>+  $ hg metaedit -r 0 --fold
>+  abort: cannot fold public revisions
>+  [255]
>+  $ hg metaedit '18::20' --fold --config
>'experimental.evolution=createmarkers, allnewcommands'
>+  abort: cannot fold chain not ending with a head or with branching
>+  [255]
>+  $ hg metaedit '.^' --config 'experimental.evolution=createmarkers,
>allnewcommands'
>+  abort: cannot edit commit information in the middle of a stack
>+  [255]
>+  $ hg metaedit --fold
>+  abort: revisions must be specified with --fold
>+  [255]
>+  $ hg metaedit --user foobar
>+  $ hg log --template '{rev}: {author}\n' -r '42:' --hidden
>+  42: test
>+  43: foobar
>+  $ hg log --template '{rev}: {author}\n' -r .
>+  43: foobar
>+  $ hg status newlyadded
>+  A newlyadded
>+  $ hg resolve --list
>+  U newfile
>+
>+TODO: support this
>+  $ hg metaedit '.^::.'
>+  abort: editing multiple revisions without --fold is not currently
>supported
>+  [255]
>+
>+  $ HGEDITOR=cat hg metaedit '.^::.' --fold
>+  HG: This is a fold of 2 changesets.
>+  HG: Commit message of changeset 41.
>+  
>+  amended
>+  
>+  HG: Commit message of changeset 43.
>+  
>+  will be evolved safely
>+  
>+  
>+  
>+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
>+  HG: Leave message empty to abort commit.
>+  HG: --
>+  HG: user: test
>+  HG: branch 'default'
>+  HG: changed a
>+  HG: changed newfile
>+  2 changesets folded
>+
>+  $ glog -r .
>+  @  44:41bf1183869c at default(draft) amended
>+  |
>+
>+no new commit is created here because the date is the same
>+  $ HGEDITOR=cat hg metaedit
>+  amended
>+  
>+  
>+  will be evolved safely
>+  
>+  
>+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
>+  HG: Leave message empty to abort commit.
>+  HG: --
>+  HG: user: test
>+  HG: branch 'default'
>+  HG: changed a
>+  HG: changed newfile
>+  nothing changed
>+
>+  $ glog -r '.^::.'
>+  @  44:41bf1183869c at default(draft) amended
>+  |
>+  o  36:43c3f5ef149f at default(draft) add uu
>+  |
>+
>+'fold' one commit
>+  $ hg metaedit 39 --fold --user foobar2
>+  1 changesets folded
>+  $ hg log -r 45 --template '{rev}: {author}\n'
>+  45: foobar2
>+
>+TODO: don't create a new commit in this case
>+  $ hg metaedit --config defaults.metaedit=
>+  $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n'
>+  36: add uu
>+  46: amended
>+  $ hg status newlyadded
>+  A newlyadded
>+  $ hg resolve --list
>+  U newfile
>_______________________________________________
>Mercurial-devel mailing list
>Mercurial-devel at mercurial-scm.org
>https://urldefense.proofpoint.com/v2/url?u=https-3A__www.mercurial-2Dscm.o
>rg_mailman_listinfo_mercurial-2Ddevel&d=CwIGaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=
>qmwlQ6ljsf0--v3ANP53-V-RM6PPUtJ5zK5Y1fStJGg&m=oUZ2G_tCET_pgpZBh8g76dcs_txC
>RJ7klaiBEudWgBk&s=2SPWmaswg-nDKtTR6xX6QAEz-ilmhvgyMz0OQPFrouQ&e= 



More information about the Mercurial-devel mailing list