Patchwork [4,of,5,V2] histedit: add execute function (issue4036)

login
register
mail settings
Submitter Olle Lundberg
Date March 6, 2014, 11:26 a.m.
Message ID <655a8eecb29f6e58d56e.1394105177@SE-C02KQ0DADR55>
Download mbox | patch
Permalink /patch/3874/
State Deferred
Headers show

Comments

Olle Lundberg - March 6, 2014, 11:26 a.m.
# HG changeset patch
# User Olle Lundberg <geek@nerd.sh>
# Date 1394067184 -3600
#      Thu Mar 06 01:53:04 2014 +0100
# Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
# Parent  ab2d2ac49a0293653398995ef2de73f31485341a
histedit: add execute function (issue4036)

The basic contract is that it receives a clean working copy and
is expected to leave a clean working copy if it exits 0.
If either the command leaves the working copy dirty, or it exits non-0,
histedit aborts. If we get a clean working copy we try to gather any
eventual new children that might have spawned due to new commits by
an executed command.

We also make the surrounding functions aware of the new workflow,

verifyrules skips the repo and hash related logic since the command
we run most likely will never be a valid hash in the repo.

continuebootstrap checks for any uncommited changes/merges in the
working directory and bails if we find any.
Else set the currentnode to the working directories
first parent and let histedit do its magic. Unless the current
node ctx matches the parent ctx, then we can short circuit the
logic since there are no changes and we can just return the
parent with an empty replacements list.
Augie Fackler - March 6, 2014, 3:36 p.m.
On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
> # HG changeset patch
> # User Olle Lundberg <geek@nerd.sh>
> # Date 1394067184 -3600
> #      Thu Mar 06 01:53:04 2014 +0100
> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
> histedit: add execute function (issue4036)

queueing 1 and 3 for now, I want to talk about this more. I thought
the only common use case for this was to run the same command over all
revisions in a series?

If that's still true, I'd much rather have a 'hg filterevs' command
that took a revset. This feels very awkward to me.

