]> git.ipfire.org Git - thirdparty/git.git/commitdiff
rebase: add a config option for --rebase-merges
authorAlex Henrie <alexhenrie24@gmail.com>
Sun, 26 Mar 2023 03:06:36 +0000 (21:06 -0600)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Mar 2023 16:32:49 +0000 (09:32 -0700)
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 <alexhenrie24@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/rebase.txt
Documentation/git-rebase.txt
builtin/rebase.c
t/t3422-rebase-incompatible-options.sh
t/t3430-rebase-merges.sh

index f19bd0e04079051d6be21ee461d1a98c9a632629..afaf6dad99b5542ab06c31e4276a695128ee6b8a 100644 (file)
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
        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.
index 4e57a876241f12b1af0250641d52babc29a1765f..e7b39ad244a4bebc90e9c1974e4239ae771c56d4 100644 (file)
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
        by recreating the merge commits. Any resolved merge conflicts or
        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.rebaseMerges` config option and a previous
+       `--rebase-merges`.
 +
 When rebasing merges, there are two modes: `rebase-cousins` and
 `no-rebase-cousins`. If the mode is not specified, it defaults to
index 4b3f29a4499fff5ed24e265e6ff0da90e7d4aa98..1e8419d5056f33fdc6c1f7f304ea4e4be20b0de4 100644 (file)
@@ -124,6 +124,7 @@ struct rebase_options {
        int fork_point;
        int update_refs;
        int config_autosquash;
+       int config_rebase_merges;
        int config_update_refs;
 };
 
@@ -141,6 +142,8 @@ struct rebase_options {
                .allow_empty_message = 1,               \
                .autosquash = -1,                       \
                .config_autosquash = -1,                \
+               .rebase_merges = -1,                    \
+               .config_rebase_merges = -1,             \
                .update_refs = -1,                      \
                .config_update_refs = -1,               \
        }
@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
        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)
 {
        struct rebase_options *opts = data;
@@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *data)
                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")) {
                opts->config_update_refs = git_config_bool(var, value);
                return 0;
@@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
        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)
 {
        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;
        int ignore_whitespace = 0;
        const char *gpg_sign = NULL;
-       const char *rebase_merges = NULL;
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
@@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                           &options.allow_empty_message,
                           N_("allow rebasing commits with empty messages"),
                           PARSE_OPT_HIDDEN),
-               {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-                       N_("mode"),
+               OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
                        N_("try to rebase merges instead of skipping them"),
-                       PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+                       PARSE_OPT_OPTARG, parse_opt_rebase_merges),
                OPT_BOOL(0, "fork-point", &options.fork_point,
                         N_("use 'merge-base --fork-point' to refine upstream")),
                OPT_STRING('s', "strategy", &options.strategy,
@@ -1437,21 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.exec.nr)
                imply_merge(&options, "--exec");
 
-       if (rebase_merges) {
-               if (!*rebase_merges)
-                       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."));
-               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 (ignore_whitespace)
                        strvec_push(&options.git_am_opts,
@@ -1519,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                          "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.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)
                                die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
                        else
@@ -1531,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.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)
                imply_merge(&options, "--autosquash");
        options.autosquash = (options.autosquash >= 0) ? options.autosquash :
index 4711b37a28863e50a06e098bcbcd9417951c9b4d..2eba00bdf5898531744c8f77429a8f4d1ad1b91d 100755 (executable)
@@ -85,6 +85,11 @@ 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
@@ -101,6 +106,12 @@ test_rebase_am_only () {
                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" "
                git checkout B^0 &&
                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
        "
 
+       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_when_finished \"git reset --hard B^0\" &&
                git checkout B^0 &&
index d46d9545f292893ab2cf9b67f15871d088571269..f03599c63b969d6216f7370e1a24b0160bc6ab69 100755 (executable)
@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
        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' '
        git worktree add wt &&
        cat >wt/script-from-scratch <<-\EOF &&