diff options
Diffstat (limited to 'third_party/git/builtin/rebase.c')
-rw-r--r-- | third_party/git/builtin/rebase.c | 414 |
1 files changed, 149 insertions, 265 deletions
diff --git a/third_party/git/builtin/rebase.c b/third_party/git/builtin/rebase.c index bff53d5d167e..670096c065f5 100644 --- a/third_party/git/builtin/rebase.c +++ b/third_party/git/builtin/rebase.c @@ -29,8 +29,8 @@ #include "rebase-interactive.h" static char const * const builtin_rebase_usage[] = { - N_("git rebase [-i] [options] [--exec <cmd>] " - "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"), + N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " + "[<upstream>] [<branch>]"), N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " "--root [<branch>]"), N_("git rebase --continue | --abort | --skip | --edit-todo"), @@ -44,22 +44,14 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge") enum rebase_type { REBASE_UNSPECIFIED = -1, - REBASE_APPLY, + REBASE_AM, REBASE_MERGE, + REBASE_INTERACTIVE, REBASE_PRESERVE_MERGES }; -enum empty_type { - EMPTY_UNSPECIFIED = -1, - EMPTY_DROP, - EMPTY_KEEP, - EMPTY_ASK -}; - struct rebase_options { enum rebase_type type; - enum empty_type empty; - const char *default_backend; const char *state_dir; struct commit *upstream; const char *upstream_name; @@ -70,7 +62,7 @@ struct rebase_options { const char *onto_name; const char *revisions; const char *switch_to; - int root, root_with_onto; + int root; struct object_id *squash_onto; struct commit *restrict_revision; int dont_finish_rebase; @@ -85,6 +77,7 @@ struct rebase_options { const char *action; int signoff; int allow_rerere_autoupdate; + int keep_empty; int autosquash; char *gpg_sign_opt; int autostash; @@ -99,8 +92,6 @@ struct rebase_options { #define REBASE_OPTIONS_INIT { \ .type = REBASE_UNSPECIFIED, \ - .empty = EMPTY_UNSPECIFIED, \ - .default_backend = "merge", \ .flags = REBASE_NO_QUIET, \ .git_am_opts = ARGV_ARRAY_INIT, \ .git_format_patch_opt = STRBUF_INIT \ @@ -119,9 +110,6 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) replay.allow_rerere_auto = opts->allow_rerere_autoupdate; replay.allow_empty = 1; replay.allow_empty_message = opts->allow_empty_message; - replay.drop_redundant_commits = (opts->empty == EMPTY_DROP); - replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP); - replay.quiet = !(opts->flags & REBASE_NO_QUIET); replay.verbose = opts->flags & REBASE_VERBOSE; replay.reschedule_failed_exec = opts->reschedule_failed_exec; replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); @@ -129,11 +117,6 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) if (opts->strategy_opts) parse_strategy_opts(&replay, opts->strategy_opts); - if (opts->squash_onto) { - oidcpy(&replay.squash_onto, opts->squash_onto); - replay.have_squash_onto = 1; - } - return replay; } @@ -258,17 +241,21 @@ static int edit_todo_file(unsigned flags) } static int get_revision_ranges(struct commit *upstream, struct commit *onto, - struct object_id *orig_head, const char **head_hash, + const char **head_hash, char **revisions, char **shortrevisions) { struct commit *base_rev = upstream ? upstream : onto; const char *shorthead; + struct object_id orig_head; - *head_hash = find_unique_abbrev(orig_head, GIT_MAX_HEXSZ); + if (get_oid("HEAD", &orig_head)) + return error(_("no HEAD?")); + + *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ); *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid), *head_hash); - shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV); + shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV); if (upstream) { const char *shortrev; @@ -322,8 +309,12 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) struct replay_opts replay = get_replay_opts(opts); struct string_list commands = STRING_LIST_INIT_DUP; - if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head, - &head_hash, &revisions, &shortrevisions)) + if (prepare_branch_to_be_rebased(the_repository, &replay, + opts->switch_to)) + return -1; + + if (get_revision_ranges(opts->upstream, opts->onto, &head_hash, + &revisions, &shortrevisions)) return -1; if (init_basic_state(&replay, @@ -341,8 +332,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) argv_array_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)); + argv_array_push(&make_script_args, + 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, @@ -371,7 +362,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) return ret; } -static int run_sequencer_rebase(struct rebase_options *opts, +static int run_rebase_interactive(struct rebase_options *opts, enum action command) { unsigned flags = 0; @@ -379,10 +370,10 @@ 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; switch (command) { @@ -442,21 +433,6 @@ static int run_sequencer_rebase(struct rebase_options *opts, return ret; } -static int parse_opt_keep_empty(const struct option *opt, const char *arg, - int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_ARG(arg); - - /* - * If we ever want to remap --keep-empty to --empty=keep, insert: - * opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP; - */ - opts->type = REBASE_MERGE; - return 0; -} - static const char * const builtin_rebase_interactive_usage[] = { N_("git rebase--interactive [<options>]"), NULL @@ -470,13 +446,9 @@ 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"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty }, - OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message, - N_("allow commits with empty messages"), - PARSE_OPT_HIDDEN), + 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", &opts.rebase_merges, N_("rebase merge commits")), OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins, N_("keep original branch points of cousins")), @@ -546,26 +518,28 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) warning(_("--[no-]rebase-cousins has no effect without " "--rebase-merges")); - return !!run_sequencer_rebase(&opts, command); + return !!run_rebase_interactive(&opts, command); } -static int is_merge(struct rebase_options *opts) +static int is_interactive(struct rebase_options *opts) { - return opts->type == REBASE_MERGE || + return opts->type == REBASE_INTERACTIVE || opts->type == REBASE_PRESERVE_MERGES; } -static void imply_merge(struct rebase_options *opts, const char *option) +static void imply_interactive(struct rebase_options *opts, const char *option) { switch (opts->type) { - case REBASE_APPLY: + case REBASE_AM: die(_("%s requires an interactive rebase"), option); break; - case REBASE_MERGE: + case REBASE_INTERACTIVE: case REBASE_PRESERVE_MERGES: break; + case REBASE_MERGE: + /* we now implement --merge via --interactive */ default: - opts->type = REBASE_MERGE; /* implied */ + opts->type = REBASE_INTERACTIVE; /* implied */ break; } } @@ -691,8 +665,8 @@ static int rebase_write_basic_state(struct rebase_options *opts) opts->onto ? oid_to_hex(&opts->onto->object.oid) : ""); write_file(state_dir_path("orig-head", opts), "%s", oid_to_hex(&opts->orig_head)); - if (!(opts->flags & REBASE_NO_QUIET)) - write_file(state_dir_path("quiet", opts), "%s", ""); + write_file(state_dir_path("quiet", opts), "%s", + opts->flags & REBASE_NO_QUIET ? "" : "t"); if (opts->flags & REBASE_VERBOSE) write_file(state_dir_path("verbose", opts), "%s", ""); if (opts->strategy) @@ -710,7 +684,7 @@ static int rebase_write_basic_state(struct rebase_options *opts) write_file(state_dir_path("gpg_sign_opt", opts), "%s", opts->gpg_sign_opt); if (opts->signoff) - write_file(state_dir_path("signoff", opts), "--signoff"); + write_file(state_dir_path("strategy", opts), "--signoff"); return 0; } @@ -774,7 +748,7 @@ static int finish_rebase(struct rebase_options *opts) * user should see them. */ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); - if (opts->type == REBASE_MERGE) { + if (opts->type == REBASE_INTERACTIVE) { struct replay_opts replay = REPLAY_OPTS_INIT; replay.action = REPLAY_INTERACTIVE_REBASE; @@ -1037,8 +1011,7 @@ static int run_am(struct rebase_options *opts) 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); + "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL); if (opts->git_format_patch_opt.len) argv_array_split(&format_patch.args, opts->git_format_patch_opt.buf); @@ -1107,8 +1080,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) int status; const char *backend, *backend_func; - if (opts->type == REBASE_MERGE) { - /* Run sequencer-based rebase */ + if (opts->type == REBASE_INTERACTIVE) { + /* Run builtin interactive rebase */ setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1); if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { setenv("GIT_SEQUENCE_EDITOR", ":", 1); @@ -1121,11 +1094,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) opts->gpg_sign_opt = tmp; } - status = run_sequencer_rebase(opts, action); + status = run_rebase_interactive(opts, action); goto finished_rebase; } - if (opts->type == REBASE_APPLY) { + if (opts->type == REBASE_AM) { status = run_am(opts); goto finished_rebase; } @@ -1145,6 +1118,8 @@ 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); + add_var(&script_snippet, "GIT_QUIET", + opts->flags & REBASE_NO_QUIET ? "" : "t"); sq_quote_argv_pretty(&buf, opts->git_am_opts.argv); add_var(&script_snippet, "git_am_opt", buf.buf); strbuf_release(&buf); @@ -1162,6 +1137,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); @@ -1179,7 +1155,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) add_var(&script_snippet, "git_format_patch_opt", opts->git_format_patch_opt.buf); - if (is_merge(opts) && + if (is_interactive(opts) && !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { strbuf_addstr(&script_snippet, "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; "); @@ -1204,8 +1180,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) finished_rebase: if (opts->dont_finish_rebase) ; /* do nothing */ - else if (opts->type == REBASE_MERGE) - ; /* merge backend cleans up after itself */ + else if (opts->type == REBASE_INTERACTIVE) + ; /* interactive rebase cleans up after itself */ else if (status == 0) { if (!file_exists(state_dir_path("stopped-sha", opts))) finish_rebase(opts); @@ -1263,10 +1239,6 @@ static int rebase_config(const char *var, const char *value, void *data) return 0; } - if (!strcmp(var, "rebase.backend")) { - return git_config_string(&opts->default_backend, var, value); - } - return git_default_config(var, value, data); } @@ -1288,60 +1260,28 @@ static int is_linear_history(struct commit *from, struct commit *to) return 1; } -static int can_fast_forward(struct commit *onto, struct commit *upstream, - struct commit *restrict_revision, - struct object_id *head_oid, struct object_id *merge_base) +static int can_fast_forward(struct commit *onto, struct object_id *head_oid, + struct object_id *merge_base) { struct commit *head = lookup_commit(the_repository, head_oid); - struct commit_list *merge_bases = NULL; - int res = 0; + struct commit_list *merge_bases; + int res; if (!head) - goto done; + return 0; merge_bases = get_merge_bases(onto, head); - if (!merge_bases || merge_bases->next) { + if (merge_bases && !merge_bases->next) { + oidcpy(merge_base, &merge_bases->item->object.oid); + res = oideq(merge_base, &onto->object.oid); + } else { oidcpy(merge_base, &null_oid); - goto done; + res = 0; } - - oidcpy(merge_base, &merge_bases->item->object.oid); - if (!oideq(merge_base, &onto->object.oid)) - goto done; - - if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base)) - goto done; - - if (!upstream) - goto done; - - free_commit_list(merge_bases); - merge_bases = get_merge_bases(upstream, head); - if (!merge_bases || merge_bases->next) - goto done; - - if (!oideq(&onto->object.oid, &merge_bases->item->object.oid)) - goto done; - - res = 1; - -done: free_commit_list(merge_bases); return res && is_linear_history(onto, head); } -static int parse_opt_am(const struct option *opt, const char *arg, int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - opts->type = REBASE_APPLY; - - return 0; -} - /* -i followed by -m is still -i */ static int parse_opt_merge(const struct option *opt, const char *arg, int unset) { @@ -1350,7 +1290,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset) BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - if (!is_merge(opts)) + if (!is_interactive(opts)) opts->type = REBASE_MERGE; return 0; @@ -1365,35 +1305,12 @@ static int parse_opt_interactive(const struct option *opt, const char *arg, BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - opts->type = REBASE_MERGE; + opts->type = REBASE_INTERACTIVE; opts->flags |= REBASE_INTERACTIVE_EXPLICIT; return 0; } -static enum empty_type parse_empty_value(const char *value) -{ - if (!strcasecmp(value, "drop")) - return EMPTY_DROP; - else if (!strcasecmp(value, "keep")) - return EMPTY_KEEP; - else if (!strcasecmp(value, "ask")) - return EMPTY_ASK; - - die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value); -} - -static int parse_opt_empty(const struct option *opt, const char *arg, int unset) -{ - struct rebase_options *options = opt->value; - enum empty_type value = parse_empty_value(arg); - - BUG_ON_OPT_NEG(unset); - - options->empty = value; - return 0; -} - static void NORETURN error_on_missing_default_upstream(void) { struct branch *current_branch = branch_get(NULL); @@ -1429,14 +1346,14 @@ static void set_reflog_action(struct rebase_options *options) const char *env; struct strbuf buf = STRBUF_INIT; - if (!is_merge(options)) + if (!is_interactive(options)) return; env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); if (env && strcmp("rebase", env)) return; /* only override it if it is "rebase" */ - strbuf_addf(&buf, "rebase (%s)", options->action); + strbuf_addf(&buf, "rebase -i (%s)", options->action); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1); strbuf_release(&buf); } @@ -1459,7 +1376,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct rebase_options options = REBASE_OPTIONS_INIT; const char *branch_name; int ret, flags, total_argc, in_progress = 0; - int keep_base = 0; int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; @@ -1474,18 +1390,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct object_id squash_onto; char *squash_onto_name = NULL; int reschedule_failed_exec = -1; - int allow_preemptive_ff = 1; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), N_("rebase onto given branch instead of upstream")), - OPT_BOOL(0, "keep-base", &keep_base, - N_("use the merge-base of upstream and branch as the current base")), OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, N_("allow pre-rebase hook to run")), OPT_NEGBIT('q', "quiet", &options.flags, N_("be quiet. implies --no-stat"), - REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), + REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT), OPT_BIT('v', "verbose", &options.flags, N_("display a diffstat of what changed upstream"), REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), @@ -1526,10 +1439,6 @@ 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, - N_("use apply strategies to rebase"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_am }, { OPTION_CALLBACK, 'm', "merge", &options, NULL, N_("use merging strategies to rebase"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, @@ -1538,18 +1447,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) N_("let the user edit the list of commits to rebase"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_interactive }, - OPT_SET_INT_F('p', "preserve-merges", &options.type, - N_("(DEPRECATED) try to recreate merges instead of " - "ignoring them"), - REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN), + OPT_SET_INT('p', "preserve-merges", &options.type, + N_("(DEPRECATED) try to recreate merges instead of " + "ignoring them"), REBASE_PRESERVE_MERGES), OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), - 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"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty }, + OPT_BOOL('k', "keep-empty", &options.keep_empty, + N_("preserve empty commits during rebase")), OPT_BOOL(0, "autosquash", &options.autosquash, N_("move commits that begin with " "squash!/fixup! under -i")), @@ -1561,10 +1464,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_STRING_LIST('x', "exec", &exec, N_("exec"), N_("add exec lines after each commit of the " "editable list")), - OPT_BOOL_F(0, "allow-empty-message", - &options.allow_empty_message, - N_("allow rebasing commits with empty messages"), - PARSE_OPT_HIDDEN), + OPT_BOOL(0, "allow-empty-message", + &options.allow_empty_message, + N_("allow rebasing commits with empty messages")), {OPTION_STRING, 'r', "rebase-merges", &rebase_merges, N_("mode"), N_("try to rebase merges instead of skipping them"), @@ -1604,7 +1506,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("It looks like 'git am' is in progress. Cannot rebase.")); if (is_directory(apply_dir())) { - options.type = REBASE_APPLY; + options.type = REBASE_AM; options.state_dir = apply_dir(); } else if (is_directory(merge_dir())) { strbuf_reset(&buf); @@ -1616,7 +1518,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_reset(&buf); strbuf_addf(&buf, "%s/interactive", merge_dir()); if(file_exists(buf.buf)) { - options.type = REBASE_MERGE; + options.type = REBASE_INTERACTIVE; options.flags |= REBASE_INTERACTIVE_EXPLICIT; } else options.type = REBASE_MERGE; @@ -1645,23 +1547,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) warning(_("git rebase --preserve-merges is deprecated. " "Use --rebase-merges instead.")); - if (keep_base) { - if (options.onto_name) - die(_("cannot combine '--keep-base' with '--onto'")); - if (options.root) - die(_("cannot combine '--keep-base' with '--root'")); - } - if (action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); - if (action == ACTION_EDIT_TODO && !is_merge(&options)) + if (action == ACTION_EDIT_TODO && !is_interactive(&options)) die(_("The --edit-todo action can only be used during " "interactive rebase.")); if (trace2_is_enabled()) { - if (is_merge(&options)) + if (is_interactive(&options)) trace2_cmd_mode("interactive"); else if (exec.nr) trace2_cmd_mode("interactive-exec"); @@ -1737,7 +1632,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } case ACTION_QUIT: { - if (options.type == REBASE_MERGE) { + if (options.type == REBASE_INTERACTIVE) { struct replay_opts replay = REPLAY_OPTS_INIT; replay.action = REPLAY_INTERACTIVE_REBASE; @@ -1786,20 +1681,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) state_dir_base, cmd_live_rebase, buf.buf); } - if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || - (action != ACTION_NONE) || - (exec.nr > 0) || - options.autosquash) { - allow_preemptive_ff = 0; - } - for (i = 0; i < options.git_am_opts.argc; i++) { const char *option = options.git_am_opts.argv[i], *p; if (!strcmp(option, "--committer-date-is-author-date") || !strcmp(option, "--ignore-date") || !strcmp(option, "--whitespace=fix") || !strcmp(option, "--whitespace=strip")) - allow_preemptive_ff = 0; + options.flags |= REBASE_FORCE; else if (skip_prefix(option, "-C", &p)) { while (*p) if (!isdigit(*(p++))) @@ -1819,8 +1707,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!(options.flags & REBASE_NO_QUIET)) argv_array_push(&options.git_am_opts, "-q"); - if (options.empty != EMPTY_UNSPECIFIED) - imply_merge(&options, "--empty"); + if (options.keep_empty) + imply_interactive(&options, "--keep-empty"); if (gpg_sign) { free(options.gpg_sign_opt); @@ -1830,7 +1718,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (exec.nr) { int i; - imply_merge(&options, "--exec"); + imply_interactive(&options, "--exec"); strbuf_reset(&buf); for (i = 0; i < exec.nr; i++) @@ -1846,7 +1734,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) else if (strcmp("no-rebase-cousins", rebase_merges)) die(_("Unknown mode: %s"), rebase_merges); options.rebase_merges = 1; - imply_merge(&options, "--rebase-merges"); + imply_interactive(&options, "--rebase-merges"); } if (strategy_options.nr) { @@ -1865,9 +1753,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.strategy) { options.strategy = xstrdup(options.strategy); switch (options.type) { - case REBASE_APPLY: + case REBASE_AM: die(_("--strategy requires --merge or --interactive")); case REBASE_MERGE: + case REBASE_INTERACTIVE: case REBASE_PRESERVE_MERGES: /* compatible */ break; @@ -1880,65 +1769,47 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if (options.type == REBASE_MERGE) - imply_merge(&options, "--merge"); + imply_interactive(&options, "--merge"); if (options.root && !options.onto_name) - imply_merge(&options, "--root without --onto"); + imply_interactive(&options, "--root without --onto"); 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) { - /* 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")) - break; - - if (i >= 0) { - if (is_merge(&options)) - die(_("cannot combine apply options with " - "merge options")); - else - options.type = REBASE_APPLY; - } - } - - if (options.type == REBASE_UNSPECIFIED) { - if (!strcmp(options.default_backend, "merge")) - imply_merge(&options, "--merge"); - else if (!strcmp(options.default_backend, "apply")) - options.type = REBASE_APPLY; - else - die(_("Unknown rebase backend: %s"), - options.default_backend); - } - switch (options.type) { case REBASE_MERGE: + case REBASE_INTERACTIVE: case REBASE_PRESERVE_MERGES: options.state_dir = merge_dir(); break; - case REBASE_APPLY: + case REBASE_AM: options.state_dir = apply_dir(); break; default: - BUG("options.type was just set above; should be unreachable."); + /* the default rebase backend is `--am` */ + options.type = REBASE_AM; + options.state_dir = apply_dir(); + break; } - if (options.empty == EMPTY_UNSPECIFIED) { - if (options.flags & REBASE_INTERACTIVE_EXPLICIT) - options.empty = EMPTY_ASK; - else if (exec.nr > 0) - options.empty = EMPTY_KEEP; - else - options.empty = EMPTY_DROP; - } - if (reschedule_failed_exec > 0 && !is_merge(&options)) + if (reschedule_failed_exec > 0 && !is_interactive(&options)) die(_("--reschedule-failed-exec requires " "--exec or --interactive")); if (reschedule_failed_exec >= 0) options.reschedule_failed_exec = reschedule_failed_exec; + if (options.git_am_opts.argc) { + /* all am options except -q are compatible only with --am */ + for (i = options.git_am_opts.argc - 1; i >= 0; i--) + if (strcmp(options.git_am_opts.argv[i], "-q")) + break; + + if (is_interactive(&options) && i >= 0) + die(_("cannot combine am options with either " + "interactive or merge options")); + } + if (options.signoff) { if (options.type == REBASE_PRESERVE_MERGES) die("cannot combine '--signoff' with " @@ -1962,6 +1833,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) "'--reschedule-failed-exec'")); } + if (options.rebase_merges) { + if (strategy_options.nr) + die(_("cannot combine '--rebase-merges' with " + "'--strategy-option'")); + if (options.strategy) + die(_("cannot combine '--rebase-merges' with " + "'--strategy'")); + } + if (!options.root) { if (argc < 1) { struct branch *branch; @@ -1992,9 +1872,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.squash_onto = &squash_onto; options.onto_name = squash_onto_name = xstrdup(oid_to_hex(&squash_onto)); - } else - options.root_with_onto = 1; - + } options.upstream_name = NULL; options.upstream = NULL; if (argc > 1) @@ -2004,22 +1882,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } /* Make sure the branch to rebase onto is valid. */ - if (keep_base) { - strbuf_reset(&buf); - strbuf_addstr(&buf, options.upstream_name); - strbuf_addstr(&buf, "..."); - options.onto_name = xstrdup(buf.buf); - } else if (!options.onto_name) + if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - if (get_oid_mb(options.onto_name, &merge_base) < 0) { - if (keep_base) - die(_("'%s': need exactly one merge base with branch"), - options.upstream_name); - else - die(_("'%s': need exactly one merge base"), - options.onto_name); - } + if (get_oid_mb(options.onto_name, &merge_base) < 0) + die(_("'%s': need exactly one merge base"), + options.onto_name); options.onto = lookup_commit_or_die(&merge_base, options.onto_name); } else { @@ -2044,11 +1912,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* Is it a local branch? */ strbuf_reset(&buf); strbuf_addf(&buf, "refs/heads/%s", branch_name); - if (!read_ref(buf.buf, &options.orig_head)) { - die_if_checked_out(buf.buf, 1); + if (!read_ref(buf.buf, &options.orig_head)) options.head_name = xstrdup(buf.buf); /* If not is it a valid ref (branch or commit)? */ - } else if (!get_oid(branch_name, &options.orig_head)) + else if (!get_oid(branch_name, &options.orig_head)) options.head_name = NULL; else die(_("fatal: no such branch/commit '%s'"), @@ -2101,6 +1968,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) state_dir_path("autostash", &options); struct child_process stash = CHILD_PROCESS_INIT; struct object_id oid; + struct commit *head = + lookup_commit_reference(the_repository, + &options.orig_head); argv_array_pushl(&stash.args, "stash", "create", "autostash", NULL); @@ -2121,9 +1991,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.state_dir); write_file(autostash, "%s", oid_to_hex(&oid)); printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(NULL, "reset --hard", + if (reset_head(&head->object.oid, "reset --hard", NULL, RESET_HEAD_HARD, NULL, NULL) < 0) die(_("could not reset --hard")); + printf(_("HEAD is now at %s"), + find_unique_abbrev(&head->object.oid, + DEFAULT_ABBREV)); + strbuf_reset(&buf); + pp_commit_easy(CMIT_FMT_ONELINE, head, &buf); + if (buf.len > 0) + printf(" %s", buf.buf); + putchar('\n'); if (discard_index(the_repository->index) < 0 || repo_read_index(the_repository) < 0) @@ -2144,25 +2022,31 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* * Check if we are already based on onto with linear history, - * in which case we could fast-forward without replacing the commits - * with new commits recreated by replaying their changes. - * - * Note that can_fast_forward() initializes merge_base, so we have to - * call it before checking allow_preemptive_ff. + * but this should be done only when upstream and onto are the same + * and if this is not an interactive rebase. */ - if (can_fast_forward(options.onto, options.upstream, options.restrict_revision, - &options.orig_head, &merge_base) && - allow_preemptive_ff) { + if (can_fast_forward(options.onto, &options.orig_head, &merge_base) && + !is_interactive(&options) && !options.restrict_revision && + options.upstream && + !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { int flag; if (!(options.flags & REBASE_FORCE)) { /* Lazily switch to the target branch if needed... */ if (options.switch_to) { + struct object_id oid; + + if (get_oid(options.switch_to, &oid) < 0) { + ret = !!error(_("could not parse '%s'"), + options.switch_to); + goto cleanup; + } + strbuf_reset(&buf); strbuf_addf(&buf, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.switch_to); - if (reset_head(&options.orig_head, "checkout", + if (reset_head(&oid, "checkout", options.head_name, RESET_HEAD_RUN_POST_CHECKOUT_HOOK, NULL, buf.buf) < 0) { @@ -2227,7 +2111,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) diff_flush(&opts); } - if (is_merge(&options)) + if (is_interactive(&options)) goto run_rebase; /* Detach HEAD and reset the tree */ |