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)
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;
}
if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) {
- ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+ ret = error(_("failed to find tree of %s"),
+ oid_to_hex(&head_oid));
goto leave_reset_head;
}
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);
}
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);
exit(1);
}
+static void set_reflog_action(struct rebase_options *options)
+{
+ const char *env;
+ struct strbuf buf = STRBUF_INIT;
+
+ 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 -i (%s)", options->action);
+ setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
+ strbuf_release(&buf);
+}
+
int cmd_rebase(int argc, const char **argv, const char *prefix)
{
struct rebase_options options = {
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"),
"them"), REBASE_PRESERVE_MERGES),
OPT_BOOL(0, "rerere-autoupdate",
&options.allow_rerere_autoupdate,
- N_("allow rerere to update index with resolved "
+ N_("allow rerere to update index with resolved "
"conflict")),
OPT_BOOL('k', "keep-empty", &options.keep_empty,
N_("preserve empty commits during rebase")),
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")),
"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;
if (action != NO_ACTION && !in_progress)
die(_("No rebase in progress?"));
+ setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
if (action == ACTION_EDIT_TODO && !is_interactive(&options))
die(_("The --edit-todo action can only be used during "
int fd;
options.action = "continue";
+ set_reflog_action(&options);
/* Sanity check */
if (get_oid("HEAD", &head))
struct string_list merge_rr = STRING_LIST_INIT_DUP;
options.action = "skip";
+ set_reflog_action(&options);
rerere_clear(&merge_rr);
string_list_clear(&merge_rr, 1);
case ACTION_ABORT: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
options.action = "abort";
+ set_reflog_action(&options);
rerere_clear(&merge_rr);
string_list_clear(&merge_rr, 1);
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--)
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;
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 "
}
strbuf_reset(&buf);
- strbuf_addf(&buf, "rebase: checkout %s",
+ strbuf_addf(&buf, "%s: checkout %s",
+ getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
options.switch_to);
if (reset_head(&oid, "checkout",
options.head_name, 0,
- NULL, NULL) < 0) {
+ NULL, buf.buf) < 0) {
ret = !!error(_("could not switch to "
"%s"),
options.switch_to);
if (options.flags & REBASE_DIFFSTAT) {
struct diff_options opts;
- if (options.flags & REBASE_VERBOSE)
- printf(_("Changes from %s to %s:\n"),
- oid_to_hex(&merge_base),
- oid_to_hex(&options.onto->object.oid));
+ if (options.flags & REBASE_VERBOSE) {
+ if (is_null_oid(&merge_base))
+ printf(_("Changes to %s:\n"),
+ oid_to_hex(&options.onto->object.oid));
+ else
+ printf(_("Changes from %s to %s:\n"),
+ oid_to_hex(&merge_base),
+ oid_to_hex(&options.onto->object.oid));
+ }
/* We want color (if set), but no pager */
diff_setup(&opts);
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
diff_setup_done(&opts);
- diff_tree_oid(&merge_base, &options.onto->object.oid,
- "", &opts);
+ diff_tree_oid(is_null_oid(&merge_base) ?
+ the_hash_algo->empty_tree : &merge_base,
+ &options.onto->object.oid, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
}
printf(_("First, rewinding head to replay your work on top of "
"it...\n"));
- strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
+ 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))
die(_("Could not detach HEAD"));
*/
strbuf_reset(&msg);
if (!oidcmp(&merge_base, &options.orig_head)) {
- printf(_("Fast-forwarded %s to %s. \n"),
+ printf(_("Fast-forwarded %s to %s.\n"),
branch_name, options.onto_name);
strbuf_addf(&msg, "rebase finished: %s onto %s",
options.head_name ? options.head_name : "detached HEAD",