Skip to content

Commit 6605fb7

Browse files
alexhenriegitster
authored andcommitted
rebase: add a config option for --rebase-merges
The purpose of the new option is to accommodate users who would like --rebase-merges to be on by default and to facilitate turning on --rebase-merges by default without configuration in a future version of Git. Name the new option rebase.rebaseMerges, even though it is a little redundant, for consistency with the name of the command line option and to be clear when scrolling through values in the [rebase] section of .gitconfig. Support setting rebase.rebaseMerges to the nonspecific value "true" for users who don't need to or don't want to learn about the difference between rebase-cousins and no-rebase-cousins. Make --rebase-merges without an argument on the command line override any value of rebase.rebaseMerges in the configuration, for consistency with other command line flags with optional arguments that have an associated config option. Signed-off-by: Alex Henrie <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 33561f5 commit 6605fb7

File tree

5 files changed

+118
-20
lines changed

5 files changed

+118
-20
lines changed

Documentation/config/rebase.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
6767

6868
rebase.forkPoint::
6969
If set to false set `--no-fork-point` option by default.
70+
71+
rebase.rebaseMerges::
72+
Whether and how to set the `--rebase-merges` option by default. Can
73+
be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
74+
true or to `no-rebase-cousins` is equivalent to
75+
`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
76+
equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
77+
equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
78+
command line, with or without an argument, overrides any
79+
`rebase.rebaseMerges` configuration.

Documentation/git-rebase.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
537537
by recreating the merge commits. Any resolved merge conflicts or
538538
manual amendments in these merge commits will have to be
539539
resolved/re-applied manually. `--no-rebase-merges` can be used to
540-
countermand a previous `--rebase-merges`.
540+
countermand both the `rebase.rebaseMerges` config option and a previous
541+
`--rebase-merges`.
541542
+
542543
When rebasing merges, there are two modes: `rebase-cousins` and
543544
`no-rebase-cousins`. If the mode is not specified, it defaults to

builtin/rebase.c

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct rebase_options {
124124
int fork_point;
125125
int update_refs;
126126
int config_autosquash;
127+
int config_rebase_merges;
127128
int config_update_refs;
128129
};
129130

@@ -141,6 +142,8 @@ struct rebase_options {
141142
.allow_empty_message = 1, \
142143
.autosquash = -1, \
143144
.config_autosquash = -1, \
145+
.rebase_merges = -1, \
146+
.config_rebase_merges = -1, \
144147
.update_refs = -1, \
145148
.config_update_refs = -1, \
146149
}
@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
772775
return status ? -1 : 0;
773776
}
774777

778+
static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
779+
{
780+
if (!strcmp("no-rebase-cousins", value))
781+
options->rebase_cousins = 0;
782+
else if (!strcmp("rebase-cousins", value))
783+
options->rebase_cousins = 1;
784+
else
785+
die(_("Unknown rebase-merges mode: %s"), value);
786+
}
787+
775788
static int rebase_config(const char *var, const char *value, void *data)
776789
{
777790
struct rebase_options *opts = data;
@@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *data)
801814
return 0;
802815
}
803816

817+
if (!strcmp(var, "rebase.rebasemerges")) {
818+
opts->config_rebase_merges = git_parse_maybe_bool(value);
819+
if (opts->config_rebase_merges < 0) {
820+
opts->config_rebase_merges = 1;
821+
parse_rebase_merges_value(opts, value);
822+
} else {
823+
opts->rebase_cousins = 0;
824+
}
825+
return 0;
826+
}
827+
804828
if (!strcmp(var, "rebase.updaterefs")) {
805829
opts->config_update_refs = git_config_bool(var, value);
806830
return 0;
@@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
9811005
return 0;
9821006
}
9831007

1008+
static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
1009+
{
1010+
struct rebase_options *options = opt->value;
1011+
1012+
options->rebase_merges = !unset;
1013+
options->rebase_cousins = 0;
1014+
1015+
if (arg) {
1016+
if (!*arg) {
1017+
warning(_("--rebase-merges with an empty string "
1018+
"argument is deprecated and will stop "
1019+
"working in a future version of Git. Use "
1020+
"--rebase-merges without an argument "
1021+
"instead, which does the same thing."));
1022+
return 0;
1023+
}
1024+
parse_rebase_merges_value(options, arg);
1025+
}
1026+
1027+
return 0;
1028+
}
1029+
9841030
static void NORETURN error_on_missing_default_upstream(void)
9851031
{
9861032
struct branch *current_branch = branch_get(NULL);
@@ -1036,7 +1082,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
10361082
struct object_id branch_base;
10371083
int ignore_whitespace = 0;
10381084
const char *gpg_sign = NULL;
1039-
const char *rebase_merges = NULL;
10401085
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
10411086
struct object_id squash_onto;
10421087
char *squash_onto_name = NULL;
@@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
11381183
&options.allow_empty_message,
11391184
N_("allow rebasing commits with empty messages"),
11401185
PARSE_OPT_HIDDEN),
1141-
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
1142-
N_("mode"),
1186+
OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
11431187
N_("try to rebase merges instead of skipping them"),
1144-
PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
1188+
PARSE_OPT_OPTARG, parse_opt_rebase_merges),
11451189
OPT_BOOL(0, "fork-point", &options.fork_point,
11461190
N_("use 'merge-base --fork-point' to refine upstream")),
11471191
OPT_STRING('s', "strategy", &options.strategy,
@@ -1437,21 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
14371481
if (options.exec.nr)
14381482
imply_merge(&options, "--exec");
14391483

1440-
if (rebase_merges) {
1441-
if (!*rebase_merges)
1442-
warning(_("--rebase-merges with an empty string "
1443-
"argument is deprecated and will stop "
1444-
"working in a future version of Git. Use "
1445-
"--rebase-merges without an argument "
1446-
"instead, which does the same thing."));
1447-
else if (!strcmp("rebase-cousins", rebase_merges))
1448-
options.rebase_cousins = 1;
1449-
else if (strcmp("no-rebase-cousins", rebase_merges))
1450-
die(_("Unknown mode: %s"), rebase_merges);
1451-
options.rebase_merges = 1;
1452-
imply_merge(&options, "--rebase-merges");
1453-
}
1454-
14551484
if (options.type == REBASE_APPLY) {
14561485
if (ignore_whitespace)
14571486
strvec_push(&options.git_am_opts,
@@ -1519,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
15191548
"cannot be used together"));
15201549
else if (options.autosquash == -1 && options.config_autosquash == 1)
15211550
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
1551+
else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
1552+
die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
15221553
else if (options.update_refs == -1 && options.config_update_refs == 1)
15231554
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
15241555
else
@@ -1531,6 +1562,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
15311562
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
15321563
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
15331564

1565+
if (options.rebase_merges == 1)
1566+
imply_merge(&options, "--rebase-merges");
1567+
options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
1568+
((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
1569+
15341570
if (options.autosquash == 1)
15351571
imply_merge(&options, "--autosquash");
15361572
options.autosquash = (options.autosquash >= 0) ? options.autosquash :

t/t3422-rebase-incompatible-options.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ test_rebase_am_only () {
8585
test_must_fail git rebase $opt --reapply-cherry-picks A
8686
"
8787

88+
test_expect_success "$opt incompatible with --rebase-merges" "
89+
git checkout B^0 &&
90+
test_must_fail git rebase $opt --rebase-merges A
91+
"
92+
8893
test_expect_success "$opt incompatible with --update-refs" "
8994
git checkout B^0 &&
9095
test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
101106
grep -e --no-autosquash err
102107
"
103108

109+
test_expect_success "$opt incompatible with rebase.rebaseMerges" "
110+
git checkout B^0 &&
111+
test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
112+
grep -e --no-rebase-merges err
113+
"
114+
104115
test_expect_success "$opt incompatible with rebase.updateRefs" "
105116
git checkout B^0 &&
106117
test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
113124
git -c rebase.autosquash=true rebase --no-autosquash $opt A
114125
"
115126

127+
test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
128+
test_when_finished \"git reset --hard B^0\" &&
129+
git checkout B^0 &&
130+
git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
131+
"
132+
116133
test_expect_success "$opt okay with overridden rebase.updateRefs" "
117134
test_when_finished \"git reset --hard B^0\" &&
118135
git checkout B^0 &&

t/t3430-rebase-merges.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
278278
EOF
279279
'
280280

281+
test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
282+
test_config rebase.rebaseMerges rebase-cousins &&
283+
git checkout -b config-rebase-cousins main &&
284+
git rebase HEAD^ &&
285+
test_cmp_graph HEAD^.. <<-\EOF
286+
* Merge the topic branch '\''onebranch'\''
287+
|\
288+
| * D
289+
| * G
290+
|/
291+
o H
292+
EOF
293+
'
294+
295+
test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
296+
test_config rebase.rebaseMerges no-rebase-cousins &&
297+
git checkout -b override-config-no-rebase-cousins E &&
298+
git rebase --no-rebase-merges C &&
299+
test_cmp_graph C.. <<-\EOF
300+
* B
301+
* D
302+
o C
303+
EOF
304+
'
305+
306+
test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
307+
test_config rebase.rebaseMerges rebase-cousins &&
308+
git checkout -b override-config-rebase-cousins E &&
309+
before="$(git rev-parse --verify HEAD)" &&
310+
test_tick &&
311+
git rebase --rebase-merges C &&
312+
test_cmp_rev HEAD $before
313+
'
314+
281315
test_expect_success 'refs/rewritten/* is worktree-local' '
282316
git worktree add wt &&
283317
cat >wt/script-from-scratch <<-\EOF &&

0 commit comments

Comments
 (0)