diff options
Diffstat (limited to 'builtin/rebase.c')
-rw-r--r-- | builtin/rebase.c | 466 |
1 files changed, 130 insertions, 336 deletions
diff --git a/builtin/rebase.c b/builtin/rebase.c index b126fbe940..c4ff2039ef 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -8,7 +8,7 @@ #include "builtin.h" #include "run-command.h" #include "exec-cmd.h" -#include "argv-array.h" +#include "strvec.h" #include "dir.h" #include "packfile.h" #include "refs.h" @@ -27,6 +27,9 @@ #include "branch.h" #include "sequencer.h" #include "rebase-interactive.h" +#include "reset.h" + +#define DEFAULT_REFLOG_ACTION "rebase" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] " @@ -81,10 +84,11 @@ struct rebase_options { REBASE_FORCE = 1<<3, REBASE_INTERACTIVE_EXPLICIT = 1<<4, } flags; - struct argv_array git_am_opts; + struct strvec git_am_opts; const char *action; int signoff; int allow_rerere_autoupdate; + int keep_empty; int autosquash; char *gpg_sign_opt; int autostash; @@ -97,14 +101,16 @@ struct rebase_options { struct strbuf git_format_patch_opt; int reschedule_failed_exec; int use_legacy_rebase; + int reapply_cherry_picks; }; #define REBASE_OPTIONS_INIT { \ .type = REBASE_UNSPECIFIED, \ .empty = EMPTY_UNSPECIFIED, \ + .keep_empty = 1, \ .default_backend = "merge", \ .flags = REBASE_NO_QUIET, \ - .git_am_opts = ARGV_ARRAY_INIT, \ + .git_am_opts = STRVEC_INIT, \ .git_format_patch_opt = STRBUF_INIT \ } @@ -323,7 +329,7 @@ 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 strvec make_script_args = STRVEC_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; @@ -345,13 +351,13 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) write_file(path_squash_onto(), "%s\n", oid_to_hex(opts->squash_onto)); - argv_array_pushl(&make_script_args, "", revisions, NULL); + strvec_pushl(&make_script_args, "", revisions, NULL); if (opts->restrict_revision) - argv_array_pushf(&make_script_args, "^%s", - oid_to_hex(&opts->restrict_revision->object.oid)); + strvec_pushf(&make_script_args, "^%s", + 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, + make_script_args.nr, make_script_args.v, flags); if (ret) @@ -372,7 +378,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) free(revisions); free(shortrevisions); todo_list_release(&todo_list); - argv_array_clear(&make_script_args); + strvec_clear(&make_script_args); return ret; } @@ -385,11 +391,13 @@ static int run_sequencer_rebase(struct rebase_options *opts, 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 |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0; flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0; switch (command) { case ACTION_NONE: { @@ -418,7 +426,7 @@ static int run_sequencer_rebase(struct rebase_options *opts, struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; - argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); + strvec_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); ret = run_command(&cmd); break; @@ -448,6 +456,7 @@ static int run_sequencer_rebase(struct rebase_options *opts, return ret; } +static void imply_merge(struct rebase_options *opts, const char *option); static int parse_opt_keep_empty(const struct option *opt, const char *arg, int unset) { @@ -455,10 +464,8 @@ static int parse_opt_keep_empty(const struct option *opt, const char *arg, BUG_ON_OPT_ARG(arg); - /* - * If we ever want to remap --keep-empty to --empty=keep, insert: - * opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP; - */ + imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); + opts->keep_empty = !unset; opts->type = REBASE_MERGE; return 0; } @@ -476,10 +483,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"), REBASE_FORCE), - { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL, - N_("(DEPRECATED) keep empty commits"), + OPT_CALLBACK_F('k', "keep-empty", &options, NULL, + N_("keep commits which start empty"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty }, + parse_opt_keep_empty), OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message, N_("allow commits with empty messages"), PARSE_OPT_HIDDEN), @@ -565,7 +572,7 @@ static void imply_merge(struct rebase_options *opts, const char *option) { switch (opts->type) { case REBASE_APPLY: - die(_("%s requires an interactive rebase"), option); + die(_("%s requires the merge backend"), option); break; case REBASE_MERGE: case REBASE_PRESERVE_MERGES: @@ -592,15 +599,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o return path.buf; } -/* Read one file, then strip line endings */ -static int read_one(const char *path, struct strbuf *buf) -{ - if (strbuf_read_file(buf, path, 0) < 0) - return error_errno(_("could not read '%s'"), path); - strbuf_trim_trailing_newline(buf); - return 0; -} - /* Initialize the rebase options from the state directory. */ static int read_basic_state(struct rebase_options *opts) { @@ -608,8 +606,10 @@ static int read_basic_state(struct rebase_options *opts) struct strbuf buf = STRBUF_INIT; struct object_id oid; - if (read_one(state_dir_path("head-name", opts), &head_name) || - read_one(state_dir_path("onto", opts), &buf)) + if (!read_oneliner(&head_name, state_dir_path("head-name", opts), + READ_ONELINER_WARN_MISSING) || + !read_oneliner(&buf, state_dir_path("onto", opts), + READ_ONELINER_WARN_MISSING)) return -1; opts->head_name = starts_with(head_name.buf, "refs/") ? xstrdup(head_name.buf) : NULL; @@ -625,9 +625,11 @@ static int read_basic_state(struct rebase_options *opts) */ strbuf_reset(&buf); if (file_exists(state_dir_path("orig-head", opts))) { - if (read_one(state_dir_path("orig-head", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("orig-head", opts), + READ_ONELINER_WARN_MISSING)) return -1; - } else if (read_one(state_dir_path("head", opts), &buf)) + } else if (!read_oneliner(&buf, state_dir_path("head", opts), + READ_ONELINER_WARN_MISSING)) return -1; if (get_oid(buf.buf, &opts->orig_head)) return error(_("invalid orig-head: '%s'"), buf.buf); @@ -647,8 +649,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("allow_rerere_autoupdate", opts), - &buf)) + if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), + READ_ONELINER_WARN_MISSING)) return -1; if (!strcmp(buf.buf, "--rerere-autoupdate")) opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE; @@ -661,8 +663,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("gpg_sign_opt", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("gpg_sign_opt", opts), - &buf)) + if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->gpg_sign_opt); opts->gpg_sign_opt = xstrdup(buf.buf); @@ -670,7 +672,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("strategy", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("strategy", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("strategy", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->strategy); opts->strategy = xstrdup(buf.buf); @@ -678,7 +681,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("strategy_opts", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("strategy_opts", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->strategy_opts); opts->strategy_opts = xstrdup(buf.buf); @@ -721,65 +725,19 @@ static int rebase_write_basic_state(struct rebase_options *opts) return 0; } -static int apply_autostash(struct rebase_options *opts) -{ - const char *path = state_dir_path("autostash", opts); - struct strbuf autostash = STRBUF_INIT; - struct child_process stash_apply = CHILD_PROCESS_INIT; - - if (!file_exists(path)) - return 0; - - if (read_one(path, &autostash)) - return error(_("Could not read '%s'"), path); - /* Ensure that the hash is not mistaken for a number */ - strbuf_addstr(&autostash, "^0"); - argv_array_pushl(&stash_apply.args, - "stash", "apply", autostash.buf, NULL); - stash_apply.git_cmd = 1; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin = 1; - if (!run_command(&stash_apply)) - printf(_("Applied autostash.\n")); - else { - struct argv_array args = ARGV_ARRAY_INIT; - int res = 0; - - argv_array_pushl(&args, - "stash", "store", "-m", "autostash", "-q", - autostash.buf, NULL); - if (run_command_v_opt(args.argv, RUN_GIT_CMD)) - res = error(_("Cannot store %s"), autostash.buf); - argv_array_clear(&args); - strbuf_release(&autostash); - if (res) - return res; - - fprintf(stderr, - _("Applying autostash resulted in conflicts.\n" - "Your changes are safe in the stash.\n" - "You can run \"git stash pop\" or \"git stash drop\" " - "at any time.\n")); - } - - strbuf_release(&autostash); - return 0; -} - static int finish_rebase(struct rebase_options *opts) { struct strbuf dir = STRBUF_INIT; - const char *argv_gc_auto[] = { "gc", "--auto", NULL }; int ret = 0; delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - apply_autostash(opts); + apply_autostash(state_dir_path("autostash", opts)); close_object_store(the_repository->objects); /* * We ignore errors in 'gc --auto', since the * user should see them. */ - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + run_auto_gc(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE))); if (opts->type == REBASE_MERGE) { struct replay_opts replay = REPLAY_OPTS_INIT; @@ -818,144 +776,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value) } } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) -#define RESET_HEAD_REFS_ONLY (1<<3) -#define RESET_ORIG_HEAD (1<<4) - -static int reset_head(struct object_id *oid, const char *action, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head) -{ - unsigned detach_head = flags & RESET_HEAD_DETACH; - unsigned reset_hard = flags & RESET_HEAD_HARD; - unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; - unsigned refs_only = flags & RESET_HEAD_REFS_ONLY; - unsigned update_orig_head = flags & RESET_ORIG_HEAD; - struct object_id head_oid; - struct tree_desc desc[2] = { { NULL }, { NULL } }; - struct lock_file lock = LOCK_INIT; - struct unpack_trees_options unpack_tree_opts; - struct tree *tree; - const char *reflog_action; - struct strbuf msg = STRBUF_INIT; - size_t prefix_len; - struct object_id *orig = NULL, oid_orig, - *old_orig = NULL, oid_old_orig; - int ret = 0, nr = 0; - - if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) - BUG("Not a fully qualified branch: '%s'", switch_to_branch); - - if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { - ret = -1; - goto leave_reset_head; - } - - if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { - ret = error(_("could not determine HEAD revision")); - goto leave_reset_head; - } - - if (!oid) - oid = &head_oid; - - if (refs_only) - goto reset_head_refs; - - memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); - setup_unpack_trees_porcelain(&unpack_tree_opts, action); - unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update = 1; - unpack_tree_opts.merge = 1; - init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL); - if (!detach_head) - unpack_tree_opts.reset = 1; - - if (repo_read_index_unmerged(the_repository) < 0) { - ret = error(_("could not read index")); - goto leave_reset_head; - } - - if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) { - ret = error(_("failed to find tree of %s"), - oid_to_hex(&head_oid)); - goto leave_reset_head; - } - - if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) { - ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); - goto leave_reset_head; - } - - if (unpack_trees(nr, desc, &unpack_tree_opts)) { - ret = -1; - goto leave_reset_head; - } - - tree = parse_tree_indirect(oid); - prime_cache_tree(the_repository, the_repository->index, tree); - - if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) { - ret = error(_("could not write index")); - goto leave_reset_head; - } - -reset_head_refs: - reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); - prefix_len = msg.len; - - if (update_orig_head) { - if (!get_oid("ORIG_HEAD", &oid_old_orig)) - old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { - orig = &oid_orig; - if (!reflog_orig_head) { - strbuf_addstr(&msg, "updating ORIG_HEAD"); - reflog_orig_head = msg.buf; - } - update_ref(reflog_orig_head, "ORIG_HEAD", orig, - old_orig, 0, UPDATE_REFS_MSG_ON_ERR); - } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); - } - - if (!reflog_head) { - strbuf_setlen(&msg, prefix_len); - strbuf_addstr(&msg, "updating HEAD"); - reflog_head = msg.buf; - } - if (!switch_to_branch) - ret = update_ref(reflog_head, "HEAD", oid, orig, - detach_head ? REF_NO_DEREF : 0, - UPDATE_REFS_MSG_ON_ERR); - else { - ret = update_ref(reflog_head, switch_to_branch, oid, - NULL, 0, UPDATE_REFS_MSG_ON_ERR); - if (!ret) - ret = create_symref("HEAD", switch_to_branch, - reflog_head); - } - if (run_hook) - run_hook_le(NULL, "post-checkout", - oid_to_hex(orig ? orig : &null_oid), - oid_to_hex(oid), "1", NULL); - -leave_reset_head: - strbuf_release(&msg); - rollback_lock_file(&lock); - while (nr) - free((void *)desc[--nr].buffer); - return ret; -} - static int move_to_original_branch(struct rebase_options *opts) { struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; @@ -971,8 +791,10 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid)); strbuf_addf(&head_reflog, "rebase finished: returning to %s", opts->head_name); - ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY, - orig_head_reflog.buf, head_reflog.buf); + ret = reset_head(the_repository, NULL, "", opts->head_name, + RESET_HEAD_REFS_ONLY, + orig_head_reflog.buf, head_reflog.buf, + DEFAULT_REFLOG_ACTION); strbuf_release(&orig_head_reflog); strbuf_release(&head_reflog); @@ -995,13 +817,13 @@ static int run_am(struct rebase_options *opts) char *rebased_patches; am.git_cmd = 1; - argv_array_push(&am.args, "am"); + strvec_push(&am.args, "am"); if (opts->action && !strcmp("continue", opts->action)) { - argv_array_push(&am.args, "--resolved"); - argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_push(&am.args, "--resolved"); + strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); if (opts->gpg_sign_opt) - argv_array_push(&am.args, opts->gpg_sign_opt); + strvec_push(&am.args, opts->gpg_sign_opt); status = run_command(&am); if (status) return status; @@ -1009,8 +831,8 @@ static int run_am(struct rebase_options *opts) return move_to_original_branch(opts); } if (opts->action && !strcmp("skip", opts->action)) { - argv_array_push(&am.args, "--skip"); - argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_push(&am.args, "--skip"); + strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); status = run_command(&am); if (status) return status; @@ -1018,7 +840,7 @@ static int run_am(struct rebase_options *opts) return move_to_original_branch(opts); } if (opts->action && !strcmp("show-current-patch", opts->action)) { - argv_array_push(&am.args, "--show-current-patch"); + strvec_push(&am.args, "--show-current-patch"); return run_command(&am); } @@ -1036,32 +858,33 @@ static int run_am(struct rebase_options *opts) status = error_errno(_("could not open '%s' for writing"), rebased_patches); free(rebased_patches); - argv_array_clear(&am.args); + strvec_clear(&am.args); return status; } format_patch.git_cmd = 1; - argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout", - "--full-index", "--cherry-pick", "--right-only", - "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", - "--no-cover-letter", "--pretty=mboxrd", "--topo-order", - "--no-base", NULL); + strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout", + "--full-index", "--cherry-pick", "--right-only", + "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", + "--no-cover-letter", "--pretty=mboxrd", "--topo-order", + "--no-base", NULL); if (opts->git_format_patch_opt.len) - argv_array_split(&format_patch.args, - opts->git_format_patch_opt.buf); - argv_array_push(&format_patch.args, revisions.buf); + strvec_split(&format_patch.args, + opts->git_format_patch_opt.buf); + strvec_push(&format_patch.args, revisions.buf); if (opts->restrict_revision) - argv_array_pushf(&format_patch.args, "^%s", - oid_to_hex(&opts->restrict_revision->object.oid)); + strvec_pushf(&format_patch.args, "^%s", + oid_to_hex(&opts->restrict_revision->object.oid)); status = run_command(&format_patch); if (status) { unlink(rebased_patches); free(rebased_patches); - argv_array_clear(&am.args); + strvec_clear(&am.args); - reset_head(&opts->orig_head, "checkout", opts->head_name, 0, - "HEAD", NULL); + reset_head(the_repository, &opts->orig_head, "checkout", + opts->head_name, 0, + "HEAD", NULL, DEFAULT_REFLOG_ACTION); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n" @@ -1079,20 +902,20 @@ static int run_am(struct rebase_options *opts) status = error_errno(_("could not open '%s' for reading"), rebased_patches); free(rebased_patches); - argv_array_clear(&am.args); + strvec_clear(&am.args); return status; } - argv_array_pushv(&am.args, opts->git_am_opts.argv); - argv_array_push(&am.args, "--rebasing"); - argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); - argv_array_push(&am.args, "--patch-format=mboxrd"); + strvec_pushv(&am.args, opts->git_am_opts.v); + strvec_push(&am.args, "--rebasing"); + strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_push(&am.args, "--patch-format=mboxrd"); if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) - argv_array_push(&am.args, "--rerere-autoupdate"); + strvec_push(&am.args, "--rerere-autoupdate"); else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE) - argv_array_push(&am.args, "--no-rerere-autoupdate"); + strvec_push(&am.args, "--no-rerere-autoupdate"); if (opts->gpg_sign_opt) - argv_array_push(&am.args, opts->gpg_sign_opt); + strvec_push(&am.args, opts->gpg_sign_opt); status = run_command(&am); unlink(rebased_patches); free(rebased_patches); @@ -1152,7 +975,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) add_var(&script_snippet, "revisions", opts->revisions); add_var(&script_snippet, "restrict_revision", opts->restrict_revision ? oid_to_hex(&opts->restrict_revision->object.oid) : NULL); - sq_quote_argv_pretty(&buf, opts->git_am_opts.argv); + sq_quote_argv_pretty(&buf, opts->git_am_opts.v); add_var(&script_snippet, "git_am_opt", buf.buf); strbuf_release(&buf); add_var(&script_snippet, "verbose", @@ -1169,6 +992,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) opts->allow_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); add_var(&script_snippet, "cmd", opts->cmd); @@ -1219,7 +1043,7 @@ finished_rebase: } else if (status == 2) { struct strbuf dir = STRBUF_INIT; - apply_autostash(opts); + apply_autostash(state_dir_path("autostash", opts)); strbuf_addstr(&dir, opts->state_dir); remove_dir_recursively(&dir, 0); strbuf_release(&dir); @@ -1460,7 +1284,6 @@ static int check_exec_cmd(const char *cmd) return 0; } - int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = REBASE_OPTIONS_INIT; @@ -1535,18 +1358,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_CMDMODE(0, "show-current-patch", &action, N_("show the patch file being applied or merged"), ACTION_SHOW_CURRENT_PATCH), - { OPTION_CALLBACK, 0, "apply", &options, NULL, + OPT_CALLBACK_F(0, "apply", &options, NULL, N_("use apply strategies to rebase"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_am }, - { OPTION_CALLBACK, 'm', "merge", &options, NULL, + parse_opt_am), + OPT_CALLBACK_F('m', "merge", &options, NULL, N_("use merging strategies to rebase"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_merge }, - { OPTION_CALLBACK, 'i', "interactive", &options, NULL, + parse_opt_merge), + OPT_CALLBACK_F('i', "interactive", &options, NULL, N_("let the user edit the list of commits to rebase"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_interactive }, + parse_opt_interactive), OPT_SET_INT_F('p', "preserve-merges", &options.type, N_("(DEPRECATED) try to recreate merges instead of " "ignoring them"), @@ -1555,18 +1378,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}", N_("how to handle commits that become empty"), PARSE_OPT_NONEG, parse_opt_empty), - { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL, - N_("(DEPRECATED) keep empty commits"), + OPT_CALLBACK_F('k', "keep-empty", &options, NULL, + N_("keep commits which start empty"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty }, + parse_opt_keep_empty), OPT_BOOL(0, "autosquash", &options.autosquash, N_("move commits that begin with " "squash!/fixup! under -i")), { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_BOOL(0, "autostash", &options.autostash, - N_("automatically stash/stash pop before and after")), + OPT_AUTOSTASH(&options.autostash), OPT_STRING_LIST('x', "exec", &exec, N_("exec"), N_("add exec lines after each commit of the " "editable list")), @@ -1591,6 +1413,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "reschedule-failed-exec", &reschedule_failed_exec, N_("automatically re-schedule any `exec` that fails")), + OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks, + N_("apply all changes, even those already present upstream")), OPT_END(), }; int i; @@ -1601,6 +1425,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.allow_empty_message = 1; git_config(rebase_config, &options); + /* options.gpg_sign_opt will be either "-S" or NULL */ + gpg_sign = options.gpg_sign_opt ? "" : NULL; + FREE_AND_NULL(options.gpg_sign_opt); if (options.use_legacy_rebase || !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1)) @@ -1661,6 +1488,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("cannot combine '--keep-base' with '--root'")); } + if (options.root && fork_point > 0) + die(_("cannot combine '--root' with '--fork-point'")); + if (action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); @@ -1718,8 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); - if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD, - NULL, NULL) < 0) + if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD, + NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) die(_("could not discard worktree changes")); remove_branch_state(the_repository, 0); if (read_basic_state(&options)) @@ -1736,9 +1566,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (read_basic_state(&options)) exit(1); - if (reset_head(&options.orig_head, "reset", + if (reset_head(the_repository, &options.orig_head, "reset", options.head_name, RESET_HEAD_HARD, - NULL, NULL) < 0) + NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) die(_("could not move back to %s"), oid_to_hex(&options.orig_head)); remove_branch_state(the_repository, 0); @@ -1746,6 +1576,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } case ACTION_QUIT: { + save_autostash(state_dir_path("autostash", &options)); if (options.type == REBASE_MERGE) { struct replay_opts replay = REPLAY_OPTS_INIT; @@ -1804,8 +1635,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.committer_date_is_author_date || options.ignore_date) options.flags |= REBASE_FORCE; - for (i = 0; i < options.git_am_opts.argc; i++) { - const char *option = options.git_am_opts.argv[i], *p; + for (i = 0; i < options.git_am_opts.nr; i++) { + const char *option = options.git_am_opts.v[i], *p; if (!strcmp(option, "--whitespace=fix") || !strcmp(option, "--whitespace=strip")) allow_preemptive_ff = 0; @@ -1826,15 +1657,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) exit(1); if (!(options.flags & REBASE_NO_QUIET)) - argv_array_push(&options.git_am_opts, "-q"); + strvec_push(&options.git_am_opts, "-q"); if (options.empty != EMPTY_UNSPECIFIED) imply_merge(&options, "--empty"); - if (gpg_sign) { - free(options.gpg_sign_opt); + if (options.reapply_cherry_picks) + imply_merge(&options, "--reapply-cherry-picks"); + + if (gpg_sign) options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign); - } if (exec.nr) { int i; @@ -1860,13 +1692,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.type == REBASE_APPLY) { if (ignore_whitespace) - argv_array_push(&options.git_am_opts, - "--ignore-whitespace"); + strvec_push(&options.git_am_opts, + "--ignore-whitespace"); if (options.committer_date_is_author_date) - argv_array_push(&options.git_am_opts, - "--committer-date-is-author-date"); + strvec_push(&options.git_am_opts, + "--committer-date-is-author-date"); if (options.ignore_date) - argv_array_push(&options.git_am_opts, "--ignore-date"); + strvec_push(&options.git_am_opts, "--ignore-date"); } else { /* REBASE_MERGE and PRESERVE_MERGES */ if (ignore_whitespace) { @@ -1914,10 +1746,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (isatty(2) && options.flags & REBASE_NO_QUIET) strbuf_addstr(&options.git_format_patch_opt, " --progress"); - if (options.git_am_opts.argc || options.type == REBASE_APPLY) { + if (options.git_am_opts.nr || options.type == REBASE_APPLY) { /* all am options except -q are compatible only with --apply */ - for (i = options.git_am_opts.argc - 1; i >= 0; i--) - if (strcmp(options.git_am_opts.argv[i], "-q")) + for (i = options.git_am_opts.nr - 1; i >= 0; i--) + if (strcmp(options.git_am_opts.v[i], "-q")) break; if (i >= 0) { @@ -1969,7 +1801,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.type == REBASE_PRESERVE_MERGES) die("cannot combine '--signoff' with " "'--preserve-merges'"); - argv_array_push(&options.git_am_opts, "--signoff"); + strvec_push(&options.git_am_opts, "--signoff"); options.flags |= REBASE_FORCE; } @@ -2112,49 +1944,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("could not read index")); if (options.autostash) { - struct lock_file lock_file = LOCK_INIT; - int fd; - - fd = hold_locked_index(&lock_file, 0); - refresh_cache(REFRESH_QUIET); - if (0 <= fd) - repo_update_index_if_able(the_repository, &lock_file); - rollback_lock_file(&lock_file); - - if (has_unstaged_changes(the_repository, 1) || - has_uncommitted_changes(the_repository, 1)) { - const char *autostash = - state_dir_path("autostash", &options); - struct child_process stash = CHILD_PROCESS_INIT; - struct object_id oid; - - argv_array_pushl(&stash.args, - "stash", "create", "autostash", NULL); - stash.git_cmd = 1; - stash.no_stdin = 1; - strbuf_reset(&buf); - if (capture_command(&stash, &buf, GIT_MAX_HEXSZ)) - die(_("Cannot autostash")); - strbuf_trim_trailing_newline(&buf); - if (get_oid(buf.buf, &oid)) - die(_("Unexpected stash response: '%s'"), - buf.buf); - strbuf_reset(&buf); - strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV); - - if (safe_create_leading_directories_const(autostash)) - die(_("Could not create directory for '%s'"), - options.state_dir); - write_file(autostash, "%s", oid_to_hex(&oid)); - printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(NULL, "reset --hard", - NULL, RESET_HEAD_HARD, NULL, NULL) < 0) - die(_("could not reset --hard")); - - if (discard_index(the_repository->index) < 0 || - repo_read_index(the_repository) < 0) - die(_("could not read index")); - } + create_autostash(the_repository, state_dir_path("autostash", &options), + DEFAULT_REFLOG_ACTION); } if (require_clean_work_tree(the_repository, "rebase", @@ -2188,10 +1979,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&buf, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.switch_to); - if (reset_head(&options.orig_head, "checkout", + if (reset_head(the_repository, + &options.orig_head, "checkout", options.head_name, RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, buf.buf) < 0) { + NULL, buf.buf, + DEFAULT_REFLOG_ACTION) < 0) { ret = !!error(_("could not switch to " "%s"), options.switch_to); @@ -2263,10 +2056,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name); - if (reset_head(&options.onto->object.oid, "checkout", NULL, + if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL, RESET_HEAD_DETACH | RESET_ORIG_HEAD | RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, msg.buf)) + NULL, msg.buf, DEFAULT_REFLOG_ACTION)) die(_("Could not detach HEAD")); strbuf_release(&msg); @@ -2281,8 +2074,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "rebase finished: %s onto %s", options.head_name ? options.head_name : "detached HEAD", oid_to_hex(&options.onto->object.oid)); - reset_head(NULL, "Fast-forwarded", options.head_name, - RESET_HEAD_REFS_ONLY, "HEAD", msg.buf); + reset_head(the_repository, NULL, "Fast-forwarded", options.head_name, + RESET_HEAD_REFS_ONLY, "HEAD", msg.buf, + DEFAULT_REFLOG_ACTION); strbuf_release(&msg); ret = !!finish_rebase(&options); goto cleanup; |