[PATCH RFC] histedit: add exec command (issue4036)

Durham Goode durham at fb.com
Tue Nov 12 12:01:21 CST 2013


# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1383437476 25200
#      Sat Nov 02 17:11:16 2013 -0700
# Node ID 08b86341b720d65be3f1ce80295ad50f5ca188eb
# Parent  aa80446aacc3b1574211649cd8f190250b6b04b3
histedit: add exec command (issue4036)

This is RFC. Don't queue it (there's no documentation changes included yet).

Adds an exec/x command to the available histedit commands. This allows users
working with a stack of commits to execute an arbitrary shell command on each
commit. This is the same as the git rebase -i exec command.

For example, assume the user has a shell command 'arc amend' that adds
a "Reviewed By: soandso" line to the commit message before they push the change.
They can now do:

pick d33tb11f Blah blah
exec arc amend
pick a53cb361 More blah
exec arc amend

To run 'arc amend' on all commits at once.

Unfortunately this patch required a change to how pick works. Previously pick
would do nothing if the picked commit was already on the correct parent. The
problem with this is that it may still have child commits, so an exec can't run
amend. The patch changes pick to always 'pick' the commit, resulting in a new
commit that exec can then operate on before it picks the next commit. This is a
perf hit, adds a little more output spam, and will require updating a lot of the
hard coded hashes in the tests, so I wanted to throw it out there first before I
go update all the histedit tests.

Just some food for thought before the sprint.

diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -297,9 +297,6 @@
 
 def pick(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
-    if oldctx.parents()[0] == ctx:
-        ui.debug('node %s unchanged\n' % ha)
-        return oldctx, []
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
     if stats and stats[3] > 0:
@@ -405,6 +402,26 @@
     # We didn't make an edit, so just indicate no replaced nodes
     return newctx, []
 
+def execute(ui, repo, ctx, ha, opts):
+    hg.update(repo, ctx.node())
+    os.system(ha)
+
+    # reset caches
+    repo.changelog
+    del repo.changelog
+    repo.manifest
+    del repo.manifest
+    repo.dirstate
+    del repo.dirstate
+    repo._phasecache
+    del repo._phasecache
+
+    newctx = repo['.']
+    if not ctx.node() in repo:
+        return newctx, [(ctx.node(), (newctx.node(),))]
+
+    return newctx, []
+
 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
     """utility function to find the first outgoing changeset
 
@@ -439,6 +456,8 @@
                'drop': drop,
                'm': message,
                'mess': message,
+               'x': execute,
+               'exec': execute,
                }
 
 @command('histedit',
@@ -752,17 +771,20 @@
         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 == "exec" or action == "x":
+          ha = rest
+        else:
+          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 actiontable:
             raise util.Abort(_('unknown action "%s"') % action)
         parsed.append([action, ha])


More information about the Mercurial-devel mailing list