]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/rebase.c
Merge branch 'en/ort-perf-batch-9'
[thirdparty/git.git] / builtin / rebase.c
index 2748fa6f2e91d2b788d5b54edfb15de29d8aa833..783b526f6e758a05a75524d86f91088bb7ba5b87 100644 (file)
@@ -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"
 #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>] [--onto <newbase>] "
-               "[<upstream>] [<branch>]"),
+       N_("git rebase [-i] [options] [--exec <cmd>] "
+               "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
                "--root [<branch>]"),
        N_("git rebase --continue | --abort | --skip | --edit-todo"),
@@ -44,14 +47,22 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
 
 enum rebase_type {
        REBASE_UNSPECIFIED = -1,
-       REBASE_AM,
+       REBASE_APPLY,
        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;
@@ -62,7 +73,7 @@ struct rebase_options {
        const char *onto_name;
        const char *revisions;
        const char *switch_to;
-       int root;
+       int root, root_with_onto;
        struct object_id *squash_onto;
        struct commit *restrict_revision;
        int dont_finish_rebase;
@@ -73,7 +84,7 @@ 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;
@@ -81,20 +92,27 @@ struct rebase_options {
        int autosquash;
        char *gpg_sign_opt;
        int autostash;
+       int committer_date_is_author_date;
+       int ignore_date;
        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;
-       int use_legacy_rebase;
+       int reapply_cherry_picks;
+       int fork_point;
 };
 
 #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_format_patch_opt = STRBUF_INIT     \
+               .git_am_opts = STRVEC_INIT,             \
+               .git_format_patch_opt = STRBUF_INIT,    \
+               .fork_point = -1,                       \
        }
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -102,6 +120,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
        struct replay_opts replay = REPLAY_OPTS_INIT;
 
        replay.action = REPLAY_INTERACTIVE_REBASE;
+       replay.strategy = NULL;
        sequencer_init_config(&replay);
 
        replay.signoff = opts->signoff;
@@ -110,13 +129,30 @@ 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.committer_date_is_author_date =
+                                       opts->committer_date_is_author_date;
+       replay.ignore_date = opts->ignore_date;
        replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
-       replay.strategy = opts->strategy;
+       if (opts->strategy)
+               replay.strategy = opts->strategy;
+       else if (!replay.strategy && replay.default_strategy) {
+               replay.strategy = replay.default_strategy;
+               replay.default_strategy = NULL;
+       }
+
        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;
 }
 
@@ -241,21 +277,16 @@ static int edit_todo_file(unsigned flags)
 }
 
 static int get_revision_ranges(struct commit *upstream, struct commit *onto,
-                              const char **head_hash,
-                              char **revisions, char **shortrevisions)
+                              struct object_id *orig_head, char **revisions,
+                              char **shortrevisions)
 {
        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", oid_to_hex(&base_rev->object.oid),
-                                                  *head_hash);
+                            oid_to_hex(orig_head));
 
-       shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
+       shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
 
        if (upstream) {
                const char *shortrev;
@@ -271,7 +302,8 @@ static int get_revision_ranges(struct commit *upstream, struct commit *onto,
 }
 
 static int init_basic_state(struct replay_opts *opts, const char *head_name,
-                           struct commit *onto, const char *orig_head)
+                           struct commit *onto,
+                           const struct object_id *orig_head)
 {
        FILE *interactive;
 
@@ -302,24 +334,19 @@ static void split_exec_commands(const char *cmd, struct string_list *commands)
 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;
 
-       if (prepare_branch_to_be_rebased(the_repository, &replay,
-                                        opts->switch_to))
-               return -1;
-
-       if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
+       if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head,
                                &revisions, &shortrevisions))
                return -1;
 
        if (init_basic_state(&replay,
                             opts->head_name ? opts->head_name : "detached HEAD",
-                            opts->onto, head_hash)) {
+                            opts->onto, &opts->orig_head)) {
                free(revisions);
                free(shortrevisions);
 
@@ -330,13 +357,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_push(&make_script_args,
-                               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)
@@ -349,20 +376,21 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 
                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);
+                       shortrevisions, opts->onto_name, opts->onto,
+                       &opts->orig_head, &commands, opts->autosquash,
+                       &todo_list);
        }
 
        string_list_clear(&commands, 0);
        free(revisions);
        free(shortrevisions);
        todo_list_release(&todo_list);
