sequencer (rebase -i): implement the 'exec' command

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.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jan 2, 2017 at 16:26 UTC 311af5266b27523742e8be9d8297ea475137cfb5
1 file changed +57
sequencer.c
+57
@@ -18,6 +18,7 @@
18 #include "quote.h"
19 #include "trailer.h"
20 #include "log-tree.h"
21 +#include "wt-status.h"
22
23 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
24
@@ -632,6 +633,8 @@ enum todo_command {
633 TODO_PICK = 0,
634 TODO_REVERT,
635 TODO_EDIT,
636 + /* commands that do something else than handling a single commit */
637 + TODO_EXEC,
638 /* commands that do nothing but are counted for reporting progress */
639 TODO_NOOP
640 };
@@ -640,6 +643,7 @@ static const char *todo_command_strings[] = {
643 "pick",
644 "revert",
645 "edit",
646 + "exec",
647 "noop"
648 };
649
@@ -938,6 +942,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
942 return -1;
943 bol += padding;
944
945 + if (item->command == TODO_EXEC) {
946 + item->arg = bol;
947 + item->arg_len = (int)(eol - bol);
948 + return 0;
949 + }
950 +
951 end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
952 saved = *end_of_object_name;
953 *end_of_object_name = '\0';
@@ -1397,6 +1407,46 @@ static int error_with_patch(struct commit *commit,
1407 return exit_code;
1408 }
1409
1410 +static int do_exec(const char *command_line)
1411 +{
1412 + const char *child_argv[] = { NULL, NULL };
1413 + int dirty, status;
1414 +
1415 + fprintf(stderr, "Executing: %s\n", command_line);
1416 + child_argv[0] = command_line;
1417 + status = run_command_v_opt(child_argv, RUN_USING_SHELL);
1418 +
1419 + /* force re-reading of the cache */
1420 + if (discard_cache() < 0 || read_cache() < 0)
1421 + return error(_("could not read index"));
1422 +
1423 + dirty = require_clean_work_tree("rebase", NULL, 1, 1);
1424 +
1425 + if (status) {
1426 + warning(_("execution failed: %s\n%s"
1427 + "You can fix the problem, and then run\n"
1428 + "\n"
1429 + " git rebase --continue\n"
1430 + "\n"),
1431 + command_line,
1432 + dirty ? N_("and made changes to the index and/or the "
1433 + "working tree\n") : "");
1434 + if (status == 127)
1435 + /* command not found */
1436 + status = 1;
1437 + } else if (dirty) {
1438 + warning(_("execution succeeded: %s\nbut "
1439 + "left changes to the index and/or the working tree\n"
1440 + "Commit or stash your changes, and then run\n"
1441 + "\n"
1442 + " git rebase --continue\n"
1443 + "\n"), command_line);
1444 + status = 1;
1445 + }
1446 +
1447 + return status;
1448 +}
1449 +
1450 static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
1451 {
1452 int res = 0;
@@ -1425,6 +1475,13 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
1475 item->arg, item->arg_len, opts, res,
1476 !res);
1477 }
1478 + } else if (item->command == TODO_EXEC) {
1479 + char *end_of_arg = (char *)(item->arg + item->arg_len);
1480 + int saved = *end_of_arg;
1481 +
1482 + *end_of_arg = '\0';
1483 + res = do_exec(item->arg);
1484 + *end_of_arg = saved;
1485 } else if (!is_noop(item->command))
1486 return error(_("unknown command %d"), item->command);
1487