#include "revision.h"
#include "commit-reach.h"
#include "rerere.h"
+#include "branch.h"
static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf out = STRBUF_INIT;
- int ret;
+ int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1);
+
+ if (env != -1)
+ return env;
argv_array_pushl(&cp.args,
"config", "--bool", "rebase.usebuiltin", NULL);
REBASE_FORCE = 1<<3,
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
- struct strbuf git_am_opt;
+ struct argv_array git_am_opts;
const char *action;
int signoff;
int allow_rerere_autoupdate;
static int run_specific_rebase(struct rebase_options *opts)
{
const char *argv[] = { NULL, NULL };
- struct strbuf script_snippet = STRBUF_INIT;
+ struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
int status;
const char *backend, *backend_func;
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
add_var(&script_snippet, "GIT_QUIET",
opts->flags & REBASE_NO_QUIET ? "" : "t");
- add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf);
+ sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
+ add_var(&script_snippet, "git_am_opt", buf.buf);
+ strbuf_release(&buf);
add_var(&script_snippet, "verbose",
opts->flags & REBASE_VERBOSE ? "t" : "");
add_var(&script_snippet, "diffstat",
reflog_head = msg.buf;
}
if (!switch_to_branch)
- ret = update_ref(reflog_head, "HEAD", oid, orig, REF_NO_DEREF,
+ ret = update_ref(reflog_head, "HEAD", oid, orig,
+ detach_head ? REF_NO_DEREF : 0,
UPDATE_REFS_MSG_ON_ERR);
else {
ret = create_symref("HEAD", switch_to_branch, msg.buf);
{
struct rebase_options *opts = opt->value;
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
if (!is_interactive(opts))
opts->type = REBASE_MERGE;
{
struct rebase_options *opts = opt->value;
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
opts->type = REBASE_INTERACTIVE;
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
struct rebase_options options = {
.type = REBASE_UNSPECIFIED,
.flags = REBASE_NO_QUIET,
- .git_am_opt = STRBUF_INIT,
+ .git_am_opts = ARGV_ARRAY_INIT,
.allow_rerere_autoupdate = -1,
.allow_empty_message = 1,
.git_format_patch_opt = STRBUF_INIT,
ACTION_EDIT_TODO,
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
- int committer_date_is_author_date = 0;
- int ignore_date = 0;
- int ignore_whitespace = 0;
const char *gpg_sign = NULL;
- int opt_c = -1;
- struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
int fork_point = -1;
{OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
- OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
- N_("passed to 'git apply'")),
OPT_BOOL(0, "signoff", &options.signoff,
N_("add a Signed-off-by: line to each commit")),
- OPT_BOOL(0, "committer-date-is-author-date",
- &committer_date_is_author_date,
- N_("passed to 'git am'")),
- OPT_BOOL(0, "ignore-date", &ignore_date,
- N_("passed to 'git am'")),
+ 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),
+ OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
+ N_("passed to 'git apply'"), 0),
+ OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
+ N_("action"), N_("passed to 'git apply'"), 0),
OPT_BIT('f', "force-rebase", &options.flags,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_STRING_LIST(0, "whitespace", &whitespace,
- N_("whitespace"), N_("passed to 'git apply'")),
- OPT_SET_INT('C', NULL, &opt_c, N_("passed to 'git apply'"),
- REBASE_AM),
OPT_BOOL(0, "autostash", &options.autostash,
N_("automatically stash/stash pop before and after")),
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
N_("rebase all reachable commits up to the root(s)")),
OPT_END(),
};
+ int i;
/*
* NEEDSWORK: Once the builtin rebase has been tested enough
if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
NULL, NULL) < 0)
die(_("could not discard worktree changes"));
+ remove_branch_state();
if (read_basic_state(&options))
exit(1);
goto run_rebase;
NULL, NULL) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
+ remove_branch_state();
ret = finish_rebase(&options);
goto cleanup;
}
state_dir_base, cmd_live_rebase, buf.buf);
}
- if (!(options.flags & REBASE_NO_QUIET))
- strbuf_addstr(&options.git_am_opt, " -q");
-
- if (committer_date_is_author_date) {
- strbuf_addstr(&options.git_am_opt,
- " --committer-date-is-author-date");
- options.flags |= REBASE_FORCE;
+ for (i = 0; i < options.git_am_opts.argc; i++) {
+ const char *option = options.git_am_opts.argv[i], *p;
+ if (!strcmp(option, "--committer-date-is-author-date") ||
+ !strcmp(option, "--ignore-date") ||
+ !strcmp(option, "--whitespace=fix") ||
+ !strcmp(option, "--whitespace=strip"))
+ options.flags |= REBASE_FORCE;
+ else if (skip_prefix(option, "-C", &p)) {
+ while (*p)
+ if (!isdigit(*(p++)))
+ die(_("switch `C' expects a "
+ "numerical value"));
+ } else if (skip_prefix(option, "--whitespace=", &p)) {
+ if (*p && strcmp(p, "warn") && strcmp(p, "nowarn") &&
+ strcmp(p, "error") && strcmp(p, "error-all"))
+ die("Invalid whitespace option: '%s'", p);
+ }
}
- if (ignore_whitespace)
- strbuf_addstr(&options.git_am_opt, " --ignore-whitespace");
-
- if (ignore_date) {
- strbuf_addstr(&options.git_am_opt, " --ignore-date");
- options.flags |= REBASE_FORCE;
- }
+ if (!(options.flags & REBASE_NO_QUIET))
+ argv_array_push(&options.git_am_opts, "-q");
if (options.keep_empty)
imply_interactive(&options, "--keep-empty");
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
}
- if (opt_c >= 0)
- strbuf_addf(&options.git_am_opt, " -C%d", opt_c);
-
- if (whitespace.nr) {
- int i;
-
- for (i = 0; i < whitespace.nr; i++) {
- const char *item = whitespace.items[i].string;
-
- strbuf_addf(&options.git_am_opt, " --whitespace=%s",
- item);
-
- if ((!strcmp(item, "fix")) || (!strcmp(item, "strip")))
- options.flags |= REBASE_FORCE;
- }
- }
-
if (exec.nr) {
int i;
break;
}
- if (options.git_am_opt.len) {
- const char *p;
-
+ if (options.git_am_opts.argc) {
/* all am options except -q are compatible only with --am */
- strbuf_reset(&buf);
- strbuf_addbuf(&buf, &options.git_am_opt);
- strbuf_addch(&buf, ' ');
- while ((p = strstr(buf.buf, " -q ")))
- strbuf_splice(&buf, p - buf.buf, 4, " ", 1);
- strbuf_trim(&buf);
+ 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) && buf.len)
+ if (is_interactive(&options) && i >= 0)
die(_("error: cannot combine interactive options "
"(--interactive, --exec, --rebase-merges, "
"--preserve-merges, --keep-empty, --root + "
"--onto) with am options (%s)"), buf.buf);
- if (options.type == REBASE_MERGE && buf.len)
+ if (options.type == REBASE_MERGE && i >= 0)
die(_("error: cannot combine merge options (--merge, "
"--strategy, --strategy-option) with am options "
"(%s)"), buf.buf);
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
"'--preserve-merges'");
- strbuf_addstr(&options.git_am_opt, " --signoff");
+ argv_array_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}