summaryrefslogtreecommitdiff
path: root/builtin/rebase.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/rebase.c')
-rw-r--r--builtin/rebase.c126
1 files changed, 72 insertions, 54 deletions
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b22768ca5b..7171be40ee 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -113,7 +113,7 @@ struct rebase_options {
int autostash;
int committer_date_is_author_date;
int ignore_date;
- char *cmd;
+ struct string_list exec;
int allow_empty_message;
int rebase_merges, rebase_cousins;
char *strategy, *strategy_opts;
@@ -122,6 +122,8 @@ struct rebase_options {
int reapply_cherry_picks;
int fork_point;
int update_refs;
+ int config_autosquash;
+ int config_update_refs;
};
#define REBASE_OPTIONS_INIT { \
@@ -131,8 +133,15 @@ struct rebase_options {
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
+ .exec = STRING_LIST_INIT_NODUP, \
.git_format_patch_opt = STRBUF_INIT, \
.fork_point = -1, \
+ .reapply_cherry_picks = -1, \
+ .allow_empty_message = 1, \
+ .autosquash = -1, \
+ .config_autosquash = -1, \
+ .update_refs = -1, \
+ .config_update_refs = -1, \
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -243,17 +252,6 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
return write_basic_state(opts, head_name, onto, orig_head);
}
-static void split_exec_commands(const char *cmd, struct string_list *commands)
-{
- if (cmd && *cmd) {
- string_list_split(commands, cmd, '\n', -1);
-
- /* rebase.c adds a new line to cmd after every command,
- * so here the last command is always empty */
- string_list_remove_empty_items(commands, 0);
- }
-}
-
static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
{
int ret;
@@ -261,7 +259,6 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
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 (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
&revisions, &shortrevisions))
@@ -297,14 +294,12 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
&todo_list))
BUG("unusable todo list");
- split_exec_commands(opts->cmd, &commands);
ret = complete_action(the_repository, &replay, flags,
shortrevisions, opts->onto_name, opts->onto,
- &opts->orig_head->object.oid, &commands,
+ &opts->orig_head->object.oid, &opts->exec,
opts->autosquash, opts->update_refs, &todo_list);
}
- string_list_clear(&commands, 0);
free(revisions);
free(shortrevisions);
todo_list_release(&todo_list);
@@ -789,7 +784,7 @@ static int rebase_config(const char *var, const char *value, void *data)
}
if (!strcmp(var, "rebase.autosquash")) {
- opts->autosquash = git_config_bool(var, value);
+ opts->config_autosquash = git_config_bool(var, value);
return 0;
}
@@ -806,7 +801,7 @@ static int rebase_config(const char *var, const char *value, void *data)
}
if (!strcmp(var, "rebase.updaterefs")) {
- opts->update_refs = git_config_bool(var, value);
+ opts->config_update_refs = git_config_bool(var, value);
return 0;
}
@@ -920,6 +915,9 @@ static int parse_opt_am(const struct option *opt, const char *arg, int unset)
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_APPLY;
return 0;
@@ -933,8 +931,10 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- if (!is_merge(opts))
- opts->type = REBASE_MERGE;
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
+ opts->type = REBASE_MERGE;
return 0;
}
@@ -948,6 +948,9 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_MERGE;
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
@@ -1032,7 +1035,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id branch_base;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
- struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
@@ -1127,7 +1129,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_AUTOSTASH(&options.autostash),
- OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+ OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
OPT_BOOL_F(0, "allow-empty-message",
@@ -1164,8 +1166,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- options.reapply_cherry_picks = -1;
- options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
gpg_sign = options.gpg_sign_opt ? "" : NULL;
@@ -1230,13 +1230,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.fork_point < 0)
options.fork_point = 0;
}
- /*
- * --keep-base defaults to --reapply-cherry-picks to avoid losing
- * commits when using this option.
- */
- if (options.reapply_cherry_picks < 0)
- options.reapply_cherry_picks = keep_base;
-
if (options.root && options.fork_point > 0)
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
@@ -1250,7 +1243,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (trace2_is_enabled()) {
if (is_merge(&options))
trace2_cmd_mode("interactive");
- else if (exec.nr)
+ else if (options.exec.nr)
trace2_cmd_mode("interactive-exec");
else
trace2_cmd_mode(action_names[options.action]);
@@ -1322,6 +1315,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reset_head(the_repository, &ropts) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head->object.oid));
+ strbuf_release(&head_msg);
remove_branch_state(the_repository, 0);
ret = finish_rebase(&options);
goto cleanup;
@@ -1377,8 +1371,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
(options.action != ACTION_NONE) ||
- (exec.nr > 0) ||
- options.autosquash) {
+ (options.exec.nr > 0) ||
+ (options.autosquash == -1 && options.config_autosquash == 1) ||
+ options.autosquash == 1) {
allow_preemptive_ff = 0;
}
if (options.committer_date_is_author_date || options.ignore_date)
@@ -1401,8 +1396,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
}
- for (i = 0; i < exec.nr; i++)
- if (check_exec_cmd(exec.items[i].string))
+ for (i = 0; i < options.exec.nr; i++)
+ if (check_exec_cmd(options.exec.items[i].string))
exit(1);
if (!(options.flags & REBASE_NO_QUIET))
@@ -1411,27 +1406,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
- /*
- * --keep-base implements --reapply-cherry-picks by altering upstream so
- * it works with both backends.
- */
- if (options.reapply_cherry_picks && !keep_base)
- imply_merge(&options, "--reapply-cherry-picks");
+ if (options.reapply_cherry_picks < 0)
+ /*
+ * We default to --no-reapply-cherry-picks unless
+ * --keep-base is given; when --keep-base is given, we want
+ * to default to --reapply-cherry-picks.
+ */
+ options.reapply_cherry_picks = keep_base;
+ else if (!keep_base)
+ /*
+ * The apply backend always searches for and drops cherry
+ * picks. This is often not wanted with --keep-base, so
+ * --keep-base allows --reapply-cherry-picks to be
+ * simulated by altering the upstream such that
+ * cherry-picks cannot be detected and thus all commits are
+ * reapplied. Thus, --[no-]reapply-cherry-picks is
+ * supported when --keep-base is specified, but not when
+ * --keep-base is left out.
+ */
+ imply_merge(&options, options.reapply_cherry_picks ?
+ "--reapply-cherry-picks" :
+ "--no-reapply-cherry-picks");
if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
- if (exec.nr) {
- int i;
-
+ if (options.exec.nr)
imply_merge(&options, "--exec");
- strbuf_reset(&buf);
- for (i = 0; i < exec.nr; i++)
- strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
- options.cmd = xstrdup(buf.buf);
- }
-
if (rebase_merges) {
if (!*rebase_merges)
; /* default mode; do nothing */
@@ -1504,15 +1506,29 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.v[i], "-q"))
break;
- if (i >= 0) {
+ if (i >= 0 || options.type == REBASE_APPLY) {
if (is_merge(&options))
die(_("apply options and merge options "
"cannot be used together"));
+ else if (options.autosquash == -1 && options.config_autosquash == 1)
+ die(_("apply options are incompatible with rebase.autosquash. Consider adding --no-autosquash"));
+ else if (options.update_refs == -1 && options.config_update_refs == 1)
+ die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
else
options.type = REBASE_APPLY;
}
}
+ if (options.update_refs == 1)
+ imply_merge(&options, "--update-refs");
+ options.update_refs = (options.update_refs >= 0) ? options.update_refs :
+ ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+
+ if (options.autosquash == 1)
+ imply_merge(&options, "--autosquash");
+ options.autosquash = (options.autosquash >= 0) ? options.autosquash :
+ ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
imply_merge(&options, "--merge");
@@ -1542,7 +1558,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.empty == EMPTY_UNSPECIFIED) {
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
options.empty = EMPTY_ASK;
- else if (exec.nr > 0)
+ else if (options.exec.nr > 0)
options.empty = EMPTY_KEEP;
else
options.empty = EMPTY_DROP;
@@ -1828,10 +1844,12 @@ cleanup:
strbuf_release(&revisions);
free(options.reflog_action);
free(options.head_name);
+ strvec_clear(&options.git_am_opts);
free(options.gpg_sign_opt);
- free(options.cmd);
+ string_list_clear(&options.exec, 0);
free(options.strategy);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
+ string_list_clear(&strategy_options, 0);
return !!ret;
}