-       argv_array_clear(&make_script_args);
+       strvec_clear(&make_script_args);
 
        return ret;
 }
 
-static int run_rebase_interactive(struct rebase_options *opts,
+static int run_sequencer_rebase(struct rebase_options *opts,
                                  enum action command)
 {
        unsigned flags = 0;
@@ -374,7 +402,9 @@ static int run_rebase_interactive(struct rebase_options *opts,
        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: {
@@ -403,7 +433,7 @@ static int run_rebase_interactive(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;
@@ -433,6 +463,20 @@ static int run_rebase_interactive(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)
+{
+       struct rebase_options *opts = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+
+       imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
+       opts->keep_empty = !unset;
+       opts->type = REBASE_MERGE;
+       return 0;
+}
+
 static const char * const builtin_rebase_interactive_usage[] = {
        N_("git rebase--interactive [<options>]"),
        NULL
@@ -446,9 +490,13 @@ 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),
-               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_CALLBACK_F('k', "keep-empty", &options, NULL,
+                       N_("keep commits which start empty"),
+                       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, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
                OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
                         N_("keep original branch points of cousins")),
@@ -508,7 +556,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        if (argc == 1)
                usage_with_options(builtin_rebase_interactive_usage, options);
 
-       argc = parse_options(argc, argv, NULL, options,
+       argc = parse_options(argc, argv, prefix, options,
                        builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 
        if (!is_null_oid(&squash_onto))
@@ -518,28 +566,26 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                warning(_("--[no-]rebase-cousins has no effect without "
                          "--rebase-merges"));
 
-       return !!run_rebase_interactive(&opts, command);
+       return !!run_sequencer_rebase(&opts, command);
 }
 
-static int is_interactive(struct rebase_options *opts)
+static int is_merge(struct rebase_options *opts)
 {
-       return opts->type == REBASE_INTERACTIVE ||
+       return opts->type == REBASE_MERGE ||
                opts->type == REBASE_PRESERVE_MERGES;
 }
 
-static void imply_interactive(struct rebase_options *opts, const char *option)
+static void imply_merge(struct rebase_options *opts, const char *option)
 {
        switch (opts->type) {
-       case REBASE_AM:
-               die(_("%s requires an interactive rebase"), option);
+       case REBASE_APPLY:
+               die(_("%s requires the merge backend"), option);
                break;
-       case REBASE_INTERACTIVE:
+       case REBASE_MERGE:
        case REBASE_PRESERVE_MERGES:
                break;
-       case REBASE_MERGE:
-               /* we now implement --merge via --interactive */
        default:
-               opts->type = REBASE_INTERACTIVE; /* implied */
+               opts->type = REBASE_MERGE; /* implied */
                break;
        }
 }
@@ -560,15 +606,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)
 {
@@ -576,8 +613,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;
@@ -593,9 +632,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);
@@ -615,8 +656,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;
@@ -629,8 +670,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);
@@ -638,7 +679,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);
@@ -646,7 +688,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);
@@ -665,8 +708,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));
-       write_file(state_dir_path("quiet", opts), "%s",
-                  opts->flags & REBASE_NO_QUIET ? "" : "t");
+       if (!(opts->flags & REBASE_NO_QUIET))
+               write_file(state_dir_path("quiet", opts), "%s", "");
        if (opts->flags & REBASE_VERBOSE)
                write_file(state_dir_path("verbose", opts), "%s", "");
        if (opts->strategy)
@@ -684,71 +727,25 @@ 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("strategy", opts), "--signoff");
-
-       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;
+               write_file(state_dir_path("signoff", opts), "--signoff");
 
-       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
+        * We ignore errors in 'git maintenance run --auto', since the
         * user should see them.
         */
-       run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-       if (opts->type == REBASE_INTERACTIVE) {
+       run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
+       if (opts->type == REBASE_MERGE) {
                struct replay_opts replay = REPLAY_OPTS_INIT;
 
                replay.action = REPLAY_INTERACTIVE_REBASE;
@@ -786,143 +783,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;
-       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(&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(&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;
@@ -938,8 +798,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);
@@ -962,13 +824,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;
@@ -976,8 +838,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;
@@ -985,7 +847,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);
        }
 
@@ -1003,31 +865,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", 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"
@@ -1045,20 +909,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);
@@ -1080,8 +944,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        int status;
        const char *backend, *backend_func;
 
