Merge branch 'bp/checkout-new-branch-optim'
[gitweb.git] / builtin / rebase.c
index b5c99ec10c200a5fccb293c0acadae80642f2808..774264bae8dba0815f95e3000d10ff495c477569 100644 (file)
@@ -104,6 +104,7 @@ struct rebase_options {
        int rebase_merges, rebase_cousins;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
+       int reschedule_failed_exec;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -415,6 +416,8 @@ static int run_specific_rebase(struct rebase_options *opts)
                        argv_array_push(&child.args, opts->gpg_sign_opt);
                if (opts->signoff)
                        argv_array_push(&child.args, "--signoff");
+               if (opts->reschedule_failed_exec)
+                       argv_array_push(&child.args, "--reschedule-failed-exec");
 
                status = run_command(&child);
                goto finished_rebase;
@@ -530,6 +533,7 @@ static int run_specific_rebase(struct rebase_options *opts)
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
 
 static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
@@ -537,6 +541,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
        unsigned detach_head = flags & RESET_HEAD_DETACH;
        unsigned reset_hard = flags & RESET_HEAD_HARD;
+       unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
@@ -598,7 +603,7 @@ static int reset_head(struct object_id *oid, const char *action,
        }
 
        tree = parse_tree_indirect(oid);
-       prime_cache_tree(the_repository->index, tree);
+       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"));
@@ -636,6 +641,10 @@ static int reset_head(struct object_id *oid, const char *action,
                        ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
                                         UPDATE_REFS_MSG_ON_ERR);
        }
+       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);
@@ -674,6 +683,11 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.reschedulefailedexec")) {
+               opts->reschedule_failed_exec = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -746,6 +760,23 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        return 0;
 }
 
+struct opt_y {
+       struct string_list *list;
+       struct rebase_options *options;
+};
+
+static int parse_opt_y(const struct option *opt, const char *arg, int unset)
+{
+       struct opt_y *o = opt->value;
+
+       if (unset || !arg)
+               return -1;
+
+       o->options->reschedule_failed_exec = 1;
+       string_list_append(o->list, arg);
+       return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -826,6 +857,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       struct opt_y opt_y = { .list = &exec, .options = &options };
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
@@ -903,6 +935,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")),
+               { OPTION_CALLBACK, 'y', NULL, &opt_y, N_("<cmd>"),
+                       N_("same as --reschedule-failed-exec -x <cmd>"),
+                       PARSE_OPT_NONEG, parse_opt_y },
                OPT_BOOL(0, "allow-empty-message",
                         &options.allow_empty_message,
                         N_("allow rebasing commits with empty messages")),
@@ -920,6 +955,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                   "strategy")),
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
+               OPT_BOOL(0, "reschedule-failed-exec",
+                        &options.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
        int i;
@@ -1024,7 +1062,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                             &lock_file);
                rollback_lock_file(&lock_file);
 
-               if (has_unstaged_changes(1)) {
+               if (has_unstaged_changes(the_repository, 1)) {
                        puts(_("You must edit all merge conflicts and then\n"
                               "mark them as resolved using git add"));
                        exit(1);
@@ -1039,13 +1077,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.action = "skip";
                set_reflog_action(&options);
 
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
 
                if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
                               NULL, NULL) < 0)
                        die(_("could not discard worktree changes"));
-               remove_branch_state();
+               remove_branch_state(the_repository);
                if (read_basic_state(&options))
                        exit(1);
                goto run_rebase;
@@ -1055,7 +1093,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.action = "abort";
                set_reflog_action(&options);
 
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
 
                if (read_basic_state(&options))
@@ -1065,7 +1103,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                               NULL, NULL) < 0)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
-               remove_branch_state();
+               remove_branch_state(the_repository);
                ret = finish_rebase(&options);
                goto cleanup;
        }
@@ -1216,6 +1254,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                break;
        }
 
+       if (options.reschedule_failed_exec && !is_interactive(&options))
+               die(_("--reschedule-failed-exec requires an interactive rebase"));
+
        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--)
@@ -1241,7 +1282,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.flags |= REBASE_FORCE;
        }
 
-       if (options.type == REBASE_PRESERVE_MERGES)
+       if (options.type == REBASE_PRESERVE_MERGES) {
                /*
                 * Note: incompatibility with --signoff handled in signoff block above
                 * Note: incompatibility with --interactive is just a strong warning;
@@ -1251,6 +1292,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        die(_("error: cannot combine '--preserve-merges' with "
                              "'--rebase-merges'"));
 
+               if (options.reschedule_failed_exec)
+                       die(_("error: cannot combine '--preserve-merges' with "
+                             "'--reschedule-failed-exec'"));
+       }
+
        if (options.rebase_merges) {
                if (strategy_options.nr)
                        die(_("error: cannot combine '--rebase-merges' with "
@@ -1381,7 +1427,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        update_index_if_able(&the_index, &lock_file);
                rollback_lock_file(&lock_file);
 
-               if (has_unstaged_changes(1) || has_uncommitted_changes(1)) {
+               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;
@@ -1427,7 +1474,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (require_clean_work_tree("rebase",
+       if (require_clean_work_tree(the_repository, "rebase",
                                    _("Please commit or stash them."), 1, 1)) {
                ret = 1;
                goto cleanup;
@@ -1465,7 +1512,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                            getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
                                            options.switch_to);
                                if (reset_head(&oid, "checkout",
-                                              options.head_name, 0,
+                                              options.head_name,
+                                              RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                                               NULL, buf.buf) < 0) {
                                        ret = !!error(_("could not switch to "
                                                        "%s"),
@@ -1539,7 +1587,8 @@ 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, NULL, msg.buf))
+                      RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+                      NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);