Skip to content

Commit 460bc3c

Browse files
phillipwoodgitster
authored andcommitted
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/[email protected]/ Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 297b1e1 commit 460bc3c

File tree

1 file changed

+86
-135
lines changed

1 file changed

+86
-135
lines changed

builtin/rebase.c

Lines changed: 86 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,9 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
316316
&revisions, &shortrevisions))
317317
return -1;
318318

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)) {
320322
free(revisions);
321323
free(shortrevisions);
322324

@@ -359,6 +361,77 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
359361
return ret;
360362
}
361363

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+
362435
static const char * const builtin_rebase_interactive_usage[] = {
363436
N_("git rebase--interactive [<options>]"),
364437
NULL
@@ -367,8 +440,6 @@ static const char * const builtin_rebase_interactive_usage[] = {
367440
int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
368441
{
369442
struct rebase_options opts = REBASE_OPTIONS_INIT;
370-
unsigned flags = 0;
371-
int abbreviate_commands = 0, ret = 0;
372443
struct object_id squash_onto = null_oid;
373444
enum action command = ACTION_NONE;
374445
struct option options[] = {
@@ -433,8 +504,6 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
433504

434505
opts.rebase_cousins = -1;
435506

436-
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
437-
438507
if (argc == 1)
439508
usage_with_options(builtin_rebase_interactive_usage, options);
440509

@@ -444,71 +513,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
444513
if (!is_null_oid(&squash_onto))
445514
opts.squash_onto = &squash_onto;
446515

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-
453516
if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
454517
warning(_("--[no-]rebase-cousins has no effect without "
455518
"--rebase-merges"));
456519

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);
512521
}
513522

514523
static int use_builtin_rebase(void)
@@ -1071,7 +1080,7 @@ static int run_am(struct rebase_options *opts)
10711080
return status;
10721081
}
10731082

1074-
static int run_specific_rebase(struct rebase_options *opts)
1083+
static int run_specific_rebase(struct rebase_options *opts, enum action action)
10751084
{
10761085
const char *argv[] = { NULL, NULL };
10771086
struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
@@ -1080,77 +1089,19 @@ static int run_specific_rebase(struct rebase_options *opts)
10801089

10811090
if (opts->type == REBASE_INTERACTIVE) {
10821091
/* 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);
10871093
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
1088-
argv_array_push(&child.env_array,
1089-
"GIT_SEQUENCE_EDITOR=:");
1094+
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
10901095
opts->autosquash = 0;
10911096
}
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+
}
10921103

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);
11541105
goto finished_rebase;
11551106
}
11561107

@@ -2211,7 +2162,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
22112162
options.revisions = revisions.buf;
22122163

22132164
run_rebase:
2214-
ret = !!run_specific_rebase(&options);
2165+
ret = !!run_specific_rebase(&options, action);
22152166

22162167
cleanup:
22172168
strbuf_release(&revisions);

0 commit comments

Comments
 (0)