-       if (opts->type == REBASE_INTERACTIVE) {
-               /* Run builtin interactive rebase */
+       if (opts->type == REBASE_MERGE) {
+               /* Run sequencer-based rebase */
                setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                        setenv("GIT_SEQUENCE_EDITOR", ":", 1);
@@ -1094,11 +958,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
                        opts->gpg_sign_opt = tmp;
                }
 
-               status = run_rebase_interactive(opts, action);
+               status = run_sequencer_rebase(opts, action);
                goto finished_rebase;
        }
 
-       if (opts->type == REBASE_AM) {
+       if (opts->type == REBASE_APPLY) {
                status = run_am(opts);
                goto finished_rebase;
        }
@@ -1118,9 +982,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);
-       add_var(&script_snippet, "GIT_QUIET",
-               opts->flags & REBASE_NO_QUIET ? "" : "t");
-       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",
@@ -1155,7 +1017,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_interactive(opts) &&
+       if (is_merge(opts) &&
            !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                strbuf_addstr(&script_snippet,
                              "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
@@ -1163,10 +1025,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        }
 
        switch (opts->type) {
-       case REBASE_AM:
-               backend = "git-rebase--am";
-               backend_func = "git_rebase__am";
-               break;
        case REBASE_PRESERVE_MERGES:
                backend = "git-rebase--preserve-merges";
                backend_func = "git_rebase__preserve_merges";
@@ -1177,23 +1035,22 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        }
 
        strbuf_addf(&script_snippet,
-                   ". git-sh-setup && . git-rebase--common &&"
-                   " . %s && %s", backend, backend_func);
+                   ". git-sh-setup && . %s && %s", backend, backend_func);
        argv[0] = script_snippet.buf;
 
        status = run_command_v_opt(argv, RUN_USING_SHELL);
 finished_rebase:
        if (opts->dont_finish_rebase)
                ; /* do nothing */
-       else if (opts->type == REBASE_INTERACTIVE)
-               ; /* interactive rebase cleans up after itself */
+       else if (opts->type == REBASE_MERGE)
+               ; /* merge backend cleans up after itself */
        else if (status == 0) {
                if (!file_exists(state_dir_path("stopped-sha", opts)))
                        finish_rebase(opts);
        } 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);
@@ -1239,11 +1096,15 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
-       if (!strcmp(var, "rebase.usebuiltin")) {
-               opts->use_legacy_rebase = !git_config_bool(var, value);
+       if (!strcmp(var, "rebase.forkpoint")) {
+               opts->fork_point = git_config_bool(var, value) ? -1 : 0;
                return 0;
        }
 
+       if (!strcmp(var, "rebase.backend")) {
+               return git_config_string(&opts->default_backend, var, value);
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -1265,28 +1126,60 @@ static int is_linear_history(struct commit *from, struct commit *to)
        return 1;
 }
 
-static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
-                           struct object_id *merge_base)
+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)
 {
        struct commit *head = lookup_commit(the_repository, head_oid);
-       struct commit_list *merge_bases;
-       int res;
+       struct commit_list *merge_bases = NULL;
+       int res = 0;
 
        if (!head)
-               return 0;
+               goto done;
 
        merge_bases = get_merge_bases(onto, head);
-       if (merge_bases && !merge_bases->next) {
-               oidcpy(merge_base, &merge_bases->item->object.oid);
-               res = oideq(merge_base, &onto->object.oid);
-       } else {
+       if (!merge_bases || merge_bases->next) {
                oidcpy(merge_base, &null_oid);
-               res = 0;
+               goto done;
        }
+
+       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)
 {
@@ -1295,7 +1188,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_interactive(opts))
+       if (!is_merge(opts))
                opts->type = REBASE_MERGE;
 
        return 0;
@@ -1310,12 +1203,35 @@ 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_INTERACTIVE;
+       opts->type = REBASE_MERGE;
        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);
