Thanks to Glen, Calvin, Junio, Jonathan, Elijah, and Phillip for your
feedback on v5. As before, if you feel strongly about one of the
suggestions that I have not incorporated into v6, or if you see
something else amiss, let's continue the discussion.
Alex Henrie (3):
rebase: add documentation and test for --no-rebase-merges
rebase: deprecate --rebase-merges=""
rebase: add a config option for --rebase-merges
Documentation/config/rebase.txt | 11 ++++
Documentation/git-rebase.txt | 19 ++++---
builtin/rebase.c | 75 ++++++++++++++++++-------
t/t3422-rebase-incompatible-options.sh | 17 ++++++
t/t3430-rebase-merges.sh | 78 ++++++++++++++++++++++++++
5 files changed, 174 insertions(+), 26 deletions(-)
Range-diff against v5:
1: 76e38ef9f8 ! 1: bf08c03ba7 rebase: add documentation and test for --no-rebase-merges
@@ Metadata
## Commit message ##
rebase: add documentation and test for --no-rebase-merges
+ As far as I can tell, --no-rebase-merges has always worked, but has
+ never been documented. It is especially important to document it before
+ a rebase.rebaseMerges option is introduced so that users know how to
+ override the config option on the command line. It's also important to
+ clarify that --rebase-merges without an argument is not the same as
+ --no-rebase-merges and not passing --rebase-merges is not the same as
+ passing --rebase-merges=no-rebase-cousins.
+
+ A test case is necessary to make sure that --no-rebase-merges keeps
+ working after its code is refactored in the following patches of this
+ series. The test case is a little contrived: It's unlikely that a user
+ would type both --rebase-merges and --no-rebase-merges at the same time.
+ However, if an alias is defined which includes --rebase-merges, the user
+ might decide to add --no-rebase-merges to countermand that part of the
+ alias but leave alone other flags set by the alias.
+
Signed-off-by: Alex Henrie <alexhenrie24@xxxxxxxxx>
## Documentation/git-rebase.txt ##
@@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
+ resolved/re-applied manually. `--no-rebase-merges` can be used to
+ countermand a previous `--rebase-merges`.
+
- By default, or when `no-rebase-cousins` was specified, commits which do not
- have `<upstream>` as direct ancestor will keep their original branch point,
+-By default, or when `no-rebase-cousins` was specified, commits which do not
+-have `<upstream>` as direct ancestor will keep their original branch point,
+-i.e. commits that would be excluded by linkgit:git-log[1]'s
+-`--ancestry-path` option will keep their original ancestry by default. If
+-the `rebase-cousins` mode is turned on, such commits are instead rebased
+-onto `<upstream>` (or `<onto>`, if specified).
++When rebasing merges, there are two modes: `rebase-cousins` and
++`no-rebase-cousins`. If the mode is not specified, it defaults to
++`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
++`<upstream>` as direct ancestor will keep their original branch point, i.e.
++commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
++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
+ `ort` merge strategy; different merge strategies can be used only via
## t/t3430-rebase-merges.sh ##
@@ t/t3430-rebase-merges.sh: test_expect_success 'with a branch tip that was cherry-picked already' '
2: c6099e6dee ! 2: 26f98b8400 rebase: deprecate --rebase-merges=""
@@ Commit message
The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
- --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid
- confusion when a rebase.merges config option is introduced, where
- rebase.merges="" will be equivalent to --no-rebase-merges.
+ --rebase-merges without an argument. Deprecate that syntax to avoid
+ confusion when a rebase.rebaseMerges config option is introduced, where
+ rebase.rebaseMerges="" will be equivalent to --no-rebase-merges.
+
+ It is not likely that anyone is actually using this syntax, but just in
+ case, deprecate the empty string argument instead of dropping support
+ for it immediately.
Signed-off-by: Alex Henrie <alexhenrie24@xxxxxxxxx>
@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
+ warning(_("--rebase-merges with an empty string "
+ "argument is deprecated and will stop "
+ "working in a future version of Git. Use "
-+ "--rebase-merges=no-rebase-cousins "
-+ "instead."));
++ "--rebase-merges without an argument "
++ "instead, which does the same thing."));
else if (!strcmp("rebase-cousins", rebase_merges))
options.rebase_cousins = 1;
else if (strcmp("no-rebase-cousins", rebase_merges))
-
- ## t/t3430-rebase-merges.sh ##
-@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '
- EOF
- '
-
-+test_expect_success '--rebase-merges="" is deprecated' '
-+ git rebase --rebase-merges="" HEAD^ 2>actual &&
-+ grep deprecated actual
-+'
-+
- test_expect_success 'refs/rewritten/* is worktree-local' '
- git worktree add wt &&
- cat >wt/script-from-scratch <<-\EOF &&
3: 95cba9588c ! 3: 402365256c rebase: add a config option for --rebase-merges
@@ Commit message
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 possibly turning
- on --rebase-merges by default without configuration in a future version
- of Git.
+ --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.
+
+ In the future, the default rebase-merges mode may change from
+ no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges
+ to the nonspecific value "true" for users who do not need or want to
+ care about the default changing in the future. Similarly, for users who
+ have --rebase-merges in an alias and want to get the future behavior
+ now, use the specific rebase-merges mode from the config if a specific
+ mode is not given on the command line.
Signed-off-by: Alex Henrie <alexhenrie24@xxxxxxxxx>
@@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::
rebase.forkPoint::
If set to false set `--no-fork-point` option by default.
+
-+rebase.merges::
++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 is equivalent to `--rebase-merges` without an argument, setting to
+ `rebase-cousins` or `no-rebase-cousins` is equivalent to
+ `--rebase-merges` with that value as its argument, and setting to false
+ is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
-+ command line without an argument overrides a `rebase.merges=false`
-+ configuration but does not override other values of `rebase.merge`.
++ command line without an argument overrides a `rebase.rebaseMerges=false`
++ configuration, but the absence of a specific rebase-merges mode on the
++ command line does not counteract a specific mode set in the configuration.
## Documentation/git-rebase.txt ##
@@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
@@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
manual amendments in these merge commits will have to be
resolved/re-applied manually. `--no-rebase-merges` can be used to
- countermand a previous `--rebase-merges`.
-+ countermand both the `rebase.merges` config option and a previous
++ 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
- have `<upstream>` as direct ancestor will keep their original branch point,
+ When rebasing merges, there are two modes: `rebase-cousins` and
+-`no-rebase-cousins`. If the mode is not specified, it defaults to
+-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+-`<upstream>` as direct ancestor will keep their original branch point, i.e.
+-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+-option will keep their original ancestry by default. In `rebase-cousins` mode,
+-such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+-specified).
++`no-rebase-cousins`. If the mode is not specified on the command line or in
++the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.
++In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
++ancestor will keep their original branch point, i.e. commits that would be
++excluded by linkgit:git-log[1]'s `--ancestry-path` 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
+ `ort` merge strategy; different merge strategies can be used only via
## builtin/rebase.c ##
@@ builtin/rebase.c: struct rebase_options {
@@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
return 0;
}
-+ if (!strcmp(var, "rebase.merges")) {
++ 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;
@@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const cha
+ warning(_("--rebase-merges with an empty string "
+ "argument is deprecated and will stop "
+ "working in a future version of Git. Use "
-+ "--rebase-merges=no-rebase-cousins "
-+ "instead."));
-+ arg = "no-rebase-cousins";
++ "--rebase-merges without an argument "
++ "instead, which does the same thing."));
++ return 0;
+ }
+ parse_rebase_merges_value(options, arg);
+ }
@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
- warning(_("--rebase-merges with an empty string "
- "argument is deprecated and will stop "
- "working in a future version of Git. Use "
-- "--rebase-merges=no-rebase-cousins "
-- "instead."));
+- "--rebase-merges without an argument "
+- "instead, which does the same thing."));
- else if (!strcmp("rebase-cousins", rebase_merges))
- options.rebase_cousins = 1;
- else if (strcmp("no-rebase-cousins", rebase_merges))
@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
+ if (options.autosquash == -1 && options.config_autosquash == 1)
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.merges. Consider adding --no-rebase-merges"));
++ die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
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 if (is_merge(&options))
@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
## t/t3422-rebase-incompatible-options.sh ##
+@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
+ 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" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --update-refs A
@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
grep -e --no-autosquash err
"
-+ test_expect_success "$opt incompatible with rebase.merges" "
++ test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+ git checkout B^0 &&
-+ test_must_fail git -c rebase.merges=true rebase $opt A 2>err &&
++ test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+ grep -e --no-rebase-merges err
+ "
+
@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
git -c rebase.autosquash=true rebase --no-autosquash $opt A
"
-+ test_expect_success "$opt okay with overridden rebase.merges" "
++ test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
-+ git -c rebase.merges=true rebase --no-rebase-merges $opt A
++ git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+ "
+
test_expect_success "$opt okay with overridden rebase.updateRefs" "
@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
git checkout B^0 &&
## t/t3430-rebase-merges.sh ##
-@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated' '
- grep deprecated actual
+@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '
+ EOF
'
-+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
-+ test_config rebase.merges rebase-cousins &&
++test_expect_success '--rebase-merges="" is deprecated' '
++ git rebase --rebase-merges="" HEAD^ 2>actual &&
++ grep deprecated actual
++'
++
++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
@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated'
+ EOF
+'
+
-+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
-+ test_config rebase.merges no-rebase-cousins &&
++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
@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated'
+ EOF
+'
+
-+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
-+ test_config rebase.merges rebase-cousins &&
++test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '
++ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b override-config-rebase-cousins main &&
+ git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
+ test_cmp_graph HEAD^.. <<-\EOF
@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated'
+ EOF
+'
+
-+test_expect_success '--rebase-merges overrides rebase.merges=false' '
-+ test_config rebase.merges false &&
++test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
++ test_config rebase.rebaseMerges false &&
+ git checkout -b override-config-merges-false E &&
+ before="$(git rev-parse --verify HEAD)" &&
+ test_tick &&
@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated'
+ test_cmp_rev HEAD $before
+'
+
-+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
-+ test_config rebase.merges rebase-cousins &&
++test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '
++ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b no-override-config-rebase-cousins main &&
+ git rebase --rebase-merges HEAD^ &&
+ test_cmp_graph HEAD^.. <<-\EOF