>
> The basic contract is that it receives a clean working copy and
> is expected to leave a clean working copy if it exits 0.
> If either the command leaves the working copy dirty, or it exits non-0,
> histedit aborts. If we get a clean working copy we try to gather any
> eventual new children that might have spawned due to new commits by
> an executed command.
>
> We also make the surrounding functions aware of the new workflow,
>
> verifyrules skips the repo and hash related logic since the command
> we run most likely will never be a valid hash in the repo.
>
> continuebootstrap checks for any uncommited changes/merges in the
> working directory and bails if we find any.
> Else set the currentnode to the working directories
> first parent and let histedit do its magic. Unless the current
> node ctx matches the parent ctx, then we can short circuit the
> logic since there are no changes and we can just return the
> parent with an empty replacements list.
>
> diff --git a/hgext/histedit.py b/hgext/histedit.py
> --- a/hgext/histedit.py
> +++ b/hgext/histedit.py
> @@ -36,10 +36,11 @@
>   #  p, pick = use commit
>   #  e, edit = use commit, but stop for amending
>   #  f, fold = use commit, but combine it with the one above
>   #  d, drop = remove commit from history
>   #  m, mess = edit message without changing commit content
> + #  x, exec = execute the given command
>   #
>
>  In this file, lines beginning with ``#`` are ignored. You must specify a rule
>  for each revision in your history. For example, if you had meant to add gamma
>  before beta, and then wanted to add delta in the same revision as beta, you
> @@ -57,10 +58,11 @@
>   #  p, pick = use commit
>   #  e, edit = use commit, but stop for amending
>   #  f, fold = use commit, but combine it with the one above
>   #  d, drop = remove commit from history
>   #  m, mess = edit message without changing commit content
> + #  x, exec = execute the given command
>   #
>
>  At which point you close the editor and ``histedit`` starts working. When you
>  specify a ``fold`` operation, ``histedit`` will open an editor when it folds
>  those revisions together, offering you a chance to clean up the commit message::
> @@ -101,10 +103,16 @@
>
>  The ``message`` operation will give you a chance to revise a commit
>  message without changing the contents. It's a shortcut for doing
>  ``edit`` immediately followed by `hg histedit --continue``.
>
> +The ``exec`` operation will let you execute arbitrary commands. With the
> +working directory updated to the last given revision. The command receive a
> +clean working copy and is expected to leave a clean working copy if the
> +executed command exits 0. If the command leaves the working copy dirty or
> +exits non-zero you are droped back to a command prompt to clean it up.
> +
>  If ``histedit`` encounters a conflict when moving a revision (while
>  handling ``pick`` or ``fold``), it'll stop in a similar manner to
>  ``edit`` with the difference that it won't prompt you for a commit
>  message when done. If you decide at this point that you don't like how
>  much work it will be to rearrange history, or that you made a mistake,
> @@ -179,10 +187,11 @@
>  #  p, pick = use commit
>  #  e, edit = use commit, but stop for amending
>  #  f, fold = use commit, but combine it with the one above
>  #  d, drop = remove commit from history
>  #  m, mess = edit message without changing commit content
> +#  x, exec = execute the given command
>  #
>  """)
>
>  def commitfuncfor(repo, src):
>      """Build a commit function for the replacement of <src>
> @@ -329,10 +338,43 @@
>      applychanges(ui, repo, oldctx, opts)
>      raise error.InterventionRequired(
>          _('Make changes as needed, you may commit or record as needed now.\n'
>            'When you are finished, run hg histedit --continue to resume.'))
>
> +def execute(ui, repo, ctx, cmd, opts):
> +    ha = ctx.node()
> +    revision = node.hex(ha)
> +    replacements = []
> +    hg.update(repo, ha)
> +    _toprepolock.releaselocks()
> +    rc = util.system(cmd, environ={'HGREVISION': revision,
> +                                   'HG_NODE': revision })
> +    _toprepolock.takelocks()
> +    if rc != 0:
> +        raise error.InterventionRequired(
> +            _('Command exited with %i. Fix up the change and run '
> +            'hg histedit --continue') % rc)
> +    if util.any(repo.status()[:4]):
> +        raise error.InterventionRequired(
> +            _('Working copy dirty, to see the state of the working copy run '
> +              'hg status.\n'
> +              'When you are finished, run hg histedit --continue to resume.'))
> +
> +    repoctx = repo[None].p1()
> +
> +    if repoctx != ctx:
> +        ha = repoctx.node()
> +        newchildren = gatherchildren(repo, ctx)
> +        if ha not in newchildren:
> +            # note: new children may be empty when the changeset is dropped.
> +            # this happen e.g during conflicting pick where we revert content
> +            # to parent.
> +            replacements.append((ha, tuple(newchildren)))
> +        if newchildren:
> +            ctx = repoctx
> +    return ctx, replacements
> +
>  def fold(ui, repo, ctx, ha, opts):
>      oldctx = repo[ha]
>      hg.update(repo, ctx.node())
>      stats = applychanges(ui, repo, oldctx, opts)
>      if stats and stats[3] > 0:
> @@ -461,10 +503,12 @@
>                 'fold': fold,
>                 'd': drop,
>                 'drop': drop,
>                 'm': message,
>                 'mess': message,
> +               'x': execute,
> +               'exec': execute,
>                 }
>
>  @command('histedit',
>      [('', 'commands', '',
>        _('Read history edits from the specified file.')),
> @@ -680,10 +724,28 @@
>          newchildren.pop(0)  # remove ctx
>      return newchildren
>
>  def bootstrapcontinue(ui, repo, parentctx, rules, opts):
>      action, currentnode = rules.pop(0)
> +
> +    # track replacements
> +    replacements = []
> +
> +    if action in ('x', 'exec'):
> +        # TODO: Do we want to auto-commit anything that the exec did for us?
> +        # That would be useful in cases where there is an external tool
> +        # modifying commits for us. The auto-commit behaviour is present in
> +        # the case when a used have used edit to split/add commits. Whatever
> +        # is present in the working dir gets commited.
> +        # If the first parent of the working direcroty is the same as the
> +        # parentctx from the histedit state, we can short circuit the logic
> +        # and just return the parentctx with no replacements.
> +        cmdutil.bailifchanged(repo)
> +        currentnode = repo[None].p1()
> +        if currentnode == parentctx:
> +            return parentctx, replacements
> +
>      ctx = repo[currentnode]
>
>      newchildren = gatherchildren(repo, parentctx)
>
>      # Commit dirty working directory if necessary
> @@ -704,12 +766,10 @@
>                       date=ctx.date(), extra=ctx.extra(),
>                       editor=editor)
>          if new is not None:
>              newchildren.append(new)
>
> -    replacements = []
> -    # track replacements
>      if ctx.node() not in newchildren:
>          # note: new children may be empty when the changeset is dropped.
>          # this happen e.g during conflicting pick where we revert content
>          # to parent.
>          replacements.append((ctx.node(), tuple(newchildren)))
> @@ -788,24 +848,28 @@
>      seen = set()
>      for r in rules:
>          if ' ' not in r:
>              raise util.Abort(_('malformed line "%s"') % r)
>          action, rest = r.split(' ', 1)
> -        ha = rest.strip().split(' ', 1)[0]
> -        try:
> -            ha = str(repo[ha])  # ensure its a short hash
> -        except error.RepoError:
> -            raise util.Abort(_('unknown changeset %s listed') % ha)
> -        if ha not in expected:
> -            raise util.Abort(
> -                _('may not use changesets other than the ones listed'))
> -        if ha in seen:
> -            raise util.Abort(_('duplicated command for changeset %s') % ha)
> -        seen.add(ha)
> +        if action not in ('x', 'exec'):
> +            args = rest.strip().split(' ', 1)[0]
> +            try:
> +                args = str(repo[args])  # ensure its a short hash
> +            except error.RepoError:
> +                raise util.Abort(_('unknown changeset %s listed') % args)
> +            if args not in expected:
> +                raise util.Abort(
> +                    _('may not use changesets other than the ones listed'))
> +            if args in seen:
> +                raise util.Abort(
> +                    _('duplicated command for changeset %s') % args)
> +            seen.add(args)
> +        else:
> +            args = rest
>          if action not in actiontable:
>              raise util.Abort(_('unknown action "%s"') % action)
> -        parsed.append([action, ha])
> +        parsed.append([action, args])
>      missing = sorted(expected - seen)  # sort to stabilize output
>      if missing:
>          raise util.Abort(_('missing rules for changeset %s') % missing[0],
>                           hint=_('do you want to use the drop action?'))
>      return parsed
> diff --git a/tests/test-histedit-arguments.t b/tests/test-histedit-arguments.t
> --- a/tests/test-histedit-arguments.t
> +++ b/tests/test-histedit-arguments.t
> @@ -57,10 +57,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>
>  Run on a revision not ancestors of the current working directory.
>  --------------------------------------------------------------------
> diff --git a/tests/test-histedit-bookmark-motion.t b/tests/test-histedit-bookmark-motion.t
> --- a/tests/test-histedit-bookmark-motion.t
> +++ b/tests/test-histedit-bookmark-motion.t
> @@ -73,10 +73,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>    $ hg histedit 1 --commands - --verbose << EOF | grep histedit
>    > pick 177f92b77385 2 c
>    > drop d2ae7f538514 1 b
> @@ -133,10 +134,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>    $ hg histedit 1 --commands - --verbose << EOF | grep histedit
>    > pick b346ab9a313d 1 c
>    > pick cacdfd884a93 3 f
> diff --git a/tests/test-histedit-commute.t b/tests/test-histedit-commute.t
> --- a/tests/test-histedit-commute.t
> +++ b/tests/test-histedit-commute.t
> @@ -67,10 +67,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>
>  edit the history
>  (use a hacky editor to check histedit-last-edit.txt backup)
> diff --git a/tests/test-histedit-exec.t b/tests/test-histedit-exec.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-histedit-exec.t
> @@ -0,0 +1,290 @@
> +  $ . "$TESTDIR/histedit-helpers.sh"
> +
> +  $ cat >> $HGRCPATH <<EOF
> +  > [extensions]
> +  > histedit=
> +  > EOF
> +
> +  $ initrepo ()
> +  > {
> +  >     hg init r
> +  >     cd r
> +  >     for x in a b c d e f ; do
> +  >         echo $x > $x
> +  >         hg add $x
> +  >         hg ci -m $x
> +  >     done
> +  > }
> +
> +  $ initrepo
> +
> +log before exec
> +  $ hg log --graph
> +  @  changeset:   5:652413bf663e
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     f
> +  |
> +  o  changeset:   4:e860deea161a
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     e
> +  |
> +  o  changeset:   3:055a42cdd887
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     d
> +  |
> +  o  changeset:   2:177f92b77385
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     c
> +  |
> +  o  changeset:   1:d2ae7f538514
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     b
> +  |
> +  o  changeset:   0:cb9a9f314b8b
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     a
> +
> +
> +execute a command with a zero exit code
> +  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
> +  > pick 177f92b77385 c
> +  > pick 055a42cdd887 d
> +  > pick e860deea161a e
> +  > exec exit 0
> +  > pick 652413bf663e f
> +  > EOF
> +  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +
> +log after exec
> +  $ hg log --graph
> +  @  changeset:   5:652413bf663e
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     f
> +  |
> +  o  changeset:   4:e860deea161a
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     e
> +  |
> +  o  changeset:   3:055a42cdd887
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     d
> +  |
> +  o  changeset:   2:177f92b77385
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     c
> +  |
> +  o  changeset:   1:d2ae7f538514
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     b
> +  |
> +  o  changeset:   0:cb9a9f314b8b
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     a
> +
> +
> +execute a command with a non-zero exit code
> +  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
> +  > pick 177f92b77385 c
> +  > pick 055a42cdd887 d
> +  > pick e860deea161a e
> +  > exec exit 1
> +  > pick 652413bf663e f
> +  > EOF
> +  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  Command exited with 1. Fix up the change and run hg histedit --continue
> +
> +  $ hg summary
> +  parent: 4:e860deea161a
> +   e
> +  branch: default
> +  commit: (clean)
> +  update: 1 new changesets (update)
> +  hist:   2 remaining (histedit --continue)
> +
> +  $ hg histedit --continue
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +
> +  $ hg log --graph
> +  @  changeset:   5:652413bf663e
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     f
> +  |
> +  o  changeset:   4:e860deea161a
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     e
> +  |
> +  o  changeset:   3:055a42cdd887
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     d
> +  |
> +  o  changeset:   2:177f92b77385
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     c
> +  |
> +  o  changeset:   1:d2ae7f538514
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     b
> +  |
> +  o  changeset:   0:cb9a9f314b8b
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     a
> +
> +
> +execute a command that modifies the working copy
> +  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
> +  > pick 177f92b77385 c
> +  > pick 055a42cdd887 d
> +  > pick e860deea161a e
> +  > exec echo foo >> e
> +  > pick 652413bf663e f
> +  > EOF
> +  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  Working copy dirty, to see the state of the working copy run hg status.
> +  When you are finished, run hg histedit --continue to resume.
> +
> +  $ hg summary
> +  parent: 4:e860deea161a
> +   e
> +  branch: default
> +  commit: 1 modified (new branch head)
> +  update: 1 new changesets (update)
> +  hist:   2 remaining (histedit --continue)
> +
> +  $ hg commit -m "foo"
> +  created new head
> +
> +  $ hg histedit --continue
> +  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  saved backup bundle to $TESTTMP/r/.hg/strip-backup/652413bf663e-backup.hg (glob)
> +
> +  $ hg cat e
> +  e
> +  foo
> +
> +  $ hg log --graph
> +  @  changeset:   6:a8b916d59ea3
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     f
> +  |
> +  o  changeset:   5:baf2479ff303
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     foo
> +  |
> +  o  changeset:   4:e860deea161a
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     e
> +  |
> +  o  changeset:   3:055a42cdd887
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     d
> +  |
> +  o  changeset:   2:177f92b77385
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     c
> +  |
> +  o  changeset:   1:d2ae7f538514
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     b
> +  |
> +  o  changeset:   0:cb9a9f314b8b
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     a
> +
> +execute a command that adds a commit
> +  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
> +  > pick 177f92b77385 c
> +  > pick 055a42cdd887 d
> +  > pick e860deea161a e
> +  > exec echo g > g; hg ci -Am "g" g
> +  > pick baf2479ff303 foo
> +  > pick a8b916d59ea3 f
> +  > EOF
> +  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  created new head
> +  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +
> +  $ hg summary
> +  parent: 7:5652d6d85ba5 tip
> +   f
> +  branch: default
> +  commit: (clean)
> +  update: (current)
> +
> +  $ hg cat g
> +  g
> +
> +  $ hg log --graph
> +  @  changeset:   7:5652d6d85ba5
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     f
> +  |
> +  o  changeset:   6:c009deb54b16
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     foo
> +  |
> +  o  changeset:   5:caf70cdd2c86
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     g
> +  |
> +  o  changeset:   4:e860deea161a
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     e
> +  |
> +  o  changeset:   3:055a42cdd887
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     d
> +  |
> +  o  changeset:   2:177f92b77385
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     c
> +  |
> +  o  changeset:   1:d2ae7f538514
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     b
> +  |
> +  o  changeset:   0:cb9a9f314b8b
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     a
> +
> diff --git a/tests/test-histedit-obsolete.t b/tests/test-histedit-obsolete.t
> --- a/tests/test-histedit-obsolete.t
> +++ b/tests/test-histedit-obsolete.t
> @@ -57,10 +57,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>    $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
>    > pick 177f92b77385 2 c
>    > drop d2ae7f538514 1 b
> diff --git a/tests/test-histedit-outgoing.t b/tests/test-histedit-outgoing.t
> --- a/tests/test-histedit-outgoing.t
> +++ b/tests/test-histedit-outgoing.t
> @@ -49,10 +49,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>    $ cd ..
>
>  show the error from unrelated repos
> @@ -80,10 +81,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>    $ cd ..
>
>  test sensitivity to branch in URL:
> @@ -103,10 +105,11 @@
>    #  p, pick = use commit
>    #  e, edit = use commit, but stop for amending
>    #  f, fold = use commit, but combine it with the one above
>    #  d, drop = remove commit from history
>    #  m, mess = edit message without changing commit content
> +  #  x, exec = execute the given command
>    #
>    0 files updated, 0 files merged, 0 files removed, 0 files unresolved
>
>  test to check number of roots in outgoing revisions
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
David Soria Parra - March 6, 2014, 6:14 p.m.
Augie Fackler <raf@durin42.com> writes:

> On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
>> # HG changeset patch
>> # User Olle Lundberg <geek@nerd.sh>
>> # Date 1394067184 -3600
>> #      Thu Mar 06 01:53:04 2014 +0100
>> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
>> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
>> histedit: add execute function (issue4036)
>
> queueing 1 and 3 for now, I want to talk about this more. I thought
> the only common use case for this was to run the same command over all
> revisions in a series?
>
> If that's still true, I'd much rather have a 'hg filterevs' command
> that took a revset. This feels very awkward to me.
>

A common usecase is to go back to a revision, fix it up and then only
send this very revision to an upstream review tool like reviewboard &
co, so not over a all revisions in a series.
David Soria Parra - March 6, 2014, 6:17 p.m.
Olle Lundberg <olle.lundberg@gmail.com> writes:

> # HG changeset patch
> # User Olle Lundberg <geek@nerd.sh>
> # Date 1394067184 -3600
> #      Thu Mar 06 01:53:04 2014 +0100
> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
> histedit: add execute function (issue4036)
>
> The basic contract is that it receives a clean working copy and
> is expected to leave a clean working copy if it exits 0.
> If either the command leaves the working copy dirty, or it exits non-0,
> histedit aborts. If we get a clean working copy we try to gather any
> eventual new children that might have spawned due to new commits by
> an executed command.
>
> We also make the surrounding functions aware of the new workflow,
>
> verifyrules skips the repo and hash related logic since the command
> we run most likely will never be a valid hash in the repo.
>
> continuebootstrap checks for any uncommited changes/merges in the
> working directory and bails if we find any.
> Else set the currentnode to the working directories
> first parent and let histedit do its magic. Unless the current
> node ctx matches the parent ctx, then we can short circuit the
> logic since there are no changes and we can just return the
> parent with an empty replacements list.
>
> diff --git a/hgext/histedit.py b/hgext/histedit.py
> --- a/hgext/histedit.py
> +++ b/hgext/histedit.py
> @@ -36,10 +36,11 @@
>   #  p, pick = use commit
>   #  e, edit = use commit, but stop for amending
>   #  f, fold = use commit, but combine it with the one above
>   #  d, drop = remove commit from history
>   #  m, mess = edit message without changing commit content
> + #  x, exec = execute the given command
>   #
>  
>  In this file, lines beginning with ``#`` are ignored. You must specify a rule
>  for each revision in your history. For example, if you had meant to add gamma
>  before beta, and then wanted to add delta in the same revision as beta, you
> @@ -57,10 +58,11 @@
>   #  p, pick = use commit
>   #  e, edit = use commit, but stop for amending
>   #  f, fold = use commit, but combine it with the one above
>   #  d, drop = remove commit from history
>   #  m, mess = edit message without changing commit content
> + #  x, exec = execute the given command
>   #
>  
>  At which point you close the editor and ``histedit`` starts working. When you
>  specify a ``fold`` operation, ``histedit`` will open an editor when it folds
>  those revisions together, offering you a chance to clean up the commit message::
> @@ -101,10 +103,16 @@
>  
>  The ``message`` operation will give you a chance to revise a commit
>  message without changing the contents. It's a shortcut for doing
>  ``edit`` immediately followed by `hg histedit --continue``.
>  
> +The ``exec`` operation will let you execute arbitrary commands. With the
> +working directory updated to the last given revision. The command receive a
> +clean working copy and is expected to leave a clean working copy if the
> +executed command exits 0. If the command leaves the working copy dirty or
> +exits non-zero you are droped back to a command prompt to clean it up.
> +
>  If ``histedit`` encounters a conflict when moving a revision (while
>  handling ``pick`` or ``fold``), it'll stop in a similar manner to
>  ``edit`` with the difference that it won't prompt you for a commit
>  message when done. If you decide at this point that you don't like how
>  much work it will be to rearrange history, or that you made a mistake,
> @@ -179,10 +187,11 @@
>  #  p, pick = use commit
>  #  e, edit = use commit, but stop for amending
>  #  f, fold = use commit, but combine it with the one above
>  #  d, drop = remove commit from history
>  #  m, mess = edit message without changing commit content
> +#  x, exec = execute the given command
>  #
>  """)
>  
>  def commitfuncfor(repo, src):
>      """Build a commit function for the replacement of <src>
> @@ -329,10 +338,43 @@
>      applychanges(ui, repo, oldctx, opts)
>      raise error.InterventionRequired(
>          _('Make changes as needed, you may commit or record as needed now.\n'
>            'When you are finished, run hg histedit --continue to resume.'))
>  
> +def execute(ui, repo, ctx, cmd, opts):
> +    ha = ctx.node()
> +    revision = node.hex(ha)
> +    replacements = []
> +    hg.update(repo, ha)
> +    _toprepolock.releaselocks()
> +    rc = util.system(cmd, environ={'HGREVISION': revision,
> +                                   'HG_NODE': revision })
HGREVISION should be the numerical local revision number, HG_NODE the
hex.