@@ -1351,14 +1267,14 @@ static void set_reflog_action(struct rebase_options *options)
        const char *env;
        struct strbuf buf = STRBUF_INIT;
 
-       if (!is_interactive(options))
+       if (!is_merge(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 -i (%s)", options->action);
+       strbuf_addf(&buf, "rebase (%s)", options->action);
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
        strbuf_release(&buf);
 }
@@ -1375,35 +1291,38 @@ 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;
        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;
        struct strbuf buf = STRBUF_INIT;
        struct object_id merge_base;
+       int ignore_whitespace = 0;
        enum action action = ACTION_NONE;
        const char *gpg_sign = NULL;
        struct string_list exec = STRING_LIST_INIT_NODUP;
        const char *rebase_merges = NULL;
-       int fork_point = -1;
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        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),
@@ -1411,17 +1330,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        N_("do not show diffstat of what changed upstream"),
                        PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
                OPT_BOOL(0, "signoff", &options.signoff,
-                        N_("add a Signed-off-by: line to each commit")),
-               OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
-                                 NULL, N_("passed to 'git am'"),
-                                 PARSE_OPT_NOARG),
-               OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
-                                 &options.git_am_opts, NULL,
-                                 N_("passed to 'git am'"), PARSE_OPT_NOARG),
-               OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
-                                 N_("passed to 'git am'"), PARSE_OPT_NOARG),
+                        N_("add a Signed-off-by trailer to each commit")),
+               OPT_BOOL(0, "committer-date-is-author-date",
+                        &options.committer_date_is_author_date,
+                        N_("make committer date match author date")),
+               OPT_BOOL(0, "reset-author-date", &options.ignore_date,
+                        N_("ignore author date and use current date")),
+               OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
+                               N_("synonym of --reset-author-date")),
                OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
                                  N_("passed to 'git apply'"), 0),
