From fc4a6735ee10b7699c22dfb58a9dd40f225e51bd Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Tue, 19 Mar 2019 19:03:07 +0000 Subject: sequencer: always discard index after checkout As the checkout runs in a separate process our index will be out of date so it should be discarded. The existing callers are not doing this consistently so do it here to avoid the callers having to worry about it. This fixes some test failures that happen if do_interactive_rebase() is called without forking rebase--interactive which we will implement shortly. Running git rebase -i master topic starting on master created empty todo lists because all the commits in topic were marked as cherry-picks. After topic was checked out in prepare_branch_to_be_rebased() the working tree contained the contents from topic but the index contained master and the cache entries were still valid. This meant that diff_populate_filespec() which loads the blobs when calculating patch-id's ended up reading the contents for master from the working tree which actually contained topic. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c index 4535523..d1a4ac1 100644 --- a/builtin/rebase--interactive.c +++ b/builtin/rebase--interactive.c @@ -171,7 +171,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, struct argv_array make_script_args = ARGV_ARRAY_INIT; struct todo_list todo_list = TODO_LIST_INIT; - if (prepare_branch_to_be_rebased(opts, switch_to)) + if (prepare_branch_to_be_rebased(the_repository, opts, switch_to)) return -1; if (get_revision_ranges(upstream, onto, &head_hash, diff --git a/sequencer.c b/sequencer.c index 281a8ad..ccc0160 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3418,10 +3418,11 @@ static const char *reflog_message(struct replay_opts *opts, return buf.buf; } -static int run_git_checkout(struct replay_opts *opts, const char *commit, - const char *action) +static int run_git_checkout(struct repository *r, struct replay_opts *opts, + const char *commit, const char *action) { struct child_process cmd = CHILD_PROCESS_INIT; + int ret; cmd.git_cmd = 1; @@ -3430,25 +3431,31 @@ static int run_git_checkout(struct replay_opts *opts, const char *commit, argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action); if (opts->verbose) - return run_command(&cmd); + ret = run_command(&cmd); else - return run_command_silent_on_success(&cmd); + ret = run_command_silent_on_success(&cmd); + + if (!ret) + discard_index(r->index); + + return ret; } -int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit) +int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts, + const char *commit) { const char *action; if (commit && *commit) { action = reflog_message(opts, "start", "checkout %s", commit); - if (run_git_checkout(opts, commit, action)) + if (run_git_checkout(r, opts, commit, action)) return error(_("could not checkout %s"), commit); } return 0; } -static int checkout_onto(struct replay_opts *opts, +static int checkout_onto(struct repository *r, struct replay_opts *opts, const char *onto_name, const char *onto, const char *orig_head) { @@ -3458,7 +3465,7 @@ static int checkout_onto(struct replay_opts *opts, if (get_oid(orig_head, &oid)) return error(_("%s: not a valid OID"), orig_head); - if (run_git_checkout(opts, onto, action)) { + if (run_git_checkout(r, opts, onto, action)) { apply_autostash(opts); sequencer_remove_state(opts); return error(_("could not detach HEAD")); @@ -4786,7 +4793,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) || todo_list_check(todo_list, &new_todo)) { fprintf(stderr, _(edit_todo_list_advice)); - checkout_onto(opts, onto_name, onto, orig_head); + checkout_onto(r, opts, onto_name, onto, orig_head); todo_list_release(&new_todo); return -1; @@ -4805,7 +4812,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla todo_list_release(&new_todo); - if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head)) + if (checkout_onto(r, opts, onto_name, oid_to_hex(&oid), orig_head)) return -1; if (require_clean_work_tree(r, "rebase", "", 1, 1)) diff --git a/sequencer.h b/sequencer.h index a515ee4..6c55aa4 100644 --- a/sequencer.h +++ b/sequencer.h @@ -175,7 +175,8 @@ void commit_post_rewrite(struct repository *r, const struct commit *current_head, const struct object_id *new_head); -int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit); +int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts, + const char *commit); #define SUMMARY_INITIAL_COMMIT (1 << 0) #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1) -- cgit v0.10.2-6-g49f6 From 146839c4b69ec47a356eb1156f2093ac5cc2cb60 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:34 +0100 Subject: rebase: don't translate trace strings commit b3a5d5a80c ("trace2:data: add subverb for rebase", 2019-02-22) mistakenly marked the subverb names for translation and unnecessarily NULL terminated the array. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 52114cb..239a54e 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1027,14 +1027,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) ACTION_EDIT_TODO, ACTION_SHOW_CURRENT_PATCH, } action = NO_ACTION; - static const char *action_names[] = { N_("undefined"), - N_("continue"), - N_("skip"), - N_("abort"), - N_("quit"), - N_("edit_todo"), - N_("show_current_patch"), - NULL }; + static const char *action_names[] = { "undefined", + "continue", + "skip", + "abort", + "quit", + "edit_todo", + "show_current_patch" }; const char *gpg_sign = NULL; struct string_list exec = STRING_LIST_INIT_NODUP; const char *rebase_merges = NULL; -- cgit v0.10.2-6-g49f6 From 28dc09de6d9dbe8270c60177948e50a13c9933ab Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:35 +0100 Subject: rebase: rename write_basic_state() This clashes with a function in sequencer.c Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 239a54e..0b1a193 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -245,7 +245,7 @@ static int read_basic_state(struct rebase_options *opts) return 0; } -static int write_basic_state(struct rebase_options *opts) +static int rebase_write_basic_state(struct rebase_options *opts) { write_file(state_dir_path("head-name", opts), "%s", opts->head_name ? opts->head_name : "detached HEAD"); @@ -640,7 +640,7 @@ static int run_am(struct rebase_options *opts) } if (is_directory(opts->state_dir)) - write_basic_state(opts); + rebase_write_basic_state(opts); return status; } -- cgit v0.10.2-6-g49f6 From 6023c928aa07128b4270446e7735beb20b2c8096 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:36 +0100 Subject: rebase: use OPT_RERERE_AUTOUPDATE() As we have a macro for this it makes sense to use it. Having cmd_rebase() and cmd_rebase__interactive() use the same values for this option will be helpful when we start running interactive rebases without forking rebase--interactive. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 0b1a193..e7b8122 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -206,14 +206,13 @@ static int read_basic_state(struct rebase_options *opts) &buf)) return -1; if (!strcmp(buf.buf, "--rerere-autoupdate")) - opts->allow_rerere_autoupdate = 1; + opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE; else if (!strcmp(buf.buf, "--no-rerere-autoupdate")) - opts->allow_rerere_autoupdate = 0; + opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE; else warning(_("ignoring invalid allow_rerere_autoupdate: " "'%s'"), buf.buf); - } else - opts->allow_rerere_autoupdate = -1; + } if (file_exists(state_dir_path("gpg_sign_opt", opts))) { strbuf_reset(&buf); @@ -263,10 +262,11 @@ static int rebase_write_basic_state(struct rebase_options *opts) if (opts->strategy_opts) write_file(state_dir_path("strategy_opts", opts), "%s", opts->strategy_opts); - if (opts->allow_rerere_autoupdate >= 0) + if (opts->allow_rerere_autoupdate > 0) write_file(state_dir_path("allow_rerere_autoupdate", opts), "-%s-rerere-autoupdate", - opts->allow_rerere_autoupdate ? "" : "-no"); + opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ? + "" : "-no"); if (opts->gpg_sign_opt) write_file(state_dir_path("gpg_sign_opt", opts), "%s", opts->gpg_sign_opt); @@ -625,9 +625,9 @@ static int run_am(struct rebase_options *opts) argv_array_push(&am.args, "--rebasing"); argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); argv_array_push(&am.args, "--patch-format=mboxrd"); - if (opts->allow_rerere_autoupdate > 0) + if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) argv_array_push(&am.args, "--rerere-autoupdate"); - else if (opts->allow_rerere_autoupdate == 0) + else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE) argv_array_push(&am.args, "--no-rerere-autoupdate"); if (opts->gpg_sign_opt) argv_array_push(&am.args, opts->gpg_sign_opt); @@ -713,9 +713,9 @@ static int run_specific_rebase(struct rebase_options *opts) argv_array_pushf(&child.args, "--cmd=%s", opts->cmd); if (opts->allow_empty_message) argv_array_push(&child.args, "--allow-empty-message"); - if (opts->allow_rerere_autoupdate > 0) + if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) argv_array_push(&child.args, "--rerere-autoupdate"); - else if (opts->allow_rerere_autoupdate == 0) + else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE) argv_array_push(&child.args, "--no-rerere-autoupdate"); if (opts->gpg_sign_opt) argv_array_push(&child.args, opts->gpg_sign_opt); @@ -764,9 +764,9 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "action", opts->action ? opts->action : ""); add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : ""); add_var(&script_snippet, "allow_rerere_autoupdate", - opts->allow_rerere_autoupdate < 0 ? "" : opts->allow_rerere_autoupdate ? - "--rerere-autoupdate" : "--no-rerere-autoupdate"); + opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ? + "--rerere-autoupdate" : "--no-rerere-autoupdate" : ""); add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : ""); add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : ""); add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt); @@ -1007,7 +1007,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) .type = REBASE_UNSPECIFIED, .flags = REBASE_NO_QUIET, .git_am_opts = ARGV_ARRAY_INIT, - .allow_rerere_autoupdate = -1, .allow_empty_message = 1, .git_format_patch_opt = STRBUF_INIT, }; @@ -1101,10 +1100,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_SET_INT('p', "preserve-merges", &options.type, N_("try to recreate merges instead of ignoring " "them"), REBASE_PRESERVE_MERGES), - OPT_BOOL(0, "rerere-autoupdate", - &options.allow_rerere_autoupdate, - N_("allow rerere to update index with resolved " - "conflict")), + OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), OPT_BOOL('k', "keep-empty", &options.keep_empty, N_("preserve empty commits during rebase")), OPT_BOOL(0, "autosquash", &options.autosquash, -- cgit v0.10.2-6-g49f6 From 0609b741a43dee2934ceb76850fea5963ab0c8f6 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:37 +0100 Subject: rebase -i: combine rebase--interactive.c with rebase.c In order to run `rebase -i` without forking `rebase--interactive` it will be convenient to have all the code from rebase--interactive.c in rebase.c. This is a straight forward copy of the code from rebase--interactive.c, it will be simplified slightly in the next commit. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 5374938..c644089 100644 --- a/Makefile +++ b/Makefile @@ -1130,7 +1130,6 @@ BUILTIN_OBJS += builtin/push.o BUILTIN_OBJS += builtin/range-diff.o BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase.o -BUILTIN_OBJS += builtin/rebase--interactive.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o BUILTIN_OBJS += builtin/remote.o diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c deleted file mode 100644 index d1a4ac1..0000000 --- a/builtin/rebase--interactive.c +++ /dev/null @@ -1,377 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "parse-options.h" -#include "sequencer.h" -#include "rebase-interactive.h" -#include "argv-array.h" -#include "refs.h" -#include "rerere.h" -#include "run-command.h" - -static GIT_PATH_FUNC(path_state_dir, "rebase-merge/") -static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto") -static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive") - -static int add_exec_commands(struct string_list *commands) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - todo_list_add_exec_commands(&todo_list, commands); - res = todo_list_write_to_file(the_repository, &todo_list, - todo_file, NULL, NULL, -1, 0); - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int rearrange_squash_in_todo_file(void) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res = 0; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - res = todo_list_rearrange_squash(&todo_list); - if (!res) - res = todo_list_write_to_file(the_repository, &todo_list, - todo_file, NULL, NULL, -1, 0); - - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int transform_todo_file(unsigned flags) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - res = todo_list_write_to_file(the_repository, &todo_list, todo_file, - NULL, NULL, -1, flags); - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int edit_todo_file(unsigned flags) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT, - new_todo = TODO_LIST_INIT; - int res = 0; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - strbuf_stripspace(&todo_list.buf, 1); - res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); - if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, - NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) - res = error_errno(_("could not write '%s'"), todo_file); - - todo_list_release(&todo_list); - todo_list_release(&new_todo); - - return res; -} - -static int get_revision_ranges(const char *upstream, const char *onto, - const char **head_hash, - char **revisions, char **shortrevisions) -{ - const char *base_rev = upstream ? upstream : onto, *shorthead; - struct object_id orig_head; - - if (get_oid("HEAD", &orig_head)) - return error(_("no HEAD?")); - - *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ); - *revisions = xstrfmt("%s...%s", base_rev, *head_hash); - - shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV); - - if (upstream) { - const char *shortrev; - struct object_id rev_oid; - - get_oid(base_rev, &rev_oid); - shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV); - - *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); - } else - *shortrevisions = xstrdup(shorthead); - - return 0; -} - -static int init_basic_state(struct replay_opts *opts, const char *head_name, - const char *onto, const char *orig_head) -{ - FILE *interactive; - - if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir())) - return error_errno(_("could not create temporary %s"), path_state_dir()); - - delete_reflog("REBASE_HEAD"); - - interactive = fopen(path_interactive(), "w"); - if (!interactive) - return error_errno(_("could not mark as interactive")); - fclose(interactive); - - return write_basic_state(opts, head_name, onto, orig_head); -} - -static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, - const char *switch_to, const char *upstream, - const char *onto, const char *onto_name, - const char *squash_onto, const char *head_name, - const char *restrict_revision, char *raw_strategies, - struct string_list *commands, unsigned autosquash) -{ - int ret; - const char *head_hash = NULL; - char *revisions = NULL, *shortrevisions = NULL; - struct argv_array make_script_args = ARGV_ARRAY_INIT; - struct todo_list todo_list = TODO_LIST_INIT; - - if (prepare_branch_to_be_rebased(the_repository, opts, switch_to)) - return -1; - - if (get_revision_ranges(upstream, onto, &head_hash, - &revisions, &shortrevisions)) - return -1; - - if (raw_strategies) - parse_strategy_opts(opts, raw_strategies); - - if (init_basic_state(opts, head_name, onto, head_hash)) { - free(revisions); - free(shortrevisions); - - return -1; - } - - if (!upstream && squash_onto) - write_file(path_squash_onto(), "%s\n", squash_onto); - - argv_array_pushl(&make_script_args, "", revisions, NULL); - if (restrict_revision) - argv_array_push(&make_script_args, restrict_revision); - - ret = sequencer_make_script(the_repository, &todo_list.buf, - make_script_args.argc, make_script_args.argv, - flags); - - if (ret) - error(_("could not generate todo list")); - else { - discard_cache(); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) - BUG("unusable todo list"); - - ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name, - onto, head_hash, commands, autosquash, &todo_list); - } - - free(revisions); - free(shortrevisions); - todo_list_release(&todo_list); - argv_array_clear(&make_script_args); - - return ret; -} - -static const char * const builtin_rebase_interactive_usage[] = { - N_("git rebase--interactive []"), - NULL -}; - -int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) -{ - struct replay_opts opts = REPLAY_OPTS_INIT; - unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; - int abbreviate_commands = 0, rebase_cousins = -1, ret = 0; - const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL, - *squash_onto = NULL, *upstream = NULL, *head_name = NULL, - *switch_to = NULL, *cmd = NULL; - struct string_list commands = STRING_LIST_INIT_DUP; - char *raw_strategies = NULL; - enum { - NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH, - SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC - } command = 0; - struct option options[] = { - OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")), - OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), - OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, - N_("allow commits with empty messages")), - OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")), - OPT_BOOL(0, "rebase-cousins", &rebase_cousins, - N_("keep original branch points of cousins")), - OPT_BOOL(0, "autosquash", &autosquash, - N_("move commits that begin with squash!/fixup!")), - OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")), - OPT__VERBOSE(&opts.verbose, N_("be verbose")), - OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), - CONTINUE), - OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP), - OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"), - EDIT_TODO), - OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"), - SHOW_CURRENT_PATCH), - OPT_CMDMODE(0, "shorten-ids", &command, - N_("shorten commit ids in the todo list"), SHORTEN_OIDS), - OPT_CMDMODE(0, "expand-ids", &command, - N_("expand commit ids in the todo list"), EXPAND_OIDS), - OPT_CMDMODE(0, "check-todo-list", &command, - N_("check the todo list"), CHECK_TODO_LIST), - OPT_CMDMODE(0, "rearrange-squash", &command, - N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), - OPT_CMDMODE(0, "add-exec-commands", &command, - N_("insert exec commands in todo list"), ADD_EXEC), - OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")), - OPT_STRING(0, "restrict-revision", &restrict_revision, - N_("restrict-revision"), N_("restrict revision")), - OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"), - N_("squash onto")), - OPT_STRING(0, "upstream", &upstream, N_("upstream"), - N_("the upstream commit")), - OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")), - { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"), - N_("rebase strategy")), - OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"), - N_("strategy options")), - OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"), - N_("the branch or commit to checkout")), - OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")), - OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")), - OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto), - OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec, - N_("automatically re-schedule any `exec` that fails")), - OPT_END() - }; - - sequencer_init_config(&opts); - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); - - opts.action = REPLAY_INTERACTIVE_REBASE; - opts.allow_ff = 1; - opts.allow_empty = 1; - - if (argc == 1) - usage_with_options(builtin_rebase_interactive_usage, options); - - argc = parse_options(argc, argv, NULL, options, - builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); - - opts.gpg_sign = xstrdup_or_null(opts.gpg_sign); - - flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; - flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; - flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; - flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; - flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; - - if (rebase_cousins >= 0 && !rebase_merges) - warning(_("--[no-]rebase-cousins has no effect without " - "--rebase-merges")); - - if (cmd && *cmd) { - string_list_split(&commands, cmd, '\n', -1); - - /* rebase.c adds a new line to cmd after every command, - * so here the last command is always empty */ - string_list_remove_empty_items(&commands, 0); - } - - switch (command) { - case NONE: - if (!onto && !upstream) - die(_("a base commit must be provided with --upstream or --onto")); - - ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto, - onto_name, squash_onto, head_name, restrict_revision, - raw_strategies, &commands, autosquash); - break; - case SKIP: { - struct string_list merge_rr = STRING_LIST_INIT_DUP; - - rerere_clear(the_repository, &merge_rr); - /* fallthrough */ - case CONTINUE: - ret = sequencer_continue(the_repository, &opts); - break; - } - case EDIT_TODO: - ret = edit_todo_file(flags); - break; - case SHOW_CURRENT_PATCH: { - struct child_process cmd = CHILD_PROCESS_INIT; - - cmd.git_cmd = 1; - argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); - ret = run_command(&cmd); - - break; - } - case SHORTEN_OIDS: - case EXPAND_OIDS: - ret = transform_todo_file(flags); - break; - case CHECK_TODO_LIST: - ret = check_todo_list_from_file(the_repository); - break; - case REARRANGE_SQUASH: - ret = rearrange_squash_in_todo_file(); - break; - case ADD_EXEC: - ret = add_exec_commands(&commands); - break; - default: - BUG("invalid command '%d'", command); - } - - string_list_clear(&commands, 0); - return !!ret; -} diff --git a/builtin/rebase.c b/builtin/rebase.c index e7b8122..57a3c9c 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -25,6 +25,8 @@ #include "commit-reach.h" #include "rerere.h" #include "branch.h" +#include "sequencer.h" +#include "rebase-interactive.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec ] [--onto ] " @@ -35,6 +37,9 @@ static char const * const builtin_rebase_usage[] = { NULL }; +static GIT_PATH_FUNC(path_state_dir, "rebase-merge/") +static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto") +static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive") static GIT_PATH_FUNC(apply_dir, "rebase-apply") static GIT_PATH_FUNC(merge_dir, "rebase-merge") @@ -46,6 +51,368 @@ enum rebase_type { REBASE_PRESERVE_MERGES }; +static int add_exec_commands(struct string_list *commands) +{ + const char *todo_file = rebase_path_todo(); + struct todo_list todo_list = TODO_LIST_INIT; + int res; + + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) + return error_errno(_("could not read '%s'."), todo_file); + + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, + &todo_list)) { + todo_list_release(&todo_list); + return error(_("unusable todo list: '%s'"), todo_file); + } + + todo_list_add_exec_commands(&todo_list, commands); + res = todo_list_write_to_file(the_repository, &todo_list, + todo_file, NULL, NULL, -1, 0); + todo_list_release(&todo_list); + + if (res) + return error_errno(_("could not write '%s'."), todo_file); + return 0; +} + +static int rearrange_squash_in_todo_file(void) +{ + const char *todo_file = rebase_path_todo(); + struct todo_list todo_list = TODO_LIST_INIT; + int res = 0; + + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) + return error_errno(_("could not read '%s'."), todo_file); + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, + &todo_list)) { + todo_list_release(&todo_list); + return error(_("unusable todo list: '%s'"), todo_file); + } + + res = todo_list_rearrange_squash(&todo_list); + if (!res) + res = todo_list_write_to_file(the_repository, &todo_list, + todo_file, NULL, NULL, -1, 0); + + todo_list_release(&todo_list); + + if (res) + return error_errno(_("could not write '%s'."), todo_file); + return 0; +} + +static int transform_todo_file(unsigned flags) +{ + const char *todo_file = rebase_path_todo(); + struct todo_list todo_list = TODO_LIST_INIT; + int res; + + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) + return error_errno(_("could not read '%s'."), todo_file); + + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, + &todo_list)) { + todo_list_release(&todo_list); + return error(_("unusable todo list: '%s'"), todo_file); + } + + res = todo_list_write_to_file(the_repository, &todo_list, todo_file, + NULL, NULL, -1, flags); + todo_list_release(&todo_list); + + if (res) + return error_errno(_("could not write '%s'."), todo_file); + return 0; +} + +static int edit_todo_file(unsigned flags) +{ + const char *todo_file = rebase_path_todo(); + struct todo_list todo_list = TODO_LIST_INIT, + new_todo = TODO_LIST_INIT; + int res = 0; + + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) + return error_errno(_("could not read '%s'."), todo_file); + + strbuf_stripspace(&todo_list.buf, 1); + res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); + if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, + NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) + res = error_errno(_("could not write '%s'"), todo_file); + + todo_list_release(&todo_list); + todo_list_release(&new_todo); + + return res; +} + +static int get_revision_ranges(const char *upstream, const char *onto, + const char **head_hash, + char **revisions, char **shortrevisions) +{ + const char *base_rev = upstream ? upstream : onto, *shorthead; + struct object_id orig_head; + + if (get_oid("HEAD", &orig_head)) + return error(_("no HEAD?")); + + *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ); + *revisions = xstrfmt("%s...%s", base_rev, *head_hash); + + shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV); + + if (upstream) { + const char *shortrev; + struct object_id rev_oid; + + get_oid(base_rev, &rev_oid); + shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV); + + *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); + } else + *shortrevisions = xstrdup(shorthead); + + return 0; +} + +static int init_basic_state(struct replay_opts *opts, const char *head_name, + const char *onto, const char *orig_head) +{ + FILE *interactive; + + if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir())) + return error_errno(_("could not create temporary %s"), path_state_dir()); + + delete_reflog("REBASE_HEAD"); + + interactive = fopen(path_interactive(), "w"); + if (!interactive) + return error_errno(_("could not mark as interactive")); + fclose(interactive); + + return write_basic_state(opts, head_name, onto, orig_head); +} + +static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, + const char *switch_to, const char *upstream, + const char *onto, const char *onto_name, + const char *squash_onto, const char *head_name, + const char *restrict_revision, char *raw_strategies, + struct string_list *commands, unsigned autosquash) +{ + int ret; + const char *head_hash = NULL; + char *revisions = NULL, *shortrevisions = NULL; + struct argv_array make_script_args = ARGV_ARRAY_INIT; + struct todo_list todo_list = TODO_LIST_INIT; + + if (prepare_branch_to_be_rebased(the_repository, opts, switch_to)) + return -1; + + if (get_revision_ranges(upstream, onto, &head_hash, + &revisions, &shortrevisions)) + return -1; + + if (raw_strategies) + parse_strategy_opts(opts, raw_strategies); + + if (init_basic_state(opts, head_name, onto, head_hash)) { + free(revisions); + free(shortrevisions); + + return -1; + } + + if (!upstream && squash_onto) + write_file(path_squash_onto(), "%s\n", squash_onto); + + argv_array_pushl(&make_script_args, "", revisions, NULL); + if (restrict_revision) + argv_array_push(&make_script_args, restrict_revision); + + ret = sequencer_make_script(the_repository, &todo_list.buf, + make_script_args.argc, make_script_args.argv, + flags); + + if (ret) + error(_("could not generate todo list")); + else { + discard_cache(); + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, + &todo_list)) + BUG("unusable todo list"); + + ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name, + onto, head_hash, commands, autosquash, &todo_list); + } + + free(revisions); + free(shortrevisions); + todo_list_release(&todo_list); + argv_array_clear(&make_script_args); + + return ret; +} + +static const char * const builtin_rebase_interactive_usage[] = { + N_("git rebase--interactive []"), + NULL +}; + +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) +{ + struct replay_opts opts = REPLAY_OPTS_INIT; + unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; + int abbreviate_commands = 0, rebase_cousins = -1, ret = 0; + const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL, + *squash_onto = NULL, *upstream = NULL, *head_name = NULL, + *switch_to = NULL, *cmd = NULL; + struct string_list commands = STRING_LIST_INIT_DUP; + char *raw_strategies = NULL; + enum { + NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH, + SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC + } command = 0; + struct option options[] = { + OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")), + OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), + OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, + N_("allow commits with empty messages")), + OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")), + OPT_BOOL(0, "rebase-cousins", &rebase_cousins, + N_("keep original branch points of cousins")), + OPT_BOOL(0, "autosquash", &autosquash, + N_("move commits that begin with squash!/fixup!")), + OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")), + OPT__VERBOSE(&opts.verbose, N_("be verbose")), + OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), + CONTINUE), + OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP), + OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"), + EDIT_TODO), + OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"), + SHOW_CURRENT_PATCH), + OPT_CMDMODE(0, "shorten-ids", &command, + N_("shorten commit ids in the todo list"), SHORTEN_OIDS), + OPT_CMDMODE(0, "expand-ids", &command, + N_("expand commit ids in the todo list"), EXPAND_OIDS), + OPT_CMDMODE(0, "check-todo-list", &command, + N_("check the todo list"), CHECK_TODO_LIST), + OPT_CMDMODE(0, "rearrange-squash", &command, + N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), + OPT_CMDMODE(0, "add-exec-commands", &command, + N_("insert exec commands in todo list"), ADD_EXEC), + OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")), + OPT_STRING(0, "restrict-revision", &restrict_revision, + N_("restrict-revision"), N_("restrict revision")), + OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"), + N_("squash onto")), + OPT_STRING(0, "upstream", &upstream, N_("upstream"), + N_("the upstream commit")), + OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")), + { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"), + N_("GPG-sign commits"), + PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"), + N_("rebase strategy")), + OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"), + N_("strategy options")), + OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"), + N_("the branch or commit to checkout")), + OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")), + OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")), + OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto), + OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec, + N_("automatically re-schedule any `exec` that fails")), + OPT_END() + }; + + sequencer_init_config(&opts); + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); + + opts.action = REPLAY_INTERACTIVE_REBASE; + opts.allow_ff = 1; + opts.allow_empty = 1; + + if (argc == 1) + usage_with_options(builtin_rebase_interactive_usage, options); + + argc = parse_options(argc, argv, NULL, options, + builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); + + opts.gpg_sign = xstrdup_or_null(opts.gpg_sign); + + flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; + flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; + flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; + flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + + if (rebase_cousins >= 0 && !rebase_merges) + warning(_("--[no-]rebase-cousins has no effect without " + "--rebase-merges")); + + if (cmd && *cmd) { + string_list_split(&commands, cmd, '\n', -1); + + /* rebase.c adds a new line to cmd after every command, + * so here the last command is always empty */ + string_list_remove_empty_items(&commands, 0); + } + + switch (command) { + case NONE: + if (!onto && !upstream) + die(_("a base commit must be provided with --upstream or --onto")); + + ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto, + onto_name, squash_onto, head_name, restrict_revision, + raw_strategies, &commands, autosquash); + break; + case SKIP: { + struct string_list merge_rr = STRING_LIST_INIT_DUP; + + rerere_clear(the_repository, &merge_rr); + /* fallthrough */ + case CONTINUE: + ret = sequencer_continue(the_repository, &opts); + break; + } + case EDIT_TODO: + ret = edit_todo_file(flags); + break; + case SHOW_CURRENT_PATCH: { + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); + ret = run_command(&cmd); + + break; + } + case SHORTEN_OIDS: + case EXPAND_OIDS: + ret = transform_todo_file(flags); + break; + case CHECK_TODO_LIST: + ret = check_todo_list_from_file(the_repository); + break; + case REARRANGE_SQUASH: + ret = rearrange_squash_in_todo_file(); + break; + case ADD_EXEC: + ret = add_exec_commands(&commands); + break; + default: + BUG("invalid command '%d'", command); + } + + string_list_clear(&commands, 0); + return !!ret; +} + static int use_builtin_rebase(void) { struct child_process cp = CHILD_PROCESS_INIT; -- cgit v0.10.2-6-g49f6 From c44c24621da1cf45e2525b42d90982c78bffc207 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:38 +0100 Subject: rebase -i: remove duplication path_state_dir() and merge_dir() refer to the same path so remove one of them. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 57a3c9c..610b678 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -37,7 +37,6 @@ static char const * const builtin_rebase_usage[] = { NULL }; -static GIT_PATH_FUNC(path_state_dir, "rebase-merge/") static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto") static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive") static GIT_PATH_FUNC(apply_dir, "rebase-apply") @@ -182,8 +181,8 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, { FILE *interactive; - if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir())) - return error_errno(_("could not create temporary %s"), path_state_dir()); + if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir())) + return error_errno(_("could not create temporary %s"), merge_dir()); delete_reflog("REBASE_HEAD"); -- cgit v0.10.2-6-g49f6 From 7d3488eb893ba53186770181171ee8ebc8100e18 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:39 +0100 Subject: rebase -i: use struct commit when parsing options This is in preparation for using `struct rebase_options` when parsing options in cmd_rebase__interactive(). Using a string for onto, restrict_revision and upstream, was a hangover from the scripted version of rebase. The functions that use these variables are updated to take a `struct commit`. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 610b678..06f6490 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -147,27 +147,28 @@ static int edit_todo_file(unsigned flags) return res; } -static int get_revision_ranges(const char *upstream, const char *onto, +static int get_revision_ranges(struct commit *upstream, struct commit *onto, const char **head_hash, char **revisions, char **shortrevisions) { - const char *base_rev = upstream ? upstream : onto, *shorthead; + struct commit *base_rev = upstream ? upstream : onto; + const char *shorthead; struct object_id orig_head; if (get_oid("HEAD", &orig_head)) return error(_("no HEAD?")); *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ); - *revisions = xstrfmt("%s...%s", base_rev, *head_hash); + *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid), + *head_hash); shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV); if (upstream) { const char *shortrev; - struct object_id rev_oid; - get_oid(base_rev, &rev_oid); - shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV); + shortrev = find_unique_abbrev(&base_rev->object.oid, + DEFAULT_ABBREV); *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); } else @@ -177,7 +178,7 @@ static int get_revision_ranges(const char *upstream, const char *onto, } static int init_basic_state(struct replay_opts *opts, const char *head_name, - const char *onto, const char *orig_head) + struct commit *onto, const char *orig_head) { FILE *interactive; @@ -195,10 +196,10 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, } static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, - const char *switch_to, const char *upstream, - const char *onto, const char *onto_name, + const char *switch_to, struct commit *upstream, + struct commit *onto, const char *onto_name, const char *squash_onto, const char *head_name, - const char *restrict_revision, char *raw_strategies, + struct commit *restrict_revision, char *raw_strategies, struct string_list *commands, unsigned autosquash) { int ret; @@ -229,7 +230,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, argv_array_pushl(&make_script_args, "", revisions, NULL); if (restrict_revision) - argv_array_push(&make_script_args, restrict_revision); + argv_array_push(&make_script_args, + oid_to_hex(&restrict_revision->object.oid)); ret = sequencer_make_script(the_repository, &todo_list.buf, make_script_args.argc, make_script_args.argv, @@ -265,9 +267,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) struct replay_opts opts = REPLAY_OPTS_INIT; unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; int abbreviate_commands = 0, rebase_cousins = -1, ret = 0; - const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL, - *squash_onto = NULL, *upstream = NULL, *head_name = NULL, + const char *onto_name = NULL, + *squash_onto = NULL, *head_name = NULL, *switch_to = NULL, *cmd = NULL; + struct commit *onto = NULL, *upstream = NULL, *restrict_revision = NULL; struct string_list commands = STRING_LIST_INIT_DUP; char *raw_strategies = NULL; enum { @@ -303,13 +306,16 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), OPT_CMDMODE(0, "add-exec-commands", &command, N_("insert exec commands in todo list"), ADD_EXEC), - OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")), - OPT_STRING(0, "restrict-revision", &restrict_revision, - N_("restrict-revision"), N_("restrict revision")), + { OPTION_CALLBACK, 0, "onto", &onto, N_("onto"), N_("onto"), + PARSE_OPT_NONEG, parse_opt_commit, 0 }, + { OPTION_CALLBACK, 0, "restrict-revision", &restrict_revision, + N_("restrict-revision"), N_("restrict revision"), + PARSE_OPT_NONEG, parse_opt_commit, 0 }, OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"), N_("squash onto")), - OPT_STRING(0, "upstream", &upstream, N_("upstream"), - N_("the upstream commit")), + { OPTION_CALLBACK, 0, "upstream", &upstream, N_("upstream"), + N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit, + 0 }, OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")), { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"), N_("GPG-sign commits"), diff --git a/parse-options-cb.c b/parse-options-cb.c index 2733393..2206eb7 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -96,6 +96,23 @@ int parse_opt_commits(const struct option *opt, const char *arg, int unset) return 0; } +int parse_opt_commit(const struct option *opt, const char *arg, int unset) +{ + struct object_id oid; + struct commit *commit; + struct commit **target = opt->value; + + if (!arg) + return -1; + if (get_oid(arg, &oid)) + return error("malformed object name %s", arg); + commit = lookup_commit_reference(the_repository, &oid); + if (!commit) + return error("no such commit %s", arg); + *target = commit; + return 0; +} + int parse_opt_object_name(const struct option *opt, const char *arg, int unset) { struct object_id oid; diff --git a/parse-options.h b/parse-options.h index 7d83e29..5a75646 100644 --- a/parse-options.h +++ b/parse-options.h @@ -266,6 +266,7 @@ int parse_opt_color_flag_cb(const struct option *, const char *, int); int parse_opt_verbosity_cb(const struct option *, const char *, int); int parse_opt_object_name(const struct option *, const char *, int); int parse_opt_commits(const struct option *, const char *, int); +int parse_opt_commit(const struct option *, const char *, int); int parse_opt_tertiary(const struct option *, const char *, int); int parse_opt_string_list(const struct option *, const char *, int); int parse_opt_noop_cb(const struct option *, const char *, int); diff --git a/sequencer.c b/sequencer.c index ccc0160..610b7ec 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2418,14 +2418,15 @@ static void write_strategy_opts(struct replay_opts *opts) } int write_basic_state(struct replay_opts *opts, const char *head_name, - const char *onto, const char *orig_head) + struct commit *onto, const char *orig_head) { const char *quiet = getenv("GIT_QUIET"); if (head_name) write_file(rebase_path_head_name(), "%s\n", head_name); if (onto) - write_file(rebase_path_onto(), "%s\n", onto); + write_file(rebase_path_onto(), "%s\n", + oid_to_hex(&onto->object.oid)); if (orig_head) write_file(rebase_path_orig_head(), "%s\n", orig_head); @@ -3456,7 +3457,7 @@ int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts, } static int checkout_onto(struct repository *r, struct replay_opts *opts, - const char *onto_name, const char *onto, + const char *onto_name, const struct object_id *onto, const char *orig_head) { struct object_id oid; @@ -3465,7 +3466,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts, if (get_oid(orig_head, &oid)) return error(_("%s: not a valid OID"), orig_head); - if (run_git_checkout(r, opts, onto, action)) { + if (run_git_checkout(r, opts, oid_to_hex(onto), action)) { apply_autostash(opts); sequencer_remove_state(opts); return error(_("could not detach HEAD")); @@ -4741,16 +4742,16 @@ static int skip_unnecessary_picks(struct repository *r, int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags, const char *shortrevisions, const char *onto_name, - const char *onto, const char *orig_head, struct string_list *commands, - unsigned autosquash, struct todo_list *todo_list) + struct commit *onto, const char *orig_head, + struct string_list *commands, unsigned autosquash, + struct todo_list *todo_list) { const char *shortonto, *todo_file = rebase_path_todo(); struct todo_list new_todo = TODO_LIST_INIT; struct strbuf *buf = &todo_list->buf; - struct object_id oid; + struct object_id oid = onto->object.oid; int res; - get_oid(onto, &oid); shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV); if (buf->len == 0) { @@ -4793,7 +4794,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) || todo_list_check(todo_list, &new_todo)) { fprintf(stderr, _(edit_todo_list_advice)); - checkout_onto(r, opts, onto_name, onto, orig_head); + checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head); todo_list_release(&new_todo); return -1; @@ -4812,7 +4813,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla todo_list_release(&new_todo); - if (checkout_onto(r, opts, onto_name, oid_to_hex(&oid), orig_head)) + if (checkout_onto(r, opts, onto_name, &oid, orig_head)) return -1; if (require_clean_work_tree(r, "rebase", "", 1, 1)) diff --git a/sequencer.h b/sequencer.h index 6c55aa4..e640ca2 100644 --- a/sequencer.h +++ b/sequencer.h @@ -150,7 +150,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list, int check_todo_list_from_file(struct repository *r); int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags, const char *shortrevisions, const char *onto_name, - const char *onto, const char *orig_head, struct string_list *commands, + struct commit *onto, const char *orig_head, struct string_list *commands, unsigned autosquash, struct todo_list *todo_list); int todo_list_rearrange_squash(struct todo_list *todo_list); @@ -191,4 +191,4 @@ int read_author_script(const char *path, char **name, char **email, char **date, void parse_strategy_opts(struct replay_opts *opts, char *raw_opts); int write_basic_state(struct replay_opts *opts, const char *head_name, - const char *onto, const char *orig_head); + struct commit *onto, const char *orig_head); -- cgit v0.10.2-6-g49f6 From 338985317eb84007f0d08979e751cbf32a375a52 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:40 +0100 Subject: rebase -i: use struct object_id for squash_onto More preparation for using `struct rebase_options` in cmd_rebase__interactive(). Using a string was a hangover from the scripted version of rebase, update the functions that use `squash_onto` to take a `sturct object_id`. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 06f6490..3b9da69 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -198,7 +198,7 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, const char *switch_to, struct commit *upstream, struct commit *onto, const char *onto_name, - const char *squash_onto, const char *head_name, + struct object_id *squash_onto, const char *head_name, struct commit *restrict_revision, char *raw_strategies, struct string_list *commands, unsigned autosquash) { @@ -226,7 +226,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, } if (!upstream && squash_onto) - write_file(path_squash_onto(), "%s\n", squash_onto); + write_file(path_squash_onto(), "%s\n", + oid_to_hex(squash_onto)); argv_array_pushl(&make_script_args, "", revisions, NULL); if (restrict_revision) @@ -267,10 +268,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) struct replay_opts opts = REPLAY_OPTS_INIT; unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; int abbreviate_commands = 0, rebase_cousins = -1, ret = 0; - const char *onto_name = NULL, - *squash_onto = NULL, *head_name = NULL, - *switch_to = NULL, *cmd = NULL; + const char *onto_name = NULL, *head_name = NULL, *switch_to = NULL, + *cmd = NULL; struct commit *onto = NULL, *upstream = NULL, *restrict_revision = NULL; + struct object_id squash_onto = null_oid; + struct object_id *squash_onto_opt = NULL; struct string_list commands = STRING_LIST_INIT_DUP; char *raw_strategies = NULL; enum { @@ -311,8 +313,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "restrict-revision", &restrict_revision, N_("restrict-revision"), N_("restrict revision"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, - OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"), - N_("squash onto")), + { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"), + N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 }, { OPTION_CALLBACK, 0, "upstream", &upstream, N_("upstream"), N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, @@ -349,6 +351,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) opts.gpg_sign = xstrdup_or_null(opts.gpg_sign); + if (!is_null_oid(&squash_onto)) + squash_onto_opt = &squash_onto; + flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; @@ -373,7 +378,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) die(_("a base commit must be provided with --upstream or --onto")); ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto, - onto_name, squash_onto, head_name, restrict_revision, + onto_name, squash_onto_opt, head_name, restrict_revision, raw_strategies, &commands, autosquash); break; case SKIP: { diff --git a/parse-options-cb.c b/parse-options-cb.c index 2206eb7..28ad5cd 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -129,6 +129,23 @@ int parse_opt_object_name(const struct option *opt, const char *arg, int unset) return 0; } +int parse_opt_object_id(const struct option *opt, const char *arg, int unset) +{ + struct object_id oid; + struct object_id *target = opt->value; + + if (unset) { + *target = null_oid; + return 0; + } + if (!arg) + return -1; + if (get_oid(arg, &oid)) + return error(_("malformed object name '%s'"), arg); + *target = oid; + return 0; +} + int parse_opt_tertiary(const struct option *opt, const char *arg, int unset) { int *target = opt->value; diff --git a/parse-options.h b/parse-options.h index 5a75646..0ab1103 100644 --- a/parse-options.h +++ b/parse-options.h @@ -264,7 +264,10 @@ int parse_opt_abbrev_cb(const struct option *, const char *, int); int parse_opt_expiry_date_cb(const struct option *, const char *, int); int parse_opt_color_flag_cb(const struct option *, const char *, int); int parse_opt_verbosity_cb(const struct option *, const char *, int); +/* value is struct oid_array* */ int parse_opt_object_name(const struct option *, const char *, int); +/* value is struct object_id* */ +int parse_opt_object_id(const struct option *, const char *, int); int parse_opt_commits(const struct option *, const char *, int); int parse_opt_commit(const struct option *, const char *, int); int parse_opt_tertiary(const struct option *, const char *, int); -- cgit v0.10.2-6-g49f6 From 73fdc535d269ba1ac25cdb832d72d47abdede049 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:41 +0100 Subject: rebase -i: use struct rebase_options to parse args In order to run `rebase -i` without forking `rebase--interactive` it will be convenient to use the same structure when parsing the options in cmd_rebase() and cmd_rebase__interactive(). Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 3b9da69..aa97d81 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -50,6 +50,73 @@ enum rebase_type { REBASE_PRESERVE_MERGES }; +struct rebase_options { + enum rebase_type type; + const char *state_dir; + struct commit *upstream; + const char *upstream_name; + const char *upstream_arg; + char *head_name; + struct object_id orig_head; + struct commit *onto; + const char *onto_name; + const char *revisions; + const char *switch_to; + int root; + struct object_id *squash_onto; + struct commit *restrict_revision; + int dont_finish_rebase; + enum { + REBASE_NO_QUIET = 1<<0, + REBASE_VERBOSE = 1<<1, + REBASE_DIFFSTAT = 1<<2, + REBASE_FORCE = 1<<3, + REBASE_INTERACTIVE_EXPLICIT = 1<<4, + } flags; + struct argv_array git_am_opts; + const char *action; + int signoff; + int allow_rerere_autoupdate; + int keep_empty; + int autosquash; + char *gpg_sign_opt; + int autostash; + char *cmd; + int allow_empty_message; + int rebase_merges, rebase_cousins; + char *strategy, *strategy_opts; + struct strbuf git_format_patch_opt; + int reschedule_failed_exec; +}; + +#define REBASE_OPTIONS_INIT { \ + .type = REBASE_UNSPECIFIED, \ + .flags = REBASE_NO_QUIET, \ + .git_am_opts = ARGV_ARRAY_INIT, \ + .git_format_patch_opt = STRBUF_INIT \ + } + +static struct replay_opts get_replay_opts(const struct rebase_options *opts) +{ + struct replay_opts replay = REPLAY_OPTS_INIT; + + replay.action = REPLAY_INTERACTIVE_REBASE; + sequencer_init_config(&replay); + + replay.signoff = opts->signoff; + replay.allow_ff = !(opts->flags & REBASE_FORCE); + if (opts->allow_rerere_autoupdate) + replay.allow_rerere_auto = opts->allow_rerere_autoupdate; + replay.allow_empty = 1; + replay.allow_empty_message = opts->allow_empty_message; + replay.verbose = opts->flags & REBASE_VERBOSE; + replay.reschedule_failed_exec = opts->reschedule_failed_exec; + replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); + replay.strategy = opts->strategy; + + return replay; +} + static int add_exec_commands(struct string_list *commands) { const char *todo_file = rebase_path_todo(); @@ -265,32 +332,30 @@ static const char * const builtin_rebase_interactive_usage[] = { int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) { - struct replay_opts opts = REPLAY_OPTS_INIT; - unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; - int abbreviate_commands = 0, rebase_cousins = -1, ret = 0; - const char *onto_name = NULL, *head_name = NULL, *switch_to = NULL, - *cmd = NULL; - struct commit *onto = NULL, *upstream = NULL, *restrict_revision = NULL; + struct rebase_options opts = REBASE_OPTIONS_INIT; + unsigned flags = 0; + int abbreviate_commands = 0, ret = 0; struct object_id squash_onto = null_oid; - struct object_id *squash_onto_opt = NULL; struct string_list commands = STRING_LIST_INIT_DUP; - char *raw_strategies = NULL; enum { NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH, SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC } command = 0; struct option options[] = { - OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")), - OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), + OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"), + REBASE_FORCE), + OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")), OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, N_("allow commits with empty messages")), - OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")), - OPT_BOOL(0, "rebase-cousins", &rebase_cousins, + OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")), + OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins, N_("keep original branch points of cousins")), - OPT_BOOL(0, "autosquash", &autosquash, + OPT_BOOL(0, "autosquash", &opts.autosquash, N_("move commits that begin with squash!/fixup!")), OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")), - OPT__VERBOSE(&opts.verbose, N_("be verbose")), + OPT_BIT('v', "verbose", &opts.flags, + N_("display a diffstat of what changed upstream"), + REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), CONTINUE), OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP), @@ -308,40 +373,37 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), OPT_CMDMODE(0, "add-exec-commands", &command, N_("insert exec commands in todo list"), ADD_EXEC), - { OPTION_CALLBACK, 0, "onto", &onto, N_("onto"), N_("onto"), + { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, - { OPTION_CALLBACK, 0, "restrict-revision", &restrict_revision, + { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision, N_("restrict-revision"), N_("restrict revision"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"), N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 }, - { OPTION_CALLBACK, 0, "upstream", &upstream, N_("upstream"), + { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"), N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, - OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")), - { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"), + OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")), + { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"), N_("rebase strategy")), - OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"), + OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"), N_("strategy options")), - OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"), + OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"), N_("the branch or commit to checkout")), - OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")), - OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")), - OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto), + OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")), + OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")), + OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate), OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec, N_("automatically re-schedule any `exec` that fails")), OPT_END() }; - sequencer_init_config(&opts); - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); + opts.rebase_cousins = -1; - opts.action = REPLAY_INTERACTIVE_REBASE; - opts.allow_ff = 1; - opts.allow_empty = 1; + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); if (argc == 1) usage_with_options(builtin_rebase_interactive_usage, options); @@ -349,23 +411,21 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, NULL, options, builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); - opts.gpg_sign = xstrdup_or_null(opts.gpg_sign); - if (!is_null_oid(&squash_onto)) - squash_onto_opt = &squash_onto; + opts.squash_onto = &squash_onto; - flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; + flags |= opts.keep_empty ? TODO_LIST_KEEP_EMPTY : 0; flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; - flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; - flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; + flags |= opts.rebase_merges ? TODO_LIST_REBASE_MERGES : 0; + flags |= opts.rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; - if (rebase_cousins >= 0 && !rebase_merges) + if (opts.rebase_cousins >= 0 && !opts.rebase_merges) warning(_("--[no-]rebase-cousins has no effect without " "--rebase-merges")); - if (cmd && *cmd) { - string_list_split(&commands, cmd, '\n', -1); + if (opts.cmd && *opts.cmd) { + string_list_split(&commands, opts.cmd, '\n', -1); /* rebase.c adds a new line to cmd after every command, * so here the last command is always empty */ @@ -373,21 +433,26 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) } switch (command) { - case NONE: - if (!onto && !upstream) + case NONE: { + struct replay_opts replay_opts = get_replay_opts(&opts); + if (!opts.onto && !opts.upstream) die(_("a base commit must be provided with --upstream or --onto")); - ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto, - onto_name, squash_onto_opt, head_name, restrict_revision, - raw_strategies, &commands, autosquash); + ret = do_interactive_rebase(&replay_opts, flags, opts.switch_to, opts.upstream, opts.onto, + opts.onto_name, opts.squash_onto, opts.head_name, opts.restrict_revision, + opts.strategy_opts, &commands, opts.autosquash); break; + } case SKIP: { struct string_list merge_rr = STRING_LIST_INIT_DUP; rerere_clear(the_repository, &merge_rr); + } /* fallthrough */ - case CONTINUE: - ret = sequencer_continue(the_repository, &opts); + case CONTINUE: { + struct replay_opts replay_opts = get_replay_opts(&opts); + + ret = sequencer_continue(the_repository, &replay_opts); break; } case EDIT_TODO: @@ -446,45 +511,6 @@ static int use_builtin_rebase(void) return ret; } -struct rebase_options { - enum rebase_type type; - const char *state_dir; - struct commit *upstream; - const char *upstream_name; - const char *upstream_arg; - char *head_name; - struct object_id orig_head; - struct commit *onto; - const char *onto_name; - const char *revisions; - const char *switch_to; - int root; - struct object_id *squash_onto; - struct commit *restrict_revision; - int dont_finish_rebase; - enum { - REBASE_NO_QUIET = 1<<0, - REBASE_VERBOSE = 1<<1, - REBASE_DIFFSTAT = 1<<2, - REBASE_FORCE = 1<<3, - REBASE_INTERACTIVE_EXPLICIT = 1<<4, - } flags; - struct argv_array git_am_opts; - const char *action; - int signoff; - int allow_rerere_autoupdate; - int keep_empty; - int autosquash; - char *gpg_sign_opt; - int autostash; - char *cmd; - int allow_empty_message; - int rebase_merges, rebase_cousins; - char *strategy, *strategy_opts; - struct strbuf git_format_patch_opt; - int reschedule_failed_exec; -}; - static int is_interactive(struct rebase_options *opts) { return opts->type == REBASE_INTERACTIVE || @@ -1380,13 +1406,7 @@ static int check_exec_cmd(const char *cmd) int cmd_rebase(int argc, const char **argv, const char *prefix) { - struct rebase_options options = { - .type = REBASE_UNSPECIFIED, - .flags = REBASE_NO_QUIET, - .git_am_opts = ARGV_ARRAY_INIT, - .allow_empty_message = 1, - .git_format_patch_opt = STRBUF_INIT, - }; + struct rebase_options options = REBASE_OPTIONS_INIT; const char *branch_name; int ret, flags, total_argc, in_progress = 0; int ok_to_skip_pre_rebase = 0; @@ -1539,6 +1559,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) trace_repo_setup(prefix); setup_work_tree(); + options.allow_empty_message = 1; git_config(rebase_config, &options); strbuf_reset(&buf); -- cgit v0.10.2-6-g49f6 From 0ea0847ef0f6d548fe3a3c6720f99323408b2a50 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:42 +0100 Subject: rebase -i: use struct rebase_options in do_interactive_rebase() All the parameters that are passed to do_interactive_rebase() apart from `flags` are already in `struct rebase_options` so there is no need to pass them separately. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index aa97d81..277dbaa 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -113,6 +113,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) replay.reschedule_failed_exec = opts->reschedule_failed_exec; replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); replay.strategy = opts->strategy; + if (opts->strategy_opts) + parse_strategy_opts(&replay, opts->strategy_opts); return replay; } @@ -262,44 +264,50 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, return write_basic_state(opts, head_name, onto, orig_head); } -static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, - const char *switch_to, struct commit *upstream, - struct commit *onto, const char *onto_name, - struct object_id *squash_onto, const char *head_name, - struct commit *restrict_revision, char *raw_strategies, - struct string_list *commands, unsigned autosquash) +static void split_exec_commands(const char *cmd, struct string_list *commands) +{ + if (cmd && *cmd) { + string_list_split(commands, cmd, '\n', -1); + + /* rebase.c adds a new line to cmd after every command, + * so here the last command is always empty */ + string_list_remove_empty_items(commands, 0); + } +} + +static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) { int ret; const char *head_hash = NULL; char *revisions = NULL, *shortrevisions = NULL; struct argv_array make_script_args = ARGV_ARRAY_INIT; struct todo_list todo_list = TODO_LIST_INIT; + struct replay_opts replay = get_replay_opts(opts); + struct string_list commands = STRING_LIST_INIT_DUP; - if (prepare_branch_to_be_rebased(the_repository, opts, switch_to)) + if (prepare_branch_to_be_rebased(the_repository, &replay, + opts->switch_to)) return -1; - if (get_revision_ranges(upstream, onto, &head_hash, + if (get_revision_ranges(opts->upstream, opts->onto, &head_hash, &revisions, &shortrevisions)) return -1; - if (raw_strategies) - parse_strategy_opts(opts, raw_strategies); - - if (init_basic_state(opts, head_name, onto, head_hash)) { + if (init_basic_state(&replay, opts->head_name, opts->onto, head_hash)) { free(revisions); free(shortrevisions); return -1; } - if (!upstream && squash_onto) + if (!opts->upstream && opts->squash_onto) write_file(path_squash_onto(), "%s\n", - oid_to_hex(squash_onto)); + oid_to_hex(opts->squash_onto)); argv_array_pushl(&make_script_args, "", revisions, NULL); - if (restrict_revision) + if (opts->restrict_revision) argv_array_push(&make_script_args, - oid_to_hex(&restrict_revision->object.oid)); + oid_to_hex(&opts->restrict_revision->object.oid)); ret = sequencer_make_script(the_repository, &todo_list.buf, make_script_args.argc, make_script_args.argv, @@ -313,10 +321,13 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, &todo_list)) BUG("unusable todo list"); - ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name, - onto, head_hash, commands, autosquash, &todo_list); + split_exec_commands(opts->cmd, &commands); + ret = complete_action(the_repository, &replay, flags, + shortrevisions, opts->onto_name, opts->onto, head_hash, + &commands, opts->autosquash, &todo_list); } + string_list_clear(&commands, 0); free(revisions); free(shortrevisions); todo_list_release(&todo_list); @@ -336,7 +347,6 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) unsigned flags = 0; int abbreviate_commands = 0, ret = 0; struct object_id squash_onto = null_oid; - struct string_list commands = STRING_LIST_INIT_DUP; enum { NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH, SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC @@ -424,23 +434,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) warning(_("--[no-]rebase-cousins has no effect without " "--rebase-merges")); - if (opts.cmd && *opts.cmd) { - string_list_split(&commands, opts.cmd, '\n', -1); - - /* rebase.c adds a new line to cmd after every command, - * so here the last command is always empty */ - string_list_remove_empty_items(&commands, 0); - } - switch (command) { case NONE: { - struct replay_opts replay_opts = get_replay_opts(&opts); if (!opts.onto && !opts.upstream) die(_("a base commit must be provided with --upstream or --onto")); - ret = do_interactive_rebase(&replay_opts, flags, opts.switch_to, opts.upstream, opts.onto, - opts.onto_name, opts.squash_onto, opts.head_name, opts.restrict_revision, - opts.strategy_opts, &commands, opts.autosquash); + ret = do_interactive_rebase(&opts, flags); break; } case SKIP: { @@ -477,14 +476,18 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) case REARRANGE_SQUASH: ret = rearrange_squash_in_todo_file(); break; - case ADD_EXEC: + case ADD_EXEC: { + struct string_list commands = STRING_LIST_INIT_DUP; + + split_exec_commands(opts.cmd, &commands); ret = add_exec_commands(&commands); + string_list_clear(&commands, 0); break; + } default: BUG("invalid command '%d'", command); } - string_list_clear(&commands, 0); return !!ret; } -- cgit v0.10.2-6-g49f6 From 297b1e177377444cba9a5364a4c6ce676ededb4d Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:43 +0100 Subject: rebase: use a common action enum cmd_rebase() and cmd_rebase__interactive() used different enums to hold the current action. Change to using a common enum so the values are the same when we change `rebase -i` to avoid forking `rebase--interactive`. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 277dbaa..0020e1e 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -119,6 +119,29 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) return replay; } +enum action { + ACTION_NONE = 0, + ACTION_CONTINUE, + ACTION_SKIP, + ACTION_ABORT, + ACTION_QUIT, + ACTION_EDIT_TODO, + ACTION_SHOW_CURRENT_PATCH, + ACTION_SHORTEN_OIDS, + ACTION_EXPAND_OIDS, + ACTION_CHECK_TODO_LIST, + ACTION_REARRANGE_SQUASH, + ACTION_ADD_EXEC +}; + +static const char *action_names[] = { "undefined", + "continue", + "skip", + "abort", + "quit", + "edit_todo", + "show_current_patch" }; + static int add_exec_commands(struct string_list *commands) { const char *todo_file = rebase_path_todo(); @@ -347,10 +370,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) unsigned flags = 0; int abbreviate_commands = 0, ret = 0; struct object_id squash_onto = null_oid; - enum { - NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH, - SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC - } command = 0; + enum action command = ACTION_NONE; struct option options[] = { OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"), REBASE_FORCE), @@ -367,22 +387,22 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) N_("display a diffstat of what changed upstream"), REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), - CONTINUE), - OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP), + ACTION_CONTINUE), + OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP), OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"), - EDIT_TODO), + ACTION_EDIT_TODO), OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"), - SHOW_CURRENT_PATCH), + ACTION_SHOW_CURRENT_PATCH), OPT_CMDMODE(0, "shorten-ids", &command, - N_("shorten commit ids in the todo list"), SHORTEN_OIDS), + N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS), OPT_CMDMODE(0, "expand-ids", &command, - N_("expand commit ids in the todo list"), EXPAND_OIDS), + N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS), OPT_CMDMODE(0, "check-todo-list", &command, - N_("check the todo list"), CHECK_TODO_LIST), + N_("check the todo list"), ACTION_CHECK_TODO_LIST), OPT_CMDMODE(0, "rearrange-squash", &command, - N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), + N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH), OPT_CMDMODE(0, "add-exec-commands", &command, - N_("insert exec commands in todo list"), ADD_EXEC), + N_("insert exec commands in todo list"), ACTION_ADD_EXEC), { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"), PARSE_OPT_NONEG, parse_opt_commit, 0 }, { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision, @@ -428,36 +448,36 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; flags |= opts.rebase_merges ? TODO_LIST_REBASE_MERGES : 0; flags |= opts.rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; - flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; if (opts.rebase_cousins >= 0 && !opts.rebase_merges) warning(_("--[no-]rebase-cousins has no effect without " "--rebase-merges")); switch (command) { - case NONE: { + case ACTION_NONE: { if (!opts.onto && !opts.upstream) die(_("a base commit must be provided with --upstream or --onto")); ret = do_interactive_rebase(&opts, flags); break; } - case SKIP: { + case ACTION_SKIP: { struct string_list merge_rr = STRING_LIST_INIT_DUP; rerere_clear(the_repository, &merge_rr); } /* fallthrough */ - case CONTINUE: { + case ACTION_CONTINUE: { struct replay_opts replay_opts = get_replay_opts(&opts); ret = sequencer_continue(the_repository, &replay_opts); break; } - case EDIT_TODO: + case ACTION_EDIT_TODO: ret = edit_todo_file(flags); break; - case SHOW_CURRENT_PATCH: { + case ACTION_SHOW_CURRENT_PATCH: { struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; @@ -466,17 +486,17 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) break; } - case SHORTEN_OIDS: - case EXPAND_OIDS: + case ACTION_SHORTEN_OIDS: + case ACTION_EXPAND_OIDS: ret = transform_todo_file(flags); break; - case CHECK_TODO_LIST: + case ACTION_CHECK_TODO_LIST: ret = check_todo_list_from_file(the_repository); break; - case REARRANGE_SQUASH: + case ACTION_REARRANGE_SQUASH: ret = rearrange_squash_in_todo_file(); break; - case ADD_EXEC: { + case ACTION_ADD_EXEC: { struct string_list commands = STRING_LIST_INIT_DUP; split_exec_commands(opts.cmd, &commands); @@ -1417,22 +1437,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct strbuf revisions = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; struct object_id merge_base; - enum { - NO_ACTION, - ACTION_CONTINUE, - ACTION_SKIP, - ACTION_ABORT, - ACTION_QUIT, - ACTION_EDIT_TODO, - ACTION_SHOW_CURRENT_PATCH, - } action = NO_ACTION; - static const char *action_names[] = { "undefined", - "continue", - "skip", - "abort", - "quit", - "edit_todo", - "show_current_patch" }; + enum action action = ACTION_NONE; const char *gpg_sign = NULL; struct string_list exec = STRING_LIST_INIT_NODUP; const char *rebase_merges = NULL; @@ -1599,7 +1604,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) builtin_rebase_options, builtin_rebase_usage, 0); - if (action != NO_ACTION && total_argc != 2) { + if (action != ACTION_NONE && total_argc != 2) { usage_with_options(builtin_rebase_usage, builtin_rebase_options); } @@ -1608,7 +1613,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) usage_with_options(builtin_rebase_usage, builtin_rebase_options); - if (action != NO_ACTION && !in_progress) + if (action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); @@ -1708,7 +1713,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.action = "show-current-patch"; options.dont_finish_rebase = 1; goto run_rebase; - case NO_ACTION: + case ACTION_NONE: break; default: BUG("action: %d", action); -- cgit v0.10.2-6-g49f6 From 460bc3ce7386582b3259f8e3a2c1ace443ddb1ae Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Wed, 17 Apr 2019 15:30:44 +0100 Subject: rebase -i: run without forking rebase--interactive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano diff --git a/builtin/rebase.c b/builtin/rebase.c index 0020e1e..cd233c6 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -316,7 +316,9 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) &revisions, &shortrevisions)) return -1; - if (init_basic_state(&replay, opts->head_name, opts->onto, head_hash)) { + if (init_basic_state(&replay, + opts->head_name ? opts->head_name : "detached HEAD", + opts->onto, head_hash)) { free(revisions); free(shortrevisions); @@ -359,6 +361,77 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) return ret; } +static int run_rebase_interactive(struct rebase_options *opts, + enum action command) +{ + unsigned flags = 0; + int abbreviate_commands = 0, ret = 0; + + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); + + flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0; + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; + flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0; + flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; + flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + + switch (command) { + case ACTION_NONE: { + if (!opts->onto && !opts->upstream) + die(_("a base commit must be provided with --upstream or --onto")); + + ret = do_interactive_rebase(opts, flags); + break; + } + case ACTION_SKIP: { + struct string_list merge_rr = STRING_LIST_INIT_DUP; + + rerere_clear(the_repository, &merge_rr); + } + /* fallthrough */ + case ACTION_CONTINUE: { + struct replay_opts replay_opts = get_replay_opts(opts); + + ret = sequencer_continue(the_repository, &replay_opts); + break; + } + case ACTION_EDIT_TODO: + ret = edit_todo_file(flags); + break; + case ACTION_SHOW_CURRENT_PATCH: { + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); + ret = run_command(&cmd); + + break; + } + case ACTION_SHORTEN_OIDS: + case ACTION_EXPAND_OIDS: + ret = transform_todo_file(flags); + break; + case ACTION_CHECK_TODO_LIST: + ret = check_todo_list_from_file(the_repository); + break; + case ACTION_REARRANGE_SQUASH: + ret = rearrange_squash_in_todo_file(); + break; + case ACTION_ADD_EXEC: { + struct string_list commands = STRING_LIST_INIT_DUP; + + split_exec_commands(opts->cmd, &commands); + ret = add_exec_commands(&commands); + string_list_clear(&commands, 0); + break; + } + default: + BUG("invalid command '%d'", command); + } + + return ret; +} + static const char * const builtin_rebase_interactive_usage[] = { N_("git rebase--interactive []"), NULL @@ -367,8 +440,6 @@ static const char * const builtin_rebase_interactive_usage[] = { int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) { struct rebase_options opts = REBASE_OPTIONS_INIT; - unsigned flags = 0; - int abbreviate_commands = 0, ret = 0; struct object_id squash_onto = null_oid; enum action command = ACTION_NONE; struct option options[] = { @@ -433,8 +504,6 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) opts.rebase_cousins = -1; - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); - if (argc == 1) usage_with_options(builtin_rebase_interactive_usage, options); @@ -444,71 +513,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) if (!is_null_oid(&squash_onto)) opts.squash_onto = &squash_onto; - flags |= opts.keep_empty ? TODO_LIST_KEEP_EMPTY : 0; - flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; - flags |= opts.rebase_merges ? TODO_LIST_REBASE_MERGES : 0; - flags |= opts.rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; - flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; - if (opts.rebase_cousins >= 0 && !opts.rebase_merges) warning(_("--[no-]rebase-cousins has no effect without " "--rebase-merges")); - switch (command) { - case ACTION_NONE: { - if (!opts.onto && !opts.upstream) - die(_("a base commit must be provided with --upstream or --onto")); - - ret = do_interactive_rebase(&opts, flags); - break; - } - case ACTION_SKIP: { - struct string_list merge_rr = STRING_LIST_INIT_DUP; - - rerere_clear(the_repository, &merge_rr); - } - /* fallthrough */ - case ACTION_CONTINUE: { - struct replay_opts replay_opts = get_replay_opts(&opts); - - ret = sequencer_continue(the_repository, &replay_opts); - break; - } - case ACTION_EDIT_TODO: - ret = edit_todo_file(flags); - break; - case ACTION_SHOW_CURRENT_PATCH: { - struct child_process cmd = CHILD_PROCESS_INIT; - - cmd.git_cmd = 1; - argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); - ret = run_command(&cmd); - - break; - } - case ACTION_SHORTEN_OIDS: - case ACTION_EXPAND_OIDS: - ret = transform_todo_file(flags); - break; - case ACTION_CHECK_TODO_LIST: - ret = check_todo_list_from_file(the_repository); - break; - case ACTION_REARRANGE_SQUASH: - ret = rearrange_squash_in_todo_file(); - break; - case ACTION_ADD_EXEC: { - struct string_list commands = STRING_LIST_INIT_DUP; - - split_exec_commands(opts.cmd, &commands); - ret = add_exec_commands(&commands); - string_list_clear(&commands, 0); - break; - } - default: - BUG("invalid command '%d'", command); - } - - return !!ret; + return !!run_rebase_interactive(&opts, command); } static int use_builtin_rebase(void) @@ -1071,7 +1080,7 @@ static int run_am(struct rebase_options *opts) return status; } -static int run_specific_rebase(struct rebase_options *opts) +static int run_specific_rebase(struct rebase_options *opts, enum action action) { const char *argv[] = { NULL, NULL }; struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT; @@ -1080,77 +1089,19 @@ static int run_specific_rebase(struct rebase_options *opts) if (opts->type == REBASE_INTERACTIVE) { /* Run builtin interactive rebase */ - struct child_process child = CHILD_PROCESS_INIT; - - argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s", - resolvemsg); + setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1); if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { - argv_array_push(&child.env_array, - "GIT_SEQUENCE_EDITOR=:"); + setenv("GIT_SEQUENCE_EDITOR", ":", 1); opts->autosquash = 0; } + if (opts->gpg_sign_opt) { + /* remove the leading "-S" */ + char *tmp = xstrdup(opts->gpg_sign_opt + 2); + free(opts->gpg_sign_opt); + opts->gpg_sign_opt = tmp; + } - child.git_cmd = 1; - argv_array_push(&child.args, "rebase--interactive"); - - if (opts->action) - argv_array_pushf(&child.args, "--%s", opts->action); - if (opts->keep_empty) - argv_array_push(&child.args, "--keep-empty"); - if (opts->rebase_merges) - argv_array_push(&child.args, "--rebase-merges"); - if (opts->rebase_cousins) - argv_array_push(&child.args, "--rebase-cousins"); - if (opts->autosquash) - argv_array_push(&child.args, "--autosquash"); - if (opts->flags & REBASE_VERBOSE) - argv_array_push(&child.args, "--verbose"); - if (opts->flags & REBASE_FORCE) - argv_array_push(&child.args, "--no-ff"); - if (opts->restrict_revision) - argv_array_pushf(&child.args, - "--restrict-revision=^%s", - oid_to_hex(&opts->restrict_revision->object.oid)); - if (opts->upstream) - argv_array_pushf(&child.args, "--upstream=%s", - oid_to_hex(&opts->upstream->object.oid)); - if (opts->onto) - argv_array_pushf(&child.args, "--onto=%s", - oid_to_hex(&opts->onto->object.oid)); - if (opts->squash_onto) - argv_array_pushf(&child.args, "--squash-onto=%s", - oid_to_hex(opts->squash_onto)); - if (opts->onto_name) - argv_array_pushf(&child.args, "--onto-name=%s", - opts->onto_name); - argv_array_pushf(&child.args, "--head-name=%s", - opts->head_name ? - opts->head_name : "detached HEAD"); - if (opts->strategy) - argv_array_pushf(&child.args, "--strategy=%s", - opts->strategy); - if (opts->strategy_opts) - argv_array_pushf(&child.args, "--strategy-opts=%s", - opts->strategy_opts); - if (opts->switch_to) - argv_array_pushf(&child.args, "--switch-to=%s", - opts->switch_to); - if (opts->cmd) - argv_array_pushf(&child.args, "--cmd=%s", opts->cmd); - if (opts->allow_empty_message) - argv_array_push(&child.args, "--allow-empty-message"); - if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) - argv_array_push(&child.args, "--rerere-autoupdate"); - else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE) - argv_array_push(&child.args, "--no-rerere-autoupdate"); - if (opts->gpg_sign_opt) - argv_array_push(&child.args, opts->gpg_sign_opt); - if (opts->signoff) - argv_array_push(&child.args, "--signoff"); - if (opts->reschedule_failed_exec) - argv_array_push(&child.args, "--reschedule-failed-exec"); - - status = run_command(&child); + status = run_rebase_interactive(opts, action); goto finished_rebase; } @@ -2211,7 +2162,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.revisions = revisions.buf; run_rebase: - ret = !!run_specific_rebase(&options); + ret = !!run_specific_rebase(&options, action); cleanup: strbuf_release(&revisions); -- cgit v0.10.2-6-g49f6