> +    _toprepolock.takelocks()
Olle Lundberg - March 6, 2014, 6:34 p.m.
On Thu, Mar 6, 2014 at 7:17 PM, David Soria Parra <dsp@experimentalworks.net
> wrote:

> Olle Lundberg <olle.lundberg@gmail.com> writes:
>
> > # HG changeset patch
> > # User Olle Lundberg <geek@nerd.sh>
> > # Date 1394067184 -3600
> > #      Thu Mar 06 01:53:04 2014 +0100
> > # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
> > # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
> > histedit: add execute function (issue4036)
> >
> > The basic contract is that it receives a clean working copy and
> > is expected to leave a clean working copy if it exits 0.
> > If either the command leaves the working copy dirty, or it exits non-0,
> > histedit aborts. If we get a clean working copy we try to gather any
> > eventual new children that might have spawned due to new commits by
> > an executed command.
> >
> > We also make the surrounding functions aware of the new workflow,
> >
> > verifyrules skips the repo and hash related logic since the command
> > we run most likely will never be a valid hash in the repo.
> >
> > continuebootstrap checks for any uncommited changes/merges in the
> > working directory and bails if we find any.
> > Else set the currentnode to the working directories
> > first parent and let histedit do its magic. Unless the current
> > node ctx matches the parent ctx, then we can short circuit the
> > logic since there are no changes and we can just return the
> > parent with an empty replacements list.
> >
> > diff --git a/hgext/histedit.py b/hgext/histedit.py
> > --- a/hgext/histedit.py
> > +++ b/hgext/histedit.py
> > @@ -36,10 +36,11 @@
> >   #  p, pick = use commit
> >   #  e, edit = use commit, but stop for amending
> >   #  f, fold = use commit, but combine it with the one above
> >   #  d, drop = remove commit from history
> >   #  m, mess = edit message without changing commit content
> > + #  x, exec = execute the given command
> >   #
> >
> >  In this file, lines beginning with ``#`` are ignored. You must specify
> a rule
> >  for each revision in your history. For example, if you had meant to add
> gamma
> >  before beta, and then wanted to add delta in the same revision as beta,
> you
> > @@ -57,10 +58,11 @@
> >   #  p, pick = use commit
> >   #  e, edit = use commit, but stop for amending
> >   #  f, fold = use commit, but combine it with the one above
> >   #  d, drop = remove commit from history
> >   #  m, mess = edit message without changing commit content
> > + #  x, exec = execute the given command
> >   #
> >
> >  At which point you close the editor and ``histedit`` starts working.
> When you
> >  specify a ``fold`` operation, ``histedit`` will open an editor when it
> folds
> >  those revisions together, offering you a chance to clean up the commit
> message::
> > @@ -101,10 +103,16 @@
> >
> >  The ``message`` operation will give you a chance to revise a commit
> >  message without changing the contents. It's a shortcut for doing
> >  ``edit`` immediately followed by `hg histedit --continue``.
> >
> > +The ``exec`` operation will let you execute arbitrary commands. With the
> > +working directory updated to the last given revision. The command
> receive a
> > +clean working copy and is expected to leave a clean working copy if the
> > +executed command exits 0. If the command leaves the working copy dirty
> or
> > +exits non-zero you are droped back to a command prompt to clean it up.
> > +
> >  If ``histedit`` encounters a conflict when moving a revision (while
> >  handling ``pick`` or ``fold``), it'll stop in a similar manner to
> >  ``edit`` with the difference that it won't prompt you for a commit
> >  message when done. If you decide at this point that you don't like how
> >  much work it will be to rearrange history, or that you made a mistake,
> > @@ -179,10 +187,11 @@
> >  #  p, pick = use commit
> >  #  e, edit = use commit, but stop for amending
> >  #  f, fold = use commit, but combine it with the one above
> >  #  d, drop = remove commit from history
> >  #  m, mess = edit message without changing commit content
> > +#  x, exec = execute the given command
> >  #
> >  """)
> >
> >  def commitfuncfor(repo, src):
> >      """Build a commit function for the replacement of <src>
> > @@ -329,10 +338,43 @@
> >      applychanges(ui, repo, oldctx, opts)
> >      raise error.InterventionRequired(
> >          _('Make changes as needed, you may commit or record as needed
> now.\n'
> >            'When you are finished, run hg histedit --continue to
> resume.'))
> >
> > +def execute(ui, repo, ctx, cmd, opts):
> > +    ha = ctx.node()
> > +    revision = node.hex(ha)
> > +    replacements = []
> > +    hg.update(repo, ha)
> > +    _toprepolock.releaselocks()
> > +    rc = util.system(cmd, environ={'HGREVISION': revision,
> > +                                   'HG_NODE': revision })
> HGREVISION should be the numerical local revision number, HG_NODE the
> hex.
>

Sorry, I see nowhere in the mercurial code base where HGREVISION is being
set to the local revision number. I see it used containing the hex of some
extra fields in e.g transplant, but that's it. I see no point in changing
this behaviour. But i'd rather just expose one of them. And HG_NODE seems
to be the one to expose.


>
> > +    _toprepolock.takelocks()
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
Augie Fackler - March 6, 2014, 6:42 p.m.
On Mar 6, 2014, at 1:14 PM, David Soria Parra <dsp@experimentalworks.net> wrote:

> Augie Fackler <raf@durin42.com> writes:
> 
>> On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
>>> # HG changeset patch
>>> # User Olle Lundberg <geek@nerd.sh>
>>> # Date 1394067184 -3600
>>> #      Thu Mar 06 01:53:04 2014 +0100
>>> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
>>> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
>>> histedit: add execute function (issue4036)
>> 
>> queueing 1 and 3 for now, I want to talk about this more. I thought
>> the only common use case for this was to run the same command over all
>> revisions in a series?
>> 
>> If that's still true, I'd much rather have a 'hg filterevs' command
>> that took a revset. This feels very awkward to me.
>> 
> 
> A common usecase is to go back to a revision, fix it up and then only
> send this very revision to an upstream review tool like reviewboard &
> co, so not over a all revisions in a series.

given:

a - b - c - d - e

'hg filterrevs c::d'

which would perform the specified operation(s) on c and d, and then do the rebase/graft/whatever for e. Does that seem reasonable?
Olle Lundberg - March 6, 2014, 6:52 p.m.
On Thu, Mar 6, 2014 at 7:42 PM, Augie Fackler <raf@durin42.com> wrote:

>
> On Mar 6, 2014, at 1:14 PM, David Soria Parra <dsp@experimentalworks.net>
> wrote:
>
> Augie Fackler <raf@durin42.com> writes:
>
>  On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
>
> # HG changeset patch
> # User Olle Lundberg <geek@nerd.sh>
> # Date 1394067184 -3600
> #      Thu Mar 06 01:53:04 2014 +0100
> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
> histedit: add execute function (issue4036)
>
>
> queueing 1 and 3 for now, I want to talk about this more. I thought
> the only common use case for this was to run the same command over all
> revisions in a series?
>
> If that's still true, I'd much rather have a 'hg filterevs' command
> that took a revset. This feels very awkward to me.
>
>
> A common usecase is to go back to a revision, fix it up and then only
> send this very revision to an upstream review tool like reviewboard &
> co, so not over a all revisions in a series.
>
>
> given:
>
> a - b - c - d - e
>
> 'hg filterrevs c::d'
>
> which would perform the specified operation(s) on c and d, and then do the
> rebase/graft/whatever for e. Does that seem reasonable?
>

It seems reasonable, it might be confusing for git refugees though. Not
that we try to cater everything for them. Just a heads up. I'm up for
changing it, if filterrevs are prefered. But then we'll get two commands
(histedit and filterrecs) doing more or less the same thing, history
editing, except one is automatic. Don't know how if that will be confusing.

>
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
>
Olle Lundberg - March 6, 2014, 8:11 p.m.
On Thu, Mar 6, 2014 at 8:41 PM, Augie Fackler <raf@durin42.com> wrote:

>
> On Mar 6, 2014, at 1:52 PM, Olle <olle.lundberg@gmail.com> wrote:
>
> It seems reasonable, it might be confusing for git refugees though. Not
> that we try to cater everything for them. Just a heads up. I'm up for
> changing it, if filterrevs are prefered. But then we'll get two commands
> (histedit and filterrecs) doing more or less the same thing, history
> editing, except one is automatic. Don't know how if that will be confusing.
>
>
> Place git out of your mind for the moment.
>
> The way I'm thinking about this is that there's interactive history
> rearrangement and editing (histedit) and pseudo-automated reformatting of
> commits without reordering (filter revs). Does that seem like a reasonable
> split?
>

Yeah, I'd like to place git as far as way as possible. And I can very much
agree that it makes sense to split them up. The use case would then become:

hg histedit deadbeef
and then

hg filterrevs deadbeef:cafebabe "everything else as a command?"
or perhaps:
hg filterrevs deadbeef:cafebabe --cmd "cool command here"
Augie Fackler - March 6, 2014, 8:48 p.m.
On Mar 6, 2014, at 3:11 PM, Olle <olle.lundberg@gmail.com> wrote:

> 
> 
> 
> On Thu, Mar 6, 2014 at 8:41 PM, Augie Fackler <raf@durin42.com> wrote:
> 
> On Mar 6, 2014, at 1:52 PM, Olle <olle.lundberg@gmail.com> wrote:
> 
>> It seems reasonable, it might be confusing for git refugees though. Not that we try to cater everything for them. Just a heads up. I'm up for changing it, if filterrevs are prefered. But then we'll get two commands (histedit and filterrecs) doing more or less the same thing, history editing, except one is automatic. Don't know how if that will be confusing.
> 
> Place git out of your mind for the moment.
> 
> The way I'm thinking about this is that there's interactive history rearrangement and editing (histedit) and pseudo-automated reformatting of commits without reordering (filter revs). Does that seem like a reasonable split?
> 
> Yeah, I'd like to place git as far as way as possible. And I can very much agree that it makes sense to split them up. The use case would then become:
> 
> hg histedit deadbeef
> and then
> 
> hg filterrevs deadbeef:cafebabe "everything else as a command?"
> or perhaps:
> hg filterrevs deadbeef:cafebabe --cmd "cool command here"
> 

Yeah, something like that.

> 
> -- 
> Olle
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Martin Geisler - March 8, 2014, 10:26 a.m.
Olle <olle.lundberg@gmail.com> writes:

> On Thu, Mar 6, 2014 at 8:41 PM, Augie Fackler <raf@durin42.com> wrote:
>
>>
>> On Mar 6, 2014, at 1:52 PM, Olle <olle.lundberg@gmail.com> wrote:
>>
>> It seems reasonable, it might be confusing for git refugees though. Not
>> that we try to cater everything for them. Just a heads up. I'm up for
>> changing it, if filterrevs are prefered. But then we'll get two commands
>> (histedit and filterrecs) doing more or less the same thing, history
>> editing, except one is automatic. Don't know how if that will be confusing.
>>
>>
>> Place git out of your mind for the moment.
>>
>> The way I'm thinking about this is that there's interactive history
>> rearrangement and editing (histedit) and pseudo-automated reformatting of
>> commits without reordering (filter revs). Does that seem like a reasonable
>> split?
>>
>
> Yeah, I'd like to place git as far as way as possible. And I can very much
> agree that it makes sense to split them up. The use case would then become:
>
> hg histedit deadbeef
> and then
>
> hg filterrevs deadbeef:cafebabe "everything else as a command?"
> or perhaps:
> hg filterrevs deadbeef:cafebabe --cmd "cool command here"

I will propose

  hg filterrevs deadbeef:cafebabe everything else as a command

If the command takes flags, one would use

  hg filterrevs deadbeef:cafebabe -- everything else as a command

to tell Mercurial where the positional arguments begin (this is the
standard way to indicate that, compare with, e.g., 'rm -- --foo').

Brief testing seems to indicate that this doesn't clash with the early
option parsing, that is, 'hg stat -- -R no-such-file' correctly tries to
show status for the two files '-R' and 'no-such-file'.
Augie Fackler - March 8, 2014, 6:03 p.m.
On Mar 8, 2014, at 5:26 AM, Martin Geisler <martin@geisler.net> wrote:

> Olle <olle.lundberg@gmail.com> writes:
> 
>> On Thu, Mar 6, 2014 at 8:41 PM, Augie Fackler <raf@durin42.com> wrote:
>> 
>>> 
>>> On Mar 6, 2014, at 1:52 PM, Olle <olle.lundberg@gmail.com> wrote:
>>> 
>>> It seems reasonable, it might be confusing for git refugees though. Not
>>> that we try to cater everything for them. Just a heads up. I'm up for
>>> changing it, if filterrevs are prefered. But then we'll get two commands
>>> (histedit and filterrecs) doing more or less the same thing, history
>>> editing, except one is automatic. Don't know how if that will be confusing.
>>> 
>>> 
>>> Place git out of your mind for the moment.
>>> 
>>> The way I'm thinking about this is that there's interactive history
>>> rearrangement and editing (histedit) and pseudo-automated reformatting of
>>> commits without reordering (filter revs). Does that seem like a reasonable
>>> split?
>>> 
>> 
>> Yeah, I'd like to place git as far as way as possible. And I can very much
>> agree that it makes sense to split them up. The use case would then become:
>> 
>> hg histedit deadbeef
>> and then
>> 
>> hg filterrevs deadbeef:cafebabe "everything else as a command?"
>> or perhaps:
>> hg filterrevs deadbeef:cafebabe --cmd "cool command here"
> 
> I will propose
> 
>  hg filterrevs deadbeef:cafebabe everything else as a command

I think I like it. Might be worth running with this as an out-of-tree extension for ~one release cycle to make sure it's "right" before we put it in core?

> 
> If the command takes flags, one would use
> 
>  hg filterrevs deadbeef:cafebabe -- everything else as a command
> 
> to tell Mercurial where the positional arguments begin (this is the
> standard way to indicate that, compare with, e.g., 'rm -- --foo').
> 
> Brief testing seems to indicate that this doesn't clash with the early
> option parsing, that is, 'hg stat -- -R no-such-file' correctly tries to
> show status for the two files '-R' and 'no-such-file'.
> 
> -- 
> Martin Geisler
> 
> http://google.com/+MartinGeisler
Sean Farley - March 8, 2014, 9:06 p.m.
Augie Fackler <raf@durin42.com> writes:

> On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
>> # HG changeset patch
>> # User Olle Lundberg <geek@nerd.sh>
>> # Date 1394067184 -3600
>> #      Thu Mar 06 01:53:04 2014 +0100
>> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
>> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
>> histedit: add execute function (issue4036)
>
> queueing 1 and 3 for now, I want to talk about this more. I thought
> the only common use case for this was to run the same command over all
> revisions in a series?
>
> If that's still true, I'd much rather have a 'hg filterevs' command
> that took a revset. This feels very awkward to me.

I want to chime in here before this gets too off-topic but I am against
having another command that edits history. We already have a command
that does history editing called histedit.

I think the talk of this feature should be moving in the direction of
"how can we allow better / finer control over 'hg edit --continue'"?
Currently, we have no way to change or keep the commit message without
human intervention.

I would like to see something done that allows the following:

hg histedit 'only(foo)'
<edit an earlier commit>
<send the changes to test server>
hg histedit --cont (with the commit message kept the same but appended
with the returned result from the server)

Having a new command 'filterrevs' forces me to do my changes, generate a
new hash, run filterrevs, then generate another hash. This is
unnecessary and becomes annoying when having many commits on top of my
earlier commit that I have to re-graft twice (expensive for large
repos).

Why is there such opposition to adding an exec flag to histedit?
Pierre-Yves David - March 9, 2014, 10:18 p.m.
On 03/08/2014 10:03 AM, Augie Fackler wrote:
>
> I think I like it. Might be worth running with this as an out-of-tree
> extension for ~one release cycle to make sure it's "right" before we put
> it in core?

Strong +1 on that.
Olle Lundberg - March 10, 2014, 11:24 a.m.
On Sat, Mar 8, 2014 at 10:06 PM, Sean Farley
<sean.michael.farley@gmail.com>wrote:

>
> Augie Fackler <raf@durin42.com> writes:
>
> > On Thu, Mar 06, 2014 at 12:26:17PM +0100, Olle Lundberg wrote:
> >> # HG changeset patch
> >> # User Olle Lundberg <geek@nerd.sh>
> >> # Date 1394067184 -3600
> >> #      Thu Mar 06 01:53:04 2014 +0100
> >> # Node ID 655a8eecb29f6e58d56eb480e0b871139f0f134e
> >> # Parent  ab2d2ac49a0293653398995ef2de73f31485341a
> >> histedit: add execute function (issue4036)
> >
> > queueing 1 and 3 for now, I want to talk about this more. I thought
> > the only common use case for this was to run the same command over all
> > revisions in a series?
> >
> > If that's still true, I'd much rather have a 'hg filterevs' command
> > that took a revset. This feels very awkward to me.
>
> I want to chime in here before this gets too off-topic but I am against
> having another command that edits history. We already have a command
> that does history editing called histedit.
>
> I think the talk of this feature should be moving in the direction of
> "how can we allow better / finer control over 'hg edit --continue'"?
> Currently, we have no way to change or keep the commit message without
> human intervention.
>
> I would like to see something done that allows the following:
>
> hg histedit 'only(foo)'
> <edit an earlier commit>
> <send the changes to test server>
> hg histedit --cont (with the commit message kept the same but appended
> with the returned result from the server)
>
> Having a new command 'filterrevs' forces me to do my changes, generate a
> new hash, run filterrevs, then generate another hash. This is
> unnecessary and becomes annoying when having many commits on top of my
> earlier commit that I have to re-graft twice (expensive for large
> repos).
>
> Why is there such opposition to adding an exec flag to histedit?
>

I actually prefer to keep them in the same command, it seems more logical
to keep the same functionality grouped together. But there seems to be some
strong preference for adding a new command. Anyway, will probably try to
massage what I have to an out of tree extension, implementing a command
called histfilter (re: IRC with mpm and others), that uses som logic from
histedit and then try to get some audience to try it out.

(In hindsight: this bug probably shouldn't have been marked as easy)
Augie Fackler - March 10, 2014, 4:17 p.m.
On Mar 10, 2014, at 4:24 AM, Olle <olle.lundberg@gmail.com> wrote:

> On Sat, Mar 8, 2014 at 10:06 PM, Sean Farley <sean.michael.farley@gmail.com> wrote:
>> Why is there such opposition to adding an exec flag to histedit?


Because it feels conceptually wrong to me. I've been wrong before. I'll be less paranoid if I can try it for a while with an out of tree extension.

> 
> I actually prefer to keep them in the same command, it seems more logical to keep the same functionality grouped together. But there seems to be some strong preference for adding a new command. Anyway, will probably try to massage what I have to an out of tree extension, implementing a command called histfilter (re: IRC with mpm and others), that uses som logic from histedit and then try to get some audience to try it out.

I'd be very open to some refactoring work in histedit so the extension could expose both models for me to try out.

> 
> (In hindsight: this bug probably shouldn't have been marked as easy)
> 
> 
> 
> -- 
> Olle
Sean Farley - March 10, 2014, 7:04 p.m.
Augie Fackler <raf@durin42.com> writes:

> On Mar 10, 2014, at 4:24 AM, Olle <olle.lundberg@gmail.com> wrote:
>
>> On Sat, Mar 8, 2014 at 10:06 PM, Sean Farley <sean.michael.farley@gmail.com> wrote:
>>> Why is there such opposition to adding an exec flag to histedit?
>
>
> Because it feels conceptually wrong to me. I've been wrong before. I'll be less paranoid if I can try it for a while with an out of tree extension.

It seems your thinking is along the lines of:

for r in revs:
  doSomething(r)

which is problematic when you want to splice together a human edit with
a script. Like I said before, your suggestion forces two loops through
the changesets which is unnecessary and sometimes expensive.

>> I actually prefer to keep them in the same command, it seems more logical to keep the same functionality grouped together. But there seems to be some strong preference for adding a new command. Anyway, will probably try to massage what I have to an out of tree extension, implementing a command called histfilter (re: IRC with mpm and others), that uses som logic from histedit and then try to get some audience to try it out.
>
> I'd be very open to some refactoring work in histedit so the extension could expose both models for me to try out.

I really want to see all human-interaction parts of Mercurial unified
with respect to editing log messages and at which part the human is
brought in (so that a script could append an id or some such to the end
of a message).
Pierre-Yves David - March 10, 2014, 7:24 p.m.
On 03/10/2014 12:04 PM, Sean Farley wrote:
> which is problematic when you want to splice together a human edit with
> a script. Like I said before, your suggestion forces two loops through
> the changesets which is unnecessary and sometimes expensive.

note: would be less expensive if we had in memory merge ;-)
Olle Lundberg - March 10, 2014, 8:13 p.m.
On Mon, Mar 10, 2014 at 8:04 PM, Sean Farley
<sean.michael.farley@gmail.com>wrote:

>
> Augie Fackler <raf@durin42.com> writes:
>
> > On Mar 10, 2014, at 4:24 AM, Olle <olle.lundberg@gmail.com> wrote:
> >
> >> On Sat, Mar 8, 2014 at 10:06 PM, Sean Farley <
> sean.michael.farley@gmail.com> wrote:
> >>> Why is there such opposition to adding an exec flag to histedit?
> >
> >
> > Because it feels conceptually wrong to me. I've been wrong before. I'll
> be less paranoid if I can try it for a while with an out of tree extension.
>
> It seems your thinking is along the lines of:
>
> for r in revs:
>   doSomething(r)
>
> which is problematic when you want to splice together a human edit with
> a script. Like I said before, your suggestion forces two loops through
> the changesets which is unnecessary and sometimes expensive.
>
> >> I actually prefer to keep them in the same command, it seems more
> logical to keep the same functionality grouped together. But there seems to
> be some strong preference for adding a new command. Anyway, will probably
> try to massage what I have to an out of tree extension, implementing a
> command called histfilter (re: IRC with mpm and others), that uses som
> logic from histedit and then try to get some audience to try it out.
> >
> > I'd be very open to some refactoring work in histedit so the extension
> could expose both models for me to try out.
>
> I really want to see all human-interaction parts of Mercurial unified
> with respect to editing log messages and at which part the human is
> brought in (so that a script could append an id or some such to the end
> of a message).
>

Perhaps You and Augie (and other people with strong opinions about this)
can write down the workflow either of you want to use (in a bit more detail
than we have currently) so I have something more to work from than what's
in the ticket and conflicting emails? :)

Patch

diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -36,10 +36,11 @@ 
  #  p, pick = use commit
  #  e, edit = use commit, but stop for amending
  #  f, fold = use commit, but combine it with the one above
  #  d, drop = remove commit from history
  #  m, mess = edit message without changing commit content
+ #  x, exec = execute the given command
  #
 
 In this file, lines beginning with ``#`` are ignored. You must specify a rule
 for each revision in your history. For example, if you had meant to add gamma
 before beta, and then wanted to add delta in the same revision as beta, you
@@ -57,10 +58,11 @@ 
  #  p, pick = use commit
  #  e, edit = use commit, but stop for amending
  #  f, fold = use commit, but combine it with the one above
  #  d, drop = remove commit from history
  #  m, mess = edit message without changing commit content
+ #  x, exec = execute the given command
  #
 
 At which point you close the editor and ``histedit`` starts working. When you
 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
 those revisions together, offering you a chance to clean up the commit message::
@@ -101,10 +103,16 @@ 
 
 The ``message`` operation will give you a chance to revise a commit
 message without changing the contents. It's a shortcut for doing
 ``edit`` immediately followed by `hg histedit --continue``.
 