+               OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
+                        N_("ignore changes in whitespace")),
                OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
                                  N_("action"), N_("passed to 'git apply'"), 0),
                OPT_BIT('f', "force-rebase", &options.flags,
@@ -1444,39 +1364,49 @@ 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, 'm', "merge", &options, NULL,
+               OPT_CALLBACK_F(0, "apply", &options, NULL,
+                       N_("use apply strategies to rebase"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       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 },
-               OPT_SET_INT('p', "preserve-merges", &options.type,
-                           N_("(DEPRECATED) try to recreate merges instead of "
-                              "ignoring them"), REBASE_PRESERVE_MERGES),
+                       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_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-               OPT_BOOL('k', "keep-empty", &options.keep_empty,
-                        N_("preserve empty commits during rebase")),
+               OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
+                              N_("how to handle commits that become empty"),
+                              PARSE_OPT_NONEG, parse_opt_empty),
+               OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
+                       N_("keep commits which start empty"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+                       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")),
-               OPT_BOOL(0, "allow-empty-message",
-                        &options.allow_empty_message,
-                        N_("allow rebasing commits with empty messages")),
+               OPT_BOOL_F(0, "allow-empty-message",
+                          &options.allow_empty_message,
+                          N_("allow rebasing commits with empty messages"),
+                          PARSE_OPT_HIDDEN),
                {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
                        N_("mode"),
                        N_("try to rebase merges instead of skipping them"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)""},
-               OPT_BOOL(0, "fork-point", &fork_point,
+               OPT_BOOL(0, "fork-point", &options.fork_point,
                         N_("use 'merge-base --fork-point' to refine upstream")),
                OPT_STRING('s', "strategy", &options.strategy,
                           N_("strategy"), N_("use the given merge strategy")),
@@ -1489,6 +1419,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;
@@ -1497,17 +1429,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
 
-       prefix = setup_git_directory();
-       trace_repo_setup(prefix);
-       setup_work_tree();
-
        options.allow_empty_message = 1;
        git_config(rebase_config, &options);
-
-       if (options.use_legacy_rebase ||
-           !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
-               warning(_("the rebase.useBuiltin support has been removed!\n"
-                         "See its entry in 'git help config' for details."));
+       /* options.gpg_sign_opt will be either "-S" or NULL */
+       gpg_sign = options.gpg_sign_opt ? "" : NULL;
+       FREE_AND_NULL(options.gpg_sign_opt);
 
        strbuf_reset(&buf);
        strbuf_addf(&buf, "%s/applying", apply_dir());
@@ -1515,7 +1441,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_AM;
+               options.type = REBASE_APPLY;
                options.state_dir = apply_dir();
        } else if (is_directory(merge_dir())) {
                strbuf_reset(&buf);
@@ -1527,7 +1453,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_INTERACTIVE;
+                               options.type = REBASE_MERGE;
                                options.flags |= REBASE_INTERACTIVE_EXPLICIT;
                        } else
                                options.type = REBASE_MERGE;
@@ -1556,16 +1482,26 @@ 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 (options.root && options.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);
 
-       if (action == ACTION_EDIT_TODO && !is_interactive(&options))
+       if (action == ACTION_EDIT_TODO && !is_merge(&options))
                die(_("The --edit-todo action can only be used during "
                      "interactive rebase."));
 
        if (trace2_is_enabled()) {
-               if (is_interactive(&options))
+               if (is_merge(&options))
                        trace2_cmd_mode("interactive");
                else if (exec.nr)
                        trace2_cmd_mode("interactive-exec");
@@ -1613,10 +1549,10 @@ 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);
+               remove_branch_state(the_repository, 0);
                if (read_basic_state(&options))
                        exit(1);
                goto run_rebase;
@@ -1631,17 +1567,18 @@ 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);
+               remove_branch_state(the_repository, 0);
                ret = !!finish_rebase(&options);
                goto cleanup;
        }
        case ACTION_QUIT: {
-               if (options.type == REBASE_INTERACTIVE) {
+               save_autostash(state_dir_path("autostash", &options));
+               if (options.type == REBASE_MERGE) {
                        struct replay_opts replay = REPLAY_OPTS_INIT;
 
                        replay.action = REPLAY_INTERACTIVE_REBASE;
@@ -1690,13 +1627,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                    state_dir_base, cmd_live_rebase, buf.buf);
        }
 
-       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") ||
+       if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+           (action != ACTION_NONE) ||
+           (exec.nr > 0) ||
+           options.autosquash) {
+               allow_preemptive_ff = 0;
+       }
+       if (options.committer_date_is_author_date || options.ignore_date)
+               options.flags |= REBASE_FORCE;
+
+       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"))
-                       options.flags |= REBASE_FORCE;
+                       allow_preemptive_ff = 0;
                else if (skip_prefix(option, "-C", &p)) {
                        while (*p)
                                if (!isdigit(*(p++)))
@@ -1714,20 +1658,21 @@ 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 (options.keep_empty)
-               imply_interactive(&options, "--keep-empty");
+       if (options.reapply_cherry_picks)
+               imply_merge(&options, "--reapply-cherry-picks");
 
-       if (gpg_sign) {
-               free(options.gpg_sign_opt);
+       if (gpg_sign)
                options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
-       }
 
        if (exec.nr) {
                int i;
 
-               imply_interactive(&options, "--exec");
+               imply_merge(&options, "--exec");
 
                strbuf_reset(&buf);
                for (i = 0; i < exec.nr; i++)
@@ -1743,7 +1688,24 @@ 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_interactive(&options, "--rebase-merges");
+               imply_merge(&options, "--rebase-merges");
+       }
+
+       if (options.type == REBASE_APPLY) {
+               if (ignore_whitespace)
+                       strvec_push(&options.git_am_opts,
+                                   "--ignore-whitespace");
+               if (options.committer_date_is_author_date)
+                       strvec_push(&options.git_am_opts,
+                                   "--committer-date-is-author-date");
+               if (options.ignore_date)
+                       strvec_push(&options.git_am_opts, "--ignore-date");
+       } else {
+               /* REBASE_MERGE and PRESERVE_MERGES */
+               if (ignore_whitespace) {
+                       string_list_append(&strategy_options,
+                                          "ignore-space-change");
+               }
        }
 
        if (strategy_options.nr) {
@@ -1762,10 +1724,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.strategy) {
                options.strategy = xstrdup(options.strategy);
                switch (options.type) {
-               case REBASE_AM:
+               case REBASE_APPLY:
                        die(_("--strategy requires --merge or --interactive"));
                case REBASE_MERGE:
-               case REBASE_INTERACTIVE:
                case REBASE_PRESERVE_MERGES:
                        /* compatible */
                        break;
@@ -1778,52 +1739,75 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        }
 
        if (options.type == REBASE_MERGE)
-               imply_interactive(&options, "--merge");
+               imply_merge(&options, "--merge");
 
        if (options.root && !options.onto_name)
-               imply_interactive(&options, "--root without --onto");
+               imply_merge(&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.nr || options.type == REBASE_APPLY) {
+               /* all am options except -q are compatible only with --apply */
+               for (i = options.git_am_opts.nr - 1; i >= 0; i--)
+                       if (strcmp(options.git_am_opts.v[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);
+       }
+
+       if (options.type == REBASE_MERGE &&
+           !options.strategy &&
+           getenv("GIT_TEST_MERGE_ALGORITHM"))
+               options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+
        switch (options.type) {
        case REBASE_MERGE:
-       case REBASE_INTERACTIVE:
        case REBASE_PRESERVE_MERGES:
                options.state_dir = merge_dir();
                break;
-       case REBASE_AM:
+       case REBASE_APPLY:
                options.state_dir = apply_dir();
                break;
        default:
-               /* the default rebase backend is `--am` */
-               options.type = REBASE_AM;
-               options.state_dir = apply_dir();
-               break;
+               BUG("options.type was just set above; should be unreachable.");
        }
 
-       if (reschedule_failed_exec > 0 && !is_interactive(&options))
+       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))
                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 "
                            "'--preserve-merges'");
-               argv_array_push(&options.git_am_opts, "--signoff");
+               strvec_push(&options.git_am_opts, "--signoff");
                options.flags |= REBASE_FORCE;
        }
 
@@ -1842,15 +1826,6 @@ 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;
@@ -1860,8 +1835,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                                                    NULL);
                        if (!options.upstream_name)
                                error_on_missing_default_upstream();
