{"thread":{"id":"59293","subject":"[PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","startedAt":"2023-02-23T05:34:50Z","lastAt":"2023-03-27T16:33:52Z","messageCount":96,"participants":["Alex Henrie","Junio C Hamano","Johannes Schindelin","Phillip Wood","Glen Choo","Calvin Wan","Sergey Organov","Felipe Contreras"],"isPatch":true,"patchVersion":4,"patchTotal":3},"messages":[{"id":"472473","messageId":"20230223053410.644503-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":null,"subject":"[PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-23T05:34:08Z","receivedAt":"2023-02-23T05:34:50Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt |  4 +++-\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..c98784a0d2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,13 +529,15 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n By default, or when `no-rebase-cousins` was specified, commits which do not\n have `<upstream>` as direct ancestor will keep their original branch point,\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.39.2\n\n"},{"id":"472474","messageId":"20230223053410.644503-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230223053410.644503-1-alexhenrie24@gmail.com","subject":"[PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-23T05:34:09Z","receivedAt":"2023-02-23T05:34:52Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid\nconfusion when a rebase.merges config option is introduced, where\nrebase.merges=\"\" will be equivalent to not passing --rebase-merges.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c         | 6 ++----\n t/t3430-rebase-merges.sh | 6 ++++++\n 2 files changed, 8 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 6635f10d52..b68fc2fbb7 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1437,9 +1437,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\timply_merge(&options, \"--exec\");\n \n \tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n+\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n \t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..c73949df18 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,12 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success '--rebase-merges=\"\" is invalid syntax' '\n+\techo \"fatal: Unknown mode: \" >expect &&\n+\ttest_must_fail git rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"472475","messageId":"20230223053410.644503-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230223053410.644503-1-alexhenrie24@gmail.com","subject":"[PATCH v4 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-23T05:34:10Z","receivedAt":"2023-02-23T05:34:57Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate possibly turning\non --rebase-merges by default without configuration in a future version\nof Git.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt | 10 ++++\n Documentation/git-rebase.txt    |  3 +-\n builtin/rebase.c                | 47 ++++++++++++----\n t/t3430-rebase-merges.sh        | 96 +++++++++++++++++++++++++++++++++\n 4 files changed, 144 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..308baa9dbb 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.merges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n+\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges` with that value as its argument, and setting to false\n+\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line without an argument overrides a `rebase.merges=false`\n+\tconfiguration but does not override other values of `rebase.merge`.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c98784a0d2..b02f9cbb8c 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.merges` config option and a previous\n+\t`--rebase-merges`.\n +\n By default, or when `no-rebase-cousins` was specified, commits which do not\n have `<upstream>` as direct ancestor will keep their original branch point,\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex b68fc2fbb7..45cf445d42 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -771,6 +771,20 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (value) {\n+\t\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\t\toptions->rebase_cousins = 0;\n+\t\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\t\toptions->rebase_cousins = 1;\n+\t\telse\n+\t\t\tdie(_(\"Unknown mode: %s\"), value);\n+\t}\n+\n+\toptions->rebase_merges = 1;\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -815,6 +829,13 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.merges\") && value && *value) {\n+\t\topts->rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->rebase_merges < 0)\n+\t\t\tparse_merges_value(opts, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.backend\")) {\n \t\treturn git_config_string(&opts->default_backend, var, value);\n \t}\n@@ -980,6 +1001,18 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\tif (unset)\n+\t\toptions->rebase_merges = 0;\n+\telse\n+\t\tparse_merges_value(options, arg);\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1035,7 +1068,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1137,10 +1169,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n+\tif (options.rebase_merges)\n \t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n \n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex c73949df18..d4b0e8fd49 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -284,6 +284,102 @@ test_expect_success '--rebase-merges=\"\" is invalid syntax' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase.merges=\"\" is equivalent to not passing --rebase-merges' '\n+\ttest_config rebase.merges \"\" &&\n+\tgit checkout -b config-merges-blank E &&\n+\tgit rebase C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n+\ttest_config rebase.merges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\to | H\n+\t|/\n+\to A\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.merges=false' '\n+\ttest_config rebase.merges false &&\n+\tgit checkout -b override-config-merges-false E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b no-override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '\n+\ttest_config_global rebase.merges true &&\n+\ttest_config rebase.merges false &&\n+\tgit checkout -b override-global-config E &&\n+\tgit rebase C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success 'local rebase.merges=\"\" does not override global rebase.merges=true' '\n+\ttest_config_global rebase.merges no-rebase-cousins &&\n+\ttest_config rebase.merges \"\" &&\n+\tgit checkout -b no-override-global-config E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"472558","messageId":"xmqqh6vc9tqy.fsf@gitster.g","threadId":"59293","inReplyTo":"20230223053410.644503-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-23T17:28:05Z","receivedAt":"2023-02-23T17:28:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n\nNo cover letter to summarize the changes?\n\nComparing this round with the previous, the only two changes are\nthat [1/3] lost one test \"do not rebase merges unless asked to\", and\n[2/3] now uses test_must_fail to mark a git command that is expected\nto stop.\n\nLooks good, but a rerolled patch series should not force reviewers\nto fetch and compare, but give a summary of changes when it is sent\nout, to save everybody's time.  Preparing a summary before sending\nit out also helps the patch author, too, that all the intended\nchanges are included in the new round.\n\nThanks.\n"},{"id":"472647","messageId":"c918797c-75cc-80d4-c978-360388877d63@gmx.de","threadId":"59293","inReplyTo":"20230223053410.644503-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-02-24T13:53:27Z","receivedAt":"2023-02-24T13:53:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Alex,\n\nOn Wed, 22 Feb 2023, Alex Henrie wrote:\n\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index b68fc2fbb7..45cf445d42 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -771,6 +771,20 @@ static int run_specific_rebase(struct rebase_options *opts)\n>  \treturn status ? -1 : 0;\n>  }\n>\n> +static void parse_merges_value(struct rebase_options *options, const char *value)\n> +{\n> +\tif (value) {\n> +\t\tif (!strcmp(\"no-rebase-cousins\", value))\n\nIf you want to support `rebase.merges=` to imply `no-rebase-cousins`, this\nwould be the correct place to do it:\n\n\t\tif (!*value || !strcmp(\"no-rebase-cousins\", value))\n\n> +\t\t\toptions->rebase_cousins = 0;\n> +\t\telse if (!strcmp(\"rebase-cousins\", value))\n> +\t\t\toptions->rebase_cousins = 1;\n> +\t\telse\n> +\t\t\tdie(_(\"Unknown mode: %s\"), value);\n> +\t}\n> +\n> +\toptions->rebase_merges = 1;\n\nI would expect `options->rebase_merges = 0` if `value == NULL`. IOW I\nwould have expected `parse_merges_value()` to start with:\n\n\tif (!value) {\n\t\toptions->rebase_merges = 0;\n\t\treturn;\n\t}\n\nThis assumes, of course, the parse_options semantics, where a `--no-*` option\npasses `NULL` as argument to the callback.\n\nHowever, this is _not_ the parse_options callback, and if the (optional)\nargument was not specified, we do end up with a `NULL` here in spite of\nwanting to enable the rebase-merges mode.\n\nHowever, a primary reason why you introduce the function is to support\nconfig value parsing. And in config value parsing, a \"maybe-bool\" with a\nNULL value is considered to be equivalent to `true`! (See\n`git_parse_maybe_bool_text()` or\nhttps://git-scm.com/docs/git-config#Documentation/git-config.txt-true for\ndetails.). For example,\n\n\t[http]\n\t\tsslVerify\n\nis equivalent to\n\n\t[http]\n\t\tsslVerify = true\n\nBut since `git_parse_maybe_bool()` already takes care of handling that\ncase (in which case we do not even want to call `git_parse_maybe_bool()`),\nyou can limit that function to handling the command-line semantics.\n\nSo with those confusingly disagreeing semantics, I see not only myself,\nbut other readers doing very, very well, indeed, with a code comment that\nexplains under what circumstances we expect this callback to be called\nwith `value == NULL`.\n\n> +}\n> +\n>  static int rebase_config(const char *var, const char *value, void *data)\n>  {\n>  \tstruct rebase_options *opts = data;\n> @@ -815,6 +829,13 @@ static int rebase_config(const char *var, const char *value, void *data)\n>  \t\treturn 0;\n>  \t}\n>\n> +\tif (!strcmp(var, \"rebase.merges\") && value && *value) {\n\nWhy do we require a non-empty `value` here?\n\n\t[rebase]\n\t\tmerges\n\nshould be equivalent to `true`,\n\n\t[rebase]\n\t\tmerges =\n\nshould probably be equivalent to `false`, and both are handled correctly\nby `git_parse_maybe_bool()`.\n\n> +\t\topts->rebase_merges = git_parse_maybe_bool(value);\n> +\t\tif (opts->rebase_merges < 0)\n> +\t\t\tparse_merges_value(opts, value);\n> +\t\treturn 0;\n> +\t}\n> +\n>  \tif (!strcmp(var, \"rebase.backend\")) {\n>  \t\treturn git_config_string(&opts->default_backend, var, value);\n>  \t}\n> @@ -980,6 +1001,18 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n>  \treturn 0;\n>  }\n>\n> +static int parse_opt_merges(const struct option *opt, const char *arg, int unset)\n> +{\n> +\tstruct rebase_options *options = opt->value;\n> +\n> +\tif (unset)\n> +\t\toptions->rebase_merges = 0;\n> +\telse\n> +\t\tparse_merges_value(options, arg);\n> +\n> +\treturn 0;\n> +}\n> +\n\nIt is kind of inelegant to require a _second_ callback for the\ncommand-line parsing, but I guess if we want a `--no-rebase-merges` option\nto override a config setting, we cannot help it.\n\n>  static void NORETURN error_on_missing_default_upstream(void)\n>  {\n>  \tstruct branch *current_branch = branch_get(NULL);\n> @@ -1035,7 +1068,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \tstruct object_id branch_base;\n>  \tint ignore_whitespace = 0;\n>  \tconst char *gpg_sign = NULL;\n> -\tconst char *rebase_merges = NULL;\n>  \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n>  \tstruct object_id squash_onto;\n>  \tchar *squash_onto_name = NULL;\n> @@ -1137,10 +1169,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\t\t   &options.allow_empty_message,\n>  \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n>  \t\t\t   PARSE_OPT_HIDDEN),\n> -\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n> -\t\t\tN_(\"mode\"),\n> +\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n>  \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n> -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n> +\t\t\tPARSE_OPT_OPTARG, parse_opt_merges),\n>  \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n>  \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n>  \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n> @@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \tif (options.exec.nr)\n>  \t\timply_merge(&options, \"--exec\");\n>\n> -\tif (rebase_merges) {\n> -\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n> -\t\t\toptions.rebase_cousins = 1;\n> -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n> -\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n> -\t\toptions.rebase_merges = 1;\n> +\tif (options.rebase_merges)\n>  \t\timply_merge(&options, \"--rebase-merges\");\n> -\t}\n>\n>  \tif (options.type == REBASE_APPLY) {\n>  \t\tif (ignore_whitespace)\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n> index c73949df18..d4b0e8fd49 100755\n> --- a/t/t3430-rebase-merges.sh\n> +++ b/t/t3430-rebase-merges.sh\n> @@ -284,6 +284,102 @@ test_expect_success '--rebase-merges=\"\" is invalid syntax' '\n>  \ttest_cmp expect actual\n>  '\n>\n> +test_expect_success 'rebase.merges=\"\" is equivalent to not passing --rebase-merges' '\n> +\ttest_config rebase.merges \"\" &&\n> +\tgit checkout -b config-merges-blank E &&\n> +\tgit rebase C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b config-rebase-cousins main &&\n> +\tgit rebase HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n> +\ttest_config rebase.merges no-rebase-cousins &&\n> +\tgit checkout -b override-config-no-rebase-cousins E &&\n> +\tgit rebase --no-rebase-merges C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\to | H\n> +\t|/\n> +\to A\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n> +\ttest_config rebase.merges false &&\n> +\tgit checkout -b override-config-merges-false E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase --rebase-merges C &&\n> +\ttest_cmp_rev HEAD $before\n> +'\n> +\n> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b no-override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n> +test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '\n> +\ttest_config_global rebase.merges true &&\n> +\ttest_config rebase.merges false &&\n> +\tgit checkout -b override-global-config E &&\n> +\tgit rebase C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success 'local rebase.merges=\"\" does not override global rebase.merges=true' '\n> +\ttest_config_global rebase.merges no-rebase-cousins &&\n> +\ttest_config rebase.merges \"\" &&\n> +\tgit checkout -b no-override-global-config E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase C &&\n> +\ttest_cmp_rev HEAD $before\n> +'\n> +\n\nI understand the temptation to introduce exhaustive matrices that test all\nthe different settings in all the different ways they can be specified.\n\nHowever, I would much prefer to keep the tests succinct, not the least to\navoid the every-increasing runtime of Git's CI. It's already taking about\nan order of magnitude or two too long to be reasonable.\n\nSo I'd suggest reducing the tests to a single one instead of eight: verify\nthat `rebase.merges=no-rebase-cousins` is heeded, and that\n`--no-rebase-cousins` overrides that. That should be plenty sufficient to\nprevent regressions.\n\nCiao,\nJohannes\n\n>  test_expect_success 'refs/rewritten/* is worktree-local' '\n>  \tgit worktree add wt &&\n>  \tcat >wt/script-from-scratch <<-\\EOF &&\n> --\n> 2.39.2\n>\n>\n"},{"id":"472648","messageId":"b4be9cb3-c24b-4377-bab3-5d53035efdf8@gmx.de","threadId":"59293","inReplyTo":"20230223053410.644503-2-alexhenrie24@gmail.com","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-02-24T13:54:14Z","receivedAt":"2023-02-24T13:54:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Alex,\n\nOn Wed, 22 Feb 2023, Alex Henrie wrote:\n\n> The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n> empty string argument) has been an undocumented synonym of\n> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid\n> confusion when a rebase.merges config option is introduced, where\n> rebase.merges=\"\" will be equivalent to not passing --rebase-merges.\n\nBeing undocumented and obscure might be a good reason for some to consider\nthis a bug; I do not. You could deprecate it, but there are probably\nbetter ideas than to remove it without prior warning.\n\nIf all you want to do is to support `rebase.merges=`, you can do that\nwithout having to change the meaning of `--rebase-merges=`.\n\nCiao,\nJohannes\n"},{"id":"472649","messageId":"f1e8b718-c54e-b228-860c-54d7defd9e7d@gmx.de","threadId":"59293","inReplyTo":"xmqqh6vc9tqy.fsf@gitster.g","subject":"Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-02-24T13:57:59Z","receivedAt":"2023-02-24T13:58:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 23 Feb 2023, Junio C Hamano wrote:\n\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> > Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> > ---\n>\n> No cover letter to summarize the changes?\n\nA range-diff would have been nice, too, as well as replying-to the\nprevious iteration so that they're all within the same email thread.\n\nAnd if you want cover letters and range-diffs and correct In-Reply-To\nheaders, I can think of a splendid way to encourage that: promote tools\nthat do that. That's much better than to require contributors to know all\nthe customs and conventions of the project and send mails in the exact\ndesired format...\n\nCiao,\nJohannes\n\n"},{"id":"472652","messageId":"7ba8a92d-8c94-5226-5416-6ed3a8e2e389@dunelm.org.uk","threadId":"59293","inReplyTo":"20230223053410.644503-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-02-24T14:55:34Z","receivedAt":"2023-02-24T14:55:41Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 23/02/2023 05:34, Alex Henrie wrote:\n> The purpose of the new option is to accommodate users who would like\n> --rebase-merges to be on by default and to facilitate possibly turning\n> on --rebase-merges by default without configuration in a future version\n> of Git.\n> \n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n>   Documentation/config/rebase.txt | 10 ++++\n>   Documentation/git-rebase.txt    |  3 +-\n>   builtin/rebase.c                | 47 ++++++++++++----\n>   t/t3430-rebase-merges.sh        | 96 +++++++++++++++++++++++++++++++++\n>   4 files changed, 144 insertions(+), 12 deletions(-)\n> \n> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\n> index f19bd0e040..308baa9dbb 100644\n> --- a/Documentation/config/rebase.txt\n> +++ b/Documentation/config/rebase.txt\n> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n>   \n>   rebase.forkPoint::\n>   \tIf set to false set `--no-fork-point` option by default.\n> +\n> +rebase.merges::\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges` with that value as its argument, and setting to false\n> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line without an argument overrides a `rebase.merges=false`\n> +\tconfiguration but does not override other values of `rebase.merge`.\n\nThanks for updating this, it is much clearer what the different settings \nmean now. I not sure if having the config setting override the default \nwhen the user passes --rebase-merges without an argument is a good idea.\n\n> [...]   \n> +static void parse_merges_value(struct rebase_options *options, const char *value)\n> +{\n> +\tif (value) {\n> +\t\tif (!strcmp(\"no-rebase-cousins\", value))\n> +\t\t\toptions->rebase_cousins = 0;\n> +\t\telse if (!strcmp(\"rebase-cousins\", value))\n> +\t\t\toptions->rebase_cousins = 1;\n> +\t\telse\n> +\t\t\tdie(_(\"Unknown mode: %s\"), value);\n> +\t}\n> +\n> +\toptions->rebase_merges = 1;\n> +}\n\nIt's a shame we seem to have grown yet another callback since v2, I'm \nnot sure we should need to add quite so much code just to support a new \nconfig option\n\n > [...]\n> @@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \tif (options.exec.nr)\n>   \t\timply_merge(&options, \"--exec\");\n>   \n> -\tif (rebase_merges) {\n> -\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n> -\t\t\toptions.rebase_cousins = 1;\n> -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n> -\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n> -\t\toptions.rebase_merges = 1;\n> +\tif (options.rebase_merges)\n>   \t\timply_merge(&options, \"--rebase-merges\");\n> -\t}\n\nAs I have said before I really think this patch needs to follow the lead \nof eddfcd8ece (rebase: provide better error message for apply options \nvs. merge config, 2023-01-25) and provide an error message if \n--rebase-merges is enabled by rebase.merges and the user provides a \ncommand line option that requires the apply backend. So\n\n\tgit -c rebase.merges=true rebase --whitespace=fix\n\nwould result in\n\n\terror: apply options are incompatible with rebase.merges.\n\tConsider adding --no-rebase-merges\n\n> [...]\n> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b no-override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges HEAD^ &&\n\nI think this behavior is confusing for users and will break scripts that \nquite reasonably assume --rebase-merges is equivalent to \n--rebase-merges=no-rebase-cousins\n\n> [...]\n> +test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '\n> +\ttest_config_global rebase.merges true &&\n> +\ttest_config rebase.merges false &&\n> +\tgit checkout -b override-global-config E &&\n> +\tgit rebase C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success 'local rebase.merges=\"\" does not override global rebase.merges=true' '\n> +\ttest_config_global rebase.merges no-rebase-cousins &&\n> +\ttest_config rebase.merges \"\" &&\n> +\tgit checkout -b no-override-global-config E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase C &&\n> +\ttest_cmp_rev HEAD $before\n> +'\n\nThese two tests seem to be testing the config subsystem rather than this \npatch. As Dscho has pointed out it is important to get a balance between \ntest coverage and test run time. I think these two tests can definitely \nbe dropped.\n\nBest Wishes\n\nPhillip\n"},{"id":"472656","messageId":"xmqqy1on0yla.fsf@gitster.g","threadId":"59293","inReplyTo":"b4be9cb3-c24b-4377-bab3-5d53035efdf8@gmx.de","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-24T17:20:33Z","receivedAt":"2023-02-24T17:20:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 22 Feb 2023, Alex Henrie wrote:\n>\n>> The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n>> empty string argument) has been an undocumented synonym of\n>> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid\n>> confusion when a rebase.merges config option is introduced, where\n>> rebase.merges=\"\" will be equivalent to not passing --rebase-merges.\n>\n> Being undocumented and obscure might be a good reason for some to consider\n> this a bug; I do not. You could deprecate it, but there are probably\n> better ideas than to remove it without prior warning.\n>\n> If all you want to do is to support `rebase.merges=`, you can do that\n> without having to change the meaning of `--rebase-merges=`.\n\nThanks for a doze of sanity.  Let me mark the topic as on hold to\nwait for a resolution.\n\n\n"},{"id":"472659","messageId":"CAMMLpeRoPnj2-YXo3tOcqU2oqx1RokocZSvU-4hXV7rdMEBDXw@mail.gmail.com","threadId":"59293","inReplyTo":"c918797c-75cc-80d4-c978-360388877d63@gmx.de","subject":"Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T17:49:21Z","receivedAt":"2023-02-24T17:49:36Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 6:53 AM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n\n> in config value parsing, a \"maybe-bool\" with a\n> NULL value is considered to be equivalent to `true`! (See\n> `git_parse_maybe_bool_text()` or\n> https://git-scm.com/docs/git-config#Documentation/git-config.txt-true for\n> details.).\n\n> On Wed, 22 Feb 2023, Alex Henrie wrote:\n>\n> > +     if (!strcmp(var, \"rebase.merges\") && value && *value) {\n>\n> Why do we require a non-empty `value` here?\n>\n>         [rebase]\n>                 merges\n>\n> should be equivalent to `true`,\n>\n>         [rebase]\n>                 merges =\n>\n> should probably be equivalent to `false`, and both are handled correctly\n> by `git_parse_maybe_bool()`.\n\nI didn't know that there was already an established convention for\nwhat NULL and \"\" mean for boolean config values. I should have looked\nat the source code of git_parse_maybe_bool more carefully. That does\nchange things because we're going to want to follow the established\nconvention here.\n\n-Alex\n"},{"id":"472660","messageId":"CAMMLpeQKJeZn4rcTJzFR-ixQXKQMT7t-BKvJqXV4o_4VM=tHYg@mail.gmail.com","threadId":"59293","inReplyTo":"xmqqy1on0yla.fsf@gitster.g","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T17:50:23Z","receivedAt":"2023-02-24T17:50:37Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 10:20 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > On Wed, 22 Feb 2023, Alex Henrie wrote:\n> >\n> >> The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n> >> empty string argument) has been an undocumented synonym of\n> >> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid\n> >> confusion when a rebase.merges config option is introduced, where\n> >> rebase.merges=\"\" will be equivalent to not passing --rebase-merges.\n> >\n> > Being undocumented and obscure might be a good reason for some to consider\n> > this a bug; I do not. You could deprecate it, but there are probably\n> > better ideas than to remove it without prior warning.\n> >\n> > If all you want to do is to support `rebase.merges=`, you can do that\n> > without having to change the meaning of `--rebase-merges=`.\n>\n> Thanks for a doze of sanity.  Let me mark the topic as on hold to\n> wait for a resolution.\n\nAs Johannes pointed out, it's going to be confusing if\nrebase.merges=\"\" does not mean rebase.merges=false. It's also going to\nbe confusing if rebase.merges=\"\" does not mean --rebase-merges=\"\". The\nonly sane option I see is to drop --rebase-merges=\"\", or to add a\ndeprecation warning now and drop it later.\n\n-Alex\n"},{"id":"472661","messageId":"CAMMLpeQ98BTCGE2tcVdZ99eU6cLh4Rd_hc8C_PmKvsBkjXUWPw@mail.gmail.com","threadId":"59293","inReplyTo":"7ba8a92d-8c94-5226-5416-6ed3a8e2e389@dunelm.org.uk","subject":"Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T17:51:37Z","receivedAt":"2023-02-24T17:51:52Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 7:55 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> Thanks for updating this, it is much clearer what the different settings\n> mean now.\n\nHappy to help. Thanks for the feedback.\n\n> I not sure if having the config setting override the default\n> when the user passes --rebase-merges without an argument is a good idea.\n\n> I think this behavior is confusing for users and will break scripts that\n> quite reasonably assume --rebase-merges is equivalent to\n> --rebase-merges=no-rebase-cousins\n\nIn that case, we're going to need two config options: A rebase.merges\nboolean for whether --rebase-merges should be on by default and a\nrebase.cousins boolean for whether rebase-cousins or no-rebase-cousins\nis the default.\n\nI will try to incorporate that change and the others that you and\nJohannes suggested in v5.\n\n-Alex\n"},{"id":"472664","messageId":"xmqqcz5z0wdb.fsf@gitster.g","threadId":"59293","inReplyTo":"CAMMLpeQKJeZn4rcTJzFR-ixQXKQMT7t-BKvJqXV4o_4VM=tHYg@mail.gmail.com","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-24T18:08:32Z","receivedAt":"2023-02-24T18:08:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> As Johannes pointed out, it's going to be confusing if\n> rebase.merges=\"\" does not mean rebase.merges=false. It's also going to\n> be confusing if rebase.merges=\"\" does not mean --rebase-merges=\"\". The\n> only sane option I see is to drop --rebase-merges=\"\", or to add a\n> deprecation warning now and drop it later.\n\nIf we were doing anything, then I think the only sensible way\nforward is to warn and then drop long after everybody forgets about\nit.\n\nBut does it really need to be changed?  I am perfectly happy to\ndeclare that those who wants to set rebase.merges to say false\nshould set it to false (or no or 0), not an empty string.\n\nAlso, \"[rebase] merges = \" in the configuration files does not have\nto mean the same thing as \"--rebase-merges=\" from the command line.\nCan't we just reserve that strange \"--rebase-merges=\" given from the\ncommand line to those who are already using it, without even\nadvertising it in the documentation, document and encourage the\nlonghand \"--rebase-merges=no-rebase-cousins\" from the command line\nand take only the  form that corresponds to it in the configuration\nfile, i.e. \"[rebase] merges = no-rebase-cousins\".  We could even\nerror out \"[rebase] merges = \" in the configuration file, as nobody\nis using such a configuration variable today.\n"},{"id":"472665","messageId":"CAMMLpeSH1itopDuON=7ms_7Li7Qk+9dhzKjT5f7UE11posKK=g@mail.gmail.com","threadId":"59293","inReplyTo":"xmqqcz5z0wdb.fsf@gitster.g","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T18:23:49Z","receivedAt":"2023-02-24T18:24:08Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 11:08 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> > As Johannes pointed out, it's going to be confusing if\n> > rebase.merges=\"\" does not mean rebase.merges=false. It's also going to\n> > be confusing if rebase.merges=\"\" does not mean --rebase-merges=\"\". The\n> > only sane option I see is to drop --rebase-merges=\"\", or to add a\n> > deprecation warning now and drop it later.\n>\n> If we were doing anything, then I think the only sensible way\n> forward is to warn and then drop long after everybody forgets about\n> it.\n>\n> But does it really need to be changed?  I am perfectly happy to\n> declare that those who wants to set rebase.merges to say false\n> should set it to false (or no or 0), not an empty string.\n>\n> Also, \"[rebase] merges = \" in the configuration files does not have\n> to mean the same thing as \"--rebase-merges=\" from the command line.\n> Can't we just reserve that strange \"--rebase-merges=\" given from the\n> command line to those who are already using it, without even\n> advertising it in the documentation, document and encourage the\n> longhand \"--rebase-merges=no-rebase-cousins\" from the command line\n> and take only the  form that corresponds to it in the configuration\n> file, i.e. \"[rebase] merges = no-rebase-cousins\".  We could even\n> error out \"[rebase] merges = \" in the configuration file, as nobody\n> is using such a configuration variable today.\n\nThe only way to truly make \"[rebase] merges =\" invalid is to print an\nerror message and die with that configuration. I think that would be\nconfusing too, especially since it's now looking like rebase.merges\nneeds to be a pure boolean and an independent rebase.cousins boolean\noption is needed as well. However, if you think that that's better\nthan deprecating --rebase-merges=\"\" with the plan to drop it long\nafter everybody forgets about it, then I'm happy to oblige. Just let\nme know what is wanted.\n\n-Alex\n"},{"id":"472666","messageId":"xmqq8rgm29fw.fsf@gitster.g","threadId":"59293","inReplyTo":"CAMMLpeSH1itopDuON=7ms_7Li7Qk+9dhzKjT5f7UE11posKK=g@mail.gmail.com","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-24T18:40:51Z","receivedAt":"2023-02-24T18:40:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> The only way to truly make \"[rebase] merges =\" invalid is to print an\n> error message and die with that configuration. I think that would be\n> confusing too, especially since it's now looking like rebase.merges\n> needs to be a pure boolean and an independent rebase.cousins boolean\n> option is needed as well.\n\nOh, I wasn't aware of that direction.\n\nI do not know why rebase.cousins, which would only be meaningful\nwhen rebase.merges is true, is a better design than rebase.merges\nthat is an enum of \"don't do the merges stuff\" plus \"do the merges\nstuff with cousins\", \"without cousins\" (which may allow us to gain\nmore different ways to do \"merges stuff\" later), but that is what\ngained consensus on the list, then \"[rebase]merges=\" would become a\nproblem.\n\nBut --rebase-merges from the command line is not a pure Boolean\nalready, so what does \"[rebase]merges\" that is a pure Boolean aim to\nhelp?\n"},{"id":"472667","messageId":"CAMMLpeTvyYgGNBQWtRBg3ZNuXeCMxiem-5LAdu0ex7XCuf0wAA@mail.gmail.com","threadId":"59293","inReplyTo":"xmqq8rgm29fw.fsf@gitster.g","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T18:55:31Z","receivedAt":"2023-02-24T18:55:51Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 11:40 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> > The only way to truly make \"[rebase] merges =\" invalid is to print an\n> > error message and die with that configuration. I think that would be\n> > confusing too, especially since it's now looking like rebase.merges\n> > needs to be a pure boolean and an independent rebase.cousins boolean\n> > option is needed as well.\n>\n> Oh, I wasn't aware of that direction.\n>\n> I do not know why rebase.cousins, which would only be meaningful\n> when rebase.merges is true, is a better design than rebase.merges\n> that is an enum of \"don't do the merges stuff\" plus \"do the merges\n> stuff with cousins\", \"without cousins\" (which may allow us to gain\n> more different ways to do \"merges stuff\" later), but that is what\n> gained consensus on the list, then \"[rebase]merges=\" would become a\n> problem.\n>\n> But --rebase-merges from the command line is not a pure Boolean\n> already, so what does \"[rebase]merges\" that is a pure Boolean aim to\n> help?\n\nPhillip is concerned about people and scripts assuming that\n--rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,\nsee [1].\n\nTao and others are probably not going to like it if --rebase-merges\nwithout an argument undoes a rebase.merges=rebase-cousins\nconfiguration.\n\nIt seems to me that the only way to make everyone happy is to have\nseparate rebase.merges and rebase.cousins options. You have a point\nthat separating the options could cause problems if --rebase-merges\nstarts accepting more arguments in the future, but if that happens we\ncould deal with it by adding more possible values to rebase.cousins or\nintroducing a third config option.\n\n-Alex\n\n[1] https://lore.kernel.org/git/CAMMLpeQ98BTCGE2tcVdZ99eU6cLh4Rd_hc8C_PmKvsBkjXUWPw@mail.gmail.com/\n"},{"id":"472668","messageId":"xmqqpm9yzxky.fsf@gitster.g","threadId":"59293","inReplyTo":"CAMMLpeTvyYgGNBQWtRBg3ZNuXeCMxiem-5LAdu0ex7XCuf0wAA@mail.gmail.com","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-24T19:13:01Z","receivedAt":"2023-02-24T19:13:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> Phillip is concerned about people and scripts assuming that\n> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,\n> see [1].\n\nIsn't that already broken when you introduce rebase.merges\nconfiguration?  People and scripts are already relying on the lack\nof rebase-merges to flatten, and script writers will be surprised to\nreceive a \"bug report\" complaining that their script does not work\nwhen the users set rebase.merges to anything but no.\n\n> Tao and others are probably not going to like it if --rebase-merges\n> without an argument undoes a rebase.merges=rebase-cousins\n> configuration.\n\nThat is why I suggested to keep --rebase-merges= (with no value or\nan empty string) only for those who came from the world where it\ndefaults to no-rebase-cousins and there was no rebase.merges\nconfiguration.  If --rebase-merges= is given from the command line\nwithout value *and* rebase.merges configuration is there (which is\nTao's concern?), the command line option can error out asking for an\nexplicit value to countermand whatever value is configured.\n\nWouldn't that work for folks from both camps?\n"},{"id":"472669","messageId":"xmqqh6vazxfo.fsf@gitster.g","threadId":"59293","inReplyTo":"f1e8b718-c54e-b228-860c-54d7defd9e7d@gmx.de","subject":"Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-24T19:16:11Z","receivedAt":"2023-02-24T19:16:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Thu, 23 Feb 2023, Junio C Hamano wrote:\n>\n>> Alex Henrie <alexhenrie24@gmail.com> writes:\n>>\n>> > Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n>> > ---\n>>\n>> No cover letter to summarize the changes?\n>\n> A range-diff would have been nice, too, as well as replying-to the\n> previous iteration so that they're all within the same email thread.\n\nYes, especially the threading would help both those who missed\nprevious iterations and those who reviewed them.  Range-diff is\noften helpful when came with a good summary.  Just like a patch\nalone without a good proposed log message is not a good way to help\nreviewers understand the issue the patch tries to solve, a cover\nwith range-diff alone only shows what is different from the previous\niteration, but does not say why the changes were made, so it is not\na good substitute for an explanation by author's words.\n\nI may have suggested GGG if the author appeared a total beginner\nwith the e-mail workflow (and especially if it were a single-patch\ntopic), but Alex seems to be doing fine otherwise, and also GGG has\nits own learning curve (e.g. it is rare to see a good cover letter\nwith per-iteration summaries, unless the author is one of those\nexperienced list regulars), so I left it up to Alex to choose how to\nreach the desired end result.\n\nThanks.\n"},{"id":"472670","messageId":"CAMMLpeRGvmpEnxN-qq+2dhzF1bzmBYjW51ojX+9mAnyP92EAKQ@mail.gmail.com","threadId":"59293","inReplyTo":"xmqqpm9yzxky.fsf@gitster.g","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T19:24:26Z","receivedAt":"2023-02-24T19:24:53Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 12:13 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> > Phillip is concerned about people and scripts assuming that\n> > --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,\n> > see [1].\n>\n> Isn't that already broken when you introduce rebase.merges\n> configuration?  People and scripts are already relying on the lack\n> of rebase-merges to flatten, and script writers will be surprised to\n> receive a \"bug report\" complaining that their script does not work\n> when the users set rebase.merges to anything but no.\n\nYeah, I don't know why breaking the assumption that --rebase-merges is\nequivalent to --rebase-merges=no-rebase-cousins is any worse than\nbreaking the assumption that not passing --rebase-merges is equivalent\nto passing --no-rebase-merges. And the assumption is broken whether\none new config option is introduced or two, so splitting the option up\nprobably wouldn't make Phillip any happier.\n\n> > Tao and others are probably not going to like it if --rebase-merges\n> > without an argument undoes a rebase.merges=rebase-cousins\n> > configuration.\n>\n> That is why I suggested to keep --rebase-merges= (with no value or\n> an empty string) only for those who came from the world where it\n> defaults to no-rebase-cousins and there was no rebase.merges\n> configuration.  If --rebase-merges= is given from the command line\n> without value *and* rebase.merges configuration is there (which is\n> Tao's concern?), the command line option can error out asking for an\n> explicit value to countermand whatever value is configured.\n>\n> Wouldn't that work for folks from both camps?\n\nMaybe. I still don't like the idea of --rebase-merges=\"\" ever being\nnot equivalent to rebase.merges=\"\".\n\n-Alex\n"},{"id":"472671","messageId":"2b28155d-f82d-06b8-2df9-135608c6bf63@dunelm.org.uk","threadId":"59293","inReplyTo":"xmqqpm9yzxky.fsf@gitster.g","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-02-24T19:24:42Z","receivedAt":"2023-02-24T19:24:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/02/2023 19:13, Junio C Hamano wrote:\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n> \n>> Phillip is concerned about people and scripts assuming that\n>> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,\n>> see [1].\n> \n> Isn't that already broken when you introduce rebase.merges\n> configuration?\n\nScripts using --rebase-merges are not broken by the introduction of \nrebase.merges so long as we follow our usual convention of always \nallowing the commandline to override the config (i.e. --rebase-merges is \nalways equivalent to --rebase-merges=no-rebase-cousins). I don't really \nunderstand why Alex is suggesting splitting the config into two based on \nmy comments.\n\n> People and scripts are already relying on the lack\n> of rebase-merges to flatten, and script writers will be surprised to\n> receive a \"bug report\" complaining that their script does not work\n> when the users set rebase.merges to anything but no.\n\nThat is true.\n\nBest Wishes\n\nPhillip\n\n>> Tao and others are probably not going to like it if --rebase-merges\n>> without an argument undoes a rebase.merges=rebase-cousins\n>> configuration.\n> \n> That is why I suggested to keep --rebase-merges= (with no value or\n> an empty string) only for those who came from the world where it\n> defaults to no-rebase-cousins and there was no rebase.merges\n> configuration.  If --rebase-merges= is given from the command line\n> without value *and* rebase.merges configuration is there (which is\n> Tao's concern?), the command line option can error out asking for an\n> explicit value to countermand whatever value is configured.\n> \n> Wouldn't that work for folks from both camps?\n"},{"id":"472672","messageId":"CAMMLpeQgY5Noyu4HMsi0LDOBktSURpZHybqwbFnqf5sGwmD=9g@mail.gmail.com","threadId":"59293","inReplyTo":"2b28155d-f82d-06b8-2df9-135608c6bf63@dunelm.org.uk","subject":"Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-24T19:56:14Z","receivedAt":"2023-02-24T19:56:34Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 12:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 24/02/2023 19:13, Junio C Hamano wrote:\n> > Alex Henrie <alexhenrie24@gmail.com> writes:\n> >\n> >> Phillip is concerned about people and scripts assuming that\n> >> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,\n> >> see [1].\n> >\n> > Isn't that already broken when you introduce rebase.merges\n> > configuration?\n>\n> Scripts using --rebase-merges are not broken by the introduction of\n> rebase.merges so long as we follow our usual convention of always\n> allowing the commandline to override the config (i.e. --rebase-merges is\n> always equivalent to --rebase-merges=no-rebase-cousins). I don't really\n> understand why Alex is suggesting splitting the config into two based on\n> my comments.\n\nI was thinking that it would be less surprising to users if the option\nthat broke the no-rebase-cousins assumption had \"cousins\" in its name.\nI should have stopped to think that that wouldn't really address your\nconcern because regardless of what the option is named, it could still\nresult in surprising behavior. I apologize for the unhelpful\nsuggestion.\n\n> > People and scripts are already relying on the lack\n> > of rebase-merges to flatten, and script writers will be surprised to\n> > receive a \"bug report\" complaining that their script does not work\n> > when the users set rebase.merges to anything but no.\n>\n> That is true.\n\nIn addition to specifying --no-rebase-merges rather than assuming it,\nshouldn't the \"usual convention\" for writing scripts also include\nbeing explicit about --rebase-merges=rebase-cousins or\n--rebase-merges=no-rebase-cousins? And if that is the case, is it\nreally much of a loss to let rebase.merges=rebase-cousins override\n--rebase-merges without an argument?\n\n-Alex\n"},{"id":"472725","messageId":"20230225180325.796624-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230223053410.644503-1-alexhenrie24@gmail.com","subject":"[PATCH v5 0/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-25T18:03:22Z","receivedAt":"2023-02-25T18:04:56Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"Changes from v4:\n\n- deprecate --rebase-merges=\"\" rather than removing it outright\n- follow the established convention for what \"\" and NULL mean as\n  booleans\n- add tailored error message for conflicting options and tests for it\n- rename parse_opt_merges to parse_opt_rebase_merges to avoid confusion\n  with parse_opt_merge\n- similarly, rename parse_merges_value to parse_rebase_merges_value\n- move null check from parse_rebase_merges_value to\n  parse_opt_rebase_merges\n- remove tests that check whether the config system itself works\n\nSuggestions not incorporated:\n\n- remove the callback function\n- make --rebase-merge without an argument override\n  rebase.merges=rebase-cousins\n- make rebase.merge accept only a subset of the possible boolean values,\n  or change the meanings of some of those values\n- make --rebase-merge=\"\" and rebase.merge=\"\" do different things without\n  warning\n- remove tests that verify that the command line option properly\n  overrides the config option\n\nThanks to Johannes, Phillip, and Junio for your help making these\npatches better. If you feel strongly about one of the unincorporated\nsuggestions, let's continue the discussion and try to figure out how to\nmake it happen.\n\nAlex Henrie (3):\n  rebase: add documentation and test for --no-rebase-merges\n  rebase: deprecate --rebase-merges=\"\"\n  rebase: add a config option for --rebase-merges\n\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           |  5 +-\n builtin/rebase.c                       | 75 ++++++++++++++++++-------\n t/t3422-rebase-incompatible-options.sh | 12 ++++\n t/t3430-rebase-merges.sh               | 78 ++++++++++++++++++++++++++\n 5 files changed, 160 insertions(+), 20 deletions(-)\n\nRange-diff against v4:\n1:  e6d44a194c = 1:  76e38ef9f8 rebase: add documentation and test for --no-rebase-merges\n2:  393b43c4e1 ! 2:  c6099e6dee rebase: stop accepting --rebase-merges=\"\"\n    @@ Metadata\n     Author: Alex Henrie <alexhenrie24@gmail.com>\n     \n      ## Commit message ##\n    -    rebase: stop accepting --rebase-merges=\"\"\n    +    rebase: deprecate --rebase-merges=\"\"\n     \n         The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n         empty string argument) has been an undocumented synonym of\n    -    --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid\n    +    --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid\n         confusion when a rebase.merges config option is introduced, where\n    -    rebase.merges=\"\" will be equivalent to not passing --rebase-merges.\n    +    rebase.merges=\"\" will be equivalent to --no-rebase-merges.\n     \n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n      \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n    - \t\timply_merge(&options, \"--exec\");\n      \n      \tif (rebase_merges) {\n    --\t\tif (!*rebase_merges)\n    + \t\tif (!*rebase_merges)\n     -\t\t\t; /* default mode; do nothing */\n    --\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n    -+\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n    ++\t\t\twarning(_(\"--rebase-merges with an empty string \"\n    ++\t\t\t\t  \"argument is deprecated and will stop \"\n    ++\t\t\t\t  \"working in a future version of Git. Use \"\n    ++\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    ++\t\t\t\t  \"instead.\"));\n    + \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n      \t\t\toptions.rebase_cousins = 1;\n      \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n    - \t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n     \n      ## t/t3430-rebase-merges.sh ##\n     @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '\n      \tEOF\n      '\n      \n    -+test_expect_success '--rebase-merges=\"\" is invalid syntax' '\n    -+\techo \"fatal: Unknown mode: \" >expect &&\n    -+\ttest_must_fail git rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n    -+\ttest_cmp expect actual\n    ++test_expect_success '--rebase-merges=\"\" is deprecated' '\n    ++\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n    ++\tgrep deprecated actual\n     +'\n     +\n      test_expect_success 'refs/rewritten/* is worktree-local' '\n3:  b1b6fbfa86 ! 3:  95cba9588c rebase: add a config option for --rebase-merges\n    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n      have `<upstream>` as direct ancestor will keep their original branch point,\n     \n      ## builtin/rebase.c ##\n    +@@ builtin/rebase.c: struct rebase_options {\n    + \tint fork_point;\n    + \tint update_refs;\n    + \tint config_autosquash;\n    ++\tint config_rebase_merges;\n    + \tint config_update_refs;\n    + };\n    + \n    +@@ builtin/rebase.c: struct rebase_options {\n    + \t\t.allow_empty_message = 1,               \\\n    + \t\t.autosquash = -1,                       \\\n    + \t\t.config_autosquash = -1,                \\\n    ++\t\t.rebase_merges = -1,                    \\\n    ++\t\t.config_rebase_merges = -1,             \\\n    + \t\t.update_refs = -1,                      \\\n    + \t\t.config_update_refs = -1,               \\\n    + \t}\n     @@ builtin/rebase.c: static int run_specific_rebase(struct rebase_options *opts)\n      \treturn status ? -1 : 0;\n      }\n      \n    -+static void parse_merges_value(struct rebase_options *options, const char *value)\n    ++static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n     +{\n    -+\tif (value) {\n    -+\t\tif (!strcmp(\"no-rebase-cousins\", value))\n    -+\t\t\toptions->rebase_cousins = 0;\n    -+\t\telse if (!strcmp(\"rebase-cousins\", value))\n    -+\t\t\toptions->rebase_cousins = 1;\n    -+\t\telse\n    -+\t\t\tdie(_(\"Unknown mode: %s\"), value);\n    -+\t}\n    -+\n    -+\toptions->rebase_merges = 1;\n    ++\tif (!strcmp(\"no-rebase-cousins\", value))\n    ++\t\toptions->rebase_cousins = 0;\n    ++\telse if (!strcmp(\"rebase-cousins\", value))\n    ++\t\toptions->rebase_cousins = 1;\n    ++\telse\n    ++\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n     +}\n     +\n      static int rebase_config(const char *var, const char *value, void *data)\n    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n      \t\treturn 0;\n      \t}\n      \n    -+\tif (!strcmp(var, \"rebase.merges\") && value && *value) {\n    -+\t\topts->rebase_merges = git_parse_maybe_bool(value);\n    -+\t\tif (opts->rebase_merges < 0)\n    -+\t\t\tparse_merges_value(opts, value);\n    ++\tif (!strcmp(var, \"rebase.merges\")) {\n    ++\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n    ++\t\tif (opts->config_rebase_merges < 0) {\n    ++\t\t\topts->config_rebase_merges = 1;\n    ++\t\t\tparse_rebase_merges_value(opts, value);\n    ++\t\t}\n     +\t\treturn 0;\n     +\t}\n     +\n    - \tif (!strcmp(var, \"rebase.backend\")) {\n    - \t\treturn git_config_string(&opts->default_backend, var, value);\n    - \t}\n    + \tif (!strcmp(var, \"rebase.updaterefs\")) {\n    + \t\topts->config_update_refs = git_config_bool(var, value);\n    + \t\treturn 0;\n     @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n      \treturn 0;\n      }\n      \n    -+static int parse_opt_merges(const struct option *opt, const char *arg, int unset)\n    ++static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n     +{\n     +\tstruct rebase_options *options = opt->value;\n     +\n    -+\tif (unset)\n    -+\t\toptions->rebase_merges = 0;\n    -+\telse\n    -+\t\tparse_merges_value(options, arg);\n    ++\toptions->rebase_merges = !unset;\n    ++\n    ++\tif (arg) {\n    ++\t\tif (!*arg) {\n    ++\t\t\twarning(_(\"--rebase-merges with an empty string \"\n    ++\t\t\t\t  \"argument is deprecated and will stop \"\n    ++\t\t\t\t  \"working in a future version of Git. Use \"\n    ++\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    ++\t\t\t\t  \"instead.\"));\n    ++\t\t\targ = \"no-rebase-cousins\";\n    ++\t\t}\n    ++\t\tparse_rebase_merges_value(options, arg);\n    ++\t}\n     +\n     +\treturn 0;\n     +}\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n     +\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n      \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n     -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n    -+\t\t\tPARSE_OPT_OPTARG, parse_opt_merges),\n    ++\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n      \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n      \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n      \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \t\timply_merge(&options, \"--exec\");\n      \n     -\tif (rebase_merges) {\n    --\t\tif (!strcmp(\"rebase-cousins\", rebase_merges))\n    +-\t\tif (!*rebase_merges)\n    +-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n    +-\t\t\t\t  \"argument is deprecated and will stop \"\n    +-\t\t\t\t  \"working in a future version of Git. Use \"\n    +-\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    +-\t\t\t\t  \"instead.\"));\n    +-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n     -\t\t\toptions.rebase_cousins = 1;\n     -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n     -\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n     -\t\toptions.rebase_merges = 1;\n    -+\tif (options.rebase_merges)\n    - \t\timply_merge(&options, \"--rebase-merges\");\n    +-\t\timply_merge(&options, \"--rebase-merges\");\n     -\t}\n    - \n    +-\n      \tif (options.type == REBASE_APPLY) {\n      \t\tif (ignore_whitespace)\n    + \t\t\tstrvec_push(&options.git_am_opts,\n    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n    + \t\t\t\tbreak;\n    + \n    + \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n    +-\t\t\tif (is_merge(&options))\n    +-\t\t\t\tdie(_(\"apply options and merge options \"\n    +-\t\t\t\t\t  \"cannot be used together\"));\n    +-\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n    ++\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n    + \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n    ++\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n    ++\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n    + \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n    + \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n    ++\t\t\telse if (is_merge(&options))\n    ++\t\t\t\tdie(_(\"apply options and merge options \"\n    ++\t\t\t\t\t  \"cannot be used together\"));\n    + \t\t\telse\n    + \t\t\t\toptions.type = REBASE_APPLY;\n    + \t\t}\n    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n    + \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n    + \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n    + \n    ++\tif (options.rebase_merges == 1)\n    ++\t\timply_merge(&options, \"--rebase-merges\");\n    ++\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n    ++\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n    ++\n    + \tif (options.autosquash == 1)\n    + \t\timply_merge(&options, \"--autosquash\");\n    + \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\n    +\n    + ## t/t3422-rebase-incompatible-options.sh ##\n    +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n    + \t\tgrep -e --no-autosquash err\n    + \t\"\n    + \n    ++\ttest_expect_success \"$opt incompatible with rebase.merges\" \"\n    ++\t\tgit checkout B^0 &&\n    ++\t\ttest_must_fail git -c rebase.merges=true rebase $opt A 2>err &&\n    ++\t\tgrep -e --no-rebase-merges err\n    ++\t\"\n    ++\n    + \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n    + \t\tgit checkout B^0 &&\n    + \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n    +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n    + \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n    + \t\"\n    + \n    ++\ttest_expect_success \"$opt okay with overridden rebase.merges\" \"\n    ++\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n    ++\t\tgit checkout B^0 &&\n    ++\t\tgit -c rebase.merges=true rebase --no-rebase-merges $opt A\n    ++\t\"\n    ++\n    + \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n    + \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n    + \t\tgit checkout B^0 &&\n     \n      ## t/t3430-rebase-merges.sh ##\n    -@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is invalid syntax' '\n    - \ttest_cmp expect actual\n    +@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated' '\n    + \tgrep deprecated actual\n      '\n      \n    -+test_expect_success 'rebase.merges=\"\" is equivalent to not passing --rebase-merges' '\n    -+\ttest_config rebase.merges \"\" &&\n    -+\tgit checkout -b config-merges-blank E &&\n    -+\tgit rebase C &&\n    -+\ttest_cmp_graph C.. <<-\\EOF\n    -+\t* B\n    -+\t* D\n    -+\to C\n    -+\tEOF\n    -+'\n    -+\n     +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n     +\ttest_config rebase.merges rebase-cousins &&\n     +\tgit checkout -b config-rebase-cousins main &&\n    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is invalid syn\n     +\to H\n     +\tEOF\n     +'\n    -+\n    -+test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '\n    -+\ttest_config_global rebase.merges true &&\n    -+\ttest_config rebase.merges false &&\n    -+\tgit checkout -b override-global-config E &&\n    -+\tgit rebase C &&\n    -+\ttest_cmp_graph C.. <<-\\EOF\n    -+\t* B\n    -+\t* D\n    -+\to C\n    -+\tEOF\n    -+'\n    -+\n    -+test_expect_success 'local rebase.merges=\"\" does not override global rebase.merges=true' '\n    -+\ttest_config_global rebase.merges no-rebase-cousins &&\n    -+\ttest_config rebase.merges \"\" &&\n    -+\tgit checkout -b no-override-global-config E &&\n    -+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n    -+\ttest_tick &&\n    -+\tgit rebase C &&\n    -+\ttest_cmp_rev HEAD $before\n    -+'\n     +\n      test_expect_success 'refs/rewritten/* is worktree-local' '\n      \tgit worktree add wt &&\n-- \n2.39.2\n\n"},{"id":"472726","messageId":"20230225180325.796624-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230225180325.796624-1-alexhenrie24@gmail.com","subject":"[PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-25T18:03:23Z","receivedAt":"2023-02-25T18:04:58Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt |  4 +++-\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..c98784a0d2 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,13 +529,15 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n By default, or when `no-rebase-cousins` was specified, commits which do not\n have `<upstream>` as direct ancestor will keep their original branch point,\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.39.2\n\n"},{"id":"472727","messageId":"20230225180325.796624-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230225180325.796624-1-alexhenrie24@gmail.com","subject":"[PATCH v5 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-25T18:03:24Z","receivedAt":"2023-02-25T18:04:59Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid\nconfusion when a rebase.merges config option is introduced, where\nrebase.merges=\"\" will be equivalent to --no-rebase-merges.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c         | 8 ++++++--\n t/t3430-rebase-merges.sh | 5 +++++\n 2 files changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 6635f10d52..ccc13200b5 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (rebase_merges) {\n \t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n+\t\t\t\t  \"instead.\"));\n \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..f50fbf1390 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,11 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success '--rebase-merges=\"\" is deprecated' '\n+\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n+\tgrep deprecated actual\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"472728","messageId":"20230225180325.796624-4-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230225180325.796624-1-alexhenrie24@gmail.com","subject":"[PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-25T18:03:25Z","receivedAt":"2023-02-25T18:05:02Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate possibly turning\non --rebase-merges by default without configuration in a future version\nof Git.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           |  3 +-\n builtin/rebase.c                       | 79 ++++++++++++++++++--------\n t/t3422-rebase-incompatible-options.sh | 12 ++++\n t/t3430-rebase-merges.sh               | 63 ++++++++++++++++++++\n 5 files changed, 143 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..308baa9dbb 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.merges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n+\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges` with that value as its argument, and setting to false\n+\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line without an argument overrides a `rebase.merges=false`\n+\tconfiguration but does not override other values of `rebase.merge`.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex c98784a0d2..b02f9cbb8c 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.merges` config option and a previous\n+\t`--rebase-merges`.\n +\n By default, or when `no-rebase-cousins` was specified, commits which do not\n have `<upstream>` as direct ancestor will keep their original branch point,\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex ccc13200b5..ac096f27e1 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -123,6 +123,7 @@ struct rebase_options {\n \tint fork_point;\n \tint update_refs;\n \tint config_autosquash;\n+\tint config_rebase_merges;\n \tint config_update_refs;\n };\n \n@@ -140,6 +141,8 @@ struct rebase_options {\n \t\t.allow_empty_message = 1,               \\\n \t\t.autosquash = -1,                       \\\n \t\t.config_autosquash = -1,                \\\n+\t\t.rebase_merges = -1,                    \\\n+\t\t.config_rebase_merges = -1,             \\\n \t\t.update_refs = -1,                      \\\n \t\t.config_update_refs = -1,               \\\n \t}\n@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 0;\n+\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 1;\n+\telse\n+\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.merges\")) {\n+\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->config_rebase_merges < 0) {\n+\t\t\topts->config_rebase_merges = 1;\n+\t\t\tparse_rebase_merges_value(opts, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.updaterefs\")) {\n \t\topts->config_update_refs = git_config_bool(var, value);\n \t\treturn 0;\n@@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\toptions->rebase_merges = !unset;\n+\n+\tif (arg) {\n+\t\tif (!*arg) {\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n+\t\t\t\t  \"instead.\"));\n+\t\t\targ = \"no-rebase-cousins\";\n+\t\t}\n+\t\tparse_rebase_merges_value(options, arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n-\t\t\t\t  \"argument is deprecated and will stop \"\n-\t\t\t\t  \"working in a future version of Git. Use \"\n-\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n-\t\t\t\t  \"instead.\"));\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n-\t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n-\n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\n \t\t\tstrvec_push(&options.git_am_opts,\n@@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tbreak;\n \n \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n-\t\t\tif (is_merge(&options))\n-\t\t\t\tdie(_(\"apply options and merge options \"\n-\t\t\t\t\t  \"cannot be used together\"));\n-\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n+\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n+\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n+\t\t\telse if (is_merge(&options))\n+\t\t\t\tdie(_(\"apply options and merge options \"\n+\t\t\t\t\t  \"cannot be used together\"));\n \t\t\telse\n \t\t\t\toptions.type = REBASE_APPLY;\n \t\t}\n@@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n \n+\tif (options.rebase_merges == 1)\n+\t\timply_merge(&options, \"--rebase-merges\");\n+\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n+\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n+\n \tif (options.autosquash == 1)\n \t\timply_merge(&options, \"--autosquash\");\n \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\ndiff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\nindex 4711b37a28..af93f13849 100755\n--- a/t/t3422-rebase-incompatible-options.sh\n+++ b/t/t3422-rebase-incompatible-options.sh\n@@ -101,6 +101,12 @@ test_rebase_am_only () {\n \t\tgrep -e --no-autosquash err\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with rebase.merges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git -c rebase.merges=true rebase $opt A 2>err &&\n+\t\tgrep -e --no-rebase-merges err\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n@@ -113,6 +119,12 @@ test_rebase_am_only () {\n \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n \t\"\n \n+\ttest_expect_success \"$opt okay with overridden rebase.merges\" \"\n+\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n+\t\tgit checkout B^0 &&\n+\t\tgit -c rebase.merges=true rebase --no-rebase-merges $opt A\n+\t\"\n+\n \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n \t\tgit checkout B^0 &&\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex f50fbf1390..a825a77fb4 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -283,6 +283,69 @@ test_expect_success '--rebase-merges=\"\" is deprecated' '\n \tgrep deprecated actual\n '\n \n+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n+\ttest_config rebase.merges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\to | H\n+\t|/\n+\to A\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.merges=false' '\n+\ttest_config rebase.merges false &&\n+\tgit checkout -b override-config-merges-false E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n+\ttest_config rebase.merges rebase-cousins &&\n+\tgit checkout -b no-override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"472729","messageId":"CAMMLpeTWsuEpC878BRh7zNZF_7pVZcaxw9A-bOwEOAPbH-rnOA@mail.gmail.com","threadId":"59293","inReplyTo":"f1e8b718-c54e-b228-860c-54d7defd9e7d@gmx.de","subject":"Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-02-25T18:09:39Z","receivedAt":"2023-02-25T18:09:59Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Fri, Feb 24, 2023 at 6:58 AM Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n\n> On Thu, 23 Feb 2023, Junio C Hamano wrote:\n>\n> > No cover letter to summarize the changes?\n\n> A range-diff would have been nice, too, as well as replying-to the\n> previous iteration so that they're all within the same email thread.\n>\n> And if you want cover letters and range-diffs and correct In-Reply-To\n> headers, I can think of a splendid way to encourage that: promote tools\n> that do that. That's much better than to require contributors to know all\n> the customs and conventions of the project and send mails in the exact\n> desired format...\n\nIt would also help if those expectations were mentioned in\nDocumentation/SubmittingPatches. There's nothing in there about\nrange-diff or In-Reply-To.\n\nI think I followed all of the customs of the Git mailing list for v5.\nPlease let me know if I missed something.\n\n-Alex\n"},{"id":"472906","messageId":"kl6lwn403vyn.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230225180325.796624-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-01T23:14:56Z","receivedAt":"2023-03-01T23:15:03Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Hi Alex! It was great to see you at Review Club today. If you'd like the\nnotes, they are available at\n\nhttps://docs.google.com/document/d/14L8BAumGTpsXpjDY8VzZ4rRtpAjuGrFSRqn3stCuS_w\n\nthough reviewers will send their feedback to the mailing list anyway, so\ndon't feel compelled to address feedback that isn't on the mailing list\n:)\n\nAlex Henrie <alexhenrie24@gmail.com> writes:\n\n> Changes from v4:\n>\n> [...]\n>\n> Suggestions not incorporated:\n>\n> [...]\n>\n\nI would have appreciated a sentence or two explaining the high level\nchanges in this series, since that would save reviewers from having to\nread the whole series to get an idea of the overall intent. For this\nseries, I think the subject line captures the intent pretty well, though\neven simple descriptions like \"Add a config option. Also clean up some\nproblems with the --rebase-merges flag.\" can be useful framing for\nreviewers.\n\nI appreciate the summary of changes [not] incorporated, I found it\nuseful for getting an idea of what was originally discussed.\n\nOn the whole, I think the config option will be quite welcome, and the\nseries is mostly mergeable, but might benefit from some documentation\ntweaking.\n"},{"id":"472907","messageId":"kl6lttz43vjo.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230225180325.796624-2-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-01T23:23:55Z","receivedAt":"2023-03-01T23:24:09Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n\nI inferred from the 'subject line' that this \"always\" worked and was\nundocumented, and that we'd want to document this not for its own sake,\nbut because we'll introduce a config option. I would personally prefer\nto get that explicitly in writing, but it's definitely not worth a\nreroll.\n\n>  -r::\n>  --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n> +--no-rebase-merges::\n>  \tBy default, a rebase will simply drop merge commits from the todo\n>  \tlist, and put the rebased commits into a single, linear branch.\n>  \tWith `--rebase-merges`, the rebase will instead try to preserve\n>  \tthe branching structure within the commits that are to be rebased,\n>  \tby recreating the merge commits. Any resolved merge conflicts or\n>  \tmanual amendments in these merge commits will have to be\n> -\tresolved/re-applied manually.\n> +\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n> +\tcountermand a previous `--rebase-merges`.\n>  +\n>  By default, or when `no-rebase-cousins` was specified, commits which do not\n>  have `<upstream>` as direct ancestor will keep their original branch point,\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n\nThis isn't the fault of this patch, but I find this description a little\nconfusing. First we say\n\n  By default (*when no option is given*), a rebase will simply drop\n  merge commits from the todo list...\n\nand then later we also say\n\n  By default (*when --rebase-merges is provided without a value), or\n  when `no-rebase-cousins`...\n\nSo although you didn't create this problem, given the changes we're\nmaking in the later patches, I think we have the chance to make this\ndescription more sensible; I'll elaborate in my response to those\npatches.\n"},{"id":"472908","messageId":"kl6lpm9s3unl.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230225180325.796624-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-01T23:43:10Z","receivedAt":"2023-03-01T23:44:18Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> +rebase.merges::\n\nAs far as I can tell, when a config option is meant to be a stand in for\na CLI option, we have typically named it <subcommand>.<camelcase\noption>, e.g. grep.fullName, log.diffMerges, push.followTags. This\nprobably matters more if we aren't specifying the subcommand that this\nflag is more (like in this patch).\n\nBy this convention, the config option would be rebase.rebaseMerges,\nwhich _does_ feel redundant and it's unlikely for \"git rebase\" to learn\na \"--merges\" flag, so I don't have a strong opinion either way. Though\nas you mentioned in review club, this consistency might make it easier\nfor users to read custom configs that use the \"rebase.\" section.\n\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges` with that value as its argument, and setting to false\n> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line without an argument overrides a `rebase.merges=false`\n> +\tconfiguration but does not override other values of `rebase.merge`.\n\nWith the explanation here, I think having \"--rebase-merges\" _not_\noverride \"rebase.merge=rebase-cousins\" is quite confusing.\n\nI think there are two possibilities forward:\n\n- Be very consistent that \"--rebase-merges\" is just a synonym of\n  \"--rebase-merges=no-rebase-cousins\". Then, have any value of\n  \"--rebase-merges\" override any config value.\n\n  I think this the easiest UX to understand, but I don't think we should\n  be commiting to a default, especially since we may add a more sensible\n  default (like rebasing 'evil merges') in the future.\n\n- Keep the existing behavior, but reword the docs to indicate that the\n  \"no value\" form means \"I want the default but don't care what it is\",\n  and an explicit value means \"I want to use a particular mode and the\n  rules of config vs CLI specificity should apply\". Perhaps something\n  like:\n\n    When rebasing merges, there are two modes: `no-rebase-cousins` and\n    `rebase-cousins`. If no mode is specified in either the config or\n    CLI, it defaults to `no-rebase-cousins`.\n\n   which I think is clearer that \"rebase.merges=some-val\" will win out\n   over \"--rebase-merges\". What's nice is that we don't commit to a\n   default value, so we can change it without having to update much of\n   the wording here.\n\nThe code looks good, no real comments there.\n"},{"id":"472909","messageId":"kl6lmt4w3uiv.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230225180325.796624-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-01T23:46:00Z","receivedAt":"2023-03-01T23:46:46Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n> empty string argument) has been an undocumented synonym of\n> --rebase-merges=no-rebase-cousins.\n\n[...]\n\n>  \tif (rebase_merges) {\n>  \t\tif (!*rebase_merges)\n> -\t\t\t; /* default mode; do nothing */\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n> +\t\t\t\t  \"instead.\"));\n>  \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n>  \t\t\toptions.rebase_cousins = 1;\n>  \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n\nAs I mentioned in my review of patch 3/3, I think we might be better\nserved by saying that --rebase-merges=\"\" is a synonym of --rebase-merges\n(aka give me a sane default) instead of giving a specific value like\n\"no-rebase-cousins\". This would be give us leeway to change the default\nbehavior in the future.\n"},{"id":"472913","messageId":"CAMMLpeRioQUag7THDKACKhS3VS+DsxzZSaFrJ8L5=1+1s=qxAA@mail.gmail.com","threadId":"59293","inReplyTo":"kl6lwn403vyn.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-02T05:02:31Z","receivedAt":"2023-03-02T05:02:52Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Wed, Mar 1, 2023 at 4:14 PM Glen Choo <chooglen@google.com> wrote:\n>\n> Hi Alex! It was great to see you at Review Club today. If you'd like the\n> notes, they are available at\n>\n> https://docs.google.com/document/d/14L8BAumGTpsXpjDY8VzZ4rRtpAjuGrFSRqn3stCuS_w\n>\n> though reviewers will send their feedback to the mailing list anyway, so\n> don't feel compelled to address feedback that isn't on the mailing list\n> :)\n\nHi Glenn,\n\nThanks for hosting the call today. I was impressed by the commitment\nto having discussion and making decisions on the public mailing list,\n\"as though the call never happened\". It was a pleasant change from\nother open source projects I have contributed to where decisions are\nsometimes made behind closed doors with little to no opportunity to\nask questions. Thanks again for making me think a little harder about\nmy patches; I expect to send a v6 by the end of the week.\n\n-Alex\n"},{"id":"472914","messageId":"CAMMLpeQ9AvFsPcr=LwRGr_ShxEKwtt+vAcs7MYPi3KchXSKs2g@mail.gmail.com","threadId":"59293","inReplyTo":"CAMMLpeRioQUag7THDKACKhS3VS+DsxzZSaFrJ8L5=1+1s=qxAA@mail.gmail.com","subject":"Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-02T05:09:58Z","receivedAt":"2023-03-02T05:10:39Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Wed, Mar 1, 2023 at 10:02 PM Alex Henrie <alexhenrie24@gmail.com> wrote:\n>\n> Hi Glenn,\n\nTypo: Glen. No matter how much I proofread, I seem to always notice\ntypos right after I send the email...\n"},{"id":"472916","messageId":"1021e6d0-0cd0-d92c-4cb3-45dbf2f6626e@dunelm.org.uk","threadId":"59293","inReplyTo":"20230225180325.796624-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-02T09:37:02Z","receivedAt":"2023-03-02T09:37:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 25/02/2023 18:03, Alex Henrie wrote:\n> The purpose of the new option is to accommodate users who would like\n> --rebase-merges to be on by default and to facilitate possibly turning\n> on --rebase-merges by default without configuration in a future version\n> of Git.\n> \n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n>   Documentation/config/rebase.txt        | 10 ++++\n>   Documentation/git-rebase.txt           |  3 +-\n>   builtin/rebase.c                       | 79 ++++++++++++++++++--------\n>   t/t3422-rebase-incompatible-options.sh | 12 ++++\n>   t/t3430-rebase-merges.sh               | 63 ++++++++++++++++++++\n>   5 files changed, 143 insertions(+), 24 deletions(-)\n> \n> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\n> index f19bd0e040..308baa9dbb 100644\n> --- a/Documentation/config/rebase.txt\n> +++ b/Documentation/config/rebase.txt\n> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n>   \n>   rebase.forkPoint::\n>   \tIf set to false set `--no-fork-point` option by default.\n> +\n> +rebase.merges::\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges` with that value as its argument, and setting to false\n> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line without an argument overrides a `rebase.merges=false`\n> +\tconfiguration but does not override other values of `rebase.merge`.\n\nI'm still not clear why the commandline doesn't override the config in \nall cases as is our usual practice. After all if the user has set \nrebase.merges then they don't need to pass --rebase-merges unless they \nwant to override the config.\n\n > [...]\n> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t\t\tbreak;\n>   \n>   \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n> -\t\t\tif (is_merge(&options))\n> -\t\t\t\tdie(_(\"apply options and merge options \"\n> -\t\t\t\t\t  \"cannot be used together\"));\n> -\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n\nThis is good, thanks for adding it\n\n > [...]\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n> index f50fbf1390..a825a77fb4 100755\n> --- a/t/t3430-rebase-merges.sh\n> +++ b/t/t3430-rebase-merges.sh\n> @@ -283,6 +283,69 @@ test_expect_success '--rebase-merges=\"\" is deprecated' '\n>   \tgrep deprecated actual\n>   '\n>   \n> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b config-rebase-cousins main &&\n> +\tgit rebase HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n> +\ttest_config rebase.merges no-rebase-cousins &&\n> +\tgit checkout -b override-config-no-rebase-cousins E &&\n> +\tgit rebase --no-rebase-merges C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\to | H\n> +\t|/\n> +\to A\n> +\tEOF\n> +'\n\nI'm not sure this test adds much value, it is hard to see what kind of \nregression would allow the others to pass but not this one.\n\n> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n> +\ttest_config rebase.merges false &&\n> +\tgit checkout -b override-config-merges-false E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase --rebase-merges C &&\n> +\ttest_cmp_rev HEAD $before\n\nThis test passes if the rebase does nothing, maybe pass --force and \ncheck the graph?\n\n\nBest Wishes\n\nPhillip\n\n> +'\n> +\n> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b no-override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n>   test_expect_success 'refs/rewritten/* is worktree-local' '\n>   \tgit worktree add wt &&\n>   \tcat >wt/script-from-scratch <<-\\EOF &&\n"},{"id":"472917","messageId":"5958167e-63ca-f0ef-3b6f-7dc523b523a6@dunelm.org.uk","threadId":"59293","inReplyTo":"20230225180325.796624-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-02T10:07:52Z","receivedAt":"2023-03-02T10:08:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 25/02/2023 18:03, Alex Henrie wrote:\n> The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n> empty string argument) has been an undocumented synonym of\n> --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid\n> confusion when a rebase.merges config option is introduced, where\n> rebase.merges=\"\" will be equivalent to --no-rebase-merges.\n\nI think deprecating this rather than making it an error is a good idea. \nI don't think we need the test though. The test suite is incredibly slow \nto run on windows (I'd heard people complain about it but it was not \nuntil I tried running it myself I realized just how diabolically slow it \nreally is) and so it is important to balance adding tests to catch \nregression vs test run time. Tests that catch bugs in the rebase \nimplementation are useful, ones like this just make everything slower \nwhich makes it harder to catch real regressions.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n>   builtin/rebase.c         | 8 ++++++--\n>   t/t3430-rebase-merges.sh | 5 +++++\n>   2 files changed, 11 insertions(+), 2 deletions(-)\n> \n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 6635f10d52..ccc13200b5 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n>   \t\t\tN_(\"mode\"),\n>   \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n> -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n> +\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n>   \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n>   \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n>   \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n> @@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \n>   \tif (rebase_merges) {\n>   \t\tif (!*rebase_merges)\n> -\t\t\t; /* default mode; do nothing */\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n> +\t\t\t\t  \"instead.\"));\n>   \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n>   \t\t\toptions.rebase_cousins = 1;\n>   \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n> index d46d9545f2..f50fbf1390 100755\n> --- a/t/t3430-rebase-merges.sh\n> +++ b/t/t3430-rebase-merges.sh\n> @@ -278,6 +278,11 @@ test_expect_success 'do not rebase cousins unless asked for' '\n>   \tEOF\n>   '\n>   \n> +test_expect_success '--rebase-merges=\"\" is deprecated' '\n> +\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n> +\tgrep deprecated actual\n> +'\n> +\n>   test_expect_success 'refs/rewritten/* is worktree-local' '\n>   \tgit worktree add wt &&\n>   \tcat >wt/script-from-scratch <<-\\EOF &&\n"},{"id":"472925","messageId":"20230302180200.2698729-1-calvinwan@google.com","threadId":"59293","inReplyTo":"20230225180325.796624-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2023-03-02T18:02:00Z","receivedAt":"2023-03-02T18:02:17Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"> -\t\t\t; /* default mode; do nothing */\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n> +\t\t\t\t  \"instead.\"));\n\nJust a small nit on the warning message. It should describe to the user\nwhat behavior is happening here and presumably the user wanted\n`--rebase-merges` with no argument. For example, \"Defaulting to\n--rebase-merges instead.\"\n"},{"id":"472926","messageId":"20230302180226.2699247-1-calvinwan@google.com","threadId":"59293","inReplyTo":"20230225180325.796624-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2023-03-02T18:02:26Z","receivedAt":"2023-03-02T18:02:34Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"> +\n> +rebase.merges::\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges` with that value as its argument, and setting to false\n> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line without an argument overrides a `rebase.merges=false`\n> +\tconfiguration but does not override other values of `rebase.merge`.\n\nI originally believed that having the boolean values as an option here\ncreated unnecessary complexity but I was convinced during Review Club\nthat the tradeoff for a user who just wants to turn this on is fine. I'm\njust wondering whether this option will possibly have additional values\nin the future. If so, what would a user expect from setting this config\nto true?\n\nSmall typo at the end s/rebase.merge/rebase.merges\n\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index c98784a0d2..b02f9cbb8c 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.\n>  \tby recreating the merge commits. Any resolved merge conflicts or\n>  \tmanual amendments in these merge commits will have to be\n>  \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n> -\tcountermand a previous `--rebase-merges`.\n> +\tcountermand both the `rebase.merges` config option and a previous\n> +\t`--rebase-merges`.\n>  +\n>  By default, or when `no-rebase-cousins` was specified, commits which do not\n>  have `<upstream>` as direct ancestor will keep their original branch point,\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index ccc13200b5..ac096f27e1 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -123,6 +123,7 @@ struct rebase_options {\n>  \tint fork_point;\n>  \tint update_refs;\n>  \tint config_autosquash;\n> +\tint config_rebase_merges;\n>  \tint config_update_refs;\n>  };\n>  \n> @@ -140,6 +141,8 @@ struct rebase_options {\n>  \t\t.allow_empty_message = 1,               \\\n>  \t\t.autosquash = -1,                       \\\n>  \t\t.config_autosquash = -1,                \\\n> +\t\t.rebase_merges = -1,                    \\\n> +\t\t.config_rebase_merges = -1,             \\\n>  \t\t.update_refs = -1,                      \\\n>  \t\t.config_update_refs = -1,               \\\n>  \t}\n> @@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n>  \treturn status ? -1 : 0;\n>  }\n>  \n> +static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n> +{\n> +\tif (!strcmp(\"no-rebase-cousins\", value))\n> +\t\toptions->rebase_cousins = 0;\n> +\telse if (!strcmp(\"rebase-cousins\", value))\n> +\t\toptions->rebase_cousins = 1;\n> +\telse\n> +\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n> +}\n> +\n>  static int rebase_config(const char *var, const char *value, void *data)\n>  {\n>  \tstruct rebase_options *opts = data;\n> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)\n>  \t\treturn 0;\n>  \t}\n>  \n> +\tif (!strcmp(var, \"rebase.merges\")) {\n> +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n> +\t\tif (opts->config_rebase_merges < 0) {\n> +\t\t\topts->config_rebase_merges = 1;\n> +\t\t\tparse_rebase_merges_value(opts, value);\n> +\t\t}\n> +\t\treturn 0;\n> +\t}\n> +\n>  \tif (!strcmp(var, \"rebase.updaterefs\")) {\n>  \t\topts->config_update_refs = git_config_bool(var, value);\n>  \t\treturn 0;\n> @@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n>  \treturn 0;\n>  }\n>  \n> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n> +{\n> +\tstruct rebase_options *options = opt->value;\n> +\n> +\toptions->rebase_merges = !unset;\n> +\n> +\tif (arg) {\n> +\t\tif (!*arg) {\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n> +\t\t\t\t  \"instead.\"));\n> +\t\t\targ = \"no-rebase-cousins\";\n> +\t\t}\n> +\t\tparse_rebase_merges_value(options, arg);\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n>  static void NORETURN error_on_missing_default_upstream(void)\n>  {\n>  \tstruct branch *current_branch = branch_get(NULL);\n> @@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \tstruct object_id branch_base;\n>  \tint ignore_whitespace = 0;\n>  \tconst char *gpg_sign = NULL;\n> -\tconst char *rebase_merges = NULL;\n>  \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n>  \tstruct object_id squash_onto;\n>  \tchar *squash_onto_name = NULL;\n> @@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\t\t   &options.allow_empty_message,\n>  \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n>  \t\t\t   PARSE_OPT_HIDDEN),\n> -\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n> -\t\t\tN_(\"mode\"),\n> +\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n>  \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n> -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n> +\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n>  \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n>  \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n>  \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n> @@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \tif (options.exec.nr)\n>  \t\timply_merge(&options, \"--exec\");\n>  \n> -\tif (rebase_merges) {\n> -\t\tif (!*rebase_merges)\n> -\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> -\t\t\t\t  \"argument is deprecated and will stop \"\n> -\t\t\t\t  \"working in a future version of Git. Use \"\n> -\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n> -\t\t\t\t  \"instead.\"));\n> -\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n> -\t\t\toptions.rebase_cousins = 1;\n> -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n> -\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n> -\t\toptions.rebase_merges = 1;\n> -\t\timply_merge(&options, \"--rebase-merges\");\n> -\t}\n> -\n>  \tif (options.type == REBASE_APPLY) {\n>  \t\tif (ignore_whitespace)\n>  \t\t\tstrvec_push(&options.git_am_opts,\n> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\t\t\tbreak;\n>  \n>  \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n> -\t\t\tif (is_merge(&options))\n> -\t\t\t\tdie(_(\"apply options and merge options \"\n> -\t\t\t\t\t  \"cannot be used together\"));\n> -\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>  \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n>  \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>  \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n> +\t\t\telse if (is_merge(&options))\n> +\t\t\t\tdie(_(\"apply options and merge options \"\n> +\t\t\t\t\t  \"cannot be used together\"));\n>  \t\t\telse\n>  \t\t\t\toptions.type = REBASE_APPLY;\n>  \t\t}\n> @@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n>  \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n>  \n> +\tif (options.rebase_merges == 1)\n> +\t\timply_merge(&options, \"--rebase-merges\");\n> +\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n> +\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n> +\n>  \tif (options.autosquash == 1)\n>  \t\timply_merge(&options, \"--autosquash\");\n>  \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\n> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\n> index 4711b37a28..af93f13849 100755\n> --- a/t/t3422-rebase-incompatible-options.sh\n> +++ b/t/t3422-rebase-incompatible-options.sh\n> @@ -101,6 +101,12 @@ test_rebase_am_only () {\n>  \t\tgrep -e --no-autosquash err\n>  \t\"\n>  \n> +\ttest_expect_success \"$opt incompatible with rebase.merges\" \"\n> +\t\tgit checkout B^0 &&\n> +\t\ttest_must_fail git -c rebase.merges=true rebase $opt A 2>err &&\n> +\t\tgrep -e --no-rebase-merges err\n> +\t\"\n> +\n\nMissing an additional test here \"$opt incompatible with --rebase-merges\"\nto match the test patterns from the other option/config combos.\n\n>  \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n>  \t\tgit checkout B^0 &&\n>  \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n> @@ -113,6 +119,12 @@ test_rebase_am_only () {\n>  \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n>  \t\"\n>  \n> +\ttest_expect_success \"$opt okay with overridden rebase.merges\" \"\n> +\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n> +\t\tgit checkout B^0 &&\n> +\t\tgit -c rebase.merges=true rebase --no-rebase-merges $opt A\n> +\t\"\n> +\n>  \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n>  \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n>  \t\tgit checkout B^0 &&\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n> index f50fbf1390..a825a77fb4 100755\n> --- a/t/t3430-rebase-merges.sh\n> +++ b/t/t3430-rebase-merges.sh\n> @@ -283,6 +283,69 @@ test_expect_success '--rebase-merges=\"\" is deprecated' '\n>  \tgrep deprecated actual\n>  '\n>  \n> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b config-rebase-cousins main &&\n> +\tgit rebase HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n> +\ttest_config rebase.merges no-rebase-cousins &&\n> +\tgit checkout -b override-config-no-rebase-cousins E &&\n> +\tgit rebase --no-rebase-merges C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\to | H\n> +\t|/\n> +\to A\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n> +\ttest_config rebase.merges false &&\n> +\tgit checkout -b override-config-merges-false E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase --rebase-merges C &&\n> +\ttest_cmp_rev HEAD $before\n> +'\n> +\n> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n> +\ttest_config rebase.merges rebase-cousins &&\n> +\tgit checkout -b no-override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n>  test_expect_success 'refs/rewritten/* is worktree-local' '\n>  \tgit worktree add wt &&\n>  \tcat >wt/script-from-scratch <<-\\EOF &&\n> -- \n> 2.39.2\n> \n> \n> \n"},{"id":"473013","messageId":"CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com","threadId":"59293","inReplyTo":"1021e6d0-0cd0-d92c-4cb3-45dbf2f6626e@dunelm.org.uk","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-04T23:24:03Z","receivedAt":"2023-03-04T23:24:49Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> On 25/02/2023 18:03, Alex Henrie wrote:\n\n> > +rebase.merges::\n> > +     Whether and how to set the `--rebase-merges` option by default. Can\n> > +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> > +     true is equivalent to `--rebase-merges` without an argument, setting to\n> > +     `rebase-cousins` or `no-rebase-cousins` is equivalent to\n> > +     `--rebase-merges` with that value as its argument, and setting to false\n> > +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> > +     command line without an argument overrides a `rebase.merges=false`\n> > +     configuration but does not override other values of `rebase.merge`.\n>\n> I'm still not clear why the commandline doesn't override the config in\n> all cases as is our usual practice. After all if the user has set\n> rebase.merges then they don't need to pass --rebase-merges unless they\n> want to override the config.\n\nGiven the current push to turn rebase-merges on by default, it seems\nlikely that rebase-cousins will also be turned on by default at some\npoint after that. There will be a warning about the default changing,\nand we'll want to let users suppress that warning by setting\nrebase.rebaseMerges=rebase-cousins. It would then be very confusing if\na --rebase-merges from some old alias continued to mean\n--rebase-merges=no-rebase-cousins when the user expects it to start\nbehaving as though the default has already changed.\n\nI will rephrase the documentation in v6 to make it more clear that the\nabsence of a specific value on the command line does not clobber a\nspecific value set in the configuration, as Glen suggested.\n\n> > +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n> > +     test_config rebase.merges rebase-cousins &&\n> > +     git checkout -b override-config-rebase-cousins main &&\n> > +     git rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n> > +     test_cmp_graph HEAD^.. <<-\\EOF\n> > +     *   Merge the topic branch '\\''onebranch'\\''\n> > +     |\\\n> > +     | * D\n> > +     | * G\n> > +     o | H\n> > +     |/\n> > +     o A\n> > +     EOF\n> > +'\n>\n> I'm not sure this test adds much value, it is hard to see what kind of\n> regression would allow the others to pass but not this one.\n\nI was worried that I or someone else would forget to explicitly set\nrebase_cousins to 0 when no-rebase-cousins is given on the command\nline, assuming that it is already 0 because that is the default. The\ntest makes me feel better, but I am happy to remove it if you still\nthink it's overkill.\n\n> > +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n> > +     test_config rebase.merges false &&\n> > +     git checkout -b override-config-merges-false E &&\n> > +     before=\"$(git rev-parse --verify HEAD)\" &&\n> > +     test_tick &&\n> > +     git rebase --rebase-merges C &&\n> > +     test_cmp_rev HEAD $before\n>\n> This test passes if the rebase does nothing, maybe pass --force and\n> check the graph?\n\nThe rebase is supposed to do nothing here. Checking that the commit\nhash is the same is just a quick way to check that the entire graph is\nthe same. What more would be checked by checking the graph instead of\nthe hash?\n\nThanks for the feedback,\n\n-Alex\n"},{"id":"473014","messageId":"CAMMLpeTiphSA=1aM89n4CQ5-L90qwZyXenx-JYox7=6NWj3TrA@mail.gmail.com","threadId":"59293","inReplyTo":"20230302180226.2699247-1-calvinwan@google.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-04T23:24:40Z","receivedAt":"2023-03-04T23:25:21Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Thu, Mar 2, 2023 at 11:02 AM Calvin Wan <calvinwan@google.com> wrote:\n\n> > +rebase.merges::\n> > +     Whether and how to set the `--rebase-merges` option by default. Can\n> > +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> > +     true is equivalent to `--rebase-merges` without an argument, setting to\n> > +     `rebase-cousins` or `no-rebase-cousins` is equivalent to\n> > +     `--rebase-merges` with that value as its argument, and setting to false\n> > +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> > +     command line without an argument overrides a `rebase.merges=false`\n> > +     configuration but does not override other values of `rebase.merge`.\n>\n> I originally believed that having the boolean values as an option here\n> created unnecessary complexity but I was convinced during Review Club\n> that the tradeoff for a user who just wants to turn this on is fine. I'm\n> just wondering whether this option will possibly have additional values\n> in the future. If so, what would a user expect from setting this config\n> to true?\n\n\"true\" will always mean \"rebase merges in the default way, I don't\ncare what it is\". During a time period when the default is\ntransitioning, \"true\" means that the user will get a warning, but\nthere's nothing wrong with continuing to use \"true\" if they really\ndon't care.\n\n-Alex\n"},{"id":"473019","messageId":"20230305050709.68736-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230225180325.796624-1-alexhenrie24@gmail.com","subject":"[PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-05T05:07:06Z","receivedAt":"2023-03-05T05:08:11Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"This patch series introduces a rebase.rebaseMerges config option to\naccommodate users who would like --rebase-merges to be on by default and\nto facilitate turning on --rebase-merges by default without\nconfiguration in a future version of Git. It also cleans up and\ndocuments the behavior of the --rebase-merges command line option to\navoid confusion about how the config option and the command line option\ninteract.\n\nChanges from v5:\n- Add commit message note about --no-rebase-merges having always worked\n- Add commit message note about the test for --no-rebase-merges being\n  somewhat contrived\n- Rephrase the documentation to avoid using the phrase \"By default\" with\n  two different meanings, and in so doing clarify that --rebase-merges\n  without an argument is not the same as --no-rebase-merges and not\n  passing --rebase-merges is not the same as passing\n  --rebase-merges=no-rebase-cousins\n- Add commit message note about keeping --rebase-merges=\"\" for now out\n  of an abundance of caution\n- Rephrase the warning about --rebase-merges=\"\" to recommend\n  --rebase-merges without an argument instead, and clarify that that\n  will do the same thing\n- Remove the test for --rebase-merges=\"\"\n- Rename the config option from rebase.merges to rebase.rebaseMerges and\n  explain why in the commit message\n- Add commit message note about why \"true\" is a valid option for\n  rebase.rebaseMerges and why --rebase-merges without an argument does\n  not clobber the mode specified in rebase.rebaseMerges\n- Rephrase the documentation to clarify that --rebase-merges without an\n  argument does not clobber the mode specified in rebase.rebaseMerges\n- Add another test for incompatible options\n\nSuggestions on v5 not incorporated in v6:\n- Make --rebase-merges without an argument clobber the mode specified in\n  rebase.rebaseMerges\n- Remove the test for --rebase-merges=no-rebase-cousins overriding\n  rebase.rebaseMerges=rebase-cousins\n- In the tests, check the graph itself instead of checking that the\n  graph has not changed by checking that the commit hash has not changed\n\nThanks to Glen, Calvin, Junio, Jonathan, Elijah, and Phillip for your\nfeedback on v5. As before, if you feel strongly about one of the\nsuggestions that I have not incorporated into v6, or if you see\nsomething else amiss, let's continue the discussion.\n\nAlex Henrie (3):\n  rebase: add documentation and test for --no-rebase-merges\n  rebase: deprecate --rebase-merges=\"\"\n  rebase: add a config option for --rebase-merges\n\n Documentation/config/rebase.txt        | 11 ++++\n Documentation/git-rebase.txt           | 19 ++++---\n builtin/rebase.c                       | 75 ++++++++++++++++++-------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 78 ++++++++++++++++++++++++++\n 5 files changed, 174 insertions(+), 26 deletions(-)\n\nRange-diff against v5:\n1:  76e38ef9f8 ! 1:  bf08c03ba7 rebase: add documentation and test for --no-rebase-merges\n    @@ Metadata\n      ## Commit message ##\n         rebase: add documentation and test for --no-rebase-merges\n     \n    +    As far as I can tell, --no-rebase-merges has always worked, but has\n    +    never been documented. It is especially important to document it before\n    +    a rebase.rebaseMerges option is introduced so that users know how to\n    +    override the config option on the command line. It's also important to\n    +    clarify that --rebase-merges without an argument is not the same as\n    +    --no-rebase-merges and not passing --rebase-merges is not the same as\n    +    passing --rebase-merges=no-rebase-cousins.\n    +\n    +    A test case is necessary to make sure that --no-rebase-merges keeps\n    +    working after its code is refactored in the following patches of this\n    +    series. The test case is a little contrived: It's unlikely that a user\n    +    would type both --rebase-merges and --no-rebase-merges at the same time.\n    +    However, if an alias is defined which includes --rebase-merges, the user\n    +    might decide to add --no-rebase-merges to countermand that part of the\n    +    alias but leave alone other flags set by the alias.\n    +\n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n      ## Documentation/git-rebase.txt ##\n    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n     +\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n     +\tcountermand a previous `--rebase-merges`.\n      +\n    - By default, or when `no-rebase-cousins` was specified, commits which do not\n    - have `<upstream>` as direct ancestor will keep their original branch point,\n    +-By default, or when `no-rebase-cousins` was specified, commits which do not\n    +-have `<upstream>` as direct ancestor will keep their original branch point,\n    +-i.e. commits that would be excluded by linkgit:git-log[1]'s\n    +-`--ancestry-path` option will keep their original ancestry by default. If\n    +-the `rebase-cousins` mode is turned on, such commits are instead rebased\n    +-onto `<upstream>` (or `<onto>`, if specified).\n    ++When rebasing merges, there are two modes: `rebase-cousins` and\n    ++`no-rebase-cousins`. If the mode is not specified, it defaults to\n    ++`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n    ++`<upstream>` as direct ancestor will keep their original branch point, i.e.\n    ++commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n    ++option will keep their original ancestry by default. In `rebase-cousins` mode,\n    ++such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n    ++specified).\n    + +\n    + It is currently only possible to recreate the merge commits using the\n    + `ort` merge strategy; different merge strategies can be used only via\n     \n      ## t/t3430-rebase-merges.sh ##\n     @@ t/t3430-rebase-merges.sh: test_expect_success 'with a branch tip that was cherry-picked already' '\n2:  c6099e6dee ! 2:  26f98b8400 rebase: deprecate --rebase-merges=\"\"\n    @@ Commit message\n     \n         The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n         empty string argument) has been an undocumented synonym of\n    -    --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid\n    -    confusion when a rebase.merges config option is introduced, where\n    -    rebase.merges=\"\" will be equivalent to --no-rebase-merges.\n    +    --rebase-merges without an argument. Deprecate that syntax to avoid\n    +    confusion when a rebase.rebaseMerges config option is introduced, where\n    +    rebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n    +\n    +    It is not likely that anyone is actually using this syntax, but just in\n    +    case, deprecate the empty string argument instead of dropping support\n    +    for it immediately.\n     \n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n     +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n     +\t\t\t\t  \"argument is deprecated and will stop \"\n     +\t\t\t\t  \"working in a future version of Git. Use \"\n    -+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    -+\t\t\t\t  \"instead.\"));\n    ++\t\t\t\t  \"--rebase-merges without an argument \"\n    ++\t\t\t\t  \"instead, which does the same thing.\"));\n      \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n      \t\t\toptions.rebase_cousins = 1;\n      \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n    -\n    - ## t/t3430-rebase-merges.sh ##\n    -@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '\n    - \tEOF\n    - '\n    - \n    -+test_expect_success '--rebase-merges=\"\" is deprecated' '\n    -+\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n    -+\tgrep deprecated actual\n    -+'\n    -+\n    - test_expect_success 'refs/rewritten/* is worktree-local' '\n    - \tgit worktree add wt &&\n    - \tcat >wt/script-from-scratch <<-\\EOF &&\n3:  95cba9588c ! 3:  402365256c rebase: add a config option for --rebase-merges\n    @@ Commit message\n         rebase: add a config option for --rebase-merges\n     \n         The purpose of the new option is to accommodate users who would like\n    -    --rebase-merges to be on by default and to facilitate possibly turning\n    -    on --rebase-merges by default without configuration in a future version\n    -    of Git.\n    +    --rebase-merges to be on by default and to facilitate turning on\n    +    --rebase-merges by default without configuration in a future version of\n    +    Git.\n    +\n    +    Name the new option rebase.rebaseMerges, even though it is a little\n    +    redundant, for consistency with the name of the command line option and\n    +    to be clear when scrolling through values in the [rebase] section of\n    +    .gitconfig.\n    +\n    +    In the future, the default rebase-merges mode may change from\n    +    no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges\n    +    to the nonspecific value \"true\" for users who do not need or want to\n    +    care about the default changing in the future. Similarly, for users who\n    +    have --rebase-merges in an alias and want to get the future behavior\n    +    now, use the specific rebase-merges mode from the config if a specific\n    +    mode is not given on the command line.\n     \n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n    @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::\n      rebase.forkPoint::\n      \tIf set to false set `--no-fork-point` option by default.\n     +\n    -+rebase.merges::\n    ++rebase.rebaseMerges::\n     +\tWhether and how to set the `--rebase-merges` option by default. Can\n     +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n     +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n     +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n     +\t`--rebase-merges` with that value as its argument, and setting to false\n     +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n    -+\tcommand line without an argument overrides a `rebase.merges=false`\n    -+\tconfiguration but does not override other values of `rebase.merge`.\n    ++\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n    ++\tconfiguration, but the absence of a specific rebase-merges mode on the\n    ++\tcommand line does not counteract a specific mode set in the configuration.\n     \n      ## Documentation/git-rebase.txt ##\n     @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n      \tmanual amendments in these merge commits will have to be\n      \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n     -\tcountermand a previous `--rebase-merges`.\n    -+\tcountermand both the `rebase.merges` config option and a previous\n    ++\tcountermand both the `rebase.rebaseMerges` config option and a previous\n     +\t`--rebase-merges`.\n      +\n    - By default, or when `no-rebase-cousins` was specified, commits which do not\n    - have `<upstream>` as direct ancestor will keep their original branch point,\n    + When rebasing merges, there are two modes: `rebase-cousins` and\n    +-`no-rebase-cousins`. If the mode is not specified, it defaults to\n    +-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n    +-`<upstream>` as direct ancestor will keep their original branch point, i.e.\n    +-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n    +-option will keep their original ancestry by default. In `rebase-cousins` mode,\n    +-such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n    +-specified).\n    ++`no-rebase-cousins`. If the mode is not specified on the command line or in\n    ++the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n    ++In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n    ++ancestor will keep their original branch point, i.e. commits that would be\n    ++excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n    ++original ancestry by default. In `rebase-cousins` mode, such commits are\n    ++instead rebased onto `<upstream>` (or `<onto>`, if specified).\n    + +\n    + It is currently only possible to recreate the merge commits using the\n    + `ort` merge strategy; different merge strategies can be used only via\n     \n      ## builtin/rebase.c ##\n     @@ builtin/rebase.c: struct rebase_options {\n    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n      \t\treturn 0;\n      \t}\n      \n    -+\tif (!strcmp(var, \"rebase.merges\")) {\n    ++\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n     +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n     +\t\tif (opts->config_rebase_merges < 0) {\n     +\t\t\topts->config_rebase_merges = 1;\n    @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const cha\n     +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n     +\t\t\t\t  \"argument is deprecated and will stop \"\n     +\t\t\t\t  \"working in a future version of Git. Use \"\n    -+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    -+\t\t\t\t  \"instead.\"));\n    -+\t\t\targ = \"no-rebase-cousins\";\n    ++\t\t\t\t  \"--rebase-merges without an argument \"\n    ++\t\t\t\t  \"instead, which does the same thing.\"));\n    ++\t\t\treturn 0;\n     +\t\t}\n     +\t\tparse_rebase_merges_value(options, arg);\n     +\t}\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n     -\t\t\twarning(_(\"--rebase-merges with an empty string \"\n     -\t\t\t\t  \"argument is deprecated and will stop \"\n     -\t\t\t\t  \"working in a future version of Git. Use \"\n    --\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n    --\t\t\t\t  \"instead.\"));\n    +-\t\t\t\t  \"--rebase-merges without an argument \"\n    +-\t\t\t\t  \"instead, which does the same thing.\"));\n     -\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n     -\t\t\toptions.rebase_cousins = 1;\n     -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n     +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n      \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n     +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n    -+\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n    ++\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n      \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n      \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n     +\t\t\telse if (is_merge(&options))\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\n     \n      ## t/t3422-rebase-incompatible-options.sh ##\n    +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n    + \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n    + \t\"\n    + \n    ++\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n    ++\t\tgit checkout B^0 &&\n    ++\t\ttest_must_fail git rebase $opt --rebase-merges A\n    ++\t\"\n    ++\n    + \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n    + \t\tgit checkout B^0 &&\n    + \t\ttest_must_fail git rebase $opt --update-refs A\n     @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n      \t\tgrep -e --no-autosquash err\n      \t\"\n      \n    -+\ttest_expect_success \"$opt incompatible with rebase.merges\" \"\n    ++\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n     +\t\tgit checkout B^0 &&\n    -+\t\ttest_must_fail git -c rebase.merges=true rebase $opt A 2>err &&\n    ++\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n     +\t\tgrep -e --no-rebase-merges err\n     +\t\"\n     +\n    @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n      \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n      \t\"\n      \n    -+\ttest_expect_success \"$opt okay with overridden rebase.merges\" \"\n    ++\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n     +\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n     +\t\tgit checkout B^0 &&\n    -+\t\tgit -c rebase.merges=true rebase --no-rebase-merges $opt A\n    ++\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n     +\t\"\n     +\n      \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n    @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n      \t\tgit checkout B^0 &&\n     \n      ## t/t3430-rebase-merges.sh ##\n    -@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated' '\n    - \tgrep deprecated actual\n    +@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '\n    + \tEOF\n      '\n      \n    -+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n    -+\ttest_config rebase.merges rebase-cousins &&\n    ++test_expect_success '--rebase-merges=\"\" is deprecated' '\n    ++\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n    ++\tgrep deprecated actual\n    ++'\n    ++\n    ++test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n    ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n     +\tgit checkout -b config-rebase-cousins main &&\n     +\tgit rebase HEAD^ &&\n     +\ttest_cmp_graph HEAD^.. <<-\\EOF\n    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n     +\tEOF\n     +'\n     +\n    -+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n    -+\ttest_config rebase.merges no-rebase-cousins &&\n    ++test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n    ++\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n     +\tgit checkout -b override-config-no-rebase-cousins E &&\n     +\tgit rebase --no-rebase-merges C &&\n     +\ttest_cmp_graph C.. <<-\\EOF\n    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n     +\tEOF\n     +'\n     +\n    -+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n    -+\ttest_config rebase.merges rebase-cousins &&\n    ++test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '\n    ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n     +\tgit checkout -b override-config-rebase-cousins main &&\n     +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n     +\ttest_cmp_graph HEAD^.. <<-\\EOF\n    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n     +\tEOF\n     +'\n     +\n    -+test_expect_success '--rebase-merges overrides rebase.merges=false' '\n    -+\ttest_config rebase.merges false &&\n    ++test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n    ++\ttest_config rebase.rebaseMerges false &&\n     +\tgit checkout -b override-config-merges-false E &&\n     +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n     +\ttest_tick &&\n    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n     +\ttest_cmp_rev HEAD $before\n     +'\n     +\n    -+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n    -+\ttest_config rebase.merges rebase-cousins &&\n    ++test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n    ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n     +\tgit checkout -b no-override-config-rebase-cousins main &&\n     +\tgit rebase --rebase-merges HEAD^ &&\n     +\ttest_cmp_graph HEAD^.. <<-\\EOF\n-- \n2.39.2\n\n"},{"id":"473021","messageId":"20230305050709.68736-4-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"[PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-05T05:07:09Z","receivedAt":"2023-03-05T05:08:38Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate turning on\n--rebase-merges by default without configuration in a future version of\nGit.\n\nName the new option rebase.rebaseMerges, even though it is a little\nredundant, for consistency with the name of the command line option and\nto be clear when scrolling through values in the [rebase] section of\n.gitconfig.\n\nIn the future, the default rebase-merges mode may change from\nno-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges\nto the nonspecific value \"true\" for users who do not need or want to\ncare about the default changing in the future. Similarly, for users who\nhave --rebase-merges in an alias and want to get the future behavior\nnow, use the specific rebase-merges mode from the config if a specific\nmode is not given on the command line.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt        | 11 ++++\n Documentation/git-rebase.txt           | 17 +++---\n builtin/rebase.c                       | 79 ++++++++++++++++++--------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 68 ++++++++++++++++++++++\n 5 files changed, 161 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..f7d3218b1d 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.rebaseMerges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n+\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges` with that value as its argument, and setting to false\n+\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n+\tconfiguration, but the absence of a specific rebase-merges mode on the\n+\tcommand line does not counteract a specific mode set in the configuration.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4e57a87624..6ec86c9c6e 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,16 +537,17 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.rebaseMerges` config option and a previous\n+\t`--rebase-merges`.\n +\n When rebasing merges, there are two modes: `rebase-cousins` and\n-`no-rebase-cousins`. If the mode is not specified, it defaults to\n-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n-`<upstream>` as direct ancestor will keep their original branch point, i.e.\n-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n-option will keep their original ancestry by default. In `rebase-cousins` mode,\n-such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n-specified).\n+`no-rebase-cousins`. If the mode is not specified on the command line or in\n+the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n+In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n+ancestor will keep their original branch point, i.e. commits that would be\n+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n+original ancestry by default. In `rebase-cousins` mode, such commits are\n+instead rebased onto `<upstream>` (or `<onto>`, if specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex c36ddc0050..04f815e3d0 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -123,6 +123,7 @@ struct rebase_options {\n \tint fork_point;\n \tint update_refs;\n \tint config_autosquash;\n+\tint config_rebase_merges;\n \tint config_update_refs;\n };\n \n@@ -140,6 +141,8 @@ struct rebase_options {\n \t\t.allow_empty_message = 1,               \\\n \t\t.autosquash = -1,                       \\\n \t\t.config_autosquash = -1,                \\\n+\t\t.rebase_merges = -1,                    \\\n+\t\t.config_rebase_merges = -1,             \\\n \t\t.update_refs = -1,                      \\\n \t\t.config_update_refs = -1,               \\\n \t}\n@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 0;\n+\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 1;\n+\telse\n+\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n+\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->config_rebase_merges < 0) {\n+\t\t\topts->config_rebase_merges = 1;\n+\t\t\tparse_rebase_merges_value(opts, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.updaterefs\")) {\n \t\topts->config_update_refs = git_config_bool(var, value);\n \t\treturn 0;\n@@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\toptions->rebase_merges = !unset;\n+\n+\tif (arg) {\n+\t\tif (!*arg) {\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n+\t\t\treturn 0;\n+\t\t}\n+\t\tparse_rebase_merges_value(options, arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n-\t\t\t\t  \"argument is deprecated and will stop \"\n-\t\t\t\t  \"working in a future version of Git. Use \"\n-\t\t\t\t  \"--rebase-merges without an argument \"\n-\t\t\t\t  \"instead, which does the same thing.\"));\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n-\t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n-\n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\n \t\t\tstrvec_push(&options.git_am_opts,\n@@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tbreak;\n \n \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n-\t\t\tif (is_merge(&options))\n-\t\t\t\tdie(_(\"apply options and merge options \"\n-\t\t\t\t\t  \"cannot be used together\"));\n-\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n+\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n+\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n+\t\t\telse if (is_merge(&options))\n+\t\t\t\tdie(_(\"apply options and merge options \"\n+\t\t\t\t\t  \"cannot be used together\"));\n \t\t\telse\n \t\t\t\toptions.type = REBASE_APPLY;\n \t\t}\n@@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n \n+\tif (options.rebase_merges == 1)\n+\t\timply_merge(&options, \"--rebase-merges\");\n+\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n+\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n+\n \tif (options.autosquash == 1)\n \t\timply_merge(&options, \"--autosquash\");\n \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\ndiff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\nindex 4711b37a28..2eba00bdf5 100755\n--- a/t/t3422-rebase-incompatible-options.sh\n+++ b/t/t3422-rebase-incompatible-options.sh\n@@ -85,6 +85,11 @@ test_rebase_am_only () {\n \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git rebase $opt --rebase-merges A\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git rebase $opt --update-refs A\n@@ -101,6 +106,12 @@ test_rebase_am_only () {\n \t\tgrep -e --no-autosquash err\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n+\t\tgrep -e --no-rebase-merges err\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n@@ -113,6 +124,12 @@ test_rebase_am_only () {\n \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n \t\"\n \n+\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n+\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n+\t\tgit checkout B^0 &&\n+\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n+\t\"\n+\n \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n \t\tgit checkout B^0 &&\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..aa75e192d1 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success '--rebase-merges=\"\" is deprecated' '\n+\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n+\tgrep deprecated actual\n+'\n+\n+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n+\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\to | H\n+\t|/\n+\to A\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n+\ttest_config rebase.rebaseMerges false &&\n+\tgit checkout -b override-config-merges-false E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b no-override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"473020","messageId":"20230305050709.68736-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"[PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-05T05:07:07Z","receivedAt":"2023-03-05T05:08:39Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"As far as I can tell, --no-rebase-merges has always worked, but has\nnever been documented. It is especially important to document it before\na rebase.rebaseMerges option is introduced so that users know how to\noverride the config option on the command line. It's also important to\nclarify that --rebase-merges without an argument is not the same as\n--no-rebase-merges and not passing --rebase-merges is not the same as\npassing --rebase-merges=no-rebase-cousins.\n\nA test case is necessary to make sure that --no-rebase-merges keeps\nworking after its code is refactored in the following patches of this\nseries. The test case is a little contrived: It's unlikely that a user\nwould type both --rebase-merges and --no-rebase-merges at the same time.\nHowever, if an alias is defined which includes --rebase-merges, the user\nmight decide to add --no-rebase-merges to countermand that part of the\nalias but leave alone other flags set by the alias.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt | 18 +++++++++++-------\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 21 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..4e57a87624 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n-By default, or when `no-rebase-cousins` was specified, commits which do not\n-have `<upstream>` as direct ancestor will keep their original branch point,\n-i.e. commits that would be excluded by linkgit:git-log[1]'s\n-`--ancestry-path` option will keep their original ancestry by default. If\n-the `rebase-cousins` mode is turned on, such commits are instead rebased\n-onto `<upstream>` (or `<onto>`, if specified).\n+When rebasing merges, there are two modes: `rebase-cousins` and\n+`no-rebase-cousins`. If the mode is not specified, it defaults to\n+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n+`<upstream>` as direct ancestor will keep their original branch point, i.e.\n+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n+option will keep their original ancestry by default. In `rebase-cousins` mode,\n+such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n+specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.39.2\n\n"},{"id":"473022","messageId":"20230305050709.68736-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"[PATCH v6 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-05T05:07:08Z","receivedAt":"2023-03-05T05:08:39Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges without an argument. Deprecate that syntax to avoid\nconfusion when a rebase.rebaseMerges config option is introduced, where\nrebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n\nIt is not likely that anyone is actually using this syntax, but just in\ncase, deprecate the empty string argument instead of dropping support\nfor it immediately.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 6635f10d52..c36ddc0050 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (rebase_merges) {\n \t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-- \n2.39.2\n\n"},{"id":"473026","messageId":"87ilff2xsl.fsf@osv.gnss.ru","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2023-03-05T12:22:02Z","receivedAt":"2023-03-05T12:22:11Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n[...]\n\n> - Rephrase the warning about --rebase-merges=\"\" to recommend\n>   --rebase-merges without an argument instead, and clarify that that\n>   will do the same thing\n\nNot a strong objection to the series as they are, but, provided options\nwith optional arguments are considered to be a bad practice in general\n(unless something had recently changed), I'd like to vote for adding:\n\n  --rebase-merges=on (≡ --reabase-merges=\"\")\n\nand for suggesting to use that one instead of --rebase-merges=\"\" rather\nthan naked --rebase-merges.\n\nIt'd be best to actually fix --rebase-merges to always have an argument,\nthe more so as we have '-r' shortcut, but as this is unlikely to fly due\nto backward compatibility considerations, this way we would at least\navoid promoting bad practices further.\n\nIf accepted, please consider to introduce\n\n  --rebase-merges=off (≡ --no-rebase-merges)\n\nas well for completeness.\n\nBTW, that's how we settled upon in the implementation of --diff-merges,\nso these two will then be mutually consistent.\n\nThanks,\n-- Sergey Organov\n"},{"id":"473035","messageId":"CAMMLpeSowxoJgPOt84Dos8BVKU=y=M+Ph0xaGe6UTxwwE5zsXQ@mail.gmail.com","threadId":"59293","inReplyTo":"87ilff2xsl.fsf@osv.gnss.ru","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-05T21:33:36Z","receivedAt":"2023-03-05T21:34:16Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> [...]\n>\n> > - Rephrase the warning about --rebase-merges=\"\" to recommend\n> >   --rebase-merges without an argument instead, and clarify that that\n> >   will do the same thing\n>\n> Not a strong objection to the series as they are, but, provided options\n> with optional arguments are considered to be a bad practice in general\n> (unless something had recently changed), I'd like to vote for adding:\n>\n>   --rebase-merges=on (≡ --reabase-merges=\"\")\n>\n> and for suggesting to use that one instead of --rebase-merges=\"\" rather\n> than naked --rebase-merges.\n>\n> It'd be best to actually fix --rebase-merges to always have an argument,\n> the more so as we have '-r' shortcut, but as this is unlikely to fly due\n> to backward compatibility considerations, this way we would at least\n> avoid promoting bad practices further.\n>\n> If accepted, please consider to introduce\n>\n>   --rebase-merges=off (≡ --no-rebase-merges)\n>\n> as well for completeness.\n>\n> BTW, that's how we settled upon in the implementation of --diff-merges,\n> so these two will then be mutually consistent.\n\nI am curious as to why you say that flags with optional arguments are\nconsidered bad practice. I don't see an advantage to adding\n--rebase-merges=on and then telling users that they ought to always\ntype the extra three characters, even if we were designing the feature\nfrom scratch. As it is, we're certainly not going to drop support for\n--no-rebase-merges or --rebase-merges without an argument, so it seems\nfine to advertise them to users. And if we do want to add support for\npassing a boolean value as an argument to --rebase-merges, that can be\ndone after the rebase.rebaseMerges config option is added; it's not a\nprerequisite.\n\n-Alex\n"},{"id":"473036","messageId":"87ttyyn71f.fsf@osv.gnss.ru","threadId":"59293","inReplyTo":"CAMMLpeSowxoJgPOt84Dos8BVKU=y=M+Ph0xaGe6UTxwwE5zsXQ@mail.gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2023-03-05T22:54:20Z","receivedAt":"2023-03-05T22:54:28Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:\n>>\n>> Alex Henrie <alexhenrie24@gmail.com> writes:\n>>\n>> [...]\n>>\n>> > - Rephrase the warning about --rebase-merges=\"\" to recommend\n>> >   --rebase-merges without an argument instead, and clarify that that\n>> >   will do the same thing\n>>\n>> Not a strong objection to the series as they are, but, provided options\n>> with optional arguments are considered to be a bad practice in general\n>> (unless something had recently changed), I'd like to vote for adding:\n>>\n>>   --rebase-merges=on (≡ --reabase-merges=\"\")\n>>\n>> and for suggesting to use that one instead of --rebase-merges=\"\" rather\n>> than naked --rebase-merges.\n>>\n>> It'd be best to actually fix --rebase-merges to always have an argument,\n>> the more so as we have '-r' shortcut, but as this is unlikely to fly due\n>> to backward compatibility considerations, this way we would at least\n>> avoid promoting bad practices further.\n>>\n>> If accepted, please consider to introduce\n>>\n>>   --rebase-merges=off (≡ --no-rebase-merges)\n>>\n>> as well for completeness.\n>>\n>> BTW, that's how we settled upon in the implementation of --diff-merges,\n>> so these two will then be mutually consistent.\n>\n> I am curious as to why you say that flags with optional arguments are\n> considered bad practice.\n\nAs far as I can tell, the core problem with such options is that generic\noptions parsing code can't tell if in \"--option value\" \"value\" is an\nargument to \"--option\", or separate argument, that in turn may lead to\nvery surprising behaviors and bugs.\n\nI believe it's a common knowledge here. If I'm wrong, then the rest\nsimply doesn't make sense.\n\n> I don't see an advantage to adding --rebase-merges=on and then telling\n> users that they ought to always type the extra three characters, even\n> if we were designing the feature from scratch.\n\nThe advantage is that we avoid optional arguments. That said, there is\n'-r' that is 13 characters shorter than --rebase-merges, so another\noption is to advertise that, provided you are still opposed to\n\"--rebase-merges=on\".\n\n> As it is, we're certainly not going to drop support for\n> --no-rebase-merges\n\n--no-rebase-merges is fine, as it has no optional arguments\n\n> or --rebase-merges without an argument,\n\nYep, but that's a pity and the whole point of my comment: if we can't\nfix it, at least don't promote it.\n\n> so it seems fine to advertise them to users.\n\n--no-rebase-merges is fine, but then you don't advertise it anyway.\n\n> And if we do want to add support for passing a boolean value as an\n> argument to --rebase-merges, that can be done after the\n> rebase.rebaseMerges config option is added; it's not a prerequisite.\n\nYep, that's why I said at the beginning that this is not an objection to\nthe series as they are, rather a nitpick.\n\nThanks,\n-- Sergey\n"},{"id":"473037","messageId":"CAMMLpeTUykcgdijRPEiBJHH1xz50s=aPK_hi=FJv6C3=cj4XEg@mail.gmail.com","threadId":"59293","inReplyTo":"87ttyyn71f.fsf@osv.gnss.ru","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-06T00:02:42Z","receivedAt":"2023-03-06T00:03:22Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Sun, Mar 5, 2023 at 3:54 PM Sergey Organov <sorganov@gmail.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> > On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:\n> >>\n> >> Alex Henrie <alexhenrie24@gmail.com> writes:\n> >>\n> >> [...]\n> >>\n> >> > - Rephrase the warning about --rebase-merges=\"\" to recommend\n> >> >   --rebase-merges without an argument instead, and clarify that that\n> >> >   will do the same thing\n> >>\n> >> Not a strong objection to the series as they are, but, provided options\n> >> with optional arguments are considered to be a bad practice in general\n> >> (unless something had recently changed), I'd like to vote for adding:\n> >>\n> >>   --rebase-merges=on (≡ --reabase-merges=\"\")\n> >>\n> >> and for suggesting to use that one instead of --rebase-merges=\"\" rather\n> >> than naked --rebase-merges.\n> >>\n> >> It'd be best to actually fix --rebase-merges to always have an argument,\n> >> the more so as we have '-r' shortcut, but as this is unlikely to fly due\n> >> to backward compatibility considerations, this way we would at least\n> >> avoid promoting bad practices further.\n> >>\n> >> If accepted, please consider to introduce\n> >>\n> >>   --rebase-merges=off (≡ --no-rebase-merges)\n> >>\n> >> as well for completeness.\n> >>\n> >> BTW, that's how we settled upon in the implementation of --diff-merges,\n> >> so these two will then be mutually consistent.\n> >\n> > I am curious as to why you say that flags with optional arguments are\n> > considered bad practice.\n>\n> As far as I can tell, the core problem with such options is that generic\n> options parsing code can't tell if in \"--option value\" \"value\" is an\n> argument to \"--option\", or separate argument, that in turn may lead to\n> very surprising behaviors and bugs.\n\nInteresting, thank you for clarifying. I just tried it and it appears\nthat --rebase-merges requires an equals sign when it has an argument.\nFor example:\n\n$ git rebase --rebase-merges=rebase-cousins\nCurrent branch master is up to date.\n\n$ git rebase --rebase-merges rebase-cousins\nfatal: invalid upstream 'rebase-cousins'\n\nSo there is no ambiguity because if an argument to a flag is optional,\nan equals sign is required.\n\nConversely, because the argument to --diff-merges is required, the\nequals sign is optional.\n\nI can see the argument (no pun intended) for avoiding optional\narguments to flags because it is confusing that '=' is required for\noptional arguments but not for mandatory arguments, and it's easy to\nforget which arguments are mandatory and which are optional.\n\nHowever, --rebase-merges already has an optional argument. If we make\nit look more like --diff-merges by accepting the value \"on\", it could\nactually be even more confusing because users would be more likely to\ntry \"--rebase-merges on\", thinking that that will work because\n\"--diff-merges on\" works.\n\nGiven the current situation, users are just going to have to learn\nthat in Git, optional arguments to flags must be preceded by an equals\nsign. Maybe someday Git will make breaking changes to get rid of\noptional arguments to flags, but for better or for worse, that day\nwill not come soon :-/\n\n> > As it is, we're certainly not going to drop support for\n> > --no-rebase-merges\n>\n> --no-rebase-merges is fine, as it has no optional arguments\n>\n> > or --rebase-merges without an argument,\n>\n> Yep, but that's a pity and the whole point of my comment: if we can't\n> fix it, at least don't promote it.\n>\n> > so it seems fine to advertise them to users.\n>\n> --no-rebase-merges is fine, but then you don't advertise it anyway.\n\nI am not sure what you mean by this. The first patch of the series\nadds documentation and a test for --no-rebase-merges, so I am\nadvertising it. Or are you saying that I /should/ advertise neither\n--no-rebase-merges nor --rebase-merges without an argument, because\nyou think --rebase-merges=off and --rebase-merges=on would be better?\n\n-Alex\n"},{"id":"473048","messageId":"87h6uy8152.fsf@osv.gnss.ru","threadId":"59293","inReplyTo":"CAMMLpeTUykcgdijRPEiBJHH1xz50s=aPK_hi=FJv6C3=cj4XEg@mail.gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2023-03-06T13:23:05Z","receivedAt":"2023-03-06T13:23:12Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n[...]\n\n>> > so it seems fine to advertise them to users.\n>>\n>> --no-rebase-merges is fine, but then you don't advertise it anyway.\n>\n> I am not sure what you mean by this. The first patch of the series\n> adds documentation and a test for --no-rebase-merges, so I am\n> advertising it.\n\nAh, yes, you do it there, and that's fine with me.\n\nI meant only the part where you suggest --rebase-merges instead of\n--rebase-merges=\"\". I have no nitpicks about any other parts of the\nseries.\n\n> Or are you saying that I /should/ advertise neither --no-rebase-merges\n> nor --rebase-merges without an argument, because you think\n> --rebase-merges=off and --rebase-merges=on would be better?\n\nNo, --no-rebase-merges is fine as far as I'm concerned.\n\nThanks,\n-- Sergey\n"},{"id":"473059","messageId":"4f313373-25c5-06d4-6d1b-89a0e595dfa1@dunelm.org.uk","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-06T16:24:37Z","receivedAt":"2023-03-06T16:34:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 05/03/2023 05:07, Alex Henrie wrote:\n> This patch series introduces a rebase.rebaseMerges config option to\n> accommodate users who would like --rebase-merges to be on by default and\n> to facilitate turning on --rebase-merges by default without\n> configuration in a future version of Git. It also cleans up and\n> documents the behavior of the --rebase-merges command line option to\n> avoid confusion about how the config option and the command line option\n> interact.\n> \n> Changes from v5:\n> - Add commit message note about --no-rebase-merges having always worked\n> - Add commit message note about the test for --no-rebase-merges being\n>    somewhat contrived\n> - Rephrase the documentation to avoid using the phrase \"By default\" with\n>    two different meanings, and in so doing clarify that --rebase-merges\n>    without an argument is not the same as --no-rebase-merges and not\n>    passing --rebase-merges is not the same as passing\n>    --rebase-merges=no-rebase-cousins\n> - Add commit message note about keeping --rebase-merges=\"\" for now out\n>    of an abundance of caution\n> - Rephrase the warning about --rebase-merges=\"\" to recommend\n>    --rebase-merges without an argument instead, and clarify that that\n>    will do the same thing\n> - Remove the test for --rebase-merges=\"\"\n> - Rename the config option from rebase.merges to rebase.rebaseMerges and\n>    explain why in the commit message\n> - Add commit message note about why \"true\" is a valid option for\n>    rebase.rebaseMerges and why --rebase-merges without an argument does\n>    not clobber the mode specified in rebase.rebaseMerges\n> - Rephrase the documentation to clarify that --rebase-merges without an\n>    argument does not clobber the mode specified in rebase.rebaseMerges\n> - Add another test for incompatible options\n> \n> Suggestions on v5 not incorporated in v6:\n\nThanks for adding this, it is really helpful to see what did not change \nas well as what did change. It is also helpful to briefly explain why \nyou disagree with the suggestions so others can understand why you \ndecided not to make these changes.\n\n> - Make --rebase-merges without an argument clobber the mode specified in\n>    rebase.rebaseMerges\n\nI'm still confused as to why we want the config value to change the \nbehavior of --rebase-merges. Is it for \"git pull --rebase=merges\"? If \nthat's the case then I think a better approach is for pull to parse \nrebase.merges and pass the appropriate argument to rebase. That way we \ndon't break the expectation that command line arguments override config \nvalues.\n\n> - Remove the test for --rebase-merges=no-rebase-cousins overriding\n>    rebase.rebaseMerges=rebase-cousins\n> - In the tests, check the graph itself instead of checking that the\n>    graph has not changed by checking that the commit hash has not changed\n\nI'm not sure what value the existing test has if it only checks that \nHEAD is unchanged after the rebase. It could be unchanged because the \nrebase fast-forwarded or because it did nothing.\n\nBest Wishes\n\nPhillip\n\n> Thanks to Glen, Calvin, Junio, Jonathan, Elijah, and Phillip for your\n> feedback on v5. As before, if you feel strongly about one of the\n> suggestions that I have not incorporated into v6, or if you see\n> something else amiss, let's continue the discussion.\n> \n> Alex Henrie (3):\n>    rebase: add documentation and test for --no-rebase-merges\n>    rebase: deprecate --rebase-merges=\"\"\n>    rebase: add a config option for --rebase-merges\n> \n>   Documentation/config/rebase.txt        | 11 ++++\n>   Documentation/git-rebase.txt           | 19 ++++---\n>   builtin/rebase.c                       | 75 ++++++++++++++++++-------\n>   t/t3422-rebase-incompatible-options.sh | 17 ++++++\n>   t/t3430-rebase-merges.sh               | 78 ++++++++++++++++++++++++++\n>   5 files changed, 174 insertions(+), 26 deletions(-)\n> \n> Range-diff against v5:\n> 1:  76e38ef9f8 ! 1:  bf08c03ba7 rebase: add documentation and test for --no-rebase-merges\n>      @@ Metadata\n>        ## Commit message ##\n>           rebase: add documentation and test for --no-rebase-merges\n>       \n>      +    As far as I can tell, --no-rebase-merges has always worked, but has\n>      +    never been documented. It is especially important to document it before\n>      +    a rebase.rebaseMerges option is introduced so that users know how to\n>      +    override the config option on the command line. It's also important to\n>      +    clarify that --rebase-merges without an argument is not the same as\n>      +    --no-rebase-merges and not passing --rebase-merges is not the same as\n>      +    passing --rebase-merges=no-rebase-cousins.\n>      +\n>      +    A test case is necessary to make sure that --no-rebase-merges keeps\n>      +    working after its code is refactored in the following patches of this\n>      +    series. The test case is a little contrived: It's unlikely that a user\n>      +    would type both --rebase-merges and --no-rebase-merges at the same time.\n>      +    However, if an alias is defined which includes --rebase-merges, the user\n>      +    might decide to add --no-rebase-merges to countermand that part of the\n>      +    alias but leave alone other flags set by the alias.\n>      +\n>           Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n>       \n>        ## Documentation/git-rebase.txt ##\n>      @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n>       +\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n>       +\tcountermand a previous `--rebase-merges`.\n>        +\n>      - By default, or when `no-rebase-cousins` was specified, commits which do not\n>      - have `<upstream>` as direct ancestor will keep their original branch point,\n>      +-By default, or when `no-rebase-cousins` was specified, commits which do not\n>      +-have `<upstream>` as direct ancestor will keep their original branch point,\n>      +-i.e. commits that would be excluded by linkgit:git-log[1]'s\n>      +-`--ancestry-path` option will keep their original ancestry by default. If\n>      +-the `rebase-cousins` mode is turned on, such commits are instead rebased\n>      +-onto `<upstream>` (or `<onto>`, if specified).\n>      ++When rebasing merges, there are two modes: `rebase-cousins` and\n>      ++`no-rebase-cousins`. If the mode is not specified, it defaults to\n>      ++`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n>      ++`<upstream>` as direct ancestor will keep their original branch point, i.e.\n>      ++commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n>      ++option will keep their original ancestry by default. In `rebase-cousins` mode,\n>      ++such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n>      ++specified).\n>      + +\n>      + It is currently only possible to recreate the merge commits using the\n>      + `ort` merge strategy; different merge strategies can be used only via\n>       \n>        ## t/t3430-rebase-merges.sh ##\n>       @@ t/t3430-rebase-merges.sh: test_expect_success 'with a branch tip that was cherry-picked already' '\n> 2:  c6099e6dee ! 2:  26f98b8400 rebase: deprecate --rebase-merges=\"\"\n>      @@ Commit message\n>       \n>           The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\n>           empty string argument) has been an undocumented synonym of\n>      -    --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid\n>      -    confusion when a rebase.merges config option is introduced, where\n>      -    rebase.merges=\"\" will be equivalent to --no-rebase-merges.\n>      +    --rebase-merges without an argument. Deprecate that syntax to avoid\n>      +    confusion when a rebase.rebaseMerges config option is introduced, where\n>      +    rebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n>      +\n>      +    It is not likely that anyone is actually using this syntax, but just in\n>      +    case, deprecate the empty string argument instead of dropping support\n>      +    for it immediately.\n>       \n>           Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n>       \n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>       +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n>       +\t\t\t\t  \"argument is deprecated and will stop \"\n>       +\t\t\t\t  \"working in a future version of Git. Use \"\n>      -+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n>      -+\t\t\t\t  \"instead.\"));\n>      ++\t\t\t\t  \"--rebase-merges without an argument \"\n>      ++\t\t\t\t  \"instead, which does the same thing.\"));\n>        \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n>        \t\t\toptions.rebase_cousins = 1;\n>        \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n>      -\n>      - ## t/t3430-rebase-merges.sh ##\n>      -@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '\n>      - \tEOF\n>      - '\n>      -\n>      -+test_expect_success '--rebase-merges=\"\" is deprecated' '\n>      -+\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n>      -+\tgrep deprecated actual\n>      -+'\n>      -+\n>      - test_expect_success 'refs/rewritten/* is worktree-local' '\n>      - \tgit worktree add wt &&\n>      - \tcat >wt/script-from-scratch <<-\\EOF &&\n> 3:  95cba9588c ! 3:  402365256c rebase: add a config option for --rebase-merges\n>      @@ Commit message\n>           rebase: add a config option for --rebase-merges\n>       \n>           The purpose of the new option is to accommodate users who would like\n>      -    --rebase-merges to be on by default and to facilitate possibly turning\n>      -    on --rebase-merges by default without configuration in a future version\n>      -    of Git.\n>      +    --rebase-merges to be on by default and to facilitate turning on\n>      +    --rebase-merges by default without configuration in a future version of\n>      +    Git.\n>      +\n>      +    Name the new option rebase.rebaseMerges, even though it is a little\n>      +    redundant, for consistency with the name of the command line option and\n>      +    to be clear when scrolling through values in the [rebase] section of\n>      +    .gitconfig.\n>      +\n>      +    In the future, the default rebase-merges mode may change from\n>      +    no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges\n>      +    to the nonspecific value \"true\" for users who do not need or want to\n>      +    care about the default changing in the future. Similarly, for users who\n>      +    have --rebase-merges in an alias and want to get the future behavior\n>      +    now, use the specific rebase-merges mode from the config if a specific\n>      +    mode is not given on the command line.\n>       \n>           Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n>       \n>      @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::\n>        rebase.forkPoint::\n>        \tIf set to false set `--no-fork-point` option by default.\n>       +\n>      -+rebase.merges::\n>      ++rebase.rebaseMerges::\n>       +\tWhether and how to set the `--rebase-merges` option by default. Can\n>       +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n>       +\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n>       +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n>       +\t`--rebase-merges` with that value as its argument, and setting to false\n>       +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n>      -+\tcommand line without an argument overrides a `rebase.merges=false`\n>      -+\tconfiguration but does not override other values of `rebase.merge`.\n>      ++\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n>      ++\tconfiguration, but the absence of a specific rebase-merges mode on the\n>      ++\tcommand line does not counteract a specific mode set in the configuration.\n>       \n>        ## Documentation/git-rebase.txt ##\n>       @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n>      @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n>        \tmanual amendments in these merge commits will have to be\n>        \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n>       -\tcountermand a previous `--rebase-merges`.\n>      -+\tcountermand both the `rebase.merges` config option and a previous\n>      ++\tcountermand both the `rebase.rebaseMerges` config option and a previous\n>       +\t`--rebase-merges`.\n>        +\n>      - By default, or when `no-rebase-cousins` was specified, commits which do not\n>      - have `<upstream>` as direct ancestor will keep their original branch point,\n>      + When rebasing merges, there are two modes: `rebase-cousins` and\n>      +-`no-rebase-cousins`. If the mode is not specified, it defaults to\n>      +-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n>      +-`<upstream>` as direct ancestor will keep their original branch point, i.e.\n>      +-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n>      +-option will keep their original ancestry by default. In `rebase-cousins` mode,\n>      +-such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n>      +-specified).\n>      ++`no-rebase-cousins`. If the mode is not specified on the command line or in\n>      ++the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n>      ++In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n>      ++ancestor will keep their original branch point, i.e. commits that would be\n>      ++excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n>      ++original ancestry by default. In `rebase-cousins` mode, such commits are\n>      ++instead rebased onto `<upstream>` (or `<onto>`, if specified).\n>      + +\n>      + It is currently only possible to recreate the merge commits using the\n>      + `ort` merge strategy; different merge strategies can be used only via\n>       \n>        ## builtin/rebase.c ##\n>       @@ builtin/rebase.c: struct rebase_options {\n>      @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n>        \t\treturn 0;\n>        \t}\n>        \n>      -+\tif (!strcmp(var, \"rebase.merges\")) {\n>      ++\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n>       +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n>       +\t\tif (opts->config_rebase_merges < 0) {\n>       +\t\t\topts->config_rebase_merges = 1;\n>      @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const cha\n>       +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n>       +\t\t\t\t  \"argument is deprecated and will stop \"\n>       +\t\t\t\t  \"working in a future version of Git. Use \"\n>      -+\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n>      -+\t\t\t\t  \"instead.\"));\n>      -+\t\t\targ = \"no-rebase-cousins\";\n>      ++\t\t\t\t  \"--rebase-merges without an argument \"\n>      ++\t\t\t\t  \"instead, which does the same thing.\"));\n>      ++\t\t\treturn 0;\n>       +\t\t}\n>       +\t\tparse_rebase_merges_value(options, arg);\n>       +\t}\n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>       -\t\t\twarning(_(\"--rebase-merges with an empty string \"\n>       -\t\t\t\t  \"argument is deprecated and will stop \"\n>       -\t\t\t\t  \"working in a future version of Git. Use \"\n>      --\t\t\t\t  \"--rebase-merges=no-rebase-cousins \"\n>      --\t\t\t\t  \"instead.\"));\n>      +-\t\t\t\t  \"--rebase-merges without an argument \"\n>      +-\t\t\t\t  \"instead, which does the same thing.\"));\n>       -\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n>       -\t\t\toptions.rebase_cousins = 1;\n>       -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>       +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>        \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n>       +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n>      -+\t\t\t\tdie(_(\"apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges\"));\n>      ++\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>        \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>        \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n>       +\t\t\telse if (is_merge(&options))\n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>        \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\n>       \n>        ## t/t3422-rebase-incompatible-options.sh ##\n>      +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n>      + \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n>      + \t\"\n>      +\n>      ++\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n>      ++\t\tgit checkout B^0 &&\n>      ++\t\ttest_must_fail git rebase $opt --rebase-merges A\n>      ++\t\"\n>      ++\n>      + \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n>      + \t\tgit checkout B^0 &&\n>      + \t\ttest_must_fail git rebase $opt --update-refs A\n>       @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n>        \t\tgrep -e --no-autosquash err\n>        \t\"\n>        \n>      -+\ttest_expect_success \"$opt incompatible with rebase.merges\" \"\n>      ++\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n>       +\t\tgit checkout B^0 &&\n>      -+\t\ttest_must_fail git -c rebase.merges=true rebase $opt A 2>err &&\n>      ++\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n>       +\t\tgrep -e --no-rebase-merges err\n>       +\t\"\n>       +\n>      @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n>        \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n>        \t\"\n>        \n>      -+\ttest_expect_success \"$opt okay with overridden rebase.merges\" \"\n>      ++\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n>       +\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n>       +\t\tgit checkout B^0 &&\n>      -+\t\tgit -c rebase.merges=true rebase --no-rebase-merges $opt A\n>      ++\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n>       +\t\"\n>       +\n>        \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n>      @@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {\n>        \t\tgit checkout B^0 &&\n>       \n>        ## t/t3430-rebase-merges.sh ##\n>      -@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated' '\n>      - \tgrep deprecated actual\n>      +@@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '\n>      + \tEOF\n>        '\n>        \n>      -+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n>      -+\ttest_config rebase.merges rebase-cousins &&\n>      ++test_expect_success '--rebase-merges=\"\" is deprecated' '\n>      ++\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n>      ++\tgrep deprecated actual\n>      ++'\n>      ++\n>      ++test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n>      ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n>       +\tgit checkout -b config-rebase-cousins main &&\n>       +\tgit rebase HEAD^ &&\n>       +\ttest_cmp_graph HEAD^.. <<-\\EOF\n>      @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n>       +\tEOF\n>       +'\n>       +\n>      -+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '\n>      -+\ttest_config rebase.merges no-rebase-cousins &&\n>      ++test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n>      ++\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n>       +\tgit checkout -b override-config-no-rebase-cousins E &&\n>       +\tgit rebase --no-rebase-merges C &&\n>       +\ttest_cmp_graph C.. <<-\\EOF\n>      @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n>       +\tEOF\n>       +'\n>       +\n>      -+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n>      -+\ttest_config rebase.merges rebase-cousins &&\n>      ++test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '\n>      ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n>       +\tgit checkout -b override-config-rebase-cousins main &&\n>       +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n>       +\ttest_cmp_graph HEAD^.. <<-\\EOF\n>      @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n>       +\tEOF\n>       +'\n>       +\n>      -+test_expect_success '--rebase-merges overrides rebase.merges=false' '\n>      -+\ttest_config rebase.merges false &&\n>      ++test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n>      ++\ttest_config rebase.rebaseMerges false &&\n>       +\tgit checkout -b override-config-merges-false E &&\n>       +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n>       +\ttest_tick &&\n>      @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges=\"\" is deprecated'\n>       +\ttest_cmp_rev HEAD $before\n>       +'\n>       +\n>      -+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '\n>      -+\ttest_config rebase.merges rebase-cousins &&\n>      ++test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n>      ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n>       +\tgit checkout -b no-override-config-rebase-cousins main &&\n>       +\tgit rebase --rebase-merges HEAD^ &&\n>       +\ttest_cmp_graph HEAD^.. <<-\\EOF\n"},{"id":"473062","messageId":"xmqqh6uxbxx7.fsf@gitster.g","threadId":"59293","inReplyTo":"87ttyyn71f.fsf@osv.gnss.ru","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-06T17:19:00Z","receivedAt":"2023-03-06T17:19:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sergey Organov <sorganov@gmail.com> writes:\n\n>> I am curious as to why you say that flags with optional arguments are\n>> considered bad practice.\n>\n> As far as I can tell, the core problem with such options is that generic\n> options parsing code can't tell if in \"--option value\" \"value\" is an\n> argument to \"--option\", or separate argument, that in turn may lead to\n> very surprising behaviors and bugs.\n\nAnother one and half reasons are\n\n * Can there be two or more such options used on a single command\n   line?  Unless we limit the command line and say \"such an option\n   can appear only at the end of options and without any non option\n   arguments\" (the latter is what you said above), we'd end up with\n   a system that cannot be explained to casual users.\n\n * What about short-form of option?  Is \"-abc\" asking for three\n   options \"-a\", \"-b\", and \"-c\"?  Or is it \"-a\" option that takes\n   optional argument \"bc\"?\n\nThere are some advices we give in \"git help cli\" to our users, which\nwe shouldn't have to have given if we rejected flags with optional\narguments in our commands.\n\nThanks.\n\n\n\n    \n"},{"id":"473066","messageId":"CAMMLpeTPNoDK9r-e9KMwfYzdG+iyuVwxQGaWHUL-ngD+-r48FQ@mail.gmail.com","threadId":"59293","inReplyTo":"4f313373-25c5-06d4-6d1b-89a0e595dfa1@dunelm.org.uk","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-06T17:36:05Z","receivedAt":"2023-03-06T17:38:18Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Mon, Mar 6, 2023 at 9:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> On 05/03/2023 05:07, Alex Henrie wrote:\n\n> > Suggestions on v5 not incorporated in v6:\n>\n> Thanks for adding this, it is really helpful to see what did not change\n> as well as what did change. It is also helpful to briefly explain why\n> you disagree with the suggestions so others can understand why you\n> decided not to make these changes.\n>\n> > - Make --rebase-merges without an argument clobber the mode specified in\n> >    rebase.rebaseMerges\n>\n> I'm still confused as to why we want the config value to change the\n> behavior of --rebase-merges. Is it for \"git pull --rebase=merges\"? If\n> that's the case then I think a better approach is for pull to parse\n> rebase.merges and pass the appropriate argument to rebase. That way we\n> don't break the expectation that command line arguments override config\n> values.\n>\n> > - Remove the test for --rebase-merges=no-rebase-cousins overriding\n> >    rebase.rebaseMerges=rebase-cousins\n> > - In the tests, check the graph itself instead of checking that the\n> >    graph has not changed by checking that the commit hash has not changed\n>\n> I'm not sure what value the existing test has if it only checks that\n> HEAD is unchanged after the rebase. It could be unchanged because the\n> rebase fast-forwarded or because it did nothing.\n\nPlease have a look at the email I sent before sending v6:\nhttps://lore.kernel.org/git/CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com/\n\nIn that email I tried to explain why I didn't incorporate your three\nsuggestions on v5. Please let me know if it still isn't clear.\n\n-Alex\n"},{"id":"473078","messageId":"xmqqh6ux8zqh.fsf@gitster.g","threadId":"59293","inReplyTo":"CAMMLpeTUykcgdijRPEiBJHH1xz50s=aPK_hi=FJv6C3=cj4XEg@mail.gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-06T19:08:06Z","receivedAt":"2023-03-06T19:08:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> Interesting, thank you for clarifying. I just tried it and it appears\n> that --rebase-merges requires an equals sign when it has an argument.\n> For example:\n>\n> $ git rebase --rebase-merges=rebase-cousins\n> Current branch master is up to date.\n>\n> $ git rebase --rebase-merges rebase-cousins\n> fatal: invalid upstream 'rebase-cousins'\n>\n> So there is no ambiguity because if an argument to a flag is optional,\n> an equals sign is required.\n\nExactly.\n\nIt is not a good excuse that users can express something\nunambiguously to the machinery once they know they need to use '=',\nwhen they are so accustomed to giving values to ordinary options\nwithout.  This is why options with optional value is considered a\nbad UI element, because the way \"--opt val\" is interpreted for them\nis different from everybody else.  And it burdens the users by\nforcing them to _know_ which ones are with optional value.\n\nSince it is an existing UI breakage, as long as the series is not\nmaking it worse or harder to fix in the future, it is fine, though.\n\n"},{"id":"473123","messageId":"7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk","threadId":"59293","inReplyTo":"20230305050709.68736-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-07T14:56:53Z","receivedAt":"2023-03-07T15:05:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 05/03/2023 05:07, Alex Henrie wrote:\n> The purpose of the new option is to accommodate users who would like\n> --rebase-merges to be on by default and to facilitate turning on\n> --rebase-merges by default without configuration in a future version of\n> Git.\n> \n> Name the new option rebase.rebaseMerges, even though it is a little\n> redundant, for consistency with the name of the command line option and\n> to be clear when scrolling through values in the [rebase] section of\n> .gitconfig.\n> \n> In the future, the default rebase-merges mode may change from\n> no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges\n> to the nonspecific value \"true\" for users who do not need or want to\n> care about the default changing in the future. Similarly, for users who\n> have --rebase-merges in an alias and want to get the future behavior\n> now, use the specific rebase-merges mode from the config if a specific\n> mode is not given on the command line.\n> \n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n>   Documentation/config/rebase.txt        | 11 ++++\n>   Documentation/git-rebase.txt           | 17 +++---\n>   builtin/rebase.c                       | 79 ++++++++++++++++++--------\n>   t/t3422-rebase-incompatible-options.sh | 17 ++++++\n>   t/t3430-rebase-merges.sh               | 68 ++++++++++++++++++++++\n>   5 files changed, 161 insertions(+), 31 deletions(-)\n> \n> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\n> index f19bd0e040..f7d3218b1d 100644\n> --- a/Documentation/config/rebase.txt\n> +++ b/Documentation/config/rebase.txt\n> @@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::\n>   \n>   rebase.forkPoint::\n>   \tIf set to false set `--no-fork-point` option by default.\n> +\n> +rebase.rebaseMerges::\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue is equivalent to `--rebase-merges` without an argument,\n\nThis is a bit picky but how can rebase.rebaseMerges=true be equivalent \nto --rebase-merges without an argument when the behavior of \n--rebase-merges without an argument depends on the value of \nrebase.rebaseMerges?\n\n> setting to\n> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges` with that value as its argument, and setting to false\n> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n> +\tconfiguration, but the absence of a specific rebase-merges mode on the\n> +\tcommand line does not counteract a specific mode set in the configuration.\n\nI may not agree the with the design choice but the documentation here \nand below is very clear about the behavior of --rebase-merges without an \nargument which is good.\n\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index 4e57a87624..6ec86c9c6e 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -537,16 +537,17 @@ See also INCOMPATIBLE OPTIONS below.\n>   \tby recreating the merge commits. Any resolved merge conflicts or\n>   \tmanual amendments in these merge commits will have to be\n>   \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n> -\tcountermand a previous `--rebase-merges`.\n> +\tcountermand both the `rebase.rebaseMerges` config option and a previous\n> +\t`--rebase-merges`.\n>   +\n>   When rebasing merges, there are two modes: `rebase-cousins` and\n> -`no-rebase-cousins`. If the mode is not specified, it defaults to\n> -`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n> -`<upstream>` as direct ancestor will keep their original branch point, i.e.\n> -commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n> -option will keep their original ancestry by default. In `rebase-cousins` mode,\n> -such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n> -specified).\n> +`no-rebase-cousins`. If the mode is not specified on the command line or in\n> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n> +In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n> +ancestor will keep their original branch point, i.e. commits that would be\n> +excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n> +original ancestry by default. In `rebase-cousins` mode, such commits are\n> +instead rebased onto `<upstream>` (or `<onto>`, if specified).\n >[...]\n>   static int rebase_config(const char *var, const char *value, void *data)\n>   {\n>   \tstruct rebase_options *opts = data;\n> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)\n>   \t\treturn 0;\n>   \t}\n>   \n> +\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n> +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n> +\t\tif (opts->config_rebase_merges < 0) {\n> +\t\t\topts->config_rebase_merges = 1;\n> +\t\t\tparse_rebase_merges_value(opts, value);\n> +\t\t}\n\nI think we need\n\n\t} else {\n\t\topts->rebase_cousins = 0;\n\t}\n\nhere. Otherwise if rebase.rebaseMerges is set twice we wont follow the \nusual \"last config wins\" convention. For example\n\n\t[rebase]\n\t\trebaseMerges=rebase-cousins\n\t\trebaseMerges=true\n\nwill result in us unexpectedly rebasing cousins\n\nThe rest of the patch looks fine\n\nBest Wishes\n\nPhillip\n\n> +\t\treturn 0;\n> +\t}\n> +\n>   \tif (!strcmp(var, \"rebase.updaterefs\")) {\n>   \t\topts->config_update_refs = git_config_bool(var, value);\n>   \t\treturn 0;\n> @@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n>   \treturn 0;\n>   }\n>   \n> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n> +{\n> +\tstruct rebase_options *options = opt->value;\n> +\n> +\toptions->rebase_merges = !unset;\n> +\n> +\tif (arg) {\n> +\t\tif (!*arg) {\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges without an argument \"\n> +\t\t\t\t  \"instead, which does the same thing.\"));\n> +\t\t\treturn 0;\n> +\t\t}\n> +\t\tparse_rebase_merges_value(options, arg);\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n>   static void NORETURN error_on_missing_default_upstream(void)\n>   {\n>   \tstruct branch *current_branch = branch_get(NULL);\n> @@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \tstruct object_id branch_base;\n>   \tint ignore_whitespace = 0;\n>   \tconst char *gpg_sign = NULL;\n> -\tconst char *rebase_merges = NULL;\n>   \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n>   \tstruct object_id squash_onto;\n>   \tchar *squash_onto_name = NULL;\n> @@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t\t   &options.allow_empty_message,\n>   \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n>   \t\t\t   PARSE_OPT_HIDDEN),\n> -\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n> -\t\t\tN_(\"mode\"),\n> +\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n>   \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n> -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n> +\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n>   \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n>   \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n>   \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n> @@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \tif (options.exec.nr)\n>   \t\timply_merge(&options, \"--exec\");\n>   \n> -\tif (rebase_merges) {\n> -\t\tif (!*rebase_merges)\n> -\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> -\t\t\t\t  \"argument is deprecated and will stop \"\n> -\t\t\t\t  \"working in a future version of Git. Use \"\n> -\t\t\t\t  \"--rebase-merges without an argument \"\n> -\t\t\t\t  \"instead, which does the same thing.\"));\n> -\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n> -\t\t\toptions.rebase_cousins = 1;\n> -\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n> -\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n> -\t\toptions.rebase_merges = 1;\n> -\t\timply_merge(&options, \"--rebase-merges\");\n> -\t}\n> -\n>   \tif (options.type == REBASE_APPLY) {\n>   \t\tif (ignore_whitespace)\n>   \t\t\tstrvec_push(&options.git_am_opts,\n> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t\t\tbreak;\n>   \n>   \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n> -\t\t\tif (is_merge(&options))\n> -\t\t\t\tdie(_(\"apply options and merge options \"\n> -\t\t\t\t\t  \"cannot be used together\"));\n> -\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>   \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n> +\t\t\telse if (is_merge(&options))\n> +\t\t\t\tdie(_(\"apply options and merge options \"\n> +\t\t\t\t\t  \"cannot be used together\"));\n>   \t\t\telse\n>   \t\t\t\toptions.type = REBASE_APPLY;\n>   \t\t}\n> @@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n>   \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n>   \n> +\tif (options.rebase_merges == 1)\n> +\t\timply_merge(&options, \"--rebase-merges\");\n> +\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n> +\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n> +\n>   \tif (options.autosquash == 1)\n>   \t\timply_merge(&options, \"--autosquash\");\n>   \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\n> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\n> index 4711b37a28..2eba00bdf5 100755\n> --- a/t/t3422-rebase-incompatible-options.sh\n> +++ b/t/t3422-rebase-incompatible-options.sh\n> @@ -85,6 +85,11 @@ test_rebase_am_only () {\n>   \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n>   \t\"\n>   \n> +\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n> +\t\tgit checkout B^0 &&\n> +\t\ttest_must_fail git rebase $opt --rebase-merges A\n> +\t\"\n> +\n>   \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n>   \t\tgit checkout B^0 &&\n>   \t\ttest_must_fail git rebase $opt --update-refs A\n> @@ -101,6 +106,12 @@ test_rebase_am_only () {\n>   \t\tgrep -e --no-autosquash err\n>   \t\"\n>   \n> +\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n> +\t\tgit checkout B^0 &&\n> +\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n> +\t\tgrep -e --no-rebase-merges err\n> +\t\"\n> +\n>   \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n>   \t\tgit checkout B^0 &&\n>   \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n> @@ -113,6 +124,12 @@ test_rebase_am_only () {\n>   \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n>   \t\"\n>   \n> +\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n> +\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n> +\t\tgit checkout B^0 &&\n> +\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n> +\t\"\n> +\n>   \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n>   \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n>   \t\tgit checkout B^0 &&\n> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\n> index d46d9545f2..aa75e192d1 100755\n> --- a/t/t3430-rebase-merges.sh\n> +++ b/t/t3430-rebase-merges.sh\n> @@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '\n>   \tEOF\n>   '\n>   \n> +test_expect_success '--rebase-merges=\"\" is deprecated' '\n> +\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n> +\tgrep deprecated actual\n> +'\n> +\n> +test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n> +\ttest_config rebase.rebaseMerges rebase-cousins &&\n> +\tgit checkout -b config-rebase-cousins main &&\n> +\tgit rebase HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n> +\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n> +\tgit checkout -b override-config-no-rebase-cousins E &&\n> +\tgit rebase --no-rebase-merges C &&\n> +\ttest_cmp_graph C.. <<-\\EOF\n> +\t* B\n> +\t* D\n> +\to C\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '\n> +\ttest_config rebase.rebaseMerges rebase-cousins &&\n> +\tgit checkout -b override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\to | H\n> +\t|/\n> +\to A\n> +\tEOF\n> +'\n> +\n> +test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n> +\ttest_config rebase.rebaseMerges false &&\n> +\tgit checkout -b override-config-merges-false E &&\n> +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n> +\ttest_tick &&\n> +\tgit rebase --rebase-merges C &&\n> +\ttest_cmp_rev HEAD $before\n> +'\n> +\n> +test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n> +\ttest_config rebase.rebaseMerges rebase-cousins &&\n> +\tgit checkout -b no-override-config-rebase-cousins main &&\n> +\tgit rebase --rebase-merges HEAD^ &&\n> +\ttest_cmp_graph HEAD^.. <<-\\EOF\n> +\t*   Merge the topic branch '\\''onebranch'\\''\n> +\t|\\\n> +\t| * D\n> +\t| * G\n> +\t|/\n> +\to H\n> +\tEOF\n> +'\n> +\n>   test_expect_success 'refs/rewritten/* is worktree-local' '\n>   \tgit worktree add wt &&\n>   \tcat >wt/script-from-scratch <<-\\EOF &&\n"},{"id":"473124","messageId":"af07d899-4e33-e554-2ab9-4226896c695c@dunelm.org.uk","threadId":"59293","inReplyTo":"20230305050709.68736-3-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-07T14:59:09Z","receivedAt":"2023-03-07T15:06:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 05/03/2023 05:07, Alex Henrie wrote:\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 6635f10d52..c36ddc0050 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n>   \t\t\tN_(\"mode\"),\n>   \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n> -\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n> +\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n\nI've just realized there is a subtle behavior change here. If the user \npasses \"--rebase-merges=rebase-cousins --rebase-merges\" we used to \nrebase cousins but now we wont. The next patch goes back to the old \nbehavior so I don't think we need to worry about it.\n\nBest Wishes\n\nPhillip\n\n>   \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n>   \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n>   \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n> @@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \n>   \tif (rebase_merges) {\n>   \t\tif (!*rebase_merges)\n> -\t\t\t; /* default mode; do nothing */\n> +\t\t\twarning(_(\"--rebase-merges with an empty string \"\n> +\t\t\t\t  \"argument is deprecated and will stop \"\n> +\t\t\t\t  \"working in a future version of Git. Use \"\n> +\t\t\t\t  \"--rebase-merges without an argument \"\n> +\t\t\t\t  \"instead, which does the same thing.\"));\n>   \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n>   \t\t\toptions.rebase_cousins = 1;\n>   \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n"},{"id":"473125","messageId":"8366d23f-c53e-5edf-490b-e8990c9ebf28@dunelm.org.uk","threadId":"59293","inReplyTo":"CAMMLpeTPNoDK9r-e9KMwfYzdG+iyuVwxQGaWHUL-ngD+-r48FQ@mail.gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-07T15:07:57Z","receivedAt":"2023-03-07T15:14:00Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 06/03/2023 17:36, Alex Henrie wrote:\n> On Mon, Mar 6, 2023 at 9:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> Please have a look at the email I sent before sending v6:\n> https://lore.kernel.org/git/CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com/\n\nThanks for the link, I'd missed that email.\n\n> In that email I tried to explain why I didn't incorporate your three\n> suggestions on v5. Please let me know if it still isn't clear.\n\nI'll take a look in the next couple of days and let you know\n\nThanks\n\nPhillip\n\n> -Alex\n"},{"id":"473126","messageId":"5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk","threadId":"59293","inReplyTo":"CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-07T16:23:34Z","receivedAt":"2023-03-07T16:25:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 04/03/2023 23:24, Alex Henrie wrote:\n> On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> \n>> On 25/02/2023 18:03, Alex Henrie wrote:\n> \n>>> +rebase.merges::\n>>> +     Whether and how to set the `--rebase-merges` option by default. Can\n>>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n>>> +     true is equivalent to `--rebase-merges` without an argument, setting to\n>>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to\n>>> +     `--rebase-merges` with that value as its argument, and setting to false\n>>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n>>> +     command line without an argument overrides a `rebase.merges=false`\n>>> +     configuration but does not override other values of `rebase.merge`.\n>>\n>> I'm still not clear why the commandline doesn't override the config in\n>> all cases as is our usual practice. After all if the user has set\n>> rebase.merges then they don't need to pass --rebase-merges unless they\n>> want to override the config.\n> \n> Given the current push to turn rebase-merges on by default, it seems\n> likely that rebase-cousins will also be turned on by default at some\n> point after that.\n\nIt is good to try and future proof things but this seems rather \nhypothetical. I don't really see how the choice of whether \n--rebase-merges is turned on by default is related to the choice of \nwhether or not to rebase cousins by default. It is worth noting that the \ndefault was changed to from rebase-cousins to no-rebase-cousins early in \nthe development of --rebase-merges[1] as it was felt to be less surprising.\n\n> There will be a warning about the default changing,\n> and we'll want to let users suppress that warning by setting\n> rebase.rebaseMerges=rebase-cousins. It would then be very confusing if\n> a --rebase-merges from some old alias continued to mean\n> --rebase-merges=no-rebase-cousins when the user expects it to start\n> behaving as though the default has already changed.\n\nBut aren't you breaking those aliases now when \nrebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It \nseems like we're breaking things now to avoid a hypothetical future \nchange breaking them which does not seem like the right trade off to me.\n\nIt also does not fit with the way other optional arguments interact with \ntheir associated config setting. For example \"git branch/checkout/switch \n--track\" and branch.autoSetupMerge. If the optional argument to --track \nis omitted it defaults to \"direct\" irrespective of the config.\n\n[1] \nhttps://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/\n\n> I will rephrase the documentation in v6 to make it more clear that the\n> absence of a specific value on the command line does not clobber a\n> specific value set in the configuration, as Glen suggested.\n\nThe documentation in v6 is certainly quite clear on this point.\n\n>>> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '\n>>> +     test_config rebase.merges rebase-cousins &&\n>>> +     git checkout -b override-config-rebase-cousins main &&\n>>> +     git rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n>>> +     test_cmp_graph HEAD^.. <<-\\EOF\n>>> +     *   Merge the topic branch '\\''onebranch'\\''\n>>> +     |\\\n>>> +     | * D\n>>> +     | * G\n>>> +     o | H\n>>> +     |/\n>>> +     o A\n>>> +     EOF\n>>> +'\n>>\n>> I'm not sure this test adds much value, it is hard to see what kind of\n>> regression would allow the others to pass but not this one.\n> \n> I was worried that I or someone else would forget to explicitly set\n> rebase_cousins to 0 when no-rebase-cousins is given on the command\n> line, assuming that it is already 0 because that is the default. The\n> test makes me feel better, but I am happy to remove it if you still\n> think it's overkill.\n\nGiven we're using the same code to parse the command line argument and \nthe config setting and we have a test for \nrebase.rebaseMerges=no-rebase-cousins I think we could drop it.\n\n>>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n>>> +     test_config rebase.merges false &&\n>>> +     git checkout -b override-config-merges-false E &&\n>>> +     before=\"$(git rev-parse --verify HEAD)\" &&\n>>> +     test_tick &&\n>>> +     git rebase --rebase-merges C &&\n>>> +     test_cmp_rev HEAD $before\n>>\n>> This test passes if the rebase does nothing, maybe pass --force and\n>> check the graph?\n> \n> The rebase is supposed to do nothing here.\n\nIt's not doing nothing though - it is rebasing the branch, it just \nhappens that everything fast-forwards so HEAD ends up unchanged. My \npoint is that this test should verify the branch has been rebased. Maybe \nyou could check the reflog message for HEAD@{0} is\n\n\trebase (finish): returning to refs/heads/override-config-merges-false\n\n> Checking that the commit\n> hash is the same is just a quick way to check that the entire graph is\n> the same. What more would be checked by checking the graph instead of\n> the hash?\n\nBy using --force and checking the graph you check that the rebase \nactually happened.\n\nThanks for working on this\n\nPhillip\n\n> Thanks for the feedback,\n> \n> -Alex\n"},{"id":"473146","messageId":"xmqqcz5kza3b.fsf@gitster.g","threadId":"59293","inReplyTo":"7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-07T18:32:08Z","receivedAt":"2023-03-07T18:43:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> +rebase.rebaseMerges::\n>> +\tWhether and how to set the `--rebase-merges` option by default. Can\n>> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n>> +\ttrue is equivalent to `--rebase-merges` without an argument,\n>\n> This is a bit picky but how can rebase.rebaseMerges=true be equivalent\n> to --rebase-merges without an argument when the behavior of\n> --rebase-merges without an argument depends on the value of\n> rebase.rebaseMerges?\n\nTrue.  I think the configuration is meant to give (when set to\nsomething other than Boolean) the default value to the option\n\"--rebase-merges\" that is given without value, so setting to false\nshould be a no-op (a command line option would override it if given,\nand if there is no command line option, --rebase-merges is not used\nby default), setting it to a specific value between cousin choices\nwould give --rebase-merges=<value> unless --no-rebase-merges is\ngiven, but setting it to true here makes the result undefined,\nunless the built-in default between cousin choices is described\nhere.\n\n\"Setting to true is equivalent to setting to no-rebase-cousins\" and\n\"Setting to false is a no-op but accepted only for completeness\",\nperhaps?\n\n>> setting to\n>> +\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n>> +\t`--rebase-merges` with that value as its argument, and setting to false\n>> +\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n>> +\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n>> +\tconfiguration, but the absence of a specific rebase-merges mode on the\n>> +\tcommand line does not counteract a specific mode set in the configuration.\n>\n> I may not agree the with the design choice but the documentation here\n> and below is very clear about the behavior of --rebase-merges without\n> an argument which is good.\n\nIs it?  rebase.rebaseMerges=true does not give \"a specific mode set\nin the configuration\", so it still is unclear what --rebase-merges\nshould do in that case.  Unless what it means to set it to true is\ndescribed, as you pointed out above, that is.\n\n>> +`no-rebase-cousins`. If the mode is not specified on the command line or in\n>> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n\nThis side could describe what setting it to \"true\" means, but it is\na separate page so it would be more friendly to readers to cover it\non both pages.\n\n> I think we need\n>\n> \t} else {\n> \t\topts->rebase_cousins = 0;\n> \t}\n>\n> here. Otherwise if rebase.rebaseMerges is set twice we wont follow the\n> usual \"last config wins\" convention. For example\n>\n> \t[rebase]\n> \t\trebaseMerges=rebase-cousins\n> \t\trebaseMerges=true\n>\n> will result in us unexpectedly rebasing cousins\n\nThanks for a careful review.\n\n"},{"id":"473161","messageId":"kl6l7cvsi006.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230305050709.68736-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-08T00:02:01Z","receivedAt":"2023-03-08T00:02:33Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> The purpose of the new option is to accommodate users who would like\n> --rebase-merges to be on by default and to facilitate turning on\n> --rebase-merges by default without configuration in a future version of\n> Git.\n>\n> Name the new option rebase.rebaseMerges, even though it is a little\n> redundant, for consistency with the name of the command line option and\n> to be clear when scrolling through values in the [rebase] section of\n> .gitconfig.\n\nThis rationale makes sense to me.\n\n> In the future, the default rebase-merges mode may change from\n> no-rebase-cousins to rebase-cousins.\n\nI suspect a more likely future would be that the default is to rebase\n'evil' merges instead of trying to recreate merge commits, but of\ncourse, the important thing is that we promote the default, not what the\ndefault will be...\n\n>                                      Support setting rebase.rebaseMerges\n> to the nonspecific value \"true\" for users who do not need or want to\n> care about the default changing in the future. Similarly, for users who\n> have --rebase-merges in an alias and want to get the future behavior\n> now, use the specific rebase-merges mode from the config if a specific\n> mode is not given on the command line.\n\nso this rationale makes sense to me too :)\n\n> @@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '\n>  \tEOF\n>  '\n>  \n> +test_expect_success '--rebase-merges=\"\" is deprecated' '\n> +\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n> +\tgrep deprecated actual\n> +'\n\nI believe this used to be on 2/3, i.e.\n\n  https://lore.kernel.org/git/20230225180325.796624-3-alexhenrie24@gmail.com/\n\nbut your cover letter suggests that it was removed. Mechanical error?\n\nThe rest of changes look good (besides what others have spotted).\n"},{"id":"473162","messageId":"kl6l4jqwhznr.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-08T00:09:28Z","receivedAt":"2023-03-08T00:09:35Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)\n>>   \t\treturn 0;\n>>   \t}\n>>   \n>> +\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n>> +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n>> +\t\tif (opts->config_rebase_merges < 0) {\n>> +\t\t\topts->config_rebase_merges = 1;\n>> +\t\t\tparse_rebase_merges_value(opts, value);\n>> +\t\t}\n>\n> I think we need\n>\n> \t} else {\n> \t\topts->rebase_cousins = 0;\n> \t}\n>\n> here. Otherwise if rebase.rebaseMerges is set twice we wont follow the \n> usual \"last config wins\" convention. For example\n>\n> \t[rebase]\n> \t\trebaseMerges=rebase-cousins\n> \t\trebaseMerges=true\n>\n> will result in us unexpectedly rebasing cousins\n\nAh, good catch.\n\nAn alternative might be to use the \"lookup\" functions (e.g.\nrepo_config_get_maybe_bool()), which already implement \"last one wins\"\nsemantics. It's a bigger change, but it may be easier for future readers\nto understand. I don't have a strong opinion on which is the 'better'\napproach.\n"},{"id":"473163","messageId":"kl6l1qm0hzgq.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-08T00:13:41Z","receivedAt":"2023-03-08T00:14:01Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> Changes from v5:\n> - Add commit message note about --no-rebase-merges having always worked\n> - Add commit message note about the test for --no-rebase-merges being\n>   somewhat contrived\n> - Rephrase the documentation to avoid using the phrase \"By default\" with\n>   two different meanings, and in so doing clarify that --rebase-merges\n>   without an argument is not the same as --no-rebase-merges and not\n>   passing --rebase-merges is not the same as passing\n>   --rebase-merges=no-rebase-cousins\n> - Add commit message note about keeping --rebase-merges=\"\" for now out\n>   of an abundance of caution\n> - Rephrase the warning about --rebase-merges=\"\" to recommend\n>   --rebase-merges without an argument instead, and clarify that that\n>   will do the same thing\n> - Remove the test for --rebase-merges=\"\"\n> - Rename the config option from rebase.merges to rebase.rebaseMerges and\n>   explain why in the commit message\n> - Add commit message note about why \"true\" is a valid option for\n>   rebase.rebaseMerges and why --rebase-merges without an argument does\n>   not clobber the mode specified in rebase.rebaseMerges\n> - Rephrase the documentation to clarify that --rebase-merges without an\n>   argument does not clobber the mode specified in rebase.rebaseMerges\n> - Add another test for incompatible options\n\nThis version addresses all of the concerns I had with the previous\nversion. Thanks!\n\nBesides the concerns that other reviewers raised and a possible\nmechanical error, I don't have any outstanding concerns. I'd be happy\nto see this merged when those are resolved :)\n"},{"id":"473231","messageId":"878rg6zxrq.fsf@osv.gnss.ru","threadId":"59293","inReplyTo":"20230305050709.68736-2-alexhenrie24@gmail.com","subject":"Re: [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2023-03-08T22:25:13Z","receivedAt":"2023-03-08T22:25:20Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n[...]\n\n> +When rebasing merges, there are two modes: `rebase-cousins` and\n> +`no-rebase-cousins`. If the mode is not specified, it defaults to\n> +`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n> +`<upstream>` as direct ancestor will keep their original branch point, i.e.\n\nI realize this is in fact unchanged from the original, so is not exactly\nmaterial of these series, but what is the meaning of \"direct ancestor\"?\nIs it just \"parent\"?\n\n> +commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n> +option will keep their original ancestry by default.\n\nExcluded when --ancestry-path is applied to what commit, exactly? To the\ncommit being considered for rebase, or to the <upstream>, or to the\n'fork_point'?\n\nPlease notice that rebase claims to operate either on <upstream>..HEAD or\n'fork_point'..HEAD range, and --ancestry-path without arguments applies\nto the left commit of the range when used in \"git log\".\n\nLooks like some clarifications are needed here, even though maybe not in\nthese series?\n\nThanks,\n-- Sergey Organov\n"},{"id":"473435","messageId":"CAMMLpeTUbG+b89acan-GXGS4H=J7aQupbK8zdxwNg__U_We2dw@mail.gmail.com","threadId":"59293","inReplyTo":"5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T20:57:28Z","receivedAt":"2023-03-12T20:58:11Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Tue, Mar 7, 2023 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> On 04/03/2023 23:24, Alex Henrie wrote:\n> > On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >\n> >> On 25/02/2023 18:03, Alex Henrie wrote:\n> >\n> >>> +rebase.merges::\n> >>> +     Whether and how to set the `--rebase-merges` option by default. Can\n> >>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> >>> +     true is equivalent to `--rebase-merges` without an argument, setting to\n> >>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to\n> >>> +     `--rebase-merges` with that value as its argument, and setting to false\n> >>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> >>> +     command line without an argument overrides a `rebase.merges=false`\n> >>> +     configuration but does not override other values of `rebase.merge`.\n> >>\n> >> I'm still not clear why the commandline doesn't override the config in\n> >> all cases as is our usual practice. After all if the user has set\n> >> rebase.merges then they don't need to pass --rebase-merges unless they\n> >> want to override the config.\n> >\n> > Given the current push to turn rebase-merges on by default, it seems\n> > likely that rebase-cousins will also be turned on by default at some\n> > point after that.\n>\n> It is good to try and future proof things but this seems rather\n> hypothetical. I don't really see how the choice of whether\n> --rebase-merges is turned on by default is related to the choice of\n> whether or not to rebase cousins by default. It is worth noting that the\n> default was changed to from rebase-cousins to no-rebase-cousins early in\n> the development of --rebase-merges[1] as it was felt to be less surprising.\n\n> [1]\n> https://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/\n\nThank you for sharing that link. Even though I got the tests right, I\ngot confused and started thinking that rebase-cousins was a more\nthorough version of rebase-merges. In fact, they do opposite things:\nrebase-merges tries to preserve the graph and rebase-cousins\nintentionally restructures the graph. In my opinion using the word\n\"rebase\" in the names of both options was another unfortunate UI\ndecision, but I understand the difference now.\n\n> > There will be a warning about the default changing,\n> > and we'll want to let users suppress that warning by setting\n> > rebase.rebaseMerges=rebase-cousins. It would then be very confusing if\n> > a --rebase-merges from some old alias continued to mean\n> > --rebase-merges=no-rebase-cousins when the user expects it to start\n> > behaving as though the default has already changed.\n>\n> But aren't you breaking those aliases now when\n> rebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It\n> seems like we're breaking things now to avoid a hypothetical future\n> change breaking them which does not seem like the right trade off to me.\n>\n> It also does not fit with the way other optional arguments interact with\n> their associated config setting. For example \"git branch/checkout/switch\n> --track\" and branch.autoSetupMerge. If the optional argument to --track\n> is omitted it defaults to \"direct\" irrespective of the config.\n\nWhat I really don't want is to paint ourselves into a corner. You're\nright that it's unlikely that the default will ever change from\nno-rebase-cousins to rebase-cousins; I was mistaken. However, Glen\nthinks that in the future we might have some kind of\nrebase-evil-merges mode as well, and that that might become the\ndefault. If we don't let the rebase.rebaseMerges config value control\nthe default behavior of --rebase-merges without an argument on the\ncommand line, we would have to introduce a separate config option for\nthe transition, which would be ugly.\n\nMore voices would be helpful here. Does anyone else have an opinion on\nhow likely it is that the default rebase-merges mode will change in\nthe future? Or on whether rebase.rebaseMerges should be allowed to\naffect --rebase-merges in order to facilitate such a change?\n\n> >>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n> >>> +     test_config rebase.merges false &&\n> >>> +     git checkout -b override-config-merges-false E &&\n> >>> +     before=\"$(git rev-parse --verify HEAD)\" &&\n> >>> +     test_tick &&\n> >>> +     git rebase --rebase-merges C &&\n> >>> +     test_cmp_rev HEAD $before\n> >>\n> >> This test passes if the rebase does nothing, maybe pass --force and\n> >> check the graph?\n> >\n> > The rebase is supposed to do nothing here.\n>\n> It's not doing nothing though - it is rebasing the branch, it just\n> happens that everything fast-forwards so HEAD ends up unchanged. My\n> point is that this test should verify the branch has been rebased. Maybe\n> you could check the reflog message for HEAD@{0} is\n>\n>         rebase (finish): returning to refs/heads/override-config-merges-false\n>\n> > Checking that the commit\n> > hash is the same is just a quick way to check that the entire graph is\n> > the same. What more would be checked by checking the graph instead of\n> > the hash?\n>\n> By using --force and checking the graph you check that the rebase\n> actually happened.\n\nI got the impression that people like that not checking the graph\nitself (or the reflog) makes the tests more concise, but I don't care\nmuch either way. For what it's worth, the way I did it matches the\nexisting tests in the file. If you can find at least one other person\nwho thinks that it ought to change for this patch series to be\naccepted, and no one else objects, I'll change it.\n\n> Thanks for working on this\n\nYou're welcome; hopefully we can get the remaining details ironed out quickly.\n\n-Alex\n"},{"id":"473436","messageId":"CAMMLpeR_ZN1cGJdeRQVVay+Vp1fQ80mgGKm2fOhPmhBE4CyqUg@mail.gmail.com","threadId":"59293","inReplyTo":"xmqqcz5kza3b.fsf@gitster.g","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:01:56Z","receivedAt":"2023-03-12T21:02:40Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Tue, Mar 7, 2023 at 11:32 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n>\n> >> +rebase.rebaseMerges::\n> >> +    Whether and how to set the `--rebase-merges` option by default. Can\n> >> +    be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> >> +    true is equivalent to `--rebase-merges` without an argument,\n> >\n> > This is a bit picky but how can rebase.rebaseMerges=true be equivalent\n> > to --rebase-merges without an argument when the behavior of\n> > --rebase-merges without an argument depends on the value of\n> > rebase.rebaseMerges?\n>\n> True.  I think the configuration is meant to give (when set to\n> something other than Boolean) the default value to the option\n> \"--rebase-merges\" that is given without value, so setting to false\n> should be a no-op (a command line option would override it if given,\n> and if there is no command line option, --rebase-merges is not used\n> by default), setting it to a specific value between cousin choices\n> would give --rebase-merges=<value> unless --no-rebase-merges is\n> given, but setting it to true here makes the result undefined,\n> unless the built-in default between cousin choices is described\n> here.\n>\n> \"Setting to true is equivalent to setting to no-rebase-cousins\" and\n> \"Setting to false is a no-op but accepted only for completeness\",\n> perhaps?\n\nA false in the local configuration overrides a true in the global\nconfiguration, so I wouldn't call \"false\" a no-op. But it would be\nfine to say that \"true\" is equivalent to no-rebase-cousins (and then\nchange the documentation for \"true\" if and when the default changes in\nthe future).\n\n> >> +`no-rebase-cousins`. If the mode is not specified on the command line or in\n> >> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n>\n> This side could describe what setting it to \"true\" means, but it is\n> a separate page so it would be more friendly to readers to cover it\n> on both pages.\n\nThe longer the documentation is, the less likely people are to read\nany of it. I don't really want to repeat the detailed explanation that\nis in git-config.txt, but I see that other places in git-rebase.txt\nlink to git-config.txt, and having a link seems like a good idea. Fair\nenough?\n\n-Alex\n"},{"id":"473437","messageId":"CAMMLpeSYYDX2Jjet0wZFuMtpq03QZYHNr310-pHp8BsJ=izL_w@mail.gmail.com","threadId":"59293","inReplyTo":"kl6l7cvsi006.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:03:55Z","receivedAt":"2023-03-12T21:04:35Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Tue, Mar 7, 2023 at 5:02 PM Glen Choo <chooglen@google.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> > +test_expect_success '--rebase-merges=\"\" is deprecated' '\n> > +     git rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n> > +     grep deprecated actual\n> > +'\n>\n> I believe this used to be on 2/3, i.e.\n>\n>   https://lore.kernel.org/git/20230225180325.796624-3-alexhenrie24@gmail.com/\n>\n> but your cover letter suggests that it was removed. Mechanical error?\n\nI removed it from the second patch but accidentally added it back in\nthe third patch. Thanks for catching that.\n\n-Alex\n"},{"id":"473438","messageId":"20230312210456.92364-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230305050709.68736-1-alexhenrie24@gmail.com","subject":"[PATCH v7 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:04:53Z","receivedAt":"2023-03-12T21:07:21Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"This patch series introduces a rebase.rebaseMerges config option to\naccommodate users who would like --rebase-merges to be on by default and\nto facilitate turning on --rebase-merges by default without\nconfiguration in a future version of Git. It also cleans up and\ndocuments the behavior of the --rebase-merges command line option to\navoid confusion about how the config option and the command line option\ninteract.\n\nChanges from v6:\n- Don't say that the default rebase-merges mode will likely change to\n  rebase-cousins (although it might change to something else)\n- In git-config.txt, say that rebase.rebaseMerges=true is equivalent\n  to --rebase-merges=no-rebase-cousins\n- Add a link from the revised paragraph in git-rebase.txt to the\n  corresponding new section in git-config.txt\n- Clear rebase_cousins if rebase.rebaseMerges is set to true after\n  having been set to rebase-cousins or no-rebase-cousins\n- Actually remove the test for --rebase-merges=\"\"\n- Remove the test for --rebase-merges=no-rebase-cousins overriding\n  rebase.rebaseMerges=rebase-cousins\n\nSuggestions on v6 not incorporated in v7:\n- Make --rebase-merges without an argument clobber the mode specified in\n  rebase.rebaseMerges\n- In the tests, pass --force and check the graph itself or the reflog\n  instead of checking that the graph has not changed by checking that\n  the commit hash has not changed\n- Add --rebase-merges=on as a synonym of\n  --rebase-merges=no-rebase-cousins and --rebase-merges=off as a synonym\n  of --no-rebase-merges\n- Rewrite the documentation for rebase-cousins to more clearly explain\n  the difference between rebase-cousins and no-rebase-cousins\n\nThanks to Phillip, Junio, Glen, and Sergey for your feedback on v6.\n\nAlex Henrie (3):\n  rebase: add documentation and test for --no-rebase-merges\n  rebase: deprecate --rebase-merges=\"\"\n  rebase: add a config option for --rebase-merges\n\n Documentation/config/rebase.txt        | 11 ++++\n Documentation/git-rebase.txt           | 20 ++++---\n builtin/rebase.c                       | 76 +++++++++++++++++++-------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 58 ++++++++++++++++++++\n 5 files changed, 156 insertions(+), 26 deletions(-)\n\nRange-diff against v6:\n1:  bf08c03ba7 = 1:  3aee0c2277 rebase: add documentation and test for --no-rebase-merges\n2:  26f98b8400 = 2:  e57843d8b5 rebase: deprecate --rebase-merges=\"\"\n3:  402365256c ! 3:  b0c1a4dcb2 rebase: add a config option for --rebase-merges\n    @@ Commit message\n         .gitconfig.\n     \n         In the future, the default rebase-merges mode may change from\n    -    no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges\n    -    to the nonspecific value \"true\" for users who do not need or want to\n    -    care about the default changing in the future. Similarly, for users who\n    -    have --rebase-merges in an alias and want to get the future behavior\n    -    now, use the specific rebase-merges mode from the config if a specific\n    -    mode is not given on the command line.\n    +    no-rebase-cousins to some other mode that doesn't exist yet. Support\n    +    setting rebase.rebaseMerges to the nonspecific value \"true\" for users\n    +    who do not need or want to care about the default changing in the\n    +    future. Similarly, for users who have --rebase-merges in an alias and\n    +    want to get the future behavior now, use the specific rebase-merges mode\n    +    from the config if a specific mode is not given on the command line.\n     \n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n    @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::\n     +rebase.rebaseMerges::\n     +\tWhether and how to set the `--rebase-merges` option by default. Can\n     +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n    -+\ttrue is equivalent to `--rebase-merges` without an argument, setting to\n    -+\t`rebase-cousins` or `no-rebase-cousins` is equivalent to\n    -+\t`--rebase-merges` with that value as its argument, and setting to false\n    -+\tis equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n    ++\ttrue or to `no-rebase-cousins` is equivalent to\n    ++\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n    ++\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n    ++\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n     +\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n     +\tconfiguration, but the absence of a specific rebase-merges mode on the\n     +\tcommand line does not counteract a specific mode set in the configuration.\n    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n     -such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n     -specified).\n     +`no-rebase-cousins`. If the mode is not specified on the command line or in\n    -+the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.\n    -+In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n    ++the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or\n    ++\"CONFIGURATION\" below), it defaults to `no-rebase-cousins`. In\n    ++`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n     +ancestor will keep their original branch point, i.e. commits that would be\n     +excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n     +original ancestry by default. In `rebase-cousins` mode, such commits are\n    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n     +\t\tif (opts->config_rebase_merges < 0) {\n     +\t\t\topts->config_rebase_merges = 1;\n     +\t\t\tparse_rebase_merges_value(opts, value);\n    -+\t\t}\n    ++\t\t} else\n    ++\t\t\topts->rebase_cousins = 0;\n     +\t\treturn 0;\n     +\t}\n     +\n    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske\n      \tEOF\n      '\n      \n    -+test_expect_success '--rebase-merges=\"\" is deprecated' '\n    -+\tgit rebase --rebase-merges=\"\" HEAD^ 2>actual &&\n    -+\tgrep deprecated actual\n    -+'\n    -+\n     +test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n     +\ttest_config rebase.rebaseMerges rebase-cousins &&\n     +\tgit checkout -b config-rebase-cousins main &&\n    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske\n     +\tEOF\n     +'\n     +\n    -+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '\n    -+\ttest_config rebase.rebaseMerges rebase-cousins &&\n    -+\tgit checkout -b override-config-rebase-cousins main &&\n    -+\tgit rebase --rebase-merges=no-rebase-cousins HEAD^ &&\n    -+\ttest_cmp_graph HEAD^.. <<-\\EOF\n    -+\t*   Merge the topic branch '\\''onebranch'\\''\n    -+\t|\\\n    -+\t| * D\n    -+\t| * G\n    -+\to | H\n    -+\t|/\n    -+\to A\n    -+\tEOF\n    -+'\n    -+\n     +test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n     +\ttest_config rebase.rebaseMerges false &&\n     +\tgit checkout -b override-config-merges-false E &&\n-- \n2.39.2\n\n"},{"id":"473439","messageId":"20230312210456.92364-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230312210456.92364-1-alexhenrie24@gmail.com","subject":"[PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:04:54Z","receivedAt":"2023-03-12T21:07:23Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"As far as I can tell, --no-rebase-merges has always worked, but has\nnever been documented. It is especially important to document it before\na rebase.rebaseMerges option is introduced so that users know how to\noverride the config option on the command line. It's also important to\nclarify that --rebase-merges without an argument is not the same as\n--no-rebase-merges and not passing --rebase-merges is not the same as\npassing --rebase-merges=no-rebase-cousins.\n\nA test case is necessary to make sure that --no-rebase-merges keeps\nworking after its code is refactored in the following patches of this\nseries. The test case is a little contrived: It's unlikely that a user\nwould type both --rebase-merges and --no-rebase-merges at the same time.\nHowever, if an alias is defined which includes --rebase-merges, the user\nmight decide to add --no-rebase-merges to countermand that part of the\nalias but leave alone other flags set by the alias.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt | 18 +++++++++++-------\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 21 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..4e57a87624 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n-By default, or when `no-rebase-cousins` was specified, commits which do not\n-have `<upstream>` as direct ancestor will keep their original branch point,\n-i.e. commits that would be excluded by linkgit:git-log[1]'s\n-`--ancestry-path` option will keep their original ancestry by default. If\n-the `rebase-cousins` mode is turned on, such commits are instead rebased\n-onto `<upstream>` (or `<onto>`, if specified).\n+When rebasing merges, there are two modes: `rebase-cousins` and\n+`no-rebase-cousins`. If the mode is not specified, it defaults to\n+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n+`<upstream>` as direct ancestor will keep their original branch point, i.e.\n+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n+option will keep their original ancestry by default. In `rebase-cousins` mode,\n+such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n+specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.39.2\n\n"},{"id":"473440","messageId":"20230312210456.92364-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230312210456.92364-1-alexhenrie24@gmail.com","subject":"[PATCH v7 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:04:55Z","receivedAt":"2023-03-12T21:07:24Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges without an argument. Deprecate that syntax to avoid\nconfusion when a rebase.rebaseMerges config option is introduced, where\nrebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n\nIt is not likely that anyone is actually using this syntax, but just in\ncase, deprecate the empty string argument instead of dropping support\nfor it immediately.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 6635f10d52..c36ddc0050 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (rebase_merges) {\n \t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-- \n2.39.2\n\n"},{"id":"473441","messageId":"20230312210456.92364-4-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230312210456.92364-1-alexhenrie24@gmail.com","subject":"[PATCH v7 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-12T21:04:56Z","receivedAt":"2023-03-12T21:07:33Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate turning on\n--rebase-merges by default without configuration in a future version of\nGit.\n\nName the new option rebase.rebaseMerges, even though it is a little\nredundant, for consistency with the name of the command line option and\nto be clear when scrolling through values in the [rebase] section of\n.gitconfig.\n\nIn the future, the default rebase-merges mode may change from\nno-rebase-cousins to some other mode that doesn't exist yet. Support\nsetting rebase.rebaseMerges to the nonspecific value \"true\" for users\nwho do not need or want to care about the default changing in the\nfuture. Similarly, for users who have --rebase-merges in an alias and\nwant to get the future behavior now, use the specific rebase-merges mode\nfrom the config if a specific mode is not given on the command line.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt        | 11 ++++\n Documentation/git-rebase.txt           | 18 +++---\n builtin/rebase.c                       | 80 ++++++++++++++++++--------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 48 ++++++++++++++++\n 5 files changed, 143 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..98e8f193d4 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.rebaseMerges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue or to `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n+\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n+\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n+\tconfiguration, but the absence of a specific rebase-merges mode on the\n+\tcommand line does not counteract a specific mode set in the configuration.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4e57a87624..7ff02f474b 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,16 +537,18 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.rebaseMerges` config option and a previous\n+\t`--rebase-merges`.\n +\n When rebasing merges, there are two modes: `rebase-cousins` and\n-`no-rebase-cousins`. If the mode is not specified, it defaults to\n-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n-`<upstream>` as direct ancestor will keep their original branch point, i.e.\n-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n-option will keep their original ancestry by default. In `rebase-cousins` mode,\n-such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n-specified).\n+`no-rebase-cousins`. If the mode is not specified on the command line or in\n+the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or\n+\"CONFIGURATION\" below), it defaults to `no-rebase-cousins`. In\n+`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n+ancestor will keep their original branch point, i.e. commits that would be\n+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n+original ancestry by default. In `rebase-cousins` mode, such commits are\n+instead rebased onto `<upstream>` (or `<onto>`, if specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex c36ddc0050..593db9e53f 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -123,6 +123,7 @@ struct rebase_options {\n \tint fork_point;\n \tint update_refs;\n \tint config_autosquash;\n+\tint config_rebase_merges;\n \tint config_update_refs;\n };\n \n@@ -140,6 +141,8 @@ struct rebase_options {\n \t\t.allow_empty_message = 1,               \\\n \t\t.autosquash = -1,                       \\\n \t\t.config_autosquash = -1,                \\\n+\t\t.rebase_merges = -1,                    \\\n+\t\t.config_rebase_merges = -1,             \\\n \t\t.update_refs = -1,                      \\\n \t\t.config_update_refs = -1,               \\\n \t}\n@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 0;\n+\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 1;\n+\telse\n+\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -800,6 +813,16 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n+\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->config_rebase_merges < 0) {\n+\t\t\topts->config_rebase_merges = 1;\n+\t\t\tparse_rebase_merges_value(opts, value);\n+\t\t} else\n+\t\t\topts->rebase_cousins = 0;\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.updaterefs\")) {\n \t\topts->config_update_refs = git_config_bool(var, value);\n \t\treturn 0;\n@@ -980,6 +1003,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\toptions->rebase_merges = !unset;\n+\n+\tif (arg) {\n+\t\tif (!*arg) {\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n+\t\t\treturn 0;\n+\t\t}\n+\t\tparse_rebase_merges_value(options, arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1035,7 +1079,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1137,10 +1180,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1436,21 +1478,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n-\t\t\t\t  \"argument is deprecated and will stop \"\n-\t\t\t\t  \"working in a future version of Git. Use \"\n-\t\t\t\t  \"--rebase-merges without an argument \"\n-\t\t\t\t  \"instead, which does the same thing.\"));\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n-\t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n-\n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\n \t\t\tstrvec_push(&options.git_am_opts,\n@@ -1513,13 +1540,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tbreak;\n \n \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n-\t\t\tif (is_merge(&options))\n-\t\t\t\tdie(_(\"apply options and merge options \"\n-\t\t\t\t\t  \"cannot be used together\"));\n-\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n+\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n+\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n+\t\t\telse if (is_merge(&options))\n+\t\t\t\tdie(_(\"apply options and merge options \"\n+\t\t\t\t\t  \"cannot be used together\"));\n \t\t\telse\n \t\t\t\toptions.type = REBASE_APPLY;\n \t\t}\n@@ -1530,6 +1559,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n \n+\tif (options.rebase_merges == 1)\n+\t\timply_merge(&options, \"--rebase-merges\");\n+\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n+\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n+\n \tif (options.autosquash == 1)\n \t\timply_merge(&options, \"--autosquash\");\n \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\ndiff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\nindex 4711b37a28..2eba00bdf5 100755\n--- a/t/t3422-rebase-incompatible-options.sh\n+++ b/t/t3422-rebase-incompatible-options.sh\n@@ -85,6 +85,11 @@ test_rebase_am_only () {\n \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git rebase $opt --rebase-merges A\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git rebase $opt --update-refs A\n@@ -101,6 +106,12 @@ test_rebase_am_only () {\n \t\tgrep -e --no-autosquash err\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n+\t\tgrep -e --no-rebase-merges err\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n@@ -113,6 +124,12 @@ test_rebase_am_only () {\n \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n \t\"\n \n+\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n+\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n+\t\tgit checkout B^0 &&\n+\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n+\t\"\n+\n \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n \t\tgit checkout B^0 &&\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..a8d9297224 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,54 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n+\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n+\ttest_config rebase.rebaseMerges false &&\n+\tgit checkout -b override-config-merges-false E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b no-override-config-rebase-cousins main &&\n+\tgit rebase --rebase-merges HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.39.2\n\n"},{"id":"473456","messageId":"f8dc5bd3-b325-1e11-6e2d-6d202271f9ff@dunelm.org.uk","threadId":"59293","inReplyTo":"CAMMLpeTUbG+b89acan-GXGS4H=J7aQupbK8zdxwNg__U_We2dw@mail.gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-13T14:20:46Z","receivedAt":"2023-03-13T14:20:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 12/03/2023 20:57, Alex Henrie wrote:\n> On Tue, Mar 7, 2023 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> \n>> On 04/03/2023 23:24, Alex Henrie wrote:\n>>> On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>>\n>>>> On 25/02/2023 18:03, Alex Henrie wrote:\n>>>\n>>>>> +rebase.merges::\n>>>>> +     Whether and how to set the `--rebase-merges` option by default. Can\n>>>>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n>>>>> +     true is equivalent to `--rebase-merges` without an argument, setting to\n>>>>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to\n>>>>> +     `--rebase-merges` with that value as its argument, and setting to false\n>>>>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n>>>>> +     command line without an argument overrides a `rebase.merges=false`\n>>>>> +     configuration but does not override other values of `rebase.merge`.\n>>>>\n>>>> I'm still not clear why the commandline doesn't override the config in\n>>>> all cases as is our usual practice. After all if the user has set\n>>>> rebase.merges then they don't need to pass --rebase-merges unless they\n>>>> want to override the config.\n>>>\n>>> Given the current push to turn rebase-merges on by default, it seems\n>>> likely that rebase-cousins will also be turned on by default at some\n>>> point after that.\n>>\n>> It is good to try and future proof things but this seems rather\n>> hypothetical. I don't really see how the choice of whether\n>> --rebase-merges is turned on by default is related to the choice of\n>> whether or not to rebase cousins by default. It is worth noting that the\n>> default was changed to from rebase-cousins to no-rebase-cousins early in\n>> the development of --rebase-merges[1] as it was felt to be less surprising.\n> \n>> [1]\n>> https://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/\n> \n> Thank you for sharing that link. Even though I got the tests right, I\n> got confused and started thinking that rebase-cousins was a more\n> thorough version of rebase-merges. In fact, they do opposite things:\n> rebase-merges tries to preserve the graph and rebase-cousins\n> intentionally restructures the graph. In my opinion using the word\n> \"rebase\" in the names of both options was another unfortunate UI\n> decision, but I understand the difference now.\n> \n>>> There will be a warning about the default changing,\n>>> and we'll want to let users suppress that warning by setting\n>>> rebase.rebaseMerges=rebase-cousins. It would then be very confusing if\n>>> a --rebase-merges from some old alias continued to mean\n>>> --rebase-merges=no-rebase-cousins when the user expects it to start\n>>> behaving as though the default has already changed.\n>>\n>> But aren't you breaking those aliases now when\n>> rebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It\n>> seems like we're breaking things now to avoid a hypothetical future\n>> change breaking them which does not seem like the right trade off to me.\n>>\n>> It also does not fit with the way other optional arguments interact with\n>> their associated config setting. For example \"git branch/checkout/switch\n>> --track\" and branch.autoSetupMerge. If the optional argument to --track\n>> is omitted it defaults to \"direct\" irrespective of the config.\n >\n> What I really don't want is to paint ourselves into a corner.\n\nNor do I but we already seem to be in some kind of corner as what you're \nproposing breaks the status quo and the existing convention that command \nline options override config settings.\n\n> You're\n> right that it's unlikely that the default will ever change from\n> no-rebase-cousins to rebase-cousins; I was mistaken. However, Glen\n> thinks that in the future we might have some kind of\n> rebase-evil-merges mode as well, and that that might become the\n> default. If we don't let the rebase.rebaseMerges config value control\n> the default behavior of --rebase-merges without an argument on the\n> command line, we would have to introduce a separate config option for\n> the transition, which would be ugly.\n\nI'm optimistic that Elijah's work on rebasing evil merges will allow us \nto improve --rebase-merges. I expect that we'll enable it by default \nonce it is merged. I do not think that we should add another argument \nfor --rebase-merges to disable it though as that would be orthogonal to \n\"rebase-cousins\" and \"no-rebase-cousins\" which control what commits get \nrebased not how merges are rebased. If users want to disable the better \nrebasing of merges we'll need to add a --remerge option or something \nlike that.\n\n> More voices would be helpful here. Does anyone else have an opinion on\n> how likely it is that the default rebase-merges mode will change in\n> the future? Or on whether rebase.rebaseMerges should be allowed to\n> affect --rebase-merges in order to facilitate such a change?\n\nGetting other's views would indeed be helpful\n\nBest Wishes\n\nPhillip\n\n\n>>>>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '\n>>>>> +     test_config rebase.merges false &&\n>>>>> +     git checkout -b override-config-merges-false E &&\n>>>>> +     before=\"$(git rev-parse --verify HEAD)\" &&\n>>>>> +     test_tick &&\n>>>>> +     git rebase --rebase-merges C &&\n>>>>> +     test_cmp_rev HEAD $before\n>>>>\n>>>> This test passes if the rebase does nothing, maybe pass --force and\n>>>> check the graph?\n>>>\n>>> The rebase is supposed to do nothing here.\n>>\n>> It's not doing nothing though - it is rebasing the branch, it just\n>> happens that everything fast-forwards so HEAD ends up unchanged. My\n>> point is that this test should verify the branch has been rebased. Maybe\n>> you could check the reflog message for HEAD@{0} is\n>>\n>>          rebase (finish): returning to refs/heads/override-config-merges-false\n>>\n>>> Checking that the commit\n>>> hash is the same is just a quick way to check that the entire graph is\n>>> the same. What more would be checked by checking the graph instead of\n>>> the hash?\n>>\n>> By using --force and checking the graph you check that the rebase\n>> actually happened.\n> \n> I got the impression that people like that not checking the graph\n> itself (or the reflog) makes the tests more concise, but I don't care\n> much either way. For what it's worth, the way I did it matches the\n> existing tests in the file. If you can find at least one other person\n> who thinks that it ought to change for this patch series to be\n> accepted, and no one else objects, I'll change it.\n> \n>> Thanks for working on this\n> \n> You're welcome; hopefully we can get the remaining details ironed out quickly.\n> \n> -Alex\n"},{"id":"473457","messageId":"CAMP44s268c22-_+=Nzw0LBWG-=mt3nUBEOP-4MGoRpB6_w1JYw@mail.gmail.com","threadId":"59293","inReplyTo":"f8dc5bd3-b325-1e11-6e2d-6d202271f9ff@dunelm.org.uk","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-03-13T16:12:48Z","receivedAt":"2023-03-13T16:13:15Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Mon, Mar 13, 2023 at 9:38 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 12/03/2023 20:57, Alex Henrie wrote:\n\n> > More voices would be helpful here. Does anyone else have an opinion on\n> > how likely it is that the default rebase-merges mode will change in\n> > the future? Or on whether rebase.rebaseMerges should be allowed to\n> > affect --rebase-merges in order to facilitate such a change?\n>\n> Getting other's views would indeed be helpful\n\nMy view is that we shouldn't make assumptions about what is going to\nhappen in the future. Most of the UI design warts of Git come\nprecisely from the fact that somebody in the past did not think\nsomething in the future would happen, and when it does the excuse to\nnot do what's sensible in the UI is \"it's too late now\".\n\nI would vote for whatever is more flexible to whatever happens in the\nfuture, not something that seems to make sense now, but adds yet more\ninertia and later on becomes yet another wart in the list of \"oh, if\nonly we had considered that back in 2023\".\n\nCheers.\n\n-- \nFelipe Contreras\n"},{"id":"473473","messageId":"xmqqa60gtox8.fsf@gitster.g","threadId":"59293","inReplyTo":"CAMMLpeTUbG+b89acan-GXGS4H=J7aQupbK8zdxwNg__U_We2dw@mail.gmail.com","subject":"Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-13T19:46:27Z","receivedAt":"2023-03-13T19:46:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> What I really don't want is to paint ourselves into a corner. You're\n> right that it's unlikely that the default will ever change from\n> no-rebase-cousins to rebase-cousins; I was mistaken. However, Glen\n> thinks that in the future we might have some kind of\n> rebase-evil-merges mode as well, and that that might become the\n> default. If we don't let the rebase.rebaseMerges config value control\n> the default behavior of --rebase-merges without an argument on the\n> command line, we would have to introduce a separate config option for\n> the transition, which would be ugly.\n>\n> More voices would be helpful here. Does anyone else have an opinion on\n> how likely it is that the default rebase-merges mode will change in\n> the future? Or on whether rebase.rebaseMerges should be allowed to\n> affect --rebase-merges in order to facilitate such a change?\n\nAny such \"opinion\" about the future, or the belief that we can\nsomehow predict one, would be wrong anyway.  So I am not sure\nsoliciting more voices to look into crystal balls would help all\nthat much.  Having said that...\n\nThe default rebase-merges behaviour may be improved, but changing it\nin a totally backward incompatible way _without_ a carefully prepared\ntransition strategy will be very unlikely, simply because existing\nend users would not allow us to.  I do not offhand know if the\nconfiguration variable(s) you propose would serve as a good mechanism\nto help such transition.\n\nThanks.\n"},{"id":"473512","messageId":"CAMMLpeRGEETraueJTTV0tJSsycNYF24YX8n6h-pMp87VcCRJtQ@mail.gmail.com","threadId":"59293","inReplyTo":"kl6l7cvsi006.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-15T02:52:11Z","receivedAt":"2023-03-15T02:52:55Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Tue, Mar 7, 2023 at 5:02 PM Glen Choo <chooglen@google.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n\n> > In the future, the default rebase-merges mode may change from\n> > no-rebase-cousins to rebase-cousins.\n>\n> I suspect a more likely future would be that the default is to rebase\n> 'evil' merges instead of trying to recreate merge commits, but of\n> course, the important thing is that we promote the default, not what the\n> default will be...\n\nGlen, do you have any more thoughts? At this point, the only thing\nthat's keeping me from implementing Phillip's request to make\n--rebase-merges without an argument clobber rebase.rebaseMerges is\nyour suspicion that we might need to change the default rebase-merges\nmode in the future, and I assume that we would want to use the\nrebase.rebaseMerges config option to facilitate the transition.\n\n-Alex\n"},{"id":"473600","messageId":"kl6lzg8cvby2.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"CAMMLpeRGEETraueJTTV0tJSsycNYF24YX8n6h-pMp87VcCRJtQ@mail.gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-16T17:32:53Z","receivedAt":"2023-03-16T17:33:12Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n>> > In the future, the default rebase-merges mode may change from\n>> > no-rebase-cousins to rebase-cousins.\n>>\n>> I suspect a more likely future would be that the default is to rebase\n>> 'evil' merges instead of trying to recreate merge commits, but of\n>> course, the important thing is that we promote the default, not what the\n>> default will be...\n>\n> Glen, do you have any more thoughts? At this point, the only thing\n> that's keeping me from implementing Phillip's request to make\n> --rebase-merges without an argument clobber rebase.rebaseMerges is\n> your suspicion that we might need to change the default rebase-merges\n> mode in the future, and I assume that we would want to use the\n> rebase.rebaseMerges config option to facilitate the transition.\n\n(Sorry for the late reply)\n\nAh, I don't really have more thoughts on the matter. I am fairly\nconfident that we would _like_ to change the default to rebase 'evil'\nmerges, but I don't know how likely that will be.\n\nPerhaps it would help to enumerate the rules to see if it is too\nconfusing or not?\n\nThe behaviors we can tweak are:\n\n- Whether to rebase merges or not (true, false, specified mode, or\n  default)\n- What mode to use when rebasing merges (specified mode or default)\n\nAnd the sources are either CLI or config, with CLI always overriding\nconfig.\n\nShould we rebase a merge?\n\n- If neither CLI or config tells us whether or not to rebase a merge,\n  default to \"don't rebase merges\".\n- If one of CLI or config tells us whether or not to rebase a merge,\n  respect it.\n- If both CLI or config tell us whether or not to rebase a merge,\n  respect CLI and ignore config.\n\nWhat mode should we use?\n\n- If neither CLI or config tells us what mode to use, default to\n  \"no-rebase-cousins\" (or whatever default we decide).\n- If one of CLI or config tells us what mode to use, respect it.\n- If both CLI or config tell us what mode to use, respect CLI and ignore\n  config.\n\nIf users cleanly separate the two concepts, I think it is quite clear.\n(I'm not advocating for this approach, but) e.g. if we pretend that each\nbehavior were configured separately, like:\n\n--[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]\n\nI don't think there would be any confusion. (Having --rebase-merges-mode\nbe a no-op without --rebase-merges is probably even more confusing to\nusers, plus this would break backwards compatibility, so I don't think\nthis is a good idea at all.)\n\nYour doc patch explains the rules pretty clearly, but perhaps it doesn't\nexplain this mental model clearly enough, hence the confusion. If we\ndon't find a good way to communicate this (I think it is clear, but\nother reviewers seem yet unconvinced), I wouldn't mind taking Phillip's\nsuggestion to have \"--rebase-merges\" override\n\"rebase.rebaseMerges='specific-mode'\".\n\n>\n> -Alex\n"},{"id":"473604","messageId":"CAMP44s3hH0Gw71UAm10Os=6YJ4RAoAJxC3exN_jekMy6-JRsVg@mail.gmail.com","threadId":"59293","inReplyTo":"kl6lzg8cvby2.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-03-16T18:11:28Z","receivedAt":"2023-03-16T18:11:58Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Thu, Mar 16, 2023 at 11:57 AM Glen Choo <chooglen@google.com> wrote:\n\n> If users cleanly separate the two concepts, I think it is quite clear.\n> (I'm not advocating for this approach, but) e.g. if we pretend that each\n> behavior were configured separately, like:\n>\n> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]\n>\n> I don't think there would be any confusion.\n\nNot being conversant with these options I agree the above isn't confusing.\n\n> (Having --rebase-merges-mode\n> be a no-op without --rebase-merges is probably even more confusing to\n> users, plus this would break backwards compatibility, so I don't think\n> this is a good idea at all.)\n\nI don't find it confusing. And how would it break backwards\ncompatibility if --rebase-merges-mode doesn't exist now?\n\n-- \nFelipe Contreras\n"},{"id":"473605","messageId":"CAMMLpeR4x0_u=JGnWQ1xvBeVBXBw7VAgLTWgvHdMFDZLrcy1pA@mail.gmail.com","threadId":"59293","inReplyTo":"kl6lzg8cvby2.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-16T20:27:21Z","receivedAt":"2023-03-16T20:28:03Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Thu, Mar 16, 2023 at 11:32 AM Glen Choo <chooglen@google.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> >> > In the future, the default rebase-merges mode may change from\n> >> > no-rebase-cousins to rebase-cousins.\n> >>\n> >> I suspect a more likely future would be that the default is to rebase\n> >> 'evil' merges instead of trying to recreate merge commits, but of\n> >> course, the important thing is that we promote the default, not what the\n> >> default will be...\n> >\n> > Glen, do you have any more thoughts? At this point, the only thing\n> > that's keeping me from implementing Phillip's request to make\n> > --rebase-merges without an argument clobber rebase.rebaseMerges is\n> > your suspicion that we might need to change the default rebase-merges\n> > mode in the future, and I assume that we would want to use the\n> > rebase.rebaseMerges config option to facilitate the transition.\n>\n> (Sorry for the late reply)\n>\n> Ah, I don't really have more thoughts on the matter. I am fairly\n> confident that we would _like_ to change the default to rebase 'evil'\n> merges, but I don't know how likely that will be.\n>\n> Perhaps it would help to enumerate the rules to see if it is too\n> confusing or not?\n>\n> The behaviors we can tweak are:\n>\n> - Whether to rebase merges or not (true, false, specified mode, or\n>   default)\n> - What mode to use when rebasing merges (specified mode or default)\n>\n> And the sources are either CLI or config, with CLI always overriding\n> config.\n>\n> Should we rebase a merge?\n>\n> - If neither CLI or config tells us whether or not to rebase a merge,\n>   default to \"don't rebase merges\".\n> - If one of CLI or config tells us whether or not to rebase a merge,\n>   respect it.\n> - If both CLI or config tell us whether or not to rebase a merge,\n>   respect CLI and ignore config.\n>\n> What mode should we use?\n>\n> - If neither CLI or config tells us what mode to use, default to\n>   \"no-rebase-cousins\" (or whatever default we decide).\n> - If one of CLI or config tells us what mode to use, respect it.\n> - If both CLI or config tell us what mode to use, respect CLI and ignore\n>   config.\n>\n> If users cleanly separate the two concepts, I think it is quite clear.\n> (I'm not advocating for this approach, but) e.g. if we pretend that each\n> behavior were configured separately, like:\n>\n> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]\n>\n> I don't think there would be any confusion. (Having --rebase-merges-mode\n> be a no-op without --rebase-merges is probably even more confusing to\n> users, plus this would break backwards compatibility, so I don't think\n> this is a good idea at all.)\n>\n> Your doc patch explains the rules pretty clearly, but perhaps it doesn't\n> explain this mental model clearly enough, hence the confusion. If we\n> don't find a good way to communicate this (I think it is clear, but\n> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's\n> suggestion to have \"--rebase-merges\" override\n> \"rebase.rebaseMerges='specific-mode'\".\n\nI got the impression that everyone, including Phillip,[1] already\nagrees that the proposed documentation is clear about the interaction\nbetween the config option and the command line option. However, it is\na little weird when you consider that other flags with optional\narguments, like `git branch --track`, unconditionally override their\ncorresponding config options.[2]\n\nLet me ask a different but related question: If we add a\nrebase-evil-merges mode, do you think that would be orthogonal to the\nrebase-cousins mode?\n\n-Alex\n\n[1] https://lore.kernel.org/git/7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk/\n[2] https://lore.kernel.org/git/5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk/\n"},{"id":"473612","messageId":"kl6lwn3guxrv.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"CAMMLpeR4x0_u=JGnWQ1xvBeVBXBw7VAgLTWgvHdMFDZLrcy1pA@mail.gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-16T22:39:00Z","receivedAt":"2023-03-16T22:39:16Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n>> Your doc patch explains the rules pretty clearly, but perhaps it doesn't\n>> explain this mental model clearly enough, hence the confusion. If we\n>> don't find a good way to communicate this (I think it is clear, but\n>> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's\n>> suggestion to have \"--rebase-merges\" override\n>> \"rebase.rebaseMerges='specific-mode'\".\n>\n> I got the impression that everyone, including Phillip,[1] already\n> agrees that the proposed documentation is clear about the interaction\n> between the config option and the command line option. However, it is\n> a little weird when you consider that other flags with optional\n> arguments, like `git branch --track`, unconditionally override their\n> corresponding config options.[2]\n\nAh, I didn't consider other options like `git branch --track`. I haven't\nlooked into what is the norm, but I think we should follow it (whatever\nit is).\n\nIf other reviewers have a strong idea of what this norm is, I am happy\nto defer to them. If not, I can look into it given some time.\n\n> Let me ask a different but related question: If we add a\n> rebase-evil-merges mode, do you think that would be orthogonal to the\n> rebase-cousins mode?\n\nI am not an expert on this, so perhaps my opinion isn't that important\n;)\n\nMy understanding is that `[no-]rebase-cousins` affects which commits get\nrebased and onto where, whereas `rebase-evil-merges` would affect how\nthe merge commits are generated (by rebasing the evil or by simply\nrecreating the merges). From that perspective, it seems like yes, the\ntwo would be orthogonal.\n\nHm. Maybe this means that we'd be introducing a new option, and that my\nhunch that we would change the default to `rebase-evil-merges` is more\nwrong than I expected.\n\nThough I guess this doesn't really affects what we do with the CLI\noptions _now_, since the discussion is about what we do about defaults,\nand what the default is is quite immaterial.\n\n>\n> -Alex\n>\n> [1] https://lore.kernel.org/git/7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk/\n> [2] https://lore.kernel.org/git/5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk/\n"},{"id":"473613","messageId":"kl6lttykuxfu.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"59293","inReplyTo":"CAMP44s3hH0Gw71UAm10Os=6YJ4RAoAJxC3exN_jekMy6-JRsVg@mail.gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2023-03-16T22:46:13Z","receivedAt":"2023-03-16T22:46:19Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n>> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]\n>>\n>> I don't think there would be any confusion.\n>> [...]\n>> (Having --rebase-merges-mode\n>> be a no-op without --rebase-merges is probably even more confusing to\n>> users, plus this would break backwards compatibility, so I don't think\n>> this is a good idea at all.)\n>\n> I don't find it confusing. And how would it break backwards\n> compatibility if --rebase-merges-mode doesn't exist now?\n\nI meant that for the above example to work, we would need to have\n`--[no-]rebase-merges` as a boolean that says whether we rebase merges at\nall, and `--rebase-merges-mode=[whatever]` would tell us what mode to\nuse _if_ we were rebasing merges. This means that\n`--rebase-merges=not-a-boolean` would become invalid.\n\nWe were in this position before, actually, with `git log -p -m`. The\nconclusion on a recent series [1] is that having such a no-op flag was\nprobably a mistake (and unfortunately, one that is extremely hard to fix\ndue to backwards compatibility requirements), so introducing more no-op\nflags is probably a bad idea :)\n\n[1] https://lore.kernel.org/git/871qn5pyez.fsf@osv.gnss.ru\n"},{"id":"473619","messageId":"CAMP44s04zfDjXRpof5ZBTbTLD5aq5_rQc_74ipDv1N-d-dhtKw@mail.gmail.com","threadId":"59293","inReplyTo":"kl6lttykuxfu.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-03-16T23:48:13Z","receivedAt":"2023-03-16T23:48:29Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Thu, Mar 16, 2023 at 4:46 PM Glen Choo <chooglen@google.com> wrote:\n>\n> Felipe Contreras <felipe.contreras@gmail.com> writes:\n>\n> >> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]\n> >>\n> >> I don't think there would be any confusion.\n> >> [...]\n> >> (Having --rebase-merges-mode\n> >> be a no-op without --rebase-merges is probably even more confusing to\n> >> users, plus this would break backwards compatibility, so I don't think\n> >> this is a good idea at all.)\n> >\n> > I don't find it confusing. And how would it break backwards\n> > compatibility if --rebase-merges-mode doesn't exist now?\n>\n> I meant that for the above example to work, we would need to have\n> `--[no-]rebase-merges` as a boolean that says whether we rebase merges at\n> all, and `--rebase-merges-mode=[whatever]` would tell us what mode to\n> use _if_ we were rebasing merges.\n\nNo, we don't need --no-rebase-merges for --rebase-merges-mode to work.\nIt would be nice, but not necessary.\n\n> This means that\n> `--rebase-merges=not-a-boolean` would become invalid.\n\nNo, it would not become invalid, that's yet another decision to make.\n`--rebase-merges=mode` could become synonymous with `--rebase-merges\n--rebase-merges-mode=mode`. A shortcut.\n\nBecause it's redundant it could be deprecated in the future, but not\nnecessarily invalid.\n\n> We were in this position before, actually, with `git log -p -m`. The\n> conclusion on a recent series [1] is that having such a no-op flag was\n> probably a mistake (and unfortunately, one that is extremely hard to fix\n> due to backwards compatibility requirements),\n\nNo, `git log -m` was a mistake *only* if -p isn't turned on as well,\nwhich is an interface wart that could be fixed today.\n\n> so introducing more no-op flags is probably a bad idea :)\n\nWhether --rebase-merges-mode turns on --rebase-merges by default is a\ndecision that could be made in the future. Just like `git log -m`\nturning on `-p` by default is a decision that could be made in the\nfuture (and probably should).\n\n---\n\nEither way: introducing a new option cannot possibly break backwards\ncompatibility.\n\nCheers.\n\n-- \nFelipe Contreras\n"},{"id":"473681","messageId":"CAMMLpeS3+NUQa2oqpHKVo3yWQNVMgkEXrs4U5_ggvk31yQbezQ@mail.gmail.com","threadId":"59293","inReplyTo":"kl6lwn3guxrv.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-18T05:59:00Z","receivedAt":"2023-03-18T06:00:26Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Thu, Mar 16, 2023 at 4:39 PM Glen Choo <chooglen@google.com> wrote:\n>\n> Alex Henrie <alexhenrie24@gmail.com> writes:\n>\n> >> Your doc patch explains the rules pretty clearly, but perhaps it doesn't\n> >> explain this mental model clearly enough, hence the confusion. If we\n> >> don't find a good way to communicate this (I think it is clear, but\n> >> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's\n> >> suggestion to have \"--rebase-merges\" override\n> >> \"rebase.rebaseMerges='specific-mode'\".\n> >\n> > I got the impression that everyone, including Phillip,[1] already\n> > agrees that the proposed documentation is clear about the interaction\n> > between the config option and the command line option. However, it is\n> > a little weird when you consider that other flags with optional\n> > arguments, like `git branch --track`, unconditionally override their\n> > corresponding config options.[2]\n>\n> Ah, I didn't consider other options like `git branch --track`. I haven't\n> looked into what is the norm, but I think we should follow it (whatever\n> it is).\n>\n> If other reviewers have a strong idea of what this norm is, I am happy\n> to defer to them. If not, I can look into it given some time.\n>\n> > Let me ask a different but related question: If we add a\n> > rebase-evil-merges mode, do you think that would be orthogonal to the\n> > rebase-cousins mode?\n>\n> I am not an expert on this, so perhaps my opinion isn't that important\n> ;)\n>\n> My understanding is that `[no-]rebase-cousins` affects which commits get\n> rebased and onto where, whereas `rebase-evil-merges` would affect how\n> the merge commits are generated (by rebasing the evil or by simply\n> recreating the merges). From that perspective, it seems like yes, the\n> two would be orthogonal.\n>\n> Hm. Maybe this means that we'd be introducing a new option, and that my\n> hunch that we would change the default to `rebase-evil-merges` is more\n> wrong than I expected.\n>\n> Though I guess this doesn't really affects what we do with the CLI\n> options _now_, since the discussion is about what we do about defaults,\n> and what the default is is quite immaterial.\n\nI looked through the code and documentation and found 12 good examples\nof command line flags with an optional argument that always override\ntheir corresponding config options whether or not the optional\nargument is given:\n\ngit -c branch.autoSetupMerge=inherit branch --track mynewbranch\n\ngit -c commit.gpgSign=mykey commit --gpg-sign\n\ngit -c core.sharedRepository=0666 init --shared .\n\ngit -c diff.ignoreSubmodules=untracked diff --ignore-submodules\n\ngit -c diff.submodule=diff diff --submodule\n\ngit -c format.attach=myboundary format-patch --attach HEAD^\n\ngit -c format.from='Dang Git <dang@example.org>' format-patch --from HEAD^\n\ngit -c format.thread=deep format-patch --thread HEAD~3\n\ngit -c gc.pruneExpire=1.week.ago gc --prune\n\ngit -c log.decorate=full log --decorate\n\ngit -c merge.log=1 merge --log mytopicbranch\n\ngit -c pull.rebase=interactive pull --rebase\n\nI found 6 other similar examples where the config option is a\nyes/no/auto trilean, but I don't think those are good examples because\nit makes total sense for a command line flag without an argument to\noverride a nonspecific \"auto\" value in the configuration:\n\ngit -c color.diff=auto diff --color | less\n\ngit -c color.grep=auto grep --color . | less\n\ngit -c color.showbranch=auto show-branch --color master mytopicbranch | less\n\ngit -c color.ui=auto log --color | less\n\ngit -c fetch.recurseSubmodules=on-demand fetch --recurse-submodules\n\ngit -c push.gpgSign=if-asked push --signed\n\nI found 1 good example where the config option does make a difference\nif the optional argument is omitted:\n\ngit -c diff.colorMoved=plain diff --color-moved\n\nAnd I found 1 other similar example, but it's not good a example\nbecause the config option doesn't do anything unless the command line\nflag is specified:\n\ngit -c diff.dirstat=lines diff --dirstat\n\nGiven 12 good examples versus 1 good counterexample, I would say that\nthe established convention is for the command line flag to always\noverride the config option. Please let me know if there are other\nexamples that I missed.\n\nIt's worth noting that the documentation for `git format-patch\n--thread` explicitly says that format.thread takes precedence over\n--thread without an argument, but that is not correct: When I tested\nit, the command line argument always took precedence.\n\nAnother problem is that the documentation for `git pack-objects` does\nnot even mention that --unpack-unreachable has an optional argument,\nand it says that the argument to --cruft-expiration is required when\nit is not.\n\nI'm not going to fix all the documentation problems, at least not\nright now. However, in order to follow the established convention for\nflags with optional arguments, and because we probably aren't going to\nuse rebase.rebaseMerges to facilitate a future change of defaults, I\nwill redo the patch so that --rebase-merges without an argument takes\nprecedence over rebase.rebaseMerges.\n\nThanks for the feedback. I feel more confident about the way forward now.\n\n-Alex\n"},{"id":"473743","messageId":"20230320055955.461138-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230312210456.92364-1-alexhenrie24@gmail.com","subject":"[PATCH v8 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-20T05:59:52Z","receivedAt":"2023-03-20T06:00:10Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"This patch series introduces a rebase.rebaseMerges config option to\naccommodate users who would like --rebase-merges to be on by default and\nto facilitate turning on --rebase-merges by default without\nconfiguration in a future version of Git. It also cleans up and\ndocuments the behavior of the --rebase-merges command line option to\navoid confusion about how the config option and the command line option\ninteract.\n\nChanges from v7:\n- Make --rebase-merges without an argument clobber the mode specified in\n  rebase.rebaseMerges\n- Replace the test for --rebase-merges not overriding\n  rebase.rebaseMerges=rebase-cousins and the test for --rebase-merges\n  overriding rebase.rebaseMerges=false with a single test that\n  --rebase-merges does override rebase.rebaseMerges=rebase-cousins\n\nThanks to Phillip, Felipe, Junio, and Glen for your feedback on v7.\n\nAlex Henrie (3):\n  rebase: add documentation and test for --no-rebase-merges\n  rebase: deprecate --rebase-merges=\"\"\n  rebase: add a config option for --rebase-merges\n\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           | 19 ++++---\n builtin/rebase.c                       | 77 +++++++++++++++++++-------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 44 +++++++++++++++\n 5 files changed, 141 insertions(+), 26 deletions(-)\n\nRange-diff against v7:\n1:  3aee0c2277 = 1:  09fb7c1b74 rebase: add documentation and test for --no-rebase-merges\n2:  e57843d8b5 = 2:  a846716a4a rebase: deprecate --rebase-merges=\"\"\n3:  b0c1a4dcb2 ! 3:  b12a3610ba rebase: add a config option for --rebase-merges\n    @@ Commit message\n         to be clear when scrolling through values in the [rebase] section of\n         .gitconfig.\n     \n    -    In the future, the default rebase-merges mode may change from\n    -    no-rebase-cousins to some other mode that doesn't exist yet. Support\n    -    setting rebase.rebaseMerges to the nonspecific value \"true\" for users\n    -    who do not need or want to care about the default changing in the\n    -    future. Similarly, for users who have --rebase-merges in an alias and\n    -    want to get the future behavior now, use the specific rebase-merges mode\n    -    from the config if a specific mode is not given on the command line.\n    +    Support setting rebase.rebaseMerges to the nonspecific value \"true\" for\n    +    users who don't need to or don't want to learn about the difference\n    +    between rebase-cousins and no-rebase-cousins.\n    +\n    +    Make --rebase-merges without an argument on the command line override\n    +    any value of rebase.rebaseMerges in the configuration, for consistency\n    +    with other command line flags with optional arguments that have an\n    +    associated config option.\n     \n         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n     \n    @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::\n     +\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n     +\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n     +\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n    -+\tcommand line without an argument overrides a `rebase.rebaseMerges=false`\n    -+\tconfiguration, but the absence of a specific rebase-merges mode on the\n    -+\tcommand line does not counteract a specific mode set in the configuration.\n    ++\tcommand line, with or without an argument, overrides any\n    ++\t`rebase.rebaseMerges` configuration.\n     \n      ## Documentation/git-rebase.txt ##\n     @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.\n     +\t`--rebase-merges`.\n      +\n      When rebasing merges, there are two modes: `rebase-cousins` and\n    --`no-rebase-cousins`. If the mode is not specified, it defaults to\n    --`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n    --`<upstream>` as direct ancestor will keep their original branch point, i.e.\n    --commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n    --option will keep their original ancestry by default. In `rebase-cousins` mode,\n    --such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n    --specified).\n    -+`no-rebase-cousins`. If the mode is not specified on the command line or in\n    -+the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or\n    -+\"CONFIGURATION\" below), it defaults to `no-rebase-cousins`. In\n    -+`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct\n    -+ancestor will keep their original branch point, i.e. commits that would be\n    -+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their\n    -+original ancestry by default. In `rebase-cousins` mode, such commits are\n    -+instead rebased onto `<upstream>` (or `<onto>`, if specified).\n    - +\n    - It is currently only possible to recreate the merge commits using the\n    - `ort` merge strategy; different merge strategies can be used only via\n    + `no-rebase-cousins`. If the mode is not specified, it defaults to\n     \n      ## builtin/rebase.c ##\n     @@ builtin/rebase.c: struct rebase_options {\n    @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const cha\n     +\tstruct rebase_options *options = opt->value;\n     +\n     +\toptions->rebase_merges = !unset;\n    ++\toptions->rebase_cousins = 0;\n     +\n     +\tif (arg) {\n     +\t\tif (!*arg) {\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n     -\t\t\t\t\t  \"cannot be used together\"));\n     -\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n     +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n    - \t\t\t\tdie(_(\"apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash\"));\n    + \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n     +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n     +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n      \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske\n     +\tEOF\n     +'\n     +\n    -+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '\n    -+\ttest_config rebase.rebaseMerges false &&\n    -+\tgit checkout -b override-config-merges-false E &&\n    ++test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '\n    ++\ttest_config rebase.rebaseMerges rebase-cousins &&\n    ++\tgit checkout -b override-config-rebase-cousins E &&\n     +\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n     +\ttest_tick &&\n     +\tgit rebase --rebase-merges C &&\n     +\ttest_cmp_rev HEAD $before\n     +'\n    -+\n    -+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '\n    -+\ttest_config rebase.rebaseMerges rebase-cousins &&\n    -+\tgit checkout -b no-override-config-rebase-cousins main &&\n    -+\tgit rebase --rebase-merges HEAD^ &&\n    -+\ttest_cmp_graph HEAD^.. <<-\\EOF\n    -+\t*   Merge the topic branch '\\''onebranch'\\''\n    -+\t|\\\n    -+\t| * D\n    -+\t| * G\n    -+\t|/\n    -+\to H\n    -+\tEOF\n    -+'\n     +\n      test_expect_success 'refs/rewritten/* is worktree-local' '\n      \tgit worktree add wt &&\n-- \n2.40.0\n\n"},{"id":"473744","messageId":"20230320055955.461138-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230320055955.461138-1-alexhenrie24@gmail.com","subject":"[PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-20T05:59:53Z","receivedAt":"2023-03-20T06:00:12Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"As far as I can tell, --no-rebase-merges has always worked, but has\nnever been documented. It is especially important to document it before\na rebase.rebaseMerges option is introduced so that users know how to\noverride the config option on the command line. It's also important to\nclarify that --rebase-merges without an argument is not the same as\n--no-rebase-merges and not passing --rebase-merges is not the same as\npassing --rebase-merges=no-rebase-cousins.\n\nA test case is necessary to make sure that --no-rebase-merges keeps\nworking after its code is refactored in the following patches of this\nseries. The test case is a little contrived: It's unlikely that a user\nwould type both --rebase-merges and --no-rebase-merges at the same time.\nHowever, if an alias is defined which includes --rebase-merges, the user\nmight decide to add --no-rebase-merges to countermand that part of the\nalias but leave alone other flags set by the alias.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt | 18 +++++++++++-------\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 21 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..4e57a87624 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n-By default, or when `no-rebase-cousins` was specified, commits which do not\n-have `<upstream>` as direct ancestor will keep their original branch point,\n-i.e. commits that would be excluded by linkgit:git-log[1]'s\n-`--ancestry-path` option will keep their original ancestry by default. If\n-the `rebase-cousins` mode is turned on, such commits are instead rebased\n-onto `<upstream>` (or `<onto>`, if specified).\n+When rebasing merges, there are two modes: `rebase-cousins` and\n+`no-rebase-cousins`. If the mode is not specified, it defaults to\n+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n+`<upstream>` as direct ancestor will keep their original branch point, i.e.\n+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n+option will keep their original ancestry by default. In `rebase-cousins` mode,\n+such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n+specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.40.0\n\n"},{"id":"473745","messageId":"20230320055955.461138-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230320055955.461138-1-alexhenrie24@gmail.com","subject":"[PATCH v8 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-20T05:59:54Z","receivedAt":"2023-03-20T06:00:16Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges without an argument. Deprecate that syntax to avoid\nconfusion when a rebase.rebaseMerges config option is introduced, where\nrebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n\nIt is not likely that anyone is actually using this syntax, but just in\ncase, deprecate the empty string argument instead of dropping support\nfor it immediately.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex dd31d5ab91..4b3f29a449 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1141,7 +1141,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1439,7 +1439,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (rebase_merges) {\n \t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-- \n2.40.0\n\n"},{"id":"473746","messageId":"20230320055955.461138-4-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230320055955.461138-1-alexhenrie24@gmail.com","subject":"[PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-20T05:59:55Z","receivedAt":"2023-03-20T06:00:18Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate turning on\n--rebase-merges by default without configuration in a future version of\nGit.\n\nName the new option rebase.rebaseMerges, even though it is a little\nredundant, for consistency with the name of the command line option and\nto be clear when scrolling through values in the [rebase] section of\n.gitconfig.\n\nSupport setting rebase.rebaseMerges to the nonspecific value \"true\" for\nusers who don't need to or don't want to learn about the difference\nbetween rebase-cousins and no-rebase-cousins.\n\nMake --rebase-merges without an argument on the command line override\nany value of rebase.rebaseMerges in the configuration, for consistency\nwith other command line flags with optional arguments that have an\nassociated config option.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           |  3 +-\n builtin/rebase.c                       | 81 ++++++++++++++++++--------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 34 +++++++++++\n 5 files changed, 121 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..afaf6dad99 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.rebaseMerges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue or to `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n+\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n+\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line, with or without an argument, overrides any\n+\t`rebase.rebaseMerges` configuration.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4e57a87624..e7b39ad244 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.rebaseMerges` config option and a previous\n+\t`--rebase-merges`.\n +\n When rebasing merges, there are two modes: `rebase-cousins` and\n `no-rebase-cousins`. If the mode is not specified, it defaults to\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 4b3f29a449..fd284e24ab 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -124,6 +124,7 @@ struct rebase_options {\n \tint fork_point;\n \tint update_refs;\n \tint config_autosquash;\n+\tint config_rebase_merges;\n \tint config_update_refs;\n };\n \n@@ -141,6 +142,8 @@ struct rebase_options {\n \t\t.allow_empty_message = 1,               \\\n \t\t.autosquash = -1,                       \\\n \t\t.config_autosquash = -1,                \\\n+\t\t.rebase_merges = -1,                    \\\n+\t\t.config_rebase_merges = -1,             \\\n \t\t.update_refs = -1,                      \\\n \t\t.config_update_refs = -1,               \\\n \t}\n@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 0;\n+\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 1;\n+\telse\n+\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -801,6 +814,16 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n+\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->config_rebase_merges < 0) {\n+\t\t\topts->config_rebase_merges = 1;\n+\t\t\tparse_rebase_merges_value(opts, value);\n+\t\t} else\n+\t\t\topts->rebase_cousins = 0;\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.updaterefs\")) {\n \t\topts->config_update_refs = git_config_bool(var, value);\n \t\treturn 0;\n@@ -981,6 +1004,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\toptions->rebase_merges = !unset;\n+\toptions->rebase_cousins = 0;\n+\n+\tif (arg) {\n+\t\tif (!*arg) {\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n+\t\t\treturn 0;\n+\t\t}\n+\t\tparse_rebase_merges_value(options, arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1036,7 +1081,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1138,10 +1182,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1437,21 +1480,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n-\t\t\t\t  \"argument is deprecated and will stop \"\n-\t\t\t\t  \"working in a future version of Git. Use \"\n-\t\t\t\t  \"--rebase-merges without an argument \"\n-\t\t\t\t  \"instead, which does the same thing.\"));\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n-\t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n-\n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\n \t\t\tstrvec_push(&options.git_am_opts,\n@@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tbreak;\n \n \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n-\t\t\tif (is_merge(&options))\n-\t\t\t\tdie(_(\"apply options and merge options \"\n-\t\t\t\t\t  \"cannot be used together\"));\n-\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n+\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n+\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n+\t\t\telse if (is_merge(&options))\n+\t\t\t\tdie(_(\"apply options and merge options \"\n+\t\t\t\t\t  \"cannot be used together\"));\n \t\t\telse\n \t\t\t\toptions.type = REBASE_APPLY;\n \t\t}\n@@ -1531,6 +1561,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n \n+\tif (options.rebase_merges == 1)\n+\t\timply_merge(&options, \"--rebase-merges\");\n+\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n+\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n+\n \tif (options.autosquash == 1)\n \t\timply_merge(&options, \"--autosquash\");\n \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\ndiff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\nindex 4711b37a28..2eba00bdf5 100755\n--- a/t/t3422-rebase-incompatible-options.sh\n+++ b/t/t3422-rebase-incompatible-options.sh\n@@ -85,6 +85,11 @@ test_rebase_am_only () {\n \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git rebase $opt --rebase-merges A\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git rebase $opt --update-refs A\n@@ -101,6 +106,12 @@ test_rebase_am_only () {\n \t\tgrep -e --no-autosquash err\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n+\t\tgrep -e --no-rebase-merges err\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n@@ -113,6 +124,12 @@ test_rebase_am_only () {\n \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n \t\"\n \n+\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n+\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n+\t\tgit checkout B^0 &&\n+\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n+\t\"\n+\n \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n \t\tgit checkout B^0 &&\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..f03599c63b 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n+\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b override-config-rebase-cousins E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.40.0\n\n"},{"id":"473934","messageId":"f062165f-2e59-67b5-ebff-b029c29dd4d6@dunelm.org.uk","threadId":"59293","inReplyTo":"20230320055955.461138-4-alexhenrie24@gmail.com","subject":"Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-22T16:54:49Z","receivedAt":"2023-03-22T16:55:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 20/03/2023 05:59, Alex Henrie wrote:\n> The purpose of the new option is to accommodate users who would like\n> --rebase-merges to be on by default and to facilitate turning on\n> --rebase-merges by default without configuration in a future version of\n> Git.\n> \n> Name the new option rebase.rebaseMerges, even though it is a little\n> redundant, for consistency with the name of the command line option and\n> to be clear when scrolling through values in the [rebase] section of\n> .gitconfig.\n> \n> Support setting rebase.rebaseMerges to the nonspecific value \"true\" for\n> users who don't need to or don't want to learn about the difference\n> between rebase-cousins and no-rebase-cousins.\n> \n> Make --rebase-merges without an argument on the command line override\n> any value of rebase.rebaseMerges in the configuration, for consistency\n> with other command line flags with optional arguments that have an\n> associated config option.\n\nHurray! Thanks for re-rolling. I've left a couple of comments below, I \nhad hoped this would be the final version but I've realized that the way \nyou've rearranged the error handling for apply and merge options is not \na good idea (see below). Apart from that I'm happy.\n\n> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>\n> ---\n>   Documentation/config/rebase.txt        | 10 ++++\n>   Documentation/git-rebase.txt           |  3 +-\n>   builtin/rebase.c                       | 81 ++++++++++++++++++--------\n>   t/t3422-rebase-incompatible-options.sh | 17 ++++++\n>   t/t3430-rebase-merges.sh               | 34 +++++++++++\n>   5 files changed, 121 insertions(+), 24 deletions(-)\n> \n> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\n> index f19bd0e040..afaf6dad99 100644\n> --- a/Documentation/config/rebase.txt\n> +++ b/Documentation/config/rebase.txt\n> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n>   \n>   rebase.forkPoint::\n>   \tIf set to false set `--no-fork-point` option by default.\n> +\n> +rebase.rebaseMerges::\n> +\tWhether and how to set the `--rebase-merges` option by default. Can\n> +\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n> +\ttrue or to `no-rebase-cousins` is equivalent to\n> +\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n> +\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n> +\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n> +\tcommand line, with or without an argument, overrides any\n> +\t`rebase.rebaseMerges` configuration.\n\nSounds good\n\n> [...]   \n> +\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n> +\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n> +\t\tif (opts->config_rebase_merges < 0) {\n> +\t\t\topts->config_rebase_merges = 1;\n> +\t\t\tparse_rebase_merges_value(opts, value);\n> +\t\t} else\n> +\t\t\topts->rebase_cousins = 0;\n\nThe \"else\" clause should have braces because the \"if\" cause requires \nthem (see Documentation/CodingGuidelines). I don't think it is worth \nre-rolling just for this though.\n\n> [...]   \n> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n> +{\n> +\tstruct rebase_options *options = opt->value;\n> +\n> +\toptions->rebase_merges = !unset;\n> +\toptions->rebase_cousins = 0;\n\nThis makes --rebase-merges without an option override \nrebase.rebaseMerges - good.\n\n > [...]\n> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t\t\tbreak;\n>   \n>   \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n> -\t\t\tif (is_merge(&options))\n> -\t\t\t\tdie(_(\"apply options and merge options \"\n> -\t\t\t\t\t  \"cannot be used together\"));\n\nThis isn't a new change but having thought about it I'm not sure moving \nthis code is a good idea. If the user runs\n\n\tgit -c rebase.updateRefs=true rebase --whitespace=fix --exec \"make test\"\n\nthen instead of getting an message saying that they cannot use apply and \nmerge options together they'll get a message suggesting they pass \n--no-update-refs which wont fix the problem for them.\n\n> -\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>   \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n> +\t\t\telse if (is_merge(&options))\n> +\t\t\t\tdie(_(\"apply options and merge options \"\n> +\t\t\t\t\t  \"cannot be used together\"));\n>   \t\t\telse\n>   \t\t\t\toptions.type = REBASE_APPLY;\n>   \t\t}\n\nBest Wishes\n\nPhillip\n"},{"id":"474024","messageId":"xmqqttyb8fxw.fsf@gitster.g","threadId":"59293","inReplyTo":"f062165f-2e59-67b5-ebff-b029c29dd4d6@dunelm.org.uk","subject":"Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-23T18:45:31Z","receivedAt":"2023-03-23T18:45:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>>   \t\t\t\tbreak;\n>>     \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n>> -\t\t\tif (is_merge(&options))\n>> -\t\t\t\tdie(_(\"apply options and merge options \"\n>> -\t\t\t\t\t  \"cannot be used together\"));\n>\n> This isn't a new change but having thought about it I'm not sure\n> moving this code is a good idea. If the user runs\n>\n> \tgit -c rebase.updateRefs=true rebase --whitespace=fix --exec \"make test\"\n>\n> then instead of getting an message saying that they cannot use apply\n> and merge options together they'll get a message suggesting they pass\n> --no-update-refs which wont fix the problem for them.\n\nHmph.  Instead of dying here, ...\n\n>> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n>> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n>> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>>   \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>>   \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n\n... we get caught here, and the next one is not triggered.\n\n>> +\t\t\telse if (is_merge(&options))\n>> +\t\t\t\tdie(_(\"apply options and merge options \"\n>> +\t\t\t\t\t  \"cannot be used together\"));\n>>   \t\t\telse\n>>   \t\t\t\toptions.type = REBASE_APPLY;\n\nWhat's the reason why \"cannot be used together\" is moved to the last\nin the chain?\n\nThe first two new conditions in this chain try to catch an\ninvocation with some apply-specific command line option\n(e.g. \"--whitespace=fix\") when used with configuration variables\nspecific to the merge-backend (e.g. \"rebase.merges\") and suggest\noverriding the configuration from the command line, and I suspect\nthat the motivation behind this change is that their error messages\nare more specific than the generic \"apply and merge do not mix\".\n\nBut are these two new errors and hints universally a good suggestion\nto make?  I actually think a command line option forcing us to use\nthe apply backend should simply silently ignore (aka \"override\")\nconfiguration variables that take effect only when we are using the\nmerge-backend.  \"git rebase --whitespace=fix --rebase-merges\",\ngiving both from the command line, is making conflicting and\nunsatisfyable request, and it is worth giving an error message.  \n\nBut if you configure rebase.autosquash only because you want to it\nto be in effect for your invocations of \"git rebase -i\", you\nshouldn't have to override it from the command line when you say\n\"git rebase\" or \"git rebase --whitespace=fix\", should you?\n\nThanks.\n"},{"id":"474089","messageId":"ef9be5fb-ba3a-b971-f742-344a25fd5f26@gmx.de","threadId":"59293","inReplyTo":"CAMMLpeTUbG+b89acan-GXGS4H=J7aQupbK8zdxwNg__U_We2dw@mail.gmail.com","subject":"About replaying \"evil\" merges... Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-03-24T14:47:01Z","receivedAt":"2023-03-24T14:47:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Alex,\n\nHeads up: This mail is totally a tangent and is _not_ intended to distract\nfrom the patch series.\n\nOn Sun, 12 Mar 2023, Alex Henrie wrote:\n\n> [...] Glen thinks that in the future we might have some kind of\n> rebase-evil-merges mode as well, and that that might become the default.\n\nWhile it probably won't be the default, like, ever, I can well imagine\nthat there would be a good opportunity to add a config setting to make it\nthe default _for that user_.\n\nAbout implementing that mode... I recently picked up a repair item from my\never-growing TODO list to brush up that \"replay merge conflict resolutions\nand amendments\" idea of Phillip's that I had implemented already four\nyears ago (only to find out that it unfortunately caused a lot of nested\nmerge conflicts when using it on Git for Windows' branch thicket; I tried\nmy best to add a test case demonstrating the problematic behavior).\n\nWhile I had to stop working on it again, I at least left a rebased version\nat https://github.com/gitgitgadget/git/pull/1491 (in case anybody is\ninterested where I wanted to take this PR, I left comments describing the\nchanges I had planned on looking into).\n\nI mention this PR here in this thread not so much to derail the review, or\neven to distract from this patch series (I really detest when that happens\nin reviews of my own patches, so please accept my apology in advance\nshould that happen here, it is _really_ not my intention), but as a point\nin favor of Glen's suspicion that we might get such a mode in the future\nto replay evil merges or merges with resolved conflicts.\n\nCiao,\nJohannes\n"},{"id":"474091","messageId":"830fe4e6-b2a3-d8ad-a415-1d385e759961@dunelm.org.uk","threadId":"59293","inReplyTo":"xmqqttyb8fxw.fsf@gitster.g","subject":"Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-24T14:52:44Z","receivedAt":"2023-03-24T14:52:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Junio\n\nOn 23/03/2023 18:45, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n>>> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>>>    \t\t\t\tbreak;\n>>>      \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n>>> -\t\t\tif (is_merge(&options))\n>>> -\t\t\t\tdie(_(\"apply options and merge options \"\n>>> -\t\t\t\t\t  \"cannot be used together\"));\n>>\n>> This isn't a new change but having thought about it I'm not sure\n>> moving this code is a good idea. If the user runs\n>>\n>> \tgit -c rebase.updateRefs=true rebase --whitespace=fix --exec \"make test\"\n>>\n>> then instead of getting an message saying that they cannot use apply\n>> and merge options together they'll get a message suggesting they pass\n>> --no-update-refs which wont fix the problem for them.\n> \n> Hmph.  Instead of dying here, ...\n> \n>>> +\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>>>    \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n>>> +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n>>> +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>>>    \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>>>    \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n> \n> ... we get caught here, and the next one is not triggered.\n> \n>>> +\t\t\telse if (is_merge(&options))\n>>> +\t\t\t\tdie(_(\"apply options and merge options \"\n>>> +\t\t\t\t\t  \"cannot be used together\"));\n>>>    \t\t\telse\n>>>    \t\t\t\toptions.type = REBASE_APPLY;\n> \n> What's the reason why \"cannot be used together\" is moved to the last\n> in the chain?\n> \n> The first two new conditions in this chain try to catch an\n> invocation with some apply-specific command line option\n> (e.g. \"--whitespace=fix\") when used with configuration variables\n> specific to the merge-backend (e.g. \"rebase.merges\") and suggest\n> overriding the configuration from the command line, and I suspect\n> that the motivation behind this change is that their error messages\n> are more specific than the generic \"apply and merge do not mix\".\n> \n> But are these two new errors and hints universally a good suggestion\n> to make?  I actually think a command line option forcing us to use\n> the apply backend should simply silently ignore (aka \"override\")\n> configuration variables that take effect only when we are using the\n> merge-backend.  \"git rebase --whitespace=fix --rebase-merges\",\n> giving both from the command line, is making conflicting and\n> unsatisfyable request, and it is worth giving an error message.\n> \n> But if you configure rebase.autosquash only because you want to it\n> to be in effect for your invocations of \"git rebase -i\", you\n> shouldn't have to override it from the command line when you say\n> \"git rebase\" or \"git rebase --whitespace=fix\", should you?\n\nI think there are two conflicting viewpoints here which depend on what \none thinks the user wants when they set these configuration variables. \nAs I understand it Elijah's thinking was that if the user set \nrebase.autosquash they'd be surprised when their fixup commits were not \nsquashed by\n\n\tgit rebase --whitespace=fix\n\nand that's why his patch series changed things to error out.\n\nThe other point of view is that the user expects that these \nconfiguration variables to apply only when they are using the \"merge\" \nbackend and so we should not error out.\n\nPersonally I lean a little more towards the latter but I don't feel that \nstrongly about it and can see an argument for having either behavior. I \ndo think we should leave the ordering alone though so the user gets a \nuseful error message in the case of \"git rebase --exec 'make test' \n--whitespace=fix\"\n\nBest Wishes\n\nPhillip\n\n> Thanks.\n"},{"id":"474094","messageId":"8e2dc463-7a17-350a-e22e-184137153c24@gmx.de","threadId":"59293","inReplyTo":"CAMMLpeR4x0_u=JGnWQ1xvBeVBXBw7VAgLTWgvHdMFDZLrcy1pA@mail.gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-03-24T15:05:36Z","receivedAt":"2023-03-24T15:05:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Alex,\n\nOn Thu, 16 Mar 2023, Alex Henrie wrote:\n\n> If we add a rebase-evil-merges mode, do you think that would be\n> orthogonal to the rebase-cousins mode?\n\nThose two concepts are very much orthogonal.\n\nThe rebase-evil-merges mode needs to change the way the `merge` command\noperates, from \"forget everything but the commit message of the original\nmerge commit\" to \"do try to figure out what amendments were made to the\noriginal merge and replay them\".\n\nWhether cousins are rebased or not has everything to do with the `reset`\ncommand, though.\n\nElsewhere in the thread, you mentioned a suspicion that `rebase-cousins`\nshould become the default. However, that would be a radical shift from the\nspirit of the `git rebase [-r] <onto>` command: If `<onto>` is reachable\nfrom `HEAD`, the idea is that an unchanged rebase script will produce the\nidentical commit topology. This is in line with a regular (non-`-r`)\nrebase as long as there are no merge commits between `<onto>` and `HEAD`.\nI am not sure that such a radical shift could ever be an easy sell.\n\nCiao,\nJohannes\n"},{"id":"474147","messageId":"CAMMLpeR+_A6+agb4K9oYanqa1DHX3pDWnZSXjuw7Vubz3jdF+w@mail.gmail.com","threadId":"59293","inReplyTo":"f062165f-2e59-67b5-ebff-b029c29dd4d6@dunelm.org.uk","subject":"Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-25T05:21:15Z","receivedAt":"2023-03-25T05:21:57Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Wed, Mar 22, 2023 at 10:54 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> Hurray! Thanks for re-rolling.\n\nThanks for making sure that we got the UI right!\n\n> On 20/03/2023 05:59, Alex Henrie wrote:\n\n> > +     if (!strcmp(var, \"rebase.rebasemerges\")) {\n> > +             opts->config_rebase_merges = git_parse_maybe_bool(value);\n> > +             if (opts->config_rebase_merges < 0) {\n> > +                     opts->config_rebase_merges = 1;\n> > +                     parse_rebase_merges_value(opts, value);\n> > +             } else\n> > +                     opts->rebase_cousins = 0;\n>\n> The \"else\" clause should have braces because the \"if\" cause requires\n> them (see Documentation/CodingGuidelines). I don't think it is worth\n> re-rolling just for this though.\n\nThanks for pointing out that documentation. I'm going to make a v9\nanyway, and I'll add the braces then. By the way, actions speak louder\nthan words: While writing the patch, I found several examples of the\nbraces being omitted in cases like this in other places in rebase.c,\nso I assumed that that was the preferred style here. If you want to\nencourage people to follow the CodingGuidelines document, the best way\nwould be to clean up the existing code to conform to it.\n\n-Alex\n"},{"id":"474148","messageId":"CAMMLpeRC5s80meD5mB0rmHRYR7iCa_WH0qOTaXvppw2ZHMriWQ@mail.gmail.com","threadId":"59293","inReplyTo":"xmqqttyb8fxw.fsf@gitster.g","subject":"Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-25T05:23:39Z","receivedAt":"2023-03-25T05:24:19Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Thu, Mar 23, 2023 at 12:45 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n>\n> >> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n> >>                              break;\n> >>              if (i >= 0 || options.type == REBASE_APPLY) {\n> >> -                    if (is_merge(&options))\n> >> -                            die(_(\"apply options and merge options \"\n> >> -                                      \"cannot be used together\"));\n> >\n> > This isn't a new change but having thought about it I'm not sure\n> > moving this code is a good idea. If the user runs\n> >\n> >       git -c rebase.updateRefs=true rebase --whitespace=fix --exec \"make test\"\n> >\n> > then instead of getting an message saying that they cannot use apply\n> > and merge options together they'll get a message suggesting they pass\n> > --no-update-refs which wont fix the problem for them.\n>\n> Hmph.  Instead of dying here, ...\n>\n> >> +                    if (options.autosquash == -1 && options.config_autosquash == 1)\n> >>                              die(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n> >> +                    else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n> >> +                            die(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n> >>                      else if (options.update_refs == -1 && options.config_update_refs == 1)\n> >>                              die(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n>\n> ... we get caught here, and the next one is not triggered.\n>\n> >> +                    else if (is_merge(&options))\n> >> +                            die(_(\"apply options and merge options \"\n> >> +                                      \"cannot be used together\"));\n> >>                      else\n> >>                              options.type = REBASE_APPLY;\n>\n> What's the reason why \"cannot be used together\" is moved to the last\n> in the chain?\n>\n> The first two new conditions in this chain try to catch an\n> invocation with some apply-specific command line option\n> (e.g. \"--whitespace=fix\") when used with configuration variables\n> specific to the merge-backend (e.g. \"rebase.merges\") and suggest\n> overriding the configuration from the command line, and I suspect\n> that the motivation behind this change is that their error messages\n> are more specific than the generic \"apply and merge do not mix\".\n\nPhillip specifically asked for `git -c rebase.rebaseMerges=true rebase\n--whitespace=fix` to print \"error: apply options are incompatible with\nrebase.rebaseMerges. Consider adding --no-rebase-merges\".[1] I could\nhave sworn that \"if (is_merge(&options))\" had to be after \"if\n(options.rebase_merges == -1 && options.config_rebase_merges == 1)\" to\nmake that happen, but now it works without that change. I must have\nbeen debugging with some intermediate version that still had\n'imply_merge(&options, \"--rebase-merges\")' before this if chain. I'll\nsend a v9 that puts \"if (is_merge(&options))\" back at the top.\n\nThanks for your attention to detail!\n\n-Alex\n\n[1] https://lore.kernel.org/git/7ba8a92d-8c94-5226-5416-6ed3a8e2e389@dunelm.org.uk/\n"},{"id":"474178","messageId":"87o7og6a2y.fsf@osv.gnss.ru","threadId":"59293","inReplyTo":"CAMMLpeR4x0_u=JGnWQ1xvBeVBXBw7VAgLTWgvHdMFDZLrcy1pA@mail.gmail.com","subject":"Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2023-03-25T16:59:33Z","receivedAt":"2023-03-25T16:59:40Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Alex Henrie <alexhenrie24@gmail.com> writes:\n\n[...]\n\n> Let me ask a different but related question: If we add a\n> rebase-evil-merges mode, do you think that would be orthogonal to the\n> rebase-cousins mode?\n\nThis is the question that I considered as well. To me they look\northogonal, and I'm afraid we will have hard times extending\n--rebase-merges gracefully in a backward-compatible manner.\n\nThe set:\n\n--rebase-merges=remerge\n--rebase-merges=rebase\n--rebase-merges=off\n\nlooks natural to me, but \"cousins\" that affect what is to be rebased (as\nopposed to how, if at all), will apparently get in the way. Besides, I\nrecently ran into similar problem with --diff-merges (when|how)\ndichotomy, and there was no elegant solution found, even though a\nworking one has been implemented, and then there were some discussions\naround.\n\nAlso, I'm not quite sure that these \"cousins\" options make no sense when\nwe in fact flatten history (--no-rebase-merges case). To me it looks\nlike there should have been separate --[no-]rebase-cousins option,\nprovided we do need this choice in the first place, but I definitely\nmight be wrong here as well.\n\nAs a side note, it sounds both unfair and confusing to use\n\"rebase-evil-merges\" term to describe a mode that will actually rebase\n(all the) merges, as neither a merge needs to be 'evil' to be the object\nof rebase operation, nor dedicated mode is necessarily needed to rebase\nspecifically 'evil' merges (whatever they actually are.)\n\nThanks,\n-- Sergey Organov\n"},{"id":"474182","messageId":"20230326030636.2635642-1-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230320055955.461138-1-alexhenrie24@gmail.com","subject":"[PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-26T03:06:33Z","receivedAt":"2023-03-26T03:09:49Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"This patch series introduces a rebase.rebaseMerges config option to\naccommodate users who would like --rebase-merges to be on by default and\nto facilitate turning on --rebase-merges by default without\nconfiguration in a future version of Git. It also cleans up and\ndocuments the behavior of the --rebase-merges command line option to\navoid confusion about how the config option and the command line option\ninteract.\n\nChanges from v8:\n- Add braces around one-line else clause\n- Remove unnecessary change to error message priority\n\nThanks to Phillip, Junio, Johannes and Sergey for your feedback on v8.\n\nAlex Henrie (3):\n  rebase: add documentation and test for --no-rebase-merges\n  rebase: deprecate --rebase-merges=\"\"\n  rebase: add a config option for --rebase-merges\n\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           | 19 ++++---\n builtin/rebase.c                       | 70 ++++++++++++++++++++------\n t/t3422-rebase-incompatible-options.sh | 17 +++++++\n t/t3430-rebase-merges.sh               | 44 ++++++++++++++++\n 5 files changed, 138 insertions(+), 22 deletions(-)\n\nRange-diff against v8:\n1:  09fb7c1b74 = 1:  a22b9d0da2 rebase: add documentation and test for --no-rebase-merges\n2:  a846716a4a = 2:  112fee4833 rebase: deprecate --rebase-merges=\"\"\n3:  b12a3610ba ! 3:  868899cd6d rebase: add a config option for --rebase-merges\n    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n     +\t\tif (opts->config_rebase_merges < 0) {\n     +\t\t\topts->config_rebase_merges = 1;\n     +\t\t\tparse_rebase_merges_value(opts, value);\n    -+\t\t} else\n    ++\t\t} else {\n     +\t\t\topts->rebase_cousins = 0;\n    ++\t\t}\n     +\t\treturn 0;\n     +\t}\n     +\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \t\tif (ignore_whitespace)\n      \t\t\tstrvec_push(&options.git_am_opts,\n     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n    - \t\t\t\tbreak;\n    - \n    - \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n    --\t\t\tif (is_merge(&options))\n    --\t\t\t\tdie(_(\"apply options and merge options \"\n    --\t\t\t\t\t  \"cannot be used together\"));\n    --\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n    -+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n    + \t\t\t\t\t  \"cannot be used together\"));\n    + \t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n      \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n     +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n     +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n      \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n      \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n    -+\t\t\telse if (is_merge(&options))\n    -+\t\t\t\tdie(_(\"apply options and merge options \"\n    -+\t\t\t\t\t  \"cannot be used together\"));\n      \t\t\telse\n    - \t\t\t\toptions.type = REBASE_APPLY;\n    - \t\t}\n     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n      \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n      \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n-- \n2.40.0\n\n"},{"id":"474183","messageId":"20230326030636.2635642-2-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230326030636.2635642-1-alexhenrie24@gmail.com","subject":"[PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-26T03:06:34Z","receivedAt":"2023-03-26T03:09:58Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"As far as I can tell, --no-rebase-merges has always worked, but has\nnever been documented. It is especially important to document it before\na rebase.rebaseMerges option is introduced so that users know how to\noverride the config option on the command line. It's also important to\nclarify that --rebase-merges without an argument is not the same as\n--no-rebase-merges and not passing --rebase-merges is not the same as\npassing --rebase-merges=no-rebase-cousins.\n\nA test case is necessary to make sure that --no-rebase-merges keeps\nworking after its code is refactored in the following patches of this\nseries. The test case is a little contrived: It's unlikely that a user\nwould type both --rebase-merges and --no-rebase-merges at the same time.\nHowever, if an alias is defined which includes --rebase-merges, the user\nmight decide to add --no-rebase-merges to countermand that part of the\nalias but leave alone other flags set by the alias.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/git-rebase.txt | 18 +++++++++++-------\n t/t3430-rebase-merges.sh     | 10 ++++++++++\n 2 files changed, 21 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9a295bcee4..4e57a87624 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.\n \n -r::\n --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::\n+--no-rebase-merges::\n \tBy default, a rebase will simply drop merge commits from the todo\n \tlist, and put the rebased commits into a single, linear branch.\n \tWith `--rebase-merges`, the rebase will instead try to preserve\n \tthe branching structure within the commits that are to be rebased,\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n-\tresolved/re-applied manually.\n+\tresolved/re-applied manually. `--no-rebase-merges` can be used to\n+\tcountermand a previous `--rebase-merges`.\n +\n-By default, or when `no-rebase-cousins` was specified, commits which do not\n-have `<upstream>` as direct ancestor will keep their original branch point,\n-i.e. commits that would be excluded by linkgit:git-log[1]'s\n-`--ancestry-path` option will keep their original ancestry by default. If\n-the `rebase-cousins` mode is turned on, such commits are instead rebased\n-onto `<upstream>` (or `<onto>`, if specified).\n+When rebasing merges, there are two modes: `rebase-cousins` and\n+`no-rebase-cousins`. If the mode is not specified, it defaults to\n+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have\n+`<upstream>` as direct ancestor will keep their original branch point, i.e.\n+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`\n+option will keep their original ancestry by default. In `rebase-cousins` mode,\n+such commits are instead rebased onto `<upstream>` (or `<onto>`, if\n+specified).\n +\n It is currently only possible to recreate the merge commits using the\n `ort` merge strategy; different merge strategies can be used only via\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex fa2a06c19f..d46d9545f2 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '\n \tEOF\n '\n \n+test_expect_success '--no-rebase-merges countermands --rebase-merges' '\n+\tgit checkout -b no-rebase-merges E &&\n+\tgit rebase --rebase-merges --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n test_expect_success 'do not rebase cousins unless asked for' '\n \tgit checkout -b cousins main &&\n \tbefore=\"$(git rev-parse --verify HEAD)\" &&\n-- \n2.40.0\n\n"},{"id":"474184","messageId":"20230326030636.2635642-3-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230326030636.2635642-1-alexhenrie24@gmail.com","subject":"[PATCH v9 2/3] rebase: deprecate --rebase-merges=\"\"","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-26T03:06:35Z","receivedAt":"2023-03-26T03:09:58Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The unusual syntax --rebase-merges=\"\" (that is, --rebase-merges with an\nempty string argument) has been an undocumented synonym of\n--rebase-merges without an argument. Deprecate that syntax to avoid\nconfusion when a rebase.rebaseMerges config option is introduced, where\nrebase.rebaseMerges=\"\" will be equivalent to --no-rebase-merges.\n\nIt is not likely that anyone is actually using this syntax, but just in\ncase, deprecate the empty string argument instead of dropping support\nfor it immediately.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n builtin/rebase.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 5b7b908b66..9448f7d87f 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1141,7 +1141,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n \t\t\tN_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"\"},\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1439,7 +1439,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (rebase_merges) {\n \t\tif (!*rebase_merges)\n-\t\t\t; /* default mode; do nothing */\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n \t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n \t\t\toptions.rebase_cousins = 1;\n \t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-- \n2.40.0\n\n"},{"id":"474185","messageId":"20230326030636.2635642-4-alexhenrie24@gmail.com","threadId":"59293","inReplyTo":"20230326030636.2635642-1-alexhenrie24@gmail.com","subject":"[PATCH v9 3/3] rebase: add a config option for --rebase-merges","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2023-03-26T03:06:36Z","receivedAt":"2023-03-26T03:10:01Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"The purpose of the new option is to accommodate users who would like\n--rebase-merges to be on by default and to facilitate turning on\n--rebase-merges by default without configuration in a future version of\nGit.\n\nName the new option rebase.rebaseMerges, even though it is a little\nredundant, for consistency with the name of the command line option and\nto be clear when scrolling through values in the [rebase] section of\n.gitconfig.\n\nSupport setting rebase.rebaseMerges to the nonspecific value \"true\" for\nusers who don't need to or don't want to learn about the difference\nbetween rebase-cousins and no-rebase-cousins.\n\nMake --rebase-merges without an argument on the command line override\nany value of rebase.rebaseMerges in the configuration, for consistency\nwith other command line flags with optional arguments that have an\nassociated config option.\n\nSigned-off-by: Alex Henrie <alexhenrie24@gmail.com>\n---\n Documentation/config/rebase.txt        | 10 ++++\n Documentation/git-rebase.txt           |  3 +-\n builtin/rebase.c                       | 74 +++++++++++++++++++-------\n t/t3422-rebase-incompatible-options.sh | 17 ++++++\n t/t3430-rebase-merges.sh               | 34 ++++++++++++\n 5 files changed, 118 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt\nindex f19bd0e040..afaf6dad99 100644\n--- a/Documentation/config/rebase.txt\n+++ b/Documentation/config/rebase.txt\n@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::\n \n rebase.forkPoint::\n \tIf set to false set `--no-fork-point` option by default.\n+\n+rebase.rebaseMerges::\n+\tWhether and how to set the `--rebase-merges` option by default. Can\n+\tbe `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to\n+\ttrue or to `no-rebase-cousins` is equivalent to\n+\t`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is\n+\tequivalent to `--rebase-merges=rebase-cousins`, and setting to false is\n+\tequivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the\n+\tcommand line, with or without an argument, overrides any\n+\t`rebase.rebaseMerges` configuration.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 4e57a87624..e7b39ad244 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.\n \tby recreating the merge commits. Any resolved merge conflicts or\n \tmanual amendments in these merge commits will have to be\n \tresolved/re-applied manually. `--no-rebase-merges` can be used to\n-\tcountermand a previous `--rebase-merges`.\n+\tcountermand both the `rebase.rebaseMerges` config option and a previous\n+\t`--rebase-merges`.\n +\n When rebasing merges, there are two modes: `rebase-cousins` and\n `no-rebase-cousins`. If the mode is not specified, it defaults to\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 9448f7d87f..beebeb8f52 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -124,6 +124,7 @@ struct rebase_options {\n \tint fork_point;\n \tint update_refs;\n \tint config_autosquash;\n+\tint config_rebase_merges;\n \tint config_update_refs;\n };\n \n@@ -141,6 +142,8 @@ struct rebase_options {\n \t\t.allow_empty_message = 1,               \\\n \t\t.autosquash = -1,                       \\\n \t\t.config_autosquash = -1,                \\\n+\t\t.rebase_merges = -1,                    \\\n+\t\t.config_rebase_merges = -1,             \\\n \t\t.update_refs = -1,                      \\\n \t\t.config_update_refs = -1,               \\\n \t}\n@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)\n \treturn status ? -1 : 0;\n }\n \n+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)\n+{\n+\tif (!strcmp(\"no-rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 0;\n+\telse if (!strcmp(\"rebase-cousins\", value))\n+\t\toptions->rebase_cousins = 1;\n+\telse\n+\t\tdie(_(\"Unknown rebase-merges mode: %s\"), value);\n+}\n+\n static int rebase_config(const char *var, const char *value, void *data)\n {\n \tstruct rebase_options *opts = data;\n@@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *data)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"rebase.rebasemerges\")) {\n+\t\topts->config_rebase_merges = git_parse_maybe_bool(value);\n+\t\tif (opts->config_rebase_merges < 0) {\n+\t\t\topts->config_rebase_merges = 1;\n+\t\t\tparse_rebase_merges_value(opts, value);\n+\t\t} else {\n+\t\t\topts->rebase_cousins = 0;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"rebase.updaterefs\")) {\n \t\topts->config_update_refs = git_config_bool(var, value);\n \t\treturn 0;\n@@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct rebase_options *options = opt->value;\n+\n+\toptions->rebase_merges = !unset;\n+\toptions->rebase_cousins = 0;\n+\n+\tif (arg) {\n+\t\tif (!*arg) {\n+\t\t\twarning(_(\"--rebase-merges with an empty string \"\n+\t\t\t\t  \"argument is deprecated and will stop \"\n+\t\t\t\t  \"working in a future version of Git. Use \"\n+\t\t\t\t  \"--rebase-merges without an argument \"\n+\t\t\t\t  \"instead, which does the same thing.\"));\n+\t\t\treturn 0;\n+\t\t}\n+\t\tparse_rebase_merges_value(options, arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void NORETURN error_on_missing_default_upstream(void)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n@@ -1036,7 +1082,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tstruct object_id branch_base;\n \tint ignore_whitespace = 0;\n \tconst char *gpg_sign = NULL;\n-\tconst char *rebase_merges = NULL;\n \tstruct string_list strategy_options = STRING_LIST_INIT_NODUP;\n \tstruct object_id squash_onto;\n \tchar *squash_onto_name = NULL;\n@@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t   &options.allow_empty_message,\n \t\t\t   N_(\"allow rebasing commits with empty messages\"),\n \t\t\t   PARSE_OPT_HIDDEN),\n-\t\t{OPTION_STRING, 'r', \"rebase-merges\", &rebase_merges,\n-\t\t\tN_(\"mode\"),\n+\t\tOPT_CALLBACK_F('r', \"rebase-merges\", &options, N_(\"mode\"),\n \t\t\tN_(\"try to rebase merges instead of skipping them\"),\n-\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)\"no-rebase-cousins\"},\n+\t\t\tPARSE_OPT_OPTARG, parse_opt_rebase_merges),\n \t\tOPT_BOOL(0, \"fork-point\", &options.fork_point,\n \t\t\t N_(\"use 'merge-base --fork-point' to refine upstream\")),\n \t\tOPT_STRING('s', \"strategy\", &options.strategy,\n@@ -1437,21 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \tif (options.exec.nr)\n \t\timply_merge(&options, \"--exec\");\n \n-\tif (rebase_merges) {\n-\t\tif (!*rebase_merges)\n-\t\t\twarning(_(\"--rebase-merges with an empty string \"\n-\t\t\t\t  \"argument is deprecated and will stop \"\n-\t\t\t\t  \"working in a future version of Git. Use \"\n-\t\t\t\t  \"--rebase-merges without an argument \"\n-\t\t\t\t  \"instead, which does the same thing.\"));\n-\t\telse if (!strcmp(\"rebase-cousins\", rebase_merges))\n-\t\t\toptions.rebase_cousins = 1;\n-\t\telse if (strcmp(\"no-rebase-cousins\", rebase_merges))\n-\t\t\tdie(_(\"Unknown mode: %s\"), rebase_merges);\n-\t\toptions.rebase_merges = 1;\n-\t\timply_merge(&options, \"--rebase-merges\");\n-\t}\n-\n \tif (options.type == REBASE_APPLY) {\n \t\tif (ignore_whitespace)\n \t\t\tstrvec_push(&options.git_am_opts,\n@@ -1519,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t  \"cannot be used together\"));\n \t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n+\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n+\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n \t\t\telse\n@@ -1531,6 +1562,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n \n+\tif (options.rebase_merges == 1)\n+\t\timply_merge(&options, \"--rebase-merges\");\n+\toptions.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :\n+\t\t\t\t((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);\n+\n \tif (options.autosquash == 1)\n \t\timply_merge(&options, \"--autosquash\");\n \toptions.autosquash = (options.autosquash >= 0) ? options.autosquash :\ndiff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh\nindex 4711b37a28..2eba00bdf5 100755\n--- a/t/t3422-rebase-incompatible-options.sh\n+++ b/t/t3422-rebase-incompatible-options.sh\n@@ -85,6 +85,11 @@ test_rebase_am_only () {\n \t\ttest_must_fail git rebase $opt --reapply-cherry-picks A\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with --rebase-merges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git rebase $opt --rebase-merges A\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with --update-refs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git rebase $opt --update-refs A\n@@ -101,6 +106,12 @@ test_rebase_am_only () {\n \t\tgrep -e --no-autosquash err\n \t\"\n \n+\ttest_expect_success \"$opt incompatible with rebase.rebaseMerges\" \"\n+\t\tgit checkout B^0 &&\n+\t\ttest_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&\n+\t\tgrep -e --no-rebase-merges err\n+\t\"\n+\n \ttest_expect_success \"$opt incompatible with rebase.updateRefs\" \"\n \t\tgit checkout B^0 &&\n \t\ttest_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&\n@@ -113,6 +124,12 @@ test_rebase_am_only () {\n \t\tgit -c rebase.autosquash=true rebase --no-autosquash $opt A\n \t\"\n \n+\ttest_expect_success \"$opt okay with overridden rebase.rebaseMerges\" \"\n+\t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n+\t\tgit checkout B^0 &&\n+\t\tgit -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A\n+\t\"\n+\n \ttest_expect_success \"$opt okay with overridden rebase.updateRefs\" \"\n \t\ttest_when_finished \\\"git reset --hard B^0\\\" &&\n \t\tgit checkout B^0 &&\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex d46d9545f2..f03599c63b 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '\n \tEOF\n '\n \n+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b config-rebase-cousins main &&\n+\tgit rebase HEAD^ &&\n+\ttest_cmp_graph HEAD^.. <<-\\EOF\n+\t*   Merge the topic branch '\\''onebranch'\\''\n+\t|\\\n+\t| * D\n+\t| * G\n+\t|/\n+\to H\n+\tEOF\n+'\n+\n+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '\n+\ttest_config rebase.rebaseMerges no-rebase-cousins &&\n+\tgit checkout -b override-config-no-rebase-cousins E &&\n+\tgit rebase --no-rebase-merges C &&\n+\ttest_cmp_graph C.. <<-\\EOF\n+\t* B\n+\t* D\n+\to C\n+\tEOF\n+'\n+\n+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '\n+\ttest_config rebase.rebaseMerges rebase-cousins &&\n+\tgit checkout -b override-config-rebase-cousins E &&\n+\tbefore=\"$(git rev-parse --verify HEAD)\" &&\n+\ttest_tick &&\n+\tgit rebase --rebase-merges C &&\n+\ttest_cmp_rev HEAD $before\n+'\n+\n test_expect_success 'refs/rewritten/* is worktree-local' '\n \tgit worktree add wt &&\n \tcat >wt/script-from-scratch <<-\\EOF &&\n-- \n2.40.0\n\n"},{"id":"474194","messageId":"19ab92b4-6b83-d788-78f2-43a3ec3ec412@dunelm.org.uk","threadId":"59293","inReplyTo":"20230326030636.2635642-1-alexhenrie24@gmail.com","subject":"Re: [PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-03-26T15:12:03Z","receivedAt":"2023-03-26T15:12:12Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alex\n\nOn 26/03/2023 04:06, Alex Henrie wrote:\n> This patch series introduces a rebase.rebaseMerges config option to\n> accommodate users who would like --rebase-merges to be on by default and\n> to facilitate turning on --rebase-merges by default without\n> configuration in a future version of Git. It also cleans up and\n> documents the behavior of the --rebase-merges command line option to\n> avoid confusion about how the config option and the command line option\n> interact.\n> \n> Changes from v8:\n> - Add braces around one-line else clause\n> - Remove unnecessary change to error message priority\n\nThe range-diff looks good to me. This iteration addresses all of my \noutstanding concerns.\n\nThanks\n\nPhillip\n\n> Thanks to Phillip, Junio, Johannes and Sergey for your feedback on v8.\n> \n> Alex Henrie (3):\n>    rebase: add documentation and test for --no-rebase-merges\n>    rebase: deprecate --rebase-merges=\"\"\n>    rebase: add a config option for --rebase-merges\n> \n>   Documentation/config/rebase.txt        | 10 ++++\n>   Documentation/git-rebase.txt           | 19 ++++---\n>   builtin/rebase.c                       | 70 ++++++++++++++++++++------\n>   t/t3422-rebase-incompatible-options.sh | 17 +++++++\n>   t/t3430-rebase-merges.sh               | 44 ++++++++++++++++\n>   5 files changed, 138 insertions(+), 22 deletions(-)\n> \n> Range-diff against v8:\n> 1:  09fb7c1b74 = 1:  a22b9d0da2 rebase: add documentation and test for --no-rebase-merges\n> 2:  a846716a4a = 2:  112fee4833 rebase: deprecate --rebase-merges=\"\"\n> 3:  b12a3610ba ! 3:  868899cd6d rebase: add a config option for --rebase-merges\n>      @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v\n>       +\t\tif (opts->config_rebase_merges < 0) {\n>       +\t\t\topts->config_rebase_merges = 1;\n>       +\t\t\tparse_rebase_merges_value(opts, value);\n>      -+\t\t} else\n>      ++\t\t} else {\n>       +\t\t\topts->rebase_cousins = 0;\n>      ++\t\t}\n>       +\t\treturn 0;\n>       +\t}\n>       +\n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>        \t\tif (ignore_whitespace)\n>        \t\t\tstrvec_push(&options.git_am_opts,\n>       @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n>      - \t\t\t\tbreak;\n>      -\n>      - \t\tif (i >= 0 || options.type == REBASE_APPLY) {\n>      --\t\t\tif (is_merge(&options))\n>      --\t\t\t\tdie(_(\"apply options and merge options \"\n>      --\t\t\t\t\t  \"cannot be used together\"));\n>      --\t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n>      -+\t\t\tif (options.autosquash == -1 && options.config_autosquash == 1)\n>      + \t\t\t\t\t  \"cannot be used together\"));\n>      + \t\t\telse if (options.autosquash == -1 && options.config_autosquash == 1)\n>        \t\t\t\tdie(_(\"apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash\"));\n>       +\t\t\telse if (options.rebase_merges == -1 && options.config_rebase_merges == 1)\n>       +\t\t\t\tdie(_(\"apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges\"));\n>        \t\t\telse if (options.update_refs == -1 && options.config_update_refs == 1)\n>        \t\t\t\tdie(_(\"apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs\"));\n>      -+\t\t\telse if (is_merge(&options))\n>      -+\t\t\t\tdie(_(\"apply options and merge options \"\n>      -+\t\t\t\t\t  \"cannot be used together\"));\n>        \t\t\telse\n>      - \t\t\t\toptions.type = REBASE_APPLY;\n>      - \t\t}\n>       @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n>        \toptions.update_refs = (options.update_refs >= 0) ? options.update_refs :\n>        \t\t\t     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);\n"},{"id":"474243","messageId":"xmqq8rficfxn.fsf@gitster.g","threadId":"59293","inReplyTo":"19ab92b4-6b83-d788-78f2-43a3ec3ec412@dunelm.org.uk","subject":"Re: [PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-03-27T16:33:24Z","receivedAt":"2023-03-27T16:33:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Hi Alex\n>\n> On 26/03/2023 04:06, Alex Henrie wrote:\n>> This patch series introduces a rebase.rebaseMerges config option to\n>> accommodate users who would like --rebase-merges to be on by default and\n>> to facilitate turning on --rebase-merges by default without\n>> configuration in a future version of Git. It also cleans up and\n>> documents the behavior of the --rebase-merges command line option to\n>> avoid confusion about how the config option and the command line option\n>> interact.\n>> Changes from v8:\n>> - Add braces around one-line else clause\n>> - Remove unnecessary change to error message priority\n>\n> The range-diff looks good to me. This iteration addresses all of my\n> outstanding concerns.\n\nThanks, both.  Let's mark the topic as ready for 'next', then.\n"}]}