+The ``exec`` operation will let you execute arbitrary commands. With the
+working directory updated to the last given revision. The command receive a
+clean working copy and is expected to leave a clean working copy if the
+executed command exits 0. If the command leaves the working copy dirty or
+exits non-zero you are droped back to a command prompt to clean it up.
+
 If ``histedit`` encounters a conflict when moving a revision (while
 handling ``pick`` or ``fold``), it'll stop in a similar manner to
 ``edit`` with the difference that it won't prompt you for a commit
 message when done. If you decide at this point that you don't like how
 much work it will be to rearrange history, or that you made a mistake,
@@ -179,10 +187,11 @@ 
 #  p, pick = use commit
 #  e, edit = use commit, but stop for amending
 #  f, fold = use commit, but combine it with the one above
 #  d, drop = remove commit from history
 #  m, mess = edit message without changing commit content
+#  x, exec = execute the given command
 #
 """)
 
 def commitfuncfor(repo, src):
     """Build a commit function for the replacement of <src>
@@ -329,10 +338,43 @@ 
     applychanges(ui, repo, oldctx, opts)
     raise error.InterventionRequired(
         _('Make changes as needed, you may commit or record as needed now.\n'
           'When you are finished, run hg histedit --continue to resume.'))
 
+def execute(ui, repo, ctx, cmd, opts):
+    ha = ctx.node()
+    revision = node.hex(ha)
+    replacements = []
+    hg.update(repo, ha)
+    _toprepolock.releaselocks()
+    rc = util.system(cmd, environ={'HGREVISION': revision,
+                                   'HG_NODE': revision })
+    _toprepolock.takelocks()
+    if rc != 0:
+        raise error.InterventionRequired(
+            _('Command exited with %i. Fix up the change and run '
+            'hg histedit --continue') % rc)
+    if util.any(repo.status()[:4]):
+        raise error.InterventionRequired(
+            _('Working copy dirty, to see the state of the working copy run '
+              'hg status.\n'
+              'When you are finished, run hg histedit --continue to resume.'))
+
+    repoctx = repo[None].p1()
+
+    if repoctx != ctx:
+        ha = repoctx.node()
+        newchildren = gatherchildren(repo, ctx)
+        if ha not in newchildren:
+            # note: new children may be empty when the changeset is dropped.
+            # this happen e.g during conflicting pick where we revert content
+            # to parent.
+            replacements.append((ha, tuple(newchildren)))
+        if newchildren:
+            ctx = repoctx
+    return ctx, replacements
+
 def fold(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
     if stats and stats[3] > 0:
@@ -461,10 +503,12 @@ 
                'fold': fold,
                'd': drop,
                'drop': drop,
                'm': message,
                'mess': message,
+               'x': execute,
+               'exec': execute,
                }
 
 @command('histedit',
     [('', 'commands', '',
       _('Read history edits from the specified file.')),
@@ -680,10 +724,28 @@ 
         newchildren.pop(0)  # remove ctx
     return newchildren
 
 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
     action, currentnode = rules.pop(0)
+
+    # track replacements
+    replacements = []
+
+    if action in ('x', 'exec'):
+        # TODO: Do we want to auto-commit anything that the exec did for us?
+        # That would be useful in cases where there is an external tool
+        # modifying commits for us. The auto-commit behaviour is present in
+        # the case when a used have used edit to split/add commits. Whatever
+        # is present in the working dir gets commited.
+        # If the first parent of the working direcroty is the same as the
+        # parentctx from the histedit state, we can short circuit the logic
+        # and just return the parentctx with no replacements.
+        cmdutil.bailifchanged(repo)
+        currentnode = repo[None].p1()
+        if currentnode == parentctx:
+            return parentctx, replacements
+
     ctx = repo[currentnode]
 
     newchildren = gatherchildren(repo, parentctx)
 
     # Commit dirty working directory if necessary
@@ -704,12 +766,10 @@ 
                      date=ctx.date(), extra=ctx.extra(),
                      editor=editor)
         if new is not None:
             newchildren.append(new)
 
-    replacements = []
-    # track replacements
     if ctx.node() not in newchildren:
         # note: new children may be empty when the changeset is dropped.
         # this happen e.g during conflicting pick where we revert content
         # to parent.
         replacements.append((ctx.node(), tuple(newchildren)))
@@ -788,24 +848,28 @@ 
     seen = set()
     for r in rules:
         if ' ' not in r:
             raise util.Abort(_('malformed line "%s"') % r)
         action, rest = r.split(' ', 1)
-        ha = rest.strip().split(' ', 1)[0]
-        try:
-            ha = str(repo[ha])  # ensure its a short hash
-        except error.RepoError:
-            raise util.Abort(_('unknown changeset %s listed') % ha)
-        if ha not in expected:
-            raise util.Abort(
-                _('may not use changesets other than the ones listed'))
-        if ha in seen:
-            raise util.Abort(_('duplicated command for changeset %s') % ha)
-        seen.add(ha)
+        if action not in ('x', 'exec'):
+            args = rest.strip().split(' ', 1)[0]
+            try:
+                args = str(repo[args])  # ensure its a short hash
+            except error.RepoError:
+                raise util.Abort(_('unknown changeset %s listed') % args)
+            if args not in expected:
+                raise util.Abort(
+                    _('may not use changesets other than the ones listed'))
+            if args in seen:
+                raise util.Abort(
+                    _('duplicated command for changeset %s') % args)
+            seen.add(args)
+        else:
+            args = rest
         if action not in actiontable:
             raise util.Abort(_('unknown action "%s"') % action)
-        parsed.append([action, ha])
+        parsed.append([action, args])
     missing = sorted(expected - seen)  # sort to stabilize output
     if missing:
         raise util.Abort(_('missing rules for changeset %s') % missing[0],
                          hint=_('do you want to use the drop action?'))
     return parsed
diff --git a/tests/test-histedit-arguments.t b/tests/test-histedit-arguments.t
--- a/tests/test-histedit-arguments.t
+++ b/tests/test-histedit-arguments.t
@@ -57,10 +57,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Run on a revision not ancestors of the current working directory.
 --------------------------------------------------------------------
diff --git a/tests/test-histedit-bookmark-motion.t b/tests/test-histedit-bookmark-motion.t
--- a/tests/test-histedit-bookmark-motion.t
+++ b/tests/test-histedit-bookmark-motion.t
@@ -73,10 +73,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
   > pick 177f92b77385 2 c
   > drop d2ae7f538514 1 b
@@ -133,10 +134,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose << EOF | grep histedit
   > pick b346ab9a313d 1 c
   > pick cacdfd884a93 3 f
diff --git a/tests/test-histedit-commute.t b/tests/test-histedit-commute.t
--- a/tests/test-histedit-commute.t
+++ b/tests/test-histedit-commute.t
@@ -67,10 +67,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 edit the history
 (use a hacky editor to check histedit-last-edit.txt backup)
diff --git a/tests/test-histedit-exec.t b/tests/test-histedit-exec.t
new file mode 100644
--- /dev/null
+++ b/tests/test-histedit-exec.t
@@ -0,0 +1,290 @@ 
+  $ . "$TESTDIR/histedit-helpers.sh"
+
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > histedit=
+  > EOF
+
+  $ initrepo ()
+  > {
+  >     hg init r
+  >     cd r
+  >     for x in a b c d e f ; do
+  >         echo $x > $x
+  >         hg add $x
+  >         hg ci -m $x
+  >     done
+  > }
+
+  $ initrepo
+
+log before exec
+  $ hg log --graph
+  @  changeset:   5:652413bf663e
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     f
+  |
+  o  changeset:   4:e860deea161a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     e
+  |
+  o  changeset:   3:055a42cdd887
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     d
+  |
+  o  changeset:   2:177f92b77385
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  o  changeset:   1:d2ae7f538514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:cb9a9f314b8b
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
+
+execute a command with a zero exit code
+  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
+  > pick 177f92b77385 c
+  > pick 055a42cdd887 d
+  > pick e860deea161a e
+  > exec exit 0
+  > pick 652413bf663e f
+  > EOF
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+log after exec
+  $ hg log --graph
+  @  changeset:   5:652413bf663e
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     f
+  |
+  o  changeset:   4:e860deea161a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     e
+  |
+  o  changeset:   3:055a42cdd887
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     d
+  |
+  o  changeset:   2:177f92b77385
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  o  changeset:   1:d2ae7f538514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:cb9a9f314b8b
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
+
+execute a command with a non-zero exit code
+  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
+  > pick 177f92b77385 c
+  > pick 055a42cdd887 d
+  > pick e860deea161a e
+  > exec exit 1
+  > pick 652413bf663e f
+  > EOF
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  Command exited with 1. Fix up the change and run hg histedit --continue
+
+  $ hg summary
+  parent: 4:e860deea161a 
+   e
+  branch: default
+  commit: (clean)
+  update: 1 new changesets (update)
+  hist:   2 remaining (histedit --continue)
+
+  $ hg histedit --continue
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg log --graph
+  @  changeset:   5:652413bf663e
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     f
+  |
+  o  changeset:   4:e860deea161a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     e
+  |
+  o  changeset:   3:055a42cdd887
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     d
+  |
+  o  changeset:   2:177f92b77385
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  o  changeset:   1:d2ae7f538514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:cb9a9f314b8b
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
+
+execute a command that modifies the working copy
+  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
+  > pick 177f92b77385 c
+  > pick 055a42cdd887 d
+  > pick e860deea161a e
+  > exec echo foo >> e
+  > pick 652413bf663e f
+  > EOF
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  Working copy dirty, to see the state of the working copy run hg status.
+  When you are finished, run hg histedit --continue to resume.
+
+  $ hg summary
+  parent: 4:e860deea161a 
+   e
+  branch: default
+  commit: 1 modified (new branch head)
+  update: 1 new changesets (update)
+  hist:   2 remaining (histedit --continue)
+
+  $ hg commit -m "foo"
+  created new head
+
+  $ hg histedit --continue
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/r/.hg/strip-backup/652413bf663e-backup.hg (glob)
+
+  $ hg cat e
+  e
+  foo
+
+  $ hg log --graph
+  @  changeset:   6:a8b916d59ea3
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     f
+  |
+  o  changeset:   5:baf2479ff303
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     foo
+  |
+  o  changeset:   4:e860deea161a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     e
+  |
+  o  changeset:   3:055a42cdd887
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     d
+  |
+  o  changeset:   2:177f92b77385
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  o  changeset:   1:d2ae7f538514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:cb9a9f314b8b
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
+execute a command that adds a commit
+  $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
+  > pick 177f92b77385 c
+  > pick 055a42cdd887 d
+  > pick e860deea161a e
+  > exec echo g > g; hg ci -Am "g" g
+  > pick baf2479ff303 foo
+  > pick a8b916d59ea3 f
+  > EOF
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  created new head
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg summary
+  parent: 7:5652d6d85ba5 tip
+   f
+  branch: default
+  commit: (clean)
+  update: (current)
+
+  $ hg cat g
+  g
+
+  $ hg log --graph
+  @  changeset:   7:5652d6d85ba5
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     f
+  |
+  o  changeset:   6:c009deb54b16
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     foo
+  |
+  o  changeset:   5:caf70cdd2c86
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     g
+  |
+  o  changeset:   4:e860deea161a
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     e
+  |
+  o  changeset:   3:055a42cdd887
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     d
+  |
+  o  changeset:   2:177f92b77385
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  o  changeset:   1:d2ae7f538514
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:cb9a9f314b8b
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
diff --git a/tests/test-histedit-obsolete.t b/tests/test-histedit-obsolete.t
--- a/tests/test-histedit-obsolete.t
+++ b/tests/test-histedit-obsolete.t
@@ -57,10 +57,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
   > pick 177f92b77385 2 c
   > drop d2ae7f538514 1 b
diff --git a/tests/test-histedit-outgoing.t b/tests/test-histedit-outgoing.t
--- a/tests/test-histedit-outgoing.t
+++ b/tests/test-histedit-outgoing.t
@@ -49,10 +49,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
 
 show the error from unrelated repos
@@ -80,10 +81,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd ..
 
 test sensitivity to branch in URL:
@@ -103,10 +105,11 @@ 
   #  p, pick = use commit
   #  e, edit = use commit, but stop for amending
   #  f, fold = use commit, but combine it with the one above
   #  d, drop = remove commit from history
   #  m, mess = edit message without changing commit content
+  #  x, exec = execute the given command
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 test to check number of roots in outgoing revisions