Skip to content

Commit

Permalink
Merge branch 'en/rebase-incompatible-opts'
Browse files Browse the repository at this point in the history
"git rebase" often ignored incompatible options instead of
complaining, which has been corrected.

* en/rebase-incompatible-opts:
  rebase: provide better error message for apply options vs. merge config
  rebase: put rebase_options initialization in single place
  rebase: fix formatting of rebase --reapply-cherry-picks option in docs
  rebase: clarify the OPT_CMDMODE incompatibilities
  rebase: add coverage of other incompatible options
  rebase: fix incompatiblity checks for --[no-]reapply-cherry-picks
  rebase: fix docs about incompatibilities with --root
  rebase: remove --allow-empty-message from incompatible opts
  rebase: flag --apply and --merge as incompatible
  rebase: mark --update-refs as requiring the merge backend
  • Loading branch information
gitster committed Feb 4, 2023
2 parents c7757b2 + eddfcd8 commit 2c6e5b3
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 64 deletions.
77 changes: 39 additions & 38 deletions Documentation/git-rebase.txt
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,39 @@ Alternatively, you can undo the 'git rebase' with

git rebase --abort

MODE OPTIONS
------------

The options in this section cannot be used with any other option,
including not with each other:

--continue::
Restart the rebasing process after having resolved a merge conflict.

--skip::
Restart the rebasing process by skipping the current patch.

--abort::
Abort the rebase operation and reset HEAD to the original
branch. If `<branch>` was provided when the rebase operation was
started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
will be reset to where it was when the rebase operation was
started.

--quit::
Abort the rebase operation but `HEAD` is not reset back to the
original branch. The index and working tree are also left
unchanged as a result. If a temporary stash entry was created
using `--autostash`, it will be saved to the stash list.

--edit-todo::
Edit the todo list during an interactive rebase.

--show-current-patch::
Show the current patch in an interactive rebase or when rebase
is stopped because of conflicts. This is the equivalent of
`git show REBASE_HEAD`.

OPTIONS
-------
--onto <newbase>::
Expand Down Expand Up @@ -249,22 +282,6 @@ See also INCOMPATIBLE OPTIONS below.
<branch>::
Working branch; defaults to `HEAD`.

--continue::
Restart the rebasing process after having resolved a merge conflict.

--abort::
Abort the rebase operation and reset HEAD to the original
branch. If `<branch>` was provided when the rebase operation was
started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
will be reset to where it was when the rebase operation was
started.

--quit::
Abort the rebase operation but `HEAD` is not reset back to the
original branch. The index and working tree are also left
unchanged as a result. If a temporary stash entry was created
using `--autostash`, it will be saved to the stash list.

--apply::
Use applying strategies to rebase (calling `git-am`
internally). This option may become a no-op in the future
Expand Down Expand Up @@ -321,7 +338,6 @@ See also INCOMPATIBLE OPTIONS below.
upstream changes, the behavior towards them is controlled by
the `--empty` flag.)
+

In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is
given), these commits will be automatically dropped. Because this
necessitates reading all upstream commits, this can be expensive in
Expand All @@ -330,7 +346,6 @@ read. When using the 'merge' backend, warnings will be issued for each
dropped commit (unless `--quiet` is given). Advice will also be issued
unless `advice.skippedCherryPicks` is set to false (see
linkgit:git-config[1]).

+
`--reapply-cherry-picks` allows rebase to forgo reading all upstream
commits, potentially improving performance.
Expand All @@ -345,17 +360,6 @@ See also INCOMPATIBLE OPTIONS below.
+
See also INCOMPATIBLE OPTIONS below.

--skip::
Restart the rebasing process by skipping the current patch.

--edit-todo::
Edit the todo list during an interactive rebase.

--show-current-patch::
Show the current patch in an interactive rebase or when rebase
is stopped because of conflicts. This is the equivalent of
`git show REBASE_HEAD`.

-m::
--merge::
Using merging strategies to rebase (default).
Expand Down Expand Up @@ -574,10 +578,7 @@ See also INCOMPATIBLE OPTIONS below.
--root::
Rebase all commits reachable from `<branch>`, instead of
limiting them with an `<upstream>`. This allows you to rebase
the root commit(s) on a branch. When used with `--onto`, it
will skip changes already contained in `<newbase>` (instead of
`<upstream>`) whereas without `--onto` it will operate on every
change.
the root commit(s) on a branch.
+
See also INCOMPATIBLE OPTIONS below.

Expand Down Expand Up @@ -630,6 +631,8 @@ start would be overridden by the presence of
+
If the configuration variable `rebase.updateRefs` is set, then this option
can be used to override and disable this setting.
+
See also INCOMPATIBLE OPTIONS below.

INCOMPATIBLE OPTIONS
--------------------
Expand All @@ -645,17 +648,15 @@ are incompatible with the following options:
* --merge
* --strategy
* --strategy-option
* --allow-empty-message
* --[no-]autosquash
* --autosquash
* --rebase-merges
* --interactive
* --exec
* --no-keep-empty
* --empty=
* --reapply-cherry-picks
* --edit-todo
* --[no-]reapply-cherry-picks when used without --keep-base
* --update-refs
* --root when used in combination with --onto
* --root when used without --onto

In addition, the following pairs of options are incompatible:

Expand Down
79 changes: 58 additions & 21 deletions builtin/rebase.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 { \
Expand All @@ -134,6 +136,12 @@ struct rebase_options {
.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)
Expand Down Expand Up @@ -776,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;
}

Expand All @@ -793,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;
}

Expand Down Expand Up @@ -907,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;
Expand All @@ -920,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;
}
Expand All @@ -935,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;

Expand Down Expand Up @@ -1150,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;
Expand Down Expand Up @@ -1216,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");

Expand Down Expand Up @@ -1365,7 +1372,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
(options.action != ACTION_NONE) ||
(options.exec.nr > 0) ||
options.autosquash) {
(options.autosquash == -1 && options.config_autosquash == 1) ||
options.autosquash == 1) {
allow_preemptive_ff = 0;
}
if (options.committer_date_is_author_date || options.ignore_date)
Expand Down Expand Up @@ -1398,12 +1406,27 @@ 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);
Expand Down Expand Up @@ -1483,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");
Expand Down
Loading

0 comments on commit 2c6e5b3

Please sign in to comment.