The 'exec' command is a little special among rebase -i's commands, as it
does *not* have a SHA-1 as first parameter. Instead, everything after the
`exec` command is treated as command-line to execute.

Let's reuse the arg/arg_len fields of the todo_item structure (which hold
the oneline for pick/edit commands) to point to the command-line.

Signed-off-by: Johannes Schindelin <johannes.schinde...@gmx.de>
---
 sequencer.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/sequencer.c b/sequencer.c
index fe2f6e7..a58bb91 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -17,6 +17,7 @@
 #include "argv-array.h"
 #include "quote.h"
 #include "log-tree.h"
+#include "wt-status.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -661,6 +662,7 @@ enum todo_command {
        TODO_PICK,
        TODO_REVERT,
        TODO_EDIT,
+       TODO_EXEC,
        TODO_NOOP
 };
 
@@ -668,6 +670,7 @@ static const char *todo_command_strings[] = {
        "pick",
        "revert",
        "edit",
+       "exec",
        "noop"
 };
 
@@ -964,6 +967,12 @@ static int parse_insn_line(struct todo_item *item, const 
char *bol, char *eol)
                return -1;
        bol += padding;
 
+       if (item->command == TODO_EXEC) {
+               item->arg = bol;
+               item->arg_len = (int)(eol - bol);
+               return 0;
+       }
+
        end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
        saved = *end_of_object_name;
        *end_of_object_name = '\0';
@@ -1372,6 +1381,47 @@ static int error_with_patch(struct commit *commit,
        return exit_code;
 }
 
+static int do_exec(const char *command_line)
+{
+       const char *child_argv[] = { NULL, NULL };
+       int dirty, status;
+
+       fprintf(stderr, "Executing: %s\n", command_line);
+       child_argv[0] = command_line;
+       status = run_command_v_opt(child_argv, RUN_USING_SHELL);
+
+       /* force re-reading of the cache */
+       if (discard_cache() < 0 || read_cache() < 0)
+               return error(_("Could not read index"));
+
+       dirty = require_clean_work_tree("rebase", NULL, 1, 1);
+
+       if (status) {
+               warning("Execution failed: %s\n%s"
+                       "You can fix the problem, and then run\n"
+                       "\n"
+                       "  git rebase --continue\n"
+                       "\n",
+                       command_line,
+                       dirty ? "and made changes to the index and/or the "
+                               "working tree\n" : "");
+               if (status == 127)
+                       /* command not found */
+                       status = 1;
+       }
+       else if (dirty) {
+               warning("Execution succeeded: %s\nbut "
+                       "left changes to the index and/or the working tree\n"
+                       "Commit or stash your changes, and then run\n"
+                       "\n"
+                       "  git rebase --continue\n"
+                       "\n", command_line);
+               status = 1;
+       }
+
+       return status;
+}
+
 static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
 {
        int res = 0;
@@ -1401,6 +1451,14 @@ static int pick_commits(struct todo_list *todo_list, 
struct replay_opts *opts)
                                        !res);
                        }
                }
+               else if (item->command == TODO_EXEC) {
+                       char *end_of_arg = (char *)(item->arg + item->arg_len);
+                       int saved = *end_of_arg;
+
+                       *end_of_arg = '\0';
+                       res = do_exec(item->arg);
+                       *end_of_arg = saved;
+               }
                else if (item->command != TODO_NOOP)
                        return error("Unknown command %d", item->command);
 
-- 
2.10.0.rc2.102.g5c102ec


Reply via email to