Merge branch 'ah/rebase-merges-config'
Streamline --rebase-merges command line option handling and introduce rebase.merges configuration variable. * ah/rebase-merges-config: rebase: add a config option for --rebase-merges rebase: deprecate --rebase-merges="" rebase: add documentation and test for --no-rebase-merges
This commit is contained in:
commit
9142fce9b0
@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
|
|||||||
|
|
||||||
rebase.forkPoint::
|
rebase.forkPoint::
|
||||||
If set to false set `--no-fork-point` option by default.
|
If set to false set `--no-fork-point` option by default.
|
||||||
|
|
||||||
|
rebase.rebaseMerges::
|
||||||
|
Whether and how to set the `--rebase-merges` option by default. Can
|
||||||
|
be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
|
||||||
|
true or to `no-rebase-cousins` is equivalent to
|
||||||
|
`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
|
||||||
|
equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
|
||||||
|
equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
|
||||||
|
command line, with or without an argument, overrides any
|
||||||
|
`rebase.rebaseMerges` configuration.
|
||||||
|
@ -529,20 +529,25 @@ See also INCOMPATIBLE OPTIONS below.
|
|||||||
|
|
||||||
-r::
|
-r::
|
||||||
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
|
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
|
||||||
|
--no-rebase-merges::
|
||||||
By default, a rebase will simply drop merge commits from the todo
|
By default, a rebase will simply drop merge commits from the todo
|
||||||
list, and put the rebased commits into a single, linear branch.
|
list, and put the rebased commits into a single, linear branch.
|
||||||
With `--rebase-merges`, the rebase will instead try to preserve
|
With `--rebase-merges`, the rebase will instead try to preserve
|
||||||
the branching structure within the commits that are to be rebased,
|
the branching structure within the commits that are to be rebased,
|
||||||
by recreating the merge commits. Any resolved merge conflicts or
|
by recreating the merge commits. Any resolved merge conflicts or
|
||||||
manual amendments in these merge commits will have to be
|
manual amendments in these merge commits will have to be
|
||||||
resolved/re-applied manually.
|
resolved/re-applied manually. `--no-rebase-merges` can be used to
|
||||||
|
countermand both the `rebase.rebaseMerges` config option and a previous
|
||||||
|
`--rebase-merges`.
|
||||||
+
|
+
|
||||||
By default, or when `no-rebase-cousins` was specified, commits which do not
|
When rebasing merges, there are two modes: `rebase-cousins` and
|
||||||
have `<upstream>` as direct ancestor will keep their original branch point,
|
`no-rebase-cousins`. If the mode is not specified, it defaults to
|
||||||
i.e. commits that would be excluded by linkgit:git-log[1]'s
|
`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
|
||||||
`--ancestry-path` option will keep their original ancestry by default. If
|
`<upstream>` as direct ancestor will keep their original branch point, i.e.
|
||||||
the `rebase-cousins` mode is turned on, such commits are instead rebased
|
commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
|
||||||
onto `<upstream>` (or `<onto>`, if specified).
|
option will keep their original ancestry by default. In `rebase-cousins` mode,
|
||||||
|
such commits are instead rebased onto `<upstream>` (or `<onto>`, if
|
||||||
|
specified).
|
||||||
+
|
+
|
||||||
It is currently only possible to recreate the merge commits using the
|
It is currently only possible to recreate the merge commits using the
|
||||||
`ort` merge strategy; different merge strategies can be used only via
|
`ort` merge strategy; different merge strategies can be used only via
|
||||||
|
@ -124,6 +124,7 @@ struct rebase_options {
|
|||||||
int fork_point;
|
int fork_point;
|
||||||
int update_refs;
|
int update_refs;
|
||||||
int config_autosquash;
|
int config_autosquash;
|
||||||
|
int config_rebase_merges;
|
||||||
int config_update_refs;
|
int config_update_refs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,6 +142,8 @@ struct rebase_options {
|
|||||||
.allow_empty_message = 1, \
|
.allow_empty_message = 1, \
|
||||||
.autosquash = -1, \
|
.autosquash = -1, \
|
||||||
.config_autosquash = -1, \
|
.config_autosquash = -1, \
|
||||||
|
.rebase_merges = -1, \
|
||||||
|
.config_rebase_merges = -1, \
|
||||||
.update_refs = -1, \
|
.update_refs = -1, \
|
||||||
.config_update_refs = -1, \
|
.config_update_refs = -1, \
|
||||||
}
|
}
|
||||||
@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
|
|||||||
return status ? -1 : 0;
|
return status ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
|
||||||
|
{
|
||||||
|
if (!strcmp("no-rebase-cousins", value))
|
||||||
|
options->rebase_cousins = 0;
|
||||||
|
else if (!strcmp("rebase-cousins", value))
|
||||||
|
options->rebase_cousins = 1;
|
||||||
|
else
|
||||||
|
die(_("Unknown rebase-merges mode: %s"), value);
|
||||||
|
}
|
||||||
|
|
||||||
static int rebase_config(const char *var, const char *value, void *data)
|
static int rebase_config(const char *var, const char *value, void *data)
|
||||||
{
|
{
|
||||||
struct rebase_options *opts = data;
|
struct rebase_options *opts = data;
|
||||||
@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(var, "rebase.rebasemerges")) {
|
||||||
|
opts->config_rebase_merges = git_parse_maybe_bool(value);
|
||||||
|
if (opts->config_rebase_merges < 0) {
|
||||||
|
opts->config_rebase_merges = 1;
|
||||||
|
parse_rebase_merges_value(opts, value);
|
||||||
|
} else {
|
||||||
|
opts->rebase_cousins = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(var, "rebase.updaterefs")) {
|
if (!strcmp(var, "rebase.updaterefs")) {
|
||||||
opts->config_update_refs = git_config_bool(var, value);
|
opts->config_update_refs = git_config_bool(var, value);
|
||||||
return 0;
|
return 0;
|
||||||
@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
|
||||||
|
{
|
||||||
|
struct rebase_options *options = opt->value;
|
||||||
|
|
||||||
|
options->rebase_merges = !unset;
|
||||||
|
options->rebase_cousins = 0;
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
if (!*arg) {
|
||||||
|
warning(_("--rebase-merges with an empty string "
|
||||||
|
"argument is deprecated and will stop "
|
||||||
|
"working in a future version of Git. Use "
|
||||||
|
"--rebase-merges without an argument "
|
||||||
|
"instead, which does the same thing."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
parse_rebase_merges_value(options, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void NORETURN error_on_missing_default_upstream(void)
|
static void NORETURN error_on_missing_default_upstream(void)
|
||||||
{
|
{
|
||||||
struct branch *current_branch = branch_get(NULL);
|
struct branch *current_branch = branch_get(NULL);
|
||||||
@ -1036,7 +1082,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
struct object_id branch_base;
|
struct object_id branch_base;
|
||||||
int ignore_whitespace = 0;
|
int ignore_whitespace = 0;
|
||||||
const char *gpg_sign = NULL;
|
const char *gpg_sign = NULL;
|
||||||
const char *rebase_merges = NULL;
|
|
||||||
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
|
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
|
||||||
struct object_id squash_onto;
|
struct object_id squash_onto;
|
||||||
char *squash_onto_name = NULL;
|
char *squash_onto_name = NULL;
|
||||||
@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
&options.allow_empty_message,
|
&options.allow_empty_message,
|
||||||
N_("allow rebasing commits with empty messages"),
|
N_("allow rebasing commits with empty messages"),
|
||||||
PARSE_OPT_HIDDEN),
|
PARSE_OPT_HIDDEN),
|
||||||
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
|
OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
|
||||||
N_("mode"),
|
|
||||||
N_("try to rebase merges instead of skipping them"),
|
N_("try to rebase merges instead of skipping them"),
|
||||||
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
|
PARSE_OPT_OPTARG, parse_opt_rebase_merges),
|
||||||
OPT_BOOL(0, "fork-point", &options.fork_point,
|
OPT_BOOL(0, "fork-point", &options.fork_point,
|
||||||
N_("use 'merge-base --fork-point' to refine upstream")),
|
N_("use 'merge-base --fork-point' to refine upstream")),
|
||||||
OPT_STRING('s', "strategy", &options.strategy,
|
OPT_STRING('s', "strategy", &options.strategy,
|
||||||
@ -1437,17 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
if (options.exec.nr)
|
if (options.exec.nr)
|
||||||
imply_merge(&options, "--exec");
|
imply_merge(&options, "--exec");
|
||||||
|
|
||||||
if (rebase_merges) {
|
|
||||||
if (!*rebase_merges)
|
|
||||||
; /* default mode; do nothing */
|
|
||||||
else if (!strcmp("rebase-cousins", rebase_merges))
|
|
||||||
options.rebase_cousins = 1;
|
|
||||||
else if (strcmp("no-rebase-cousins", rebase_merges))
|
|
||||||
die(_("Unknown mode: %s"), rebase_merges);
|
|
||||||
options.rebase_merges = 1;
|
|
||||||
imply_merge(&options, "--rebase-merges");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.type == REBASE_APPLY) {
|
if (options.type == REBASE_APPLY) {
|
||||||
if (ignore_whitespace)
|
if (ignore_whitespace)
|
||||||
strvec_push(&options.git_am_opts,
|
strvec_push(&options.git_am_opts,
|
||||||
@ -1515,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
"cannot be used together"));
|
"cannot be used together"));
|
||||||
else if (options.autosquash == -1 && options.config_autosquash == 1)
|
else if (options.autosquash == -1 && options.config_autosquash == 1)
|
||||||
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
|
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
|
||||||
|
else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
|
||||||
|
die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
|
||||||
else if (options.update_refs == -1 && options.config_update_refs == 1)
|
else if (options.update_refs == -1 && options.config_update_refs == 1)
|
||||||
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
|
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
|
||||||
else
|
else
|
||||||
@ -1527,6 +1562,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
|
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
|
||||||
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
|
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
|
||||||
|
|
||||||
|
if (options.rebase_merges == 1)
|
||||||
|
imply_merge(&options, "--rebase-merges");
|
||||||
|
options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
|
||||||
|
((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
|
||||||
|
|
||||||
if (options.autosquash == 1)
|
if (options.autosquash == 1)
|
||||||
imply_merge(&options, "--autosquash");
|
imply_merge(&options, "--autosquash");
|
||||||
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
|
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
|
||||||
|
@ -85,6 +85,11 @@ test_rebase_am_only () {
|
|||||||
test_must_fail git rebase $opt --reapply-cherry-picks A
|
test_must_fail git rebase $opt --reapply-cherry-picks A
|
||||||
"
|
"
|
||||||
|
|
||||||
|
test_expect_success "$opt incompatible with --rebase-merges" "
|
||||||
|
git checkout B^0 &&
|
||||||
|
test_must_fail git rebase $opt --rebase-merges A
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success "$opt incompatible with --update-refs" "
|
test_expect_success "$opt incompatible with --update-refs" "
|
||||||
git checkout B^0 &&
|
git checkout B^0 &&
|
||||||
test_must_fail git rebase $opt --update-refs A
|
test_must_fail git rebase $opt --update-refs A
|
||||||
@ -101,6 +106,12 @@ test_rebase_am_only () {
|
|||||||
grep -e --no-autosquash err
|
grep -e --no-autosquash err
|
||||||
"
|
"
|
||||||
|
|
||||||
|
test_expect_success "$opt incompatible with rebase.rebaseMerges" "
|
||||||
|
git checkout B^0 &&
|
||||||
|
test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
|
||||||
|
grep -e --no-rebase-merges err
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success "$opt incompatible with rebase.updateRefs" "
|
test_expect_success "$opt incompatible with rebase.updateRefs" "
|
||||||
git checkout B^0 &&
|
git checkout B^0 &&
|
||||||
test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
|
test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
|
||||||
@ -113,6 +124,12 @@ test_rebase_am_only () {
|
|||||||
git -c rebase.autosquash=true rebase --no-autosquash $opt A
|
git -c rebase.autosquash=true rebase --no-autosquash $opt A
|
||||||
"
|
"
|
||||||
|
|
||||||
|
test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
|
||||||
|
test_when_finished \"git reset --hard B^0\" &&
|
||||||
|
git checkout B^0 &&
|
||||||
|
git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success "$opt okay with overridden rebase.updateRefs" "
|
test_expect_success "$opt okay with overridden rebase.updateRefs" "
|
||||||
test_when_finished \"git reset --hard B^0\" &&
|
test_when_finished \"git reset --hard B^0\" &&
|
||||||
git checkout B^0 &&
|
git checkout B^0 &&
|
||||||
|
@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
|
|||||||
EOF
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--no-rebase-merges countermands --rebase-merges' '
|
||||||
|
git checkout -b no-rebase-merges E &&
|
||||||
|
git rebase --rebase-merges --no-rebase-merges C &&
|
||||||
|
test_cmp_graph C.. <<-\EOF
|
||||||
|
* B
|
||||||
|
* D
|
||||||
|
o C
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'do not rebase cousins unless asked for' '
|
test_expect_success 'do not rebase cousins unless asked for' '
|
||||||
git checkout -b cousins main &&
|
git checkout -b cousins main &&
|
||||||
before="$(git rev-parse --verify HEAD)" &&
|
before="$(git rev-parse --verify HEAD)" &&
|
||||||
@ -268,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
|
|||||||
EOF
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
|
||||||
|
test_config rebase.rebaseMerges rebase-cousins &&
|
||||||
|
git checkout -b config-rebase-cousins main &&
|
||||||
|
git rebase HEAD^ &&
|
||||||
|
test_cmp_graph HEAD^.. <<-\EOF
|
||||||
|
* Merge the topic branch '\''onebranch'\''
|
||||||
|
|\
|
||||||
|
| * D
|
||||||
|
| * G
|
||||||
|
|/
|
||||||
|
o H
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
|
||||||
|
test_config rebase.rebaseMerges no-rebase-cousins &&
|
||||||
|
git checkout -b override-config-no-rebase-cousins E &&
|
||||||
|
git rebase --no-rebase-merges C &&
|
||||||
|
test_cmp_graph C.. <<-\EOF
|
||||||
|
* B
|
||||||
|
* D
|
||||||
|
o C
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
|
||||||
|
test_config rebase.rebaseMerges rebase-cousins &&
|
||||||
|
git checkout -b override-config-rebase-cousins E &&
|
||||||
|
before="$(git rev-parse --verify HEAD)" &&
|
||||||
|
test_tick &&
|
||||||
|
git rebase --rebase-merges C &&
|
||||||
|
test_cmp_rev HEAD $before
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'refs/rewritten/* is worktree-local' '
|
test_expect_success 'refs/rewritten/* is worktree-local' '
|
||||||
git worktree add wt &&
|
git worktree add wt &&
|
||||||
cat >wt/script-from-scratch <<-\EOF &&
|
cat >wt/script-from-scratch <<-\EOF &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user