-                       if (fork_point < 0)
-                               fork_point = 1;
+                       if (options.fork_point < 0)
+                               options.fork_point = 1;
                } else {
                        options.upstream_name = argv[0];
                        argc--;
@@ -1881,7 +1856,9 @@ 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)
@@ -1891,12 +1868,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        }
 
        /* Make sure the branch to rebase onto is valid. */
-       if (!options.onto_name)
+       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)
                options.onto_name = options.upstream_name;
        if (strstr(options.onto_name, "...")) {
-               if (get_oid_mb(options.onto_name, &merge_base) < 0)
-                       die(_("'%s': need exactly one merge base"),
-                           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);
+               }
                options.onto = lookup_commit_or_die(&merge_base,
                                                    options.onto_name);
        } else {
@@ -1921,10 +1908,13 @@ 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))
+               if (!read_ref(buf.buf, &options.orig_head)) {
+                       die_if_checked_out(buf.buf, 1);
                        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) &&
+                          lookup_commit_reference(the_repository,
+                                                  &options.orig_head))
                        options.head_name = NULL;
                else
                        die(_("fatal: no such branch/commit '%s'"),
@@ -1950,7 +1940,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        } else
                BUG("unexpected number of arguments left to parse");
 
-       if (fork_point > 0) {
+       if (options.fork_point > 0) {
                struct commit *head =
                        lookup_commit_reference(the_repository,
                                                &options.orig_head);
@@ -1962,60 +1952,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;
-                       struct commit *head =
-                               lookup_commit_reference(the_repository,
-                                                       &options.orig_head);
-
-                       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(&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)
-                               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",
@@ -2031,34 +1969,30 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
        /*
         * Check if we are already based on onto with linear history,
-        * but this should be done only when upstream and onto are the same
-        * and if this is not an interactive rebase.
+        * 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.
         */
-       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)) {
+       if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
+                   &options.orig_head, &merge_base) &&
+           allow_preemptive_ff) {
                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(&oid, "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);
@@ -2120,7 +2054,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                diff_flush(&opts);
        }
 
-       if (is_interactive(&options))
+       if (is_merge(&options))
                goto run_rebase;
 
        /* Detach HEAD and reset the tree */
@@ -2130,10 +2064,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,
-                      RESET_HEAD_DETACH | RESET_ORIG_HEAD | 
+       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);
 
@@ -2148,8 +2082,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;