rebase -i: run without forking rebase--interactive

When the builtin rebase starts an interactive rebase it parses the options and then repackages them and forks `rebase--interactive`. Separate the option parsing in cmd_rebase__interactive() from the business logic to allow interactive rebases can be run without forking `rebase__interactive` by calling run_rebase_interactive() directly. Starting interactive rebases without forking makes it easy to debug the sequencer without worrying about attaching to child processes. Ævar has also reported that some of the rebase perf tests are 30% faster [1]. This patch also makes it easy to remove cmd_rebase__interactive() in the future when git-legacy-rebase.sh and git-rebase--preserve-merges.sh are retired. [1] https://public-inbox.org/git/87y359cfjj.fsf@evledraar.gmail.com/ Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Phillip Wood committed Apr 17, 2019 at 15:30 UTC 460bc3ce7386582b3259f8e3a2c1ace443ddb1ae
1 file changed +86 -135
builtin/rebase.c
+86 -135
@@ -316,7 +316,9 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
316 &revisions, &shortrevisions))
317 return -1;
318
319 - if (init_basic_state(&replay, opts->head_name, opts->onto, head_hash)) {
319 + if (init_basic_state(&replay,
320 + opts->head_name ? opts->head_name : "detached HEAD",
321 + opts->onto, head_hash)) {
322 free(revisions);
323 free(shortrevisions);
324
@@ -359,6 +361,77 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
361 return ret;
362 }
363
364 +static int run_rebase_interactive(struct rebase_options *opts,
365 + enum action command)
366 +{
367 + unsigned flags = 0;
368 + int abbreviate_commands = 0, ret = 0;
369 +
370 + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
371 +
372 + flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
373 + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
374 + flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
375 + flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
376 + flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
377 +
378 + switch (command) {
379 + case ACTION_NONE: {
380 + if (!opts->onto && !opts->upstream)
381 + die(_("a base commit must be provided with --upstream or --onto"));
382 +
383 + ret = do_interactive_rebase(opts, flags);
384 + break;
385 + }
386 + case ACTION_SKIP: {
387 + struct string_list merge_rr = STRING_LIST_INIT_DUP;
388 +
389 + rerere_clear(the_repository, &merge_rr);
390 + }
391 + /* fallthrough */
392 + case ACTION_CONTINUE: {
393 + struct replay_opts replay_opts = get_replay_opts(opts);
394 +
395 + ret = sequencer_continue(the_repository, &replay_opts);
396 + break;
397 + }
398 + case ACTION_EDIT_TODO:
399 + ret = edit_todo_file(flags);
400 + break;
401 + case ACTION_SHOW_CURRENT_PATCH: {
402 + struct child_process cmd = CHILD_PROCESS_INIT;
403 +
404 + cmd.git_cmd = 1;
405 + argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
406 + ret = run_command(&cmd);
407 +
408 + break;
409 + }
410 + case ACTION_SHORTEN_OIDS:
411 + case ACTION_EXPAND_OIDS:
412 + ret = transform_todo_file(flags);
413 + break;
414 + case ACTION_CHECK_TODO_LIST:
415 + ret = check_todo_list_from_file(the_repository);
416 + break;
417 + case ACTION_REARRANGE_SQUASH:
418 + ret = rearrange_squash_in_todo_file();
419 + break;
420 + case ACTION_ADD_EXEC: {
421 + struct string_list commands = STRING_LIST_INIT_DUP;
422 +
423 + split_exec_commands(opts->cmd, &commands);
424 + ret = add_exec_commands(&commands);
425 + string_list_clear(&commands, 0);
426 + break;
427 + }
428 + default:
429 + BUG("invalid command '%d'", command);
430 + }
431 +
432 + return ret;
433 +}
434 +
435 static const char * const builtin_rebase_interactive_usage[] = {
436 N_("git rebase--interactive [<options>]"),
437 NULL
@@ -367,8 +440,6 @@ static const char * const builtin_rebase_interactive_usage[] = {
440 int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
441 {
442 struct rebase_options opts = REBASE_OPTIONS_INIT;
370 - unsigned flags = 0;
371 - int abbreviate_commands = 0, ret = 0;
443 struct object_id squash_onto = null_oid;
444 enum action command = ACTION_NONE;
445 struct option options[] = {
@@ -433,8 +504,6 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
504
505 opts.rebase_cousins = -1;
506
436 - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
437 -
507 if (argc == 1)
508 usage_with_options(builtin_rebase_interactive_usage, options);
509
@@ -444,71 +513,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
513 if (!is_null_oid(&squash_onto))
514 opts.squash_onto = &squash_onto;
515
447 - flags |= opts.keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
448 - flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
449 - flags |= opts.rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
450 - flags |= opts.rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
451 - flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
452 -
516 if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
517 warning(_("--[no-]rebase-cousins has no effect without "
518 "--rebase-merges"));
519
457 - switch (command) {
458 - case ACTION_NONE: {
459 - if (!opts.onto && !opts.upstream)
460 - die(_("a base commit must be provided with --upstream or --onto"));
461 -
462 - ret = do_interactive_rebase(&opts, flags);
463 - break;
464 - }
465 - case ACTION_SKIP: {
466 - struct string_list merge_rr = STRING_LIST_INIT_DUP;
467 -
468 - rerere_clear(the_repository, &merge_rr);
469 - }
470 - /* fallthrough */
471 - case ACTION_CONTINUE: {
472 - struct replay_opts replay_opts = get_replay_opts(&opts);
473 -
474 - ret = sequencer_continue(the_repository, &replay_opts);
475 - break;
476 - }
477 - case ACTION_EDIT_TODO:
478 - ret = edit_todo_file(flags);
479 - break;
480 - case ACTION_SHOW_CURRENT_PATCH: {
481 - struct child_process cmd = CHILD_PROCESS_INIT;
482 -
483 - cmd.git_cmd = 1;
484 - argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
485 - ret = run_command(&cmd);
486 -
487 - break;
488 - }
489 - case ACTION_SHORTEN_OIDS:
490 - case ACTION_EXPAND_OIDS:
491 - ret = transform_todo_file(flags);
492 - break;
493 - case ACTION_CHECK_TODO_LIST:
494 - ret = check_todo_list_from_file(the_repository);
495 - break;
496 - case ACTION_REARRANGE_SQUASH:
497 - ret = rearrange_squash_in_todo_file();
498 - break;
499 - case ACTION_ADD_EXEC: {
500 - struct string_list commands = STRING_LIST_INIT_DUP;
501 -
502 - split_exec_commands(opts.cmd, &commands);
503 - ret = add_exec_commands(&commands);
504 - string_list_clear(&commands, 0);
505 - break;
506 - }
507 - default:
508 - BUG("invalid command '%d'", command);
509 - }
510 -
511 - return !!ret;
520 + return !!run_rebase_interactive(&opts, command);
521 }
522
523 static int use_builtin_rebase(void)
@@ -1071,7 +1080,7 @@ static int run_am(struct rebase_options *opts)
1080 return status;
1081 }
1082
1074 -static int run_specific_rebase(struct rebase_options *opts)
1083 +static int run_specific_rebase(struct rebase_options *opts, enum action action)
1084 {
1085 const char *argv[] = { NULL, NULL };
1086 struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
@@ -1080,77 +1089,19 @@ static int run_specific_rebase(struct rebase_options *opts)
1089
1090 if (opts->type == REBASE_INTERACTIVE) {
1091 /* Run builtin interactive rebase */
1083 - struct child_process child = CHILD_PROCESS_INIT;
1084 -
1085 - argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
1086 - resolvemsg);
1092 + setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
1093 if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
1088 - argv_array_push(&child.env_array,
1089 - "GIT_SEQUENCE_EDITOR=:");
1094 + setenv("GIT_SEQUENCE_EDITOR", ":", 1);
1095 opts->autosquash = 0;
1096 }
1097 + if (opts->gpg_sign_opt) {
1098 + /* remove the leading "-S" */
1099 + char *tmp = xstrdup(opts->gpg_sign_opt + 2);
1100 + free(opts->gpg_sign_opt);
1101 + opts->gpg_sign_opt = tmp;
1102 + }
1103
1093 - child.git_cmd = 1;
1094 - argv_array_push(&child.args, "rebase--interactive");
1095 -
1096 - if (opts->action)
1097 - argv_array_pushf(&child.args, "--%s", opts->action);
1098 - if (opts->keep_empty)
1099 - argv_array_push(&child.args, "--keep-empty");
1100 - if (opts->rebase_merges)
1101 - argv_array_push(&child.args, "--rebase-merges");
1102 - if (opts->rebase_cousins)
1103 - argv_array_push(&child.args, "--rebase-cousins");
1104 - if (opts->autosquash)
1105 - argv_array_push(&child.args, "--autosquash");
1106 - if (opts->flags & REBASE_VERBOSE)
1107 - argv_array_push(&child.args, "--verbose");
1108 - if (opts->flags & REBASE_FORCE)
1109 - argv_array_push(&child.args, "--no-ff");
1110 - if (opts->restrict_revision)
1111 - argv_array_pushf(&child.args,
1112 - "--restrict-revision=^%s",
1113 - oid_to_hex(&opts->restrict_revision->object.oid));
1114 - if (opts->upstream)
1115 - argv_array_pushf(&child.args, "--upstream=%s",
1116 - oid_to_hex(&opts->upstream->object.oid));
1117 - if (opts->onto)
1118 - argv_array_pushf(&child.args, "--onto=%s",
1119 - oid_to_hex(&opts->onto->object.oid));
1120 - if (opts->squash_onto)
1121 - argv_array_pushf(&child.args, "--squash-onto=%s",
1122 - oid_to_hex(opts->squash_onto));
1123 - if (opts->onto_name)
1124 - argv_array_pushf(&child.args, "--onto-name=%s",
1125 - opts->onto_name);
1126 - argv_array_pushf(&child.args, "--head-name=%s",
1127 - opts->head_name ?
1128 - opts->head_name : "detached HEAD");
1129 - if (opts->strategy)
1130 - argv_array_pushf(&child.args, "--strategy=%s",
1131 - opts->strategy);
1132 - if (opts->strategy_opts)
1133 - argv_array_pushf(&child.args, "--strategy-opts=%s",
1134 - opts->strategy_opts);
1135 - if (opts->switch_to)
1136 - argv_array_pushf(&child.args, "--switch-to=%s",
1137 - opts->switch_to);
1138 - if (opts->cmd)
1139 - argv_array_pushf(&child.args, "--cmd=%s", opts->cmd);
1140 - if (opts->allow_empty_message)
1141 - argv_array_push(&child.args, "--allow-empty-message");
1142 - if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
1143 - argv_array_push(&child.args, "--rerere-autoupdate");
1144 - else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
1145 - argv_array_push(&child.args, "--no-rerere-autoupdate");
1146 - if (opts->gpg_sign_opt)
1147 - argv_array_push(&child.args, opts->gpg_sign_opt);
1148 - if (opts->signoff)
1149 - argv_array_push(&child.args, "--signoff");
1150 - if (opts->reschedule_failed_exec)
1151 - argv_array_push(&child.args, "--reschedule-failed-exec");
1152 -
1153 - status = run_command(&child);
1104 + status = run_rebase_interactive(opts, action);
1105 goto finished_rebase;
1106 }
1107
@@ -2211,7 +2162,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
2162 options.revisions = revisions.buf;
2163
2164 run_rebase:
2214 - ret = !!run_specific_rebase(&options);
2165 + ret = !!run_specific_rebase(&options, action);
2166
2167 cleanup:
2168 strbuf_release(&revisions);