Git Mailing List Archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges
@ 2023-02-23  5:34 Alex Henrie
  2023-02-23  5:34 ` [PATCH v4 2/3] rebase: stop accepting --rebase-merges="" Alex Henrie
                   ` (3 more replies)
  0 siblings, 4 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-23  5:34 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt |  4 +++-
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..c98784a0d2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,13 +529,15 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
 By default, or when `no-rebase-cousins` was specified, commits which do not
 have `<upstream>` as direct ancestor will keep their original branch point,
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-23  5:34 [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-02-23  5:34 ` Alex Henrie
  2023-02-24 13:54   ` Johannes Schindelin
  2023-02-23  5:34 ` [PATCH v4 3/3] rebase: add a config option for --rebase-merges Alex Henrie
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-02-23  5:34 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid
confusion when a rebase.merges config option is introduced, where
rebase.merges="" will be equivalent to not passing --rebase-merges.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c         | 6 ++----
 t/t3430-rebase-merges.sh | 6 ++++++
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6635f10d52..b68fc2fbb7 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1437,9 +1437,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		imply_merge(&options, "--exec");
 
 	if (rebase_merges) {
-		if (!*rebase_merges)
-			; /* default mode; do nothing */
-		else if (!strcmp("rebase-cousins", rebase_merges))
+		if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
 			die(_("Unknown mode: %s"), rebase_merges);
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..c73949df18 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,12 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success '--rebase-merges="" is invalid syntax' '
+	echo "fatal: Unknown mode: " >expect &&
+	test_must_fail git rebase --rebase-merges="" HEAD^ 2>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v4 3/3] rebase: add a config option for --rebase-merges
  2023-02-23  5:34 [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-02-23  5:34 ` [PATCH v4 2/3] rebase: stop accepting --rebase-merges="" Alex Henrie
@ 2023-02-23  5:34 ` Alex Henrie
  2023-02-24 13:53   ` Johannes Schindelin
  2023-02-24 14:55   ` Phillip Wood
  2023-02-23 17:28 ` [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Junio C Hamano
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
  3 siblings, 2 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-23  5:34 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate possibly turning
on --rebase-merges by default without configuration in a future version
of Git.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt | 10 ++++
 Documentation/git-rebase.txt    |  3 +-
 builtin/rebase.c                | 47 ++++++++++++----
 t/t3430-rebase-merges.sh        | 96 +++++++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+), 12 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..308baa9dbb 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.merges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true is equivalent to `--rebase-merges` without an argument, setting to
+	`rebase-cousins` or `no-rebase-cousins` is equivalent to
+	`--rebase-merges` with that value as its argument, and setting to false
+	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line without an argument overrides a `rebase.merges=false`
+	configuration but does not override other values of `rebase.merge`.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c98784a0d2..b02f9cbb8c 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.merges` config option and a previous
+	`--rebase-merges`.
 +
 By default, or when `no-rebase-cousins` was specified, commits which do not
 have `<upstream>` as direct ancestor will keep their original branch point,
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b68fc2fbb7..45cf445d42 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -771,6 +771,20 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_merges_value(struct rebase_options *options, const char *value)
+{
+	if (value) {
+		if (!strcmp("no-rebase-cousins", value))
+			options->rebase_cousins = 0;
+		else if (!strcmp("rebase-cousins", value))
+			options->rebase_cousins = 1;
+		else
+			die(_("Unknown mode: %s"), value);
+	}
+
+	options->rebase_merges = 1;
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -815,6 +829,13 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.merges") && value && *value) {
+		opts->rebase_merges = git_parse_maybe_bool(value);
+		if (opts->rebase_merges < 0)
+			parse_merges_value(opts, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.backend")) {
 		return git_config_string(&opts->default_backend, var, value);
 	}
@@ -980,6 +1001,18 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	if (unset)
+		options->rebase_merges = 0;
+	else
+		parse_merges_value(options, arg);
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1035,7 +1068,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1137,10 +1169,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
+	if (options.rebase_merges)
 		imply_merge(&options, "--rebase-merges");
-	}
 
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index c73949df18..d4b0e8fd49 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -284,6 +284,102 @@ test_expect_success '--rebase-merges="" is invalid syntax' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase.merges="" is equivalent to not passing --rebase-merges' '
+	test_config rebase.merges "" &&
+	git checkout -b config-merges-blank E &&
+	git rebase C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
+	test_config rebase.merges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins main &&
+	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	o | H
+	|/
+	o A
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.merges=false' '
+	test_config rebase.merges false &&
+	git checkout -b override-config-merges-false E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b no-override-config-rebase-cousins main &&
+	git rebase --rebase-merges HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '
+	test_config_global rebase.merges true &&
+	test_config rebase.merges false &&
+	git checkout -b override-global-config E &&
+	git rebase C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success 'local rebase.merges="" does not override global rebase.merges=true' '
+	test_config_global rebase.merges no-rebase-cousins &&
+	test_config rebase.merges "" &&
+	git checkout -b no-override-global-config E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase C &&
+	test_cmp_rev HEAD $before
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-23  5:34 [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-02-23  5:34 ` [PATCH v4 2/3] rebase: stop accepting --rebase-merges="" Alex Henrie
  2023-02-23  5:34 ` [PATCH v4 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-02-23 17:28 ` Junio C Hamano
  2023-02-24 13:57   ` Johannes Schindelin
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
  3 siblings, 1 reply; 96+ messages in thread
From: Junio C Hamano @ 2023-02-23 17:28 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, newren, phillip.wood123, Johannes.Schindelin, sorganov

Alex Henrie <alexhenrie24@gmail.com> writes:

> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---

No cover letter to summarize the changes?

Comparing this round with the previous, the only two changes are
that [1/3] lost one test "do not rebase merges unless asked to", and
[2/3] now uses test_must_fail to mark a git command that is expected
to stop.

Looks good, but a rerolled patch series should not force reviewers
to fetch and compare, but give a summary of changes when it is sent
out, to save everybody's time.  Preparing a summary before sending
it out also helps the patch author, too, that all the intended
changes are included in the new round.

Thanks.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges
  2023-02-23  5:34 ` [PATCH v4 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-02-24 13:53   ` Johannes Schindelin
  2023-02-24 17:49     ` Alex Henrie
  2023-02-24 14:55   ` Phillip Wood
  1 sibling, 1 reply; 96+ messages in thread
From: Johannes Schindelin @ 2023-02-24 13:53 UTC (permalink / raw)
  To: Alex Henrie; +Cc: git, tao, gitster, newren, phillip.wood123, sorganov

Hi Alex,

On Wed, 22 Feb 2023, Alex Henrie wrote:

> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index b68fc2fbb7..45cf445d42 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -771,6 +771,20 @@ static int run_specific_rebase(struct rebase_options *opts)
>  	return status ? -1 : 0;
>  }
>
> +static void parse_merges_value(struct rebase_options *options, const char *value)
> +{
> +	if (value) {
> +		if (!strcmp("no-rebase-cousins", value))

If you want to support `rebase.merges=` to imply `no-rebase-cousins`, this
would be the correct place to do it:

		if (!*value || !strcmp("no-rebase-cousins", value))

> +			options->rebase_cousins = 0;
> +		else if (!strcmp("rebase-cousins", value))
> +			options->rebase_cousins = 1;
> +		else
> +			die(_("Unknown mode: %s"), value);
> +	}
> +
> +	options->rebase_merges = 1;

I would expect `options->rebase_merges = 0` if `value == NULL`. IOW I
would have expected `parse_merges_value()` to start with:

	if (!value) {
		options->rebase_merges = 0;
		return;
	}

This assumes, of course, the parse_options semantics, where a `--no-*` option
passes `NULL` as argument to the callback.

However, this is _not_ the parse_options callback, and if the (optional)
argument was not specified, we do end up with a `NULL` here in spite of
wanting to enable the rebase-merges mode.

However, a primary reason why you introduce the function is to support
config value parsing. And in config value parsing, a "maybe-bool" with a
NULL value is considered to be equivalent to `true`! (See
`git_parse_maybe_bool_text()` or
https://git-scm.com/docs/git-config#Documentation/git-config.txt-true for
details.). For example,

	[http]
		sslVerify

is equivalent to

	[http]
		sslVerify = true

But since `git_parse_maybe_bool()` already takes care of handling that
case (in which case we do not even want to call `git_parse_maybe_bool()`),
you can limit that function to handling the command-line semantics.

So with those confusingly disagreeing semantics, I see not only myself,
but other readers doing very, very well, indeed, with a code comment that
explains under what circumstances we expect this callback to be called
with `value == NULL`.

> +}
> +
>  static int rebase_config(const char *var, const char *value, void *data)
>  {
>  	struct rebase_options *opts = data;
> @@ -815,6 +829,13 @@ static int rebase_config(const char *var, const char *value, void *data)
>  		return 0;
>  	}
>
> +	if (!strcmp(var, "rebase.merges") && value && *value) {

Why do we require a non-empty `value` here?

	[rebase]
		merges

should be equivalent to `true`,

	[rebase]
		merges =

should probably be equivalent to `false`, and both are handled correctly
by `git_parse_maybe_bool()`.

> +		opts->rebase_merges = git_parse_maybe_bool(value);
> +		if (opts->rebase_merges < 0)
> +			parse_merges_value(opts, value);
> +		return 0;
> +	}
> +
>  	if (!strcmp(var, "rebase.backend")) {
>  		return git_config_string(&opts->default_backend, var, value);
>  	}
> @@ -980,6 +1001,18 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
>  	return 0;
>  }
>
> +static int parse_opt_merges(const struct option *opt, const char *arg, int unset)
> +{
> +	struct rebase_options *options = opt->value;
> +
> +	if (unset)
> +		options->rebase_merges = 0;
> +	else
> +		parse_merges_value(options, arg);
> +
> +	return 0;
> +}
> +

It is kind of inelegant to require a _second_ callback for the
command-line parsing, but I guess if we want a `--no-rebase-merges` option
to override a config setting, we cannot help it.

>  static void NORETURN error_on_missing_default_upstream(void)
>  {
>  	struct branch *current_branch = branch_get(NULL);
> @@ -1035,7 +1068,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	struct object_id branch_base;
>  	int ignore_whitespace = 0;
>  	const char *gpg_sign = NULL;
> -	const char *rebase_merges = NULL;
>  	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
>  	struct object_id squash_onto;
>  	char *squash_onto_name = NULL;
> @@ -1137,10 +1169,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  			   &options.allow_empty_message,
>  			   N_("allow rebasing commits with empty messages"),
>  			   PARSE_OPT_HIDDEN),
> -		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
> -			N_("mode"),
> +		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
>  			N_("try to rebase merges instead of skipping them"),
> -			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
> +			PARSE_OPT_OPTARG, parse_opt_merges),
>  		OPT_BOOL(0, "fork-point", &options.fork_point,
>  			 N_("use 'merge-base --fork-point' to refine upstream")),
>  		OPT_STRING('s', "strategy", &options.strategy,
> @@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	if (options.exec.nr)
>  		imply_merge(&options, "--exec");
>
> -	if (rebase_merges) {
> -		if (!strcmp("rebase-cousins", rebase_merges))
> -			options.rebase_cousins = 1;
> -		else if (strcmp("no-rebase-cousins", rebase_merges))
> -			die(_("Unknown mode: %s"), rebase_merges);
> -		options.rebase_merges = 1;
> +	if (options.rebase_merges)
>  		imply_merge(&options, "--rebase-merges");
> -	}
>
>  	if (options.type == REBASE_APPLY) {
>  		if (ignore_whitespace)
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index c73949df18..d4b0e8fd49 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -284,6 +284,102 @@ test_expect_success '--rebase-merges="" is invalid syntax' '
>  	test_cmp expect actual
>  '
>
> +test_expect_success 'rebase.merges="" is equivalent to not passing --rebase-merges' '
> +	test_config rebase.merges "" &&
> +	git checkout -b config-merges-blank E &&
> +	git rebase C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b config-rebase-cousins main &&
> +	git rebase HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
> +	test_config rebase.merges no-rebase-cousins &&
> +	git checkout -b override-config-no-rebase-cousins E &&
> +	git rebase --no-rebase-merges C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b override-config-rebase-cousins main &&
> +	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	o | H
> +	|/
> +	o A
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
> +	test_config rebase.merges false &&
> +	git checkout -b override-config-merges-false E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase --rebase-merges C &&
> +	test_cmp_rev HEAD $before
> +'
> +
> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b no-override-config-rebase-cousins main &&
> +	git rebase --rebase-merges HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
> +test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '
> +	test_config_global rebase.merges true &&
> +	test_config rebase.merges false &&
> +	git checkout -b override-global-config E &&
> +	git rebase C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success 'local rebase.merges="" does not override global rebase.merges=true' '
> +	test_config_global rebase.merges no-rebase-cousins &&
> +	test_config rebase.merges "" &&
> +	git checkout -b no-override-global-config E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase C &&
> +	test_cmp_rev HEAD $before
> +'
> +

I understand the temptation to introduce exhaustive matrices that test all
the different settings in all the different ways they can be specified.

However, I would much prefer to keep the tests succinct, not the least to
avoid the every-increasing runtime of Git's CI. It's already taking about
an order of magnitude or two too long to be reasonable.

So I'd suggest reducing the tests to a single one instead of eight: verify
that `rebase.merges=no-rebase-cousins` is heeded, and that
`--no-rebase-cousins` overrides that. That should be plenty sufficient to
prevent regressions.

Ciao,
Johannes

>  test_expect_success 'refs/rewritten/* is worktree-local' '
>  	git worktree add wt &&
>  	cat >wt/script-from-scratch <<-\EOF &&
> --
> 2.39.2
>
>

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-23  5:34 ` [PATCH v4 2/3] rebase: stop accepting --rebase-merges="" Alex Henrie
@ 2023-02-24 13:54   ` Johannes Schindelin
  2023-02-24 17:20     ` Junio C Hamano
  0 siblings, 1 reply; 96+ messages in thread
From: Johannes Schindelin @ 2023-02-24 13:54 UTC (permalink / raw)
  To: Alex Henrie; +Cc: git, tao, gitster, newren, phillip.wood123, sorganov

Hi Alex,

On Wed, 22 Feb 2023, Alex Henrie wrote:

> The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
> empty string argument) has been an undocumented synonym of
> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid
> confusion when a rebase.merges config option is introduced, where
> rebase.merges="" will be equivalent to not passing --rebase-merges.

Being undocumented and obscure might be a good reason for some to consider
this a bug; I do not. You could deprecate it, but there are probably
better ideas than to remove it without prior warning.

If all you want to do is to support `rebase.merges=`, you can do that
without having to change the meaning of `--rebase-merges=`.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-23 17:28 ` [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Junio C Hamano
@ 2023-02-24 13:57   ` Johannes Schindelin
  2023-02-24 19:16     ` Junio C Hamano
  2023-02-25 18:09     ` Alex Henrie
  0 siblings, 2 replies; 96+ messages in thread
From: Johannes Schindelin @ 2023-02-24 13:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Henrie, git, tao, newren, phillip.wood123, sorganov

Hi Junio,

On Thu, 23 Feb 2023, Junio C Hamano wrote:

> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> > ---
>
> No cover letter to summarize the changes?

A range-diff would have been nice, too, as well as replying-to the
previous iteration so that they're all within the same email thread.

And if you want cover letters and range-diffs and correct In-Reply-To
headers, I can think of a splendid way to encourage that: promote tools
that do that. That's much better than to require contributors to know all
the customs and conventions of the project and send mails in the exact
desired format...

Ciao,
Johannes


^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges
  2023-02-23  5:34 ` [PATCH v4 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-02-24 13:53   ` Johannes Schindelin
@ 2023-02-24 14:55   ` Phillip Wood
  2023-02-24 17:51     ` Alex Henrie
  1 sibling, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-02-24 14:55 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov

Hi Alex

On 23/02/2023 05:34, Alex Henrie wrote:
> The purpose of the new option is to accommodate users who would like
> --rebase-merges to be on by default and to facilitate possibly turning
> on --rebase-merges by default without configuration in a future version
> of Git.
> 
> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>   Documentation/config/rebase.txt | 10 ++++
>   Documentation/git-rebase.txt    |  3 +-
>   builtin/rebase.c                | 47 ++++++++++++----
>   t/t3430-rebase-merges.sh        | 96 +++++++++++++++++++++++++++++++++
>   4 files changed, 144 insertions(+), 12 deletions(-)
> 
> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
> index f19bd0e040..308baa9dbb 100644
> --- a/Documentation/config/rebase.txt
> +++ b/Documentation/config/rebase.txt
> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
>   
>   rebase.forkPoint::
>   	If set to false set `--no-fork-point` option by default.
> +
> +rebase.merges::
> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true is equivalent to `--rebase-merges` without an argument, setting to
> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
> +	`--rebase-merges` with that value as its argument, and setting to false
> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line without an argument overrides a `rebase.merges=false`
> +	configuration but does not override other values of `rebase.merge`.

Thanks for updating this, it is much clearer what the different settings 
mean now. I not sure if having the config setting override the default 
when the user passes --rebase-merges without an argument is a good idea.

> [...]   
> +static void parse_merges_value(struct rebase_options *options, const char *value)
> +{
> +	if (value) {
> +		if (!strcmp("no-rebase-cousins", value))
> +			options->rebase_cousins = 0;
> +		else if (!strcmp("rebase-cousins", value))
> +			options->rebase_cousins = 1;
> +		else
> +			die(_("Unknown mode: %s"), value);
> +	}
> +
> +	options->rebase_merges = 1;
> +}

It's a shame we seem to have grown yet another callback since v2, I'm 
not sure we should need to add quite so much code just to support a new 
config option

 > [...]
> @@ -1436,14 +1467,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	if (options.exec.nr)
>   		imply_merge(&options, "--exec");
>   
> -	if (rebase_merges) {
> -		if (!strcmp("rebase-cousins", rebase_merges))
> -			options.rebase_cousins = 1;
> -		else if (strcmp("no-rebase-cousins", rebase_merges))
> -			die(_("Unknown mode: %s"), rebase_merges);
> -		options.rebase_merges = 1;
> +	if (options.rebase_merges)
>   		imply_merge(&options, "--rebase-merges");
> -	}

As I have said before I really think this patch needs to follow the lead 
of eddfcd8ece (rebase: provide better error message for apply options 
vs. merge config, 2023-01-25) and provide an error message if 
--rebase-merges is enabled by rebase.merges and the user provides a 
command line option that requires the apply backend. So

	git -c rebase.merges=true rebase --whitespace=fix

would result in

	error: apply options are incompatible with rebase.merges.
	Consider adding --no-rebase-merges

> [...]
> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b no-override-config-rebase-cousins main &&
> +	git rebase --rebase-merges HEAD^ &&

I think this behavior is confusing for users and will break scripts that 
quite reasonably assume --rebase-merges is equivalent to 
--rebase-merges=no-rebase-cousins

> [...]
> +test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '
> +	test_config_global rebase.merges true &&
> +	test_config rebase.merges false &&
> +	git checkout -b override-global-config E &&
> +	git rebase C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success 'local rebase.merges="" does not override global rebase.merges=true' '
> +	test_config_global rebase.merges no-rebase-cousins &&
> +	test_config rebase.merges "" &&
> +	git checkout -b no-override-global-config E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase C &&
> +	test_cmp_rev HEAD $before
> +'

These two tests seem to be testing the config subsystem rather than this 
patch. As Dscho has pointed out it is important to get a balance between 
test coverage and test run time. I think these two tests can definitely 
be dropped.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 13:54   ` Johannes Schindelin
@ 2023-02-24 17:20     ` Junio C Hamano
  2023-02-24 17:50       ` Alex Henrie
  0 siblings, 1 reply; 96+ messages in thread
From: Junio C Hamano @ 2023-02-24 17:20 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Alex Henrie, git, tao, newren, phillip.wood123, sorganov

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> On Wed, 22 Feb 2023, Alex Henrie wrote:
>
>> The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
>> empty string argument) has been an undocumented synonym of
>> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid
>> confusion when a rebase.merges config option is introduced, where
>> rebase.merges="" will be equivalent to not passing --rebase-merges.
>
> Being undocumented and obscure might be a good reason for some to consider
> this a bug; I do not. You could deprecate it, but there are probably
> better ideas than to remove it without prior warning.
>
> If all you want to do is to support `rebase.merges=`, you can do that
> without having to change the meaning of `--rebase-merges=`.

Thanks for a doze of sanity.  Let me mark the topic as on hold to
wait for a resolution.



^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges
  2023-02-24 13:53   ` Johannes Schindelin
@ 2023-02-24 17:49     ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 17:49 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, tao, gitster, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 6:53 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:

> in config value parsing, a "maybe-bool" with a
> NULL value is considered to be equivalent to `true`! (See
> `git_parse_maybe_bool_text()` or
> https://git-scm.com/docs/git-config#Documentation/git-config.txt-true for
> details.).

> On Wed, 22 Feb 2023, Alex Henrie wrote:
>
> > +     if (!strcmp(var, "rebase.merges") && value && *value) {
>
> Why do we require a non-empty `value` here?
>
>         [rebase]
>                 merges
>
> should be equivalent to `true`,
>
>         [rebase]
>                 merges =
>
> should probably be equivalent to `false`, and both are handled correctly
> by `git_parse_maybe_bool()`.

I didn't know that there was already an established convention for
what NULL and "" mean for boolean config values. I should have looked
at the source code of git_parse_maybe_bool more carefully. That does
change things because we're going to want to follow the established
convention here.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 17:20     ` Junio C Hamano
@ 2023-02-24 17:50       ` Alex Henrie
  2023-02-24 18:08         ` Junio C Hamano
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 17:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 10:20 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > On Wed, 22 Feb 2023, Alex Henrie wrote:
> >
> >> The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
> >> empty string argument) has been an undocumented synonym of
> >> --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid
> >> confusion when a rebase.merges config option is introduced, where
> >> rebase.merges="" will be equivalent to not passing --rebase-merges.
> >
> > Being undocumented and obscure might be a good reason for some to consider
> > this a bug; I do not. You could deprecate it, but there are probably
> > better ideas than to remove it without prior warning.
> >
> > If all you want to do is to support `rebase.merges=`, you can do that
> > without having to change the meaning of `--rebase-merges=`.
>
> Thanks for a doze of sanity.  Let me mark the topic as on hold to
> wait for a resolution.

As Johannes pointed out, it's going to be confusing if
rebase.merges="" does not mean rebase.merges=false. It's also going to
be confusing if rebase.merges="" does not mean --rebase-merges="". The
only sane option I see is to drop --rebase-merges="", or to add a
deprecation warning now and drop it later.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 3/3] rebase: add a config option for --rebase-merges
  2023-02-24 14:55   ` Phillip Wood
@ 2023-02-24 17:51     ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 17:51 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov

On Fri, Feb 24, 2023 at 7:55 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> Thanks for updating this, it is much clearer what the different settings
> mean now.

Happy to help. Thanks for the feedback.

> I not sure if having the config setting override the default
> when the user passes --rebase-merges without an argument is a good idea.

> I think this behavior is confusing for users and will break scripts that
> quite reasonably assume --rebase-merges is equivalent to
> --rebase-merges=no-rebase-cousins

In that case, we're going to need two config options: A rebase.merges
boolean for whether --rebase-merges should be on by default and a
rebase.cousins boolean for whether rebase-cousins or no-rebase-cousins
is the default.

I will try to incorporate that change and the others that you and
Johannes suggested in v5.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 17:50       ` Alex Henrie
@ 2023-02-24 18:08         ` Junio C Hamano
  2023-02-24 18:23           ` Alex Henrie
  0 siblings, 1 reply; 96+ messages in thread
From: Junio C Hamano @ 2023-02-24 18:08 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

Alex Henrie <alexhenrie24@gmail.com> writes:

> As Johannes pointed out, it's going to be confusing if
> rebase.merges="" does not mean rebase.merges=false. It's also going to
> be confusing if rebase.merges="" does not mean --rebase-merges="". The
> only sane option I see is to drop --rebase-merges="", or to add a
> deprecation warning now and drop it later.

If we were doing anything, then I think the only sensible way
forward is to warn and then drop long after everybody forgets about
it.

But does it really need to be changed?  I am perfectly happy to
declare that those who wants to set rebase.merges to say false
should set it to false (or no or 0), not an empty string.

Also, "[rebase] merges = " in the configuration files does not have
to mean the same thing as "--rebase-merges=" from the command line.
Can't we just reserve that strange "--rebase-merges=" given from the
command line to those who are already using it, without even
advertising it in the documentation, document and encourage the
longhand "--rebase-merges=no-rebase-cousins" from the command line
and take only the  form that corresponds to it in the configuration
file, i.e. "[rebase] merges = no-rebase-cousins".  We could even
error out "[rebase] merges = " in the configuration file, as nobody
is using such a configuration variable today.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 18:08         ` Junio C Hamano
@ 2023-02-24 18:23           ` Alex Henrie
  2023-02-24 18:40             ` Junio C Hamano
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 18:23 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 11:08 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > As Johannes pointed out, it's going to be confusing if
> > rebase.merges="" does not mean rebase.merges=false. It's also going to
> > be confusing if rebase.merges="" does not mean --rebase-merges="". The
> > only sane option I see is to drop --rebase-merges="", or to add a
> > deprecation warning now and drop it later.
>
> If we were doing anything, then I think the only sensible way
> forward is to warn and then drop long after everybody forgets about
> it.
>
> But does it really need to be changed?  I am perfectly happy to
> declare that those who wants to set rebase.merges to say false
> should set it to false (or no or 0), not an empty string.
>
> Also, "[rebase] merges = " in the configuration files does not have
> to mean the same thing as "--rebase-merges=" from the command line.
> Can't we just reserve that strange "--rebase-merges=" given from the
> command line to those who are already using it, without even
> advertising it in the documentation, document and encourage the
> longhand "--rebase-merges=no-rebase-cousins" from the command line
> and take only the  form that corresponds to it in the configuration
> file, i.e. "[rebase] merges = no-rebase-cousins".  We could even
> error out "[rebase] merges = " in the configuration file, as nobody
> is using such a configuration variable today.

The only way to truly make "[rebase] merges =" invalid is to print an
error message and die with that configuration. I think that would be
confusing too, especially since it's now looking like rebase.merges
needs to be a pure boolean and an independent rebase.cousins boolean
option is needed as well. However, if you think that that's better
than deprecating --rebase-merges="" with the plan to drop it long
after everybody forgets about it, then I'm happy to oblige. Just let
me know what is wanted.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 18:23           ` Alex Henrie
@ 2023-02-24 18:40             ` Junio C Hamano
  2023-02-24 18:55               ` Alex Henrie
  0 siblings, 1 reply; 96+ messages in thread
From: Junio C Hamano @ 2023-02-24 18:40 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

Alex Henrie <alexhenrie24@gmail.com> writes:

> The only way to truly make "[rebase] merges =" invalid is to print an
> error message and die with that configuration. I think that would be
> confusing too, especially since it's now looking like rebase.merges
> needs to be a pure boolean and an independent rebase.cousins boolean
> option is needed as well.

Oh, I wasn't aware of that direction.

I do not know why rebase.cousins, which would only be meaningful
when rebase.merges is true, is a better design than rebase.merges
that is an enum of "don't do the merges stuff" plus "do the merges
stuff with cousins", "without cousins" (which may allow us to gain
more different ways to do "merges stuff" later), but that is what
gained consensus on the list, then "[rebase]merges=" would become a
problem.

But --rebase-merges from the command line is not a pure Boolean
already, so what does "[rebase]merges" that is a pure Boolean aim to
help?

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 18:40             ` Junio C Hamano
@ 2023-02-24 18:55               ` Alex Henrie
  2023-02-24 19:13                 ` Junio C Hamano
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 18:55 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 11:40 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > The only way to truly make "[rebase] merges =" invalid is to print an
> > error message and die with that configuration. I think that would be
> > confusing too, especially since it's now looking like rebase.merges
> > needs to be a pure boolean and an independent rebase.cousins boolean
> > option is needed as well.
>
> Oh, I wasn't aware of that direction.
>
> I do not know why rebase.cousins, which would only be meaningful
> when rebase.merges is true, is a better design than rebase.merges
> that is an enum of "don't do the merges stuff" plus "do the merges
> stuff with cousins", "without cousins" (which may allow us to gain
> more different ways to do "merges stuff" later), but that is what
> gained consensus on the list, then "[rebase]merges=" would become a
> problem.
>
> But --rebase-merges from the command line is not a pure Boolean
> already, so what does "[rebase]merges" that is a pure Boolean aim to
> help?

Phillip is concerned about people and scripts assuming that
--rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,
see [1].

Tao and others are probably not going to like it if --rebase-merges
without an argument undoes a rebase.merges=rebase-cousins
configuration.

It seems to me that the only way to make everyone happy is to have
separate rebase.merges and rebase.cousins options. You have a point
that separating the options could cause problems if --rebase-merges
starts accepting more arguments in the future, but if that happens we
could deal with it by adding more possible values to rebase.cousins or
introducing a third config option.

-Alex

[1] https://lore.kernel.org/git/CAMMLpeQ98BTCGE2tcVdZ99eU6cLh4Rd_hc8C_PmKvsBkjXUWPw@mail.gmail.com/

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 18:55               ` Alex Henrie
@ 2023-02-24 19:13                 ` Junio C Hamano
  2023-02-24 19:24                   ` Alex Henrie
  2023-02-24 19:24                   ` Phillip Wood
  0 siblings, 2 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-02-24 19:13 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

Alex Henrie <alexhenrie24@gmail.com> writes:

> Phillip is concerned about people and scripts assuming that
> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,
> see [1].

Isn't that already broken when you introduce rebase.merges
configuration?  People and scripts are already relying on the lack
of rebase-merges to flatten, and script writers will be surprised to
receive a "bug report" complaining that their script does not work
when the users set rebase.merges to anything but no.

> Tao and others are probably not going to like it if --rebase-merges
> without an argument undoes a rebase.merges=rebase-cousins
> configuration.

That is why I suggested to keep --rebase-merges= (with no value or
an empty string) only for those who came from the world where it
defaults to no-rebase-cousins and there was no rebase.merges
configuration.  If --rebase-merges= is given from the command line
without value *and* rebase.merges configuration is there (which is
Tao's concern?), the command line option can error out asking for an
explicit value to countermand whatever value is configured.

Wouldn't that work for folks from both camps?

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-24 13:57   ` Johannes Schindelin
@ 2023-02-24 19:16     ` Junio C Hamano
  2023-02-25 18:09     ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-02-24 19:16 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Alex Henrie, git, tao, newren, phillip.wood123, sorganov

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> On Thu, 23 Feb 2023, Junio C Hamano wrote:
>
>> Alex Henrie <alexhenrie24@gmail.com> writes:
>>
>> > Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
>> > ---
>>
>> No cover letter to summarize the changes?
>
> A range-diff would have been nice, too, as well as replying-to the
> previous iteration so that they're all within the same email thread.

Yes, especially the threading would help both those who missed
previous iterations and those who reviewed them.  Range-diff is
often helpful when came with a good summary.  Just like a patch
alone without a good proposed log message is not a good way to help
reviewers understand the issue the patch tries to solve, a cover
with range-diff alone only shows what is different from the previous
iteration, but does not say why the changes were made, so it is not
a good substitute for an explanation by author's words.

I may have suggested GGG if the author appeared a total beginner
with the e-mail workflow (and especially if it were a single-patch
topic), but Alex seems to be doing fine otherwise, and also GGG has
its own learning curve (e.g. it is rare to see a good cover letter
with per-iteration summaries, unless the author is one of those
experienced list regulars), so I left it up to Alex to choose how to
reach the desired end result.

Thanks.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 19:13                 ` Junio C Hamano
@ 2023-02-24 19:24                   ` Alex Henrie
  2023-02-24 19:24                   ` Phillip Wood
  1 sibling, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 19:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 12:13 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > Phillip is concerned about people and scripts assuming that
> > --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,
> > see [1].
>
> Isn't that already broken when you introduce rebase.merges
> configuration?  People and scripts are already relying on the lack
> of rebase-merges to flatten, and script writers will be surprised to
> receive a "bug report" complaining that their script does not work
> when the users set rebase.merges to anything but no.

Yeah, I don't know why breaking the assumption that --rebase-merges is
equivalent to --rebase-merges=no-rebase-cousins is any worse than
breaking the assumption that not passing --rebase-merges is equivalent
to passing --no-rebase-merges. And the assumption is broken whether
one new config option is introduced or two, so splitting the option up
probably wouldn't make Phillip any happier.

> > Tao and others are probably not going to like it if --rebase-merges
> > without an argument undoes a rebase.merges=rebase-cousins
> > configuration.
>
> That is why I suggested to keep --rebase-merges= (with no value or
> an empty string) only for those who came from the world where it
> defaults to no-rebase-cousins and there was no rebase.merges
> configuration.  If --rebase-merges= is given from the command line
> without value *and* rebase.merges configuration is there (which is
> Tao's concern?), the command line option can error out asking for an
> explicit value to countermand whatever value is configured.
>
> Wouldn't that work for folks from both camps?

Maybe. I still don't like the idea of --rebase-merges="" ever being
not equivalent to rebase.merges="".

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 19:13                 ` Junio C Hamano
  2023-02-24 19:24                   ` Alex Henrie
@ 2023-02-24 19:24                   ` Phillip Wood
  2023-02-24 19:56                     ` Alex Henrie
  1 sibling, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-02-24 19:24 UTC (permalink / raw)
  To: Junio C Hamano, Alex Henrie
  Cc: Johannes Schindelin, git, tao, newren, phillip.wood123, sorganov

On 24/02/2023 19:13, Junio C Hamano wrote:
> Alex Henrie <alexhenrie24@gmail.com> writes:
> 
>> Phillip is concerned about people and scripts assuming that
>> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,
>> see [1].
> 
> Isn't that already broken when you introduce rebase.merges
> configuration?

Scripts using --rebase-merges are not broken by the introduction of 
rebase.merges so long as we follow our usual convention of always 
allowing the commandline to override the config (i.e. --rebase-merges is 
always equivalent to --rebase-merges=no-rebase-cousins). I don't really 
understand why Alex is suggesting splitting the config into two based on 
my comments.

> People and scripts are already relying on the lack
> of rebase-merges to flatten, and script writers will be surprised to
> receive a "bug report" complaining that their script does not work
> when the users set rebase.merges to anything but no.

That is true.

Best Wishes

Phillip

>> Tao and others are probably not going to like it if --rebase-merges
>> without an argument undoes a rebase.merges=rebase-cousins
>> configuration.
> 
> That is why I suggested to keep --rebase-merges= (with no value or
> an empty string) only for those who came from the world where it
> defaults to no-rebase-cousins and there was no rebase.merges
> configuration.  If --rebase-merges= is given from the command line
> without value *and* rebase.merges configuration is there (which is
> Tao's concern?), the command line option can error out asking for an
> explicit value to countermand whatever value is configured.
> 
> Wouldn't that work for folks from both camps?

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 2/3] rebase: stop accepting --rebase-merges=""
  2023-02-24 19:24                   ` Phillip Wood
@ 2023-02-24 19:56                     ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-24 19:56 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, Johannes Schindelin, git, tao, newren,
	phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 12:24 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 24/02/2023 19:13, Junio C Hamano wrote:
> > Alex Henrie <alexhenrie24@gmail.com> writes:
> >
> >> Phillip is concerned about people and scripts assuming that
> >> --rebase-merges is equivalent to --rebase-merges=no-rebase-cousins,
> >> see [1].
> >
> > Isn't that already broken when you introduce rebase.merges
> > configuration?
>
> Scripts using --rebase-merges are not broken by the introduction of
> rebase.merges so long as we follow our usual convention of always
> allowing the commandline to override the config (i.e. --rebase-merges is
> always equivalent to --rebase-merges=no-rebase-cousins). I don't really
> understand why Alex is suggesting splitting the config into two based on
> my comments.

I was thinking that it would be less surprising to users if the option
that broke the no-rebase-cousins assumption had "cousins" in its name.
I should have stopped to think that that wouldn't really address your
concern because regardless of what the option is named, it could still
result in surprising behavior. I apologize for the unhelpful
suggestion.

> > People and scripts are already relying on the lack
> > of rebase-merges to flatten, and script writers will be surprised to
> > receive a "bug report" complaining that their script does not work
> > when the users set rebase.merges to anything but no.
>
> That is true.

In addition to specifying --no-rebase-merges rather than assuming it,
shouldn't the "usual convention" for writing scripts also include
being explicit about --rebase-merges=rebase-cousins or
--rebase-merges=no-rebase-cousins? And if that is the case, is it
really much of a loss to let rebase.merges=rebase-cousins override
--rebase-merges without an argument?

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v5 0/3] rebase: add a config option for --rebase-merges
  2023-02-23  5:34 [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                   ` (2 preceding siblings ...)
  2023-02-23 17:28 ` [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Junio C Hamano
@ 2023-02-25 18:03 ` Alex Henrie
  2023-02-25 18:03   ` [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                     ` (4 more replies)
  3 siblings, 5 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-25 18:03 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

Changes from v4:

- deprecate --rebase-merges="" rather than removing it outright
- follow the established convention for what "" and NULL mean as
  booleans
- add tailored error message for conflicting options and tests for it
- rename parse_opt_merges to parse_opt_rebase_merges to avoid confusion
  with parse_opt_merge
- similarly, rename parse_merges_value to parse_rebase_merges_value
- move null check from parse_rebase_merges_value to
  parse_opt_rebase_merges
- remove tests that check whether the config system itself works

Suggestions not incorporated:

- remove the callback function
- make --rebase-merge without an argument override
  rebase.merges=rebase-cousins
- make rebase.merge accept only a subset of the possible boolean values,
  or change the meanings of some of those values
- make --rebase-merge="" and rebase.merge="" do different things without
  warning
- remove tests that verify that the command line option properly
  overrides the config option

Thanks to Johannes, Phillip, and Junio for your help making these
patches better. If you feel strongly about one of the unincorporated
suggestions, let's continue the discussion and try to figure out how to
make it happen.

Alex Henrie (3):
  rebase: add documentation and test for --no-rebase-merges
  rebase: deprecate --rebase-merges=""
  rebase: add a config option for --rebase-merges

 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           |  5 +-
 builtin/rebase.c                       | 75 ++++++++++++++++++-------
 t/t3422-rebase-incompatible-options.sh | 12 ++++
 t/t3430-rebase-merges.sh               | 78 ++++++++++++++++++++++++++
 5 files changed, 160 insertions(+), 20 deletions(-)

Range-diff against v4:
1:  e6d44a194c = 1:  76e38ef9f8 rebase: add documentation and test for --no-rebase-merges
2:  393b43c4e1 ! 2:  c6099e6dee rebase: stop accepting --rebase-merges=""
    @@ Metadata
     Author: Alex Henrie <alexhenrie24@gmail.com>
     
      ## Commit message ##
    -    rebase: stop accepting --rebase-merges=""
    +    rebase: deprecate --rebase-merges=""
     
         The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
         empty string argument) has been an undocumented synonym of
    -    --rebase-merges=no-rebase-cousins. Stop accepting that syntax to avoid
    +    --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid
         confusion when a rebase.merges config option is introduced, where
    -    rebase.merges="" will be equivalent to not passing --rebase-merges.
    +    rebase.merges="" will be equivalent to --no-rebase-merges.
     
         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
     
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      			 N_("use 'merge-base --fork-point' to refine upstream")),
      		OPT_STRING('s', "strategy", &options.strategy,
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 		imply_merge(&options, "--exec");
      
      	if (rebase_merges) {
    --		if (!*rebase_merges)
    + 		if (!*rebase_merges)
     -			; /* default mode; do nothing */
    --		else if (!strcmp("rebase-cousins", rebase_merges))
    -+		if (!strcmp("rebase-cousins", rebase_merges))
    ++			warning(_("--rebase-merges with an empty string "
    ++				  "argument is deprecated and will stop "
    ++				  "working in a future version of Git. Use "
    ++				  "--rebase-merges=no-rebase-cousins "
    ++				  "instead."));
    + 		else if (!strcmp("rebase-cousins", rebase_merges))
      			options.rebase_cousins = 1;
      		else if (strcmp("no-rebase-cousins", rebase_merges))
    - 			die(_("Unknown mode: %s"), rebase_merges);
     
      ## t/t3430-rebase-merges.sh ##
     @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless asked for' '
      	EOF
      '
      
    -+test_expect_success '--rebase-merges="" is invalid syntax' '
    -+	echo "fatal: Unknown mode: " >expect &&
    -+	test_must_fail git rebase --rebase-merges="" HEAD^ 2>actual &&
    -+	test_cmp expect actual
    ++test_expect_success '--rebase-merges="" is deprecated' '
    ++	git rebase --rebase-merges="" HEAD^ 2>actual &&
    ++	grep deprecated actual
     +'
     +
      test_expect_success 'refs/rewritten/* is worktree-local' '
3:  b1b6fbfa86 ! 3:  95cba9588c rebase: add a config option for --rebase-merges
    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
      have `<upstream>` as direct ancestor will keep their original branch point,
     
      ## builtin/rebase.c ##
    +@@ builtin/rebase.c: struct rebase_options {
    + 	int fork_point;
    + 	int update_refs;
    + 	int config_autosquash;
    ++	int config_rebase_merges;
    + 	int config_update_refs;
    + };
    + 
    +@@ builtin/rebase.c: struct rebase_options {
    + 		.allow_empty_message = 1,               \
    + 		.autosquash = -1,                       \
    + 		.config_autosquash = -1,                \
    ++		.rebase_merges = -1,                    \
    ++		.config_rebase_merges = -1,             \
    + 		.update_refs = -1,                      \
    + 		.config_update_refs = -1,               \
    + 	}
     @@ builtin/rebase.c: static int run_specific_rebase(struct rebase_options *opts)
      	return status ? -1 : 0;
      }
      
    -+static void parse_merges_value(struct rebase_options *options, const char *value)
    ++static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
     +{
    -+	if (value) {
    -+		if (!strcmp("no-rebase-cousins", value))
    -+			options->rebase_cousins = 0;
    -+		else if (!strcmp("rebase-cousins", value))
    -+			options->rebase_cousins = 1;
    -+		else
    -+			die(_("Unknown mode: %s"), value);
    -+	}
    -+
    -+	options->rebase_merges = 1;
    ++	if (!strcmp("no-rebase-cousins", value))
    ++		options->rebase_cousins = 0;
    ++	else if (!strcmp("rebase-cousins", value))
    ++		options->rebase_cousins = 1;
    ++	else
    ++		die(_("Unknown rebase-merges mode: %s"), value);
     +}
     +
      static int rebase_config(const char *var, const char *value, void *data)
    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
      		return 0;
      	}
      
    -+	if (!strcmp(var, "rebase.merges") && value && *value) {
    -+		opts->rebase_merges = git_parse_maybe_bool(value);
    -+		if (opts->rebase_merges < 0)
    -+			parse_merges_value(opts, value);
    ++	if (!strcmp(var, "rebase.merges")) {
    ++		opts->config_rebase_merges = git_parse_maybe_bool(value);
    ++		if (opts->config_rebase_merges < 0) {
    ++			opts->config_rebase_merges = 1;
    ++			parse_rebase_merges_value(opts, value);
    ++		}
     +		return 0;
     +	}
     +
    - 	if (!strcmp(var, "rebase.backend")) {
    - 		return git_config_string(&opts->default_backend, var, value);
    - 	}
    + 	if (!strcmp(var, "rebase.updaterefs")) {
    + 		opts->config_update_refs = git_config_bool(var, value);
    + 		return 0;
     @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
      	return 0;
      }
      
    -+static int parse_opt_merges(const struct option *opt, const char *arg, int unset)
    ++static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
     +{
     +	struct rebase_options *options = opt->value;
     +
    -+	if (unset)
    -+		options->rebase_merges = 0;
    -+	else
    -+		parse_merges_value(options, arg);
    ++	options->rebase_merges = !unset;
    ++
    ++	if (arg) {
    ++		if (!*arg) {
    ++			warning(_("--rebase-merges with an empty string "
    ++				  "argument is deprecated and will stop "
    ++				  "working in a future version of Git. Use "
    ++				  "--rebase-merges=no-rebase-cousins "
    ++				  "instead."));
    ++			arg = "no-rebase-cousins";
    ++		}
    ++		parse_rebase_merges_value(options, arg);
    ++	}
     +
     +	return 0;
     +}
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
     +		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
      			N_("try to rebase merges instead of skipping them"),
     -			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
    -+			PARSE_OPT_OPTARG, parse_opt_merges),
    ++			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
      		OPT_BOOL(0, "fork-point", &options.fork_point,
      			 N_("use 'merge-base --fork-point' to refine upstream")),
      		OPT_STRING('s', "strategy", &options.strategy,
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      		imply_merge(&options, "--exec");
      
     -	if (rebase_merges) {
    --		if (!strcmp("rebase-cousins", rebase_merges))
    +-		if (!*rebase_merges)
    +-			warning(_("--rebase-merges with an empty string "
    +-				  "argument is deprecated and will stop "
    +-				  "working in a future version of Git. Use "
    +-				  "--rebase-merges=no-rebase-cousins "
    +-				  "instead."));
    +-		else if (!strcmp("rebase-cousins", rebase_merges))
     -			options.rebase_cousins = 1;
     -		else if (strcmp("no-rebase-cousins", rebase_merges))
     -			die(_("Unknown mode: %s"), rebase_merges);
     -		options.rebase_merges = 1;
    -+	if (options.rebase_merges)
    - 		imply_merge(&options, "--rebase-merges");
    +-		imply_merge(&options, "--rebase-merges");
     -	}
    - 
    +-
      	if (options.type == REBASE_APPLY) {
      		if (ignore_whitespace)
    + 			strvec_push(&options.git_am_opts,
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 				break;
    + 
    + 		if (i >= 0 || options.type == REBASE_APPLY) {
    +-			if (is_merge(&options))
    +-				die(_("apply options and merge options "
    +-					  "cannot be used together"));
    +-			else if (options.autosquash == -1 && options.config_autosquash == 1)
    ++			if (options.autosquash == -1 && options.config_autosquash == 1)
    + 				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
    ++			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
    ++				die(_("apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges"));
    + 			else if (options.update_refs == -1 && options.config_update_refs == 1)
    + 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
    ++			else if (is_merge(&options))
    ++				die(_("apply options and merge options "
    ++					  "cannot be used together"));
    + 			else
    + 				options.type = REBASE_APPLY;
    + 		}
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
    + 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
    + 
    ++	if (options.rebase_merges == 1)
    ++		imply_merge(&options, "--rebase-merges");
    ++	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
    ++				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
    ++
    + 	if (options.autosquash == 1)
    + 		imply_merge(&options, "--autosquash");
    + 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
    +
    + ## t/t3422-rebase-incompatible-options.sh ##
    +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
    + 		grep -e --no-autosquash err
    + 	"
    + 
    ++	test_expect_success "$opt incompatible with rebase.merges" "
    ++		git checkout B^0 &&
    ++		test_must_fail git -c rebase.merges=true rebase $opt A 2>err &&
    ++		grep -e --no-rebase-merges err
    ++	"
    ++
    + 	test_expect_success "$opt incompatible with rebase.updateRefs" "
    + 		git checkout B^0 &&
    + 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
    +@@ t/t3422-rebase-incompatible-options.sh: test_rebase_am_only () {
    + 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
    + 	"
    + 
    ++	test_expect_success "$opt okay with overridden rebase.merges" "
    ++		test_when_finished \"git reset --hard B^0\" &&
    ++		git checkout B^0 &&
    ++		git -c rebase.merges=true rebase --no-rebase-merges $opt A
    ++	"
    ++
    + 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
    + 		test_when_finished \"git reset --hard B^0\" &&
    + 		git checkout B^0 &&
     
      ## t/t3430-rebase-merges.sh ##
    -@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is invalid syntax' '
    - 	test_cmp expect actual
    +@@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is deprecated' '
    + 	grep deprecated actual
      '
      
    -+test_expect_success 'rebase.merges="" is equivalent to not passing --rebase-merges' '
    -+	test_config rebase.merges "" &&
    -+	git checkout -b config-merges-blank E &&
    -+	git rebase C &&
    -+	test_cmp_graph C.. <<-\EOF
    -+	* B
    -+	* D
    -+	o C
    -+	EOF
    -+'
    -+
     +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
     +	test_config rebase.merges rebase-cousins &&
     +	git checkout -b config-rebase-cousins main &&
    @@ t/t3430-rebase-merges.sh: test_expect_success '--rebase-merges="" is invalid syn
     +	o H
     +	EOF
     +'
    -+
    -+test_expect_success 'local rebase.merges=false overrides global rebase.merges=true' '
    -+	test_config_global rebase.merges true &&
    -+	test_config rebase.merges false &&
    -+	git checkout -b override-global-config E &&
    -+	git rebase C &&
    -+	test_cmp_graph C.. <<-\EOF
    -+	* B
    -+	* D
    -+	o C
    -+	EOF
    -+'
    -+
    -+test_expect_success 'local rebase.merges="" does not override global rebase.merges=true' '
    -+	test_config_global rebase.merges no-rebase-cousins &&
    -+	test_config rebase.merges "" &&
    -+	git checkout -b no-override-global-config E &&
    -+	before="$(git rev-parse --verify HEAD)" &&
    -+	test_tick &&
    -+	git rebase C &&
    -+	test_cmp_rev HEAD $before
    -+'
     +
      test_expect_success 'refs/rewritten/* is worktree-local' '
      	git worktree add wt &&
-- 
2.39.2


^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-02-25 18:03   ` Alex Henrie
  2023-03-01 23:23     ` Glen Choo
  2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-02-25 18:03 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt |  4 +++-
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..c98784a0d2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,13 +529,15 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
 By default, or when `no-rebase-cousins` was specified, commits which do not
 have `<upstream>` as direct ancestor will keep their original branch point,
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v5 2/3] rebase: deprecate --rebase-merges=""
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-02-25 18:03   ` [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-02-25 18:03   ` Alex Henrie
  2023-03-01 23:46     ` Glen Choo
                       ` (2 more replies)
  2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
                     ` (2 subsequent siblings)
  4 siblings, 3 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-25 18:03 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid
confusion when a rebase.merges config option is introduced, where
rebase.merges="" will be equivalent to --no-rebase-merges.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c         | 8 ++++++--
 t/t3430-rebase-merges.sh | 5 +++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6635f10d52..ccc13200b5 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (rebase_merges) {
 		if (!*rebase_merges)
-			; /* default mode; do nothing */
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges=no-rebase-cousins "
+				  "instead."));
 		else if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..f50fbf1390 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,11 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success '--rebase-merges="" is deprecated' '
+	git rebase --rebase-merges="" HEAD^ 2>actual &&
+	grep deprecated actual
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-02-25 18:03   ` [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-02-25 18:03   ` Alex Henrie
  2023-03-01 23:43     ` Glen Choo
                       ` (2 more replies)
  2023-03-01 23:14   ` [PATCH v5 0/3] " Glen Choo
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
  4 siblings, 3 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-25 18:03 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate possibly turning
on --rebase-merges by default without configuration in a future version
of Git.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           |  3 +-
 builtin/rebase.c                       | 79 ++++++++++++++++++--------
 t/t3422-rebase-incompatible-options.sh | 12 ++++
 t/t3430-rebase-merges.sh               | 63 ++++++++++++++++++++
 5 files changed, 143 insertions(+), 24 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..308baa9dbb 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.merges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true is equivalent to `--rebase-merges` without an argument, setting to
+	`rebase-cousins` or `no-rebase-cousins` is equivalent to
+	`--rebase-merges` with that value as its argument, and setting to false
+	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line without an argument overrides a `rebase.merges=false`
+	configuration but does not override other values of `rebase.merge`.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c98784a0d2..b02f9cbb8c 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.merges` config option and a previous
+	`--rebase-merges`.
 +
 By default, or when `no-rebase-cousins` was specified, commits which do not
 have `<upstream>` as direct ancestor will keep their original branch point,
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ccc13200b5..ac096f27e1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -123,6 +123,7 @@ struct rebase_options {
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -140,6 +141,8 @@ struct rebase_options {
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
 		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 	}
@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.merges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		}
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges=no-rebase-cousins "
+				  "instead."));
+			arg = "no-rebase-cousins";
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			warning(_("--rebase-merges with an empty string "
-				  "argument is deprecated and will stop "
-				  "working in a future version of Git. Use "
-				  "--rebase-merges=no-rebase-cousins "
-				  "instead."));
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				break;
 
 		if (i >= 0 || options.type == REBASE_APPLY) {
-			if (is_merge(&options))
-				die(_("apply options and merge options "
-					  "cannot be used together"));
-			else if (options.autosquash == -1 && options.config_autosquash == 1)
+			if (options.autosquash == -1 && options.config_autosquash == 1)
 				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
+			else if (is_merge(&options))
+				die(_("apply options and merge options "
+					  "cannot be used together"));
 			else
 				options.type = REBASE_APPLY;
 		}
@@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
 	if (options.autosquash == 1)
 		imply_merge(&options, "--autosquash");
 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..af93f13849 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -101,6 +101,12 @@ test_rebase_am_only () {
 		grep -e --no-autosquash err
 	"
 
+	test_expect_success "$opt incompatible with rebase.merges" "
+		git checkout B^0 &&
+		test_must_fail git -c rebase.merges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
+	"
+
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
 		git checkout B^0 &&
 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +119,12 @@ test_rebase_am_only () {
 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
 	"
 
+	test_expect_success "$opt okay with overridden rebase.merges" "
+		test_when_finished \"git reset --hard B^0\" &&
+		git checkout B^0 &&
+		git -c rebase.merges=true rebase --no-rebase-merges $opt A
+	"
+
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index f50fbf1390..a825a77fb4 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -283,6 +283,69 @@ test_expect_success '--rebase-merges="" is deprecated' '
 	grep deprecated actual
 '
 
+test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
+	test_config rebase.merges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins main &&
+	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	o | H
+	|/
+	o A
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.merges=false' '
+	test_config rebase.merges false &&
+	git checkout -b override-config-merges-false E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
+test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
+	test_config rebase.merges rebase-cousins &&
+	git checkout -b no-override-config-rebase-cousins main &&
+	git rebase --rebase-merges HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-24 13:57   ` Johannes Schindelin
  2023-02-24 19:16     ` Junio C Hamano
@ 2023-02-25 18:09     ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-02-25 18:09 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, git, tao, newren, phillip.wood123, sorganov

On Fri, Feb 24, 2023 at 6:58 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:

> On Thu, 23 Feb 2023, Junio C Hamano wrote:
>
> > No cover letter to summarize the changes?

> A range-diff would have been nice, too, as well as replying-to the
> previous iteration so that they're all within the same email thread.
>
> And if you want cover letters and range-diffs and correct In-Reply-To
> headers, I can think of a splendid way to encourage that: promote tools
> that do that. That's much better than to require contributors to know all
> the customs and conventions of the project and send mails in the exact
> desired format...

It would also help if those expectations were mentioned in
Documentation/SubmittingPatches. There's nothing in there about
range-diff or In-Reply-To.

I think I followed all of the customs of the Git mailing list for v5.
Please let me know if I missed something.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
                     ` (2 preceding siblings ...)
  2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-01 23:14   ` Glen Choo
  2023-03-02  5:02     ` Alex Henrie
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
  4 siblings, 1 reply; 96+ messages in thread
From: Glen Choo @ 2023-03-01 23:14 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov
  Cc: Alex Henrie

Hi Alex! It was great to see you at Review Club today. If you'd like the
notes, they are available at

https://docs.google.com/document/d/14L8BAumGTpsXpjDY8VzZ4rRtpAjuGrFSRqn3stCuS_w

though reviewers will send their feedback to the mailing list anyway, so
don't feel compelled to address feedback that isn't on the mailing list
:)

Alex Henrie <alexhenrie24@gmail.com> writes:

> Changes from v4:
>
> [...]
>
> Suggestions not incorporated:
>
> [...]
>

I would have appreciated a sentence or two explaining the high level
changes in this series, since that would save reviewers from having to
read the whole series to get an idea of the overall intent. For this
series, I think the subject line captures the intent pretty well, though
even simple descriptions like "Add a config option. Also clean up some
problems with the --rebase-merges flag." can be useful framing for
reviewers.

I appreciate the summary of changes [not] incorporated, I found it
useful for getting an idea of what was originally discussed.

On the whole, I think the config option will be quite welcome, and the
series is mostly mergeable, but might benefit from some documentation
tweaking.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-02-25 18:03   ` [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-01 23:23     ` Glen Choo
  0 siblings, 0 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-01 23:23 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov
  Cc: Alex Henrie

Alex Henrie <alexhenrie24@gmail.com> writes:

> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>

I inferred from the 'subject line' that this "always" worked and was
undocumented, and that we'd want to document this not for its own sake,
but because we'll introduce a config option. I would personally prefer
to get that explicitly in writing, but it's definitely not worth a
reroll.

>  -r::
>  --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
> +--no-rebase-merges::
>  	By default, a rebase will simply drop merge commits from the todo
>  	list, and put the rebased commits into a single, linear branch.
>  	With `--rebase-merges`, the rebase will instead try to preserve
>  	the branching structure within the commits that are to be rebased,
>  	by recreating the merge commits. Any resolved merge conflicts or
>  	manual amendments in these merge commits will have to be
> -	resolved/re-applied manually.
> +	resolved/re-applied manually. `--no-rebase-merges` can be used to
> +	countermand a previous `--rebase-merges`.
>  +
>  By default, or when `no-rebase-cousins` was specified, commits which do not
>  have `<upstream>` as direct ancestor will keep their original branch point,
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh

This isn't the fault of this patch, but I find this description a little
confusing. First we say

  By default (*when no option is given*), a rebase will simply drop
  merge commits from the todo list...

and then later we also say

  By default (*when --rebase-merges is provided without a value), or
  when `no-rebase-cousins`...

So although you didn't create this problem, given the changes we're
making in the later patches, I think we have the chance to make this
description more sensible; I'll elaborate in my response to those
patches.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-01 23:43     ` Glen Choo
  2023-03-02  9:37     ` Phillip Wood
  2023-03-02 18:02     ` Calvin Wan
  2 siblings, 0 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-01 23:43 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov
  Cc: Alex Henrie

Alex Henrie <alexhenrie24@gmail.com> writes:

> +rebase.merges::

As far as I can tell, when a config option is meant to be a stand in for
a CLI option, we have typically named it <subcommand>.<camelcase
option>, e.g. grep.fullName, log.diffMerges, push.followTags. This
probably matters more if we aren't specifying the subcommand that this
flag is more (like in this patch).

By this convention, the config option would be rebase.rebaseMerges,
which _does_ feel redundant and it's unlikely for "git rebase" to learn
a "--merges" flag, so I don't have a strong opinion either way. Though
as you mentioned in review club, this consistency might make it easier
for users to read custom configs that use the "rebase." section.

> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true is equivalent to `--rebase-merges` without an argument, setting to
> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
> +	`--rebase-merges` with that value as its argument, and setting to false
> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line without an argument overrides a `rebase.merges=false`
> +	configuration but does not override other values of `rebase.merge`.

With the explanation here, I think having "--rebase-merges" _not_
override "rebase.merge=rebase-cousins" is quite confusing.

I think there are two possibilities forward:

- Be very consistent that "--rebase-merges" is just a synonym of
  "--rebase-merges=no-rebase-cousins". Then, have any value of
  "--rebase-merges" override any config value.

  I think this the easiest UX to understand, but I don't think we should
  be commiting to a default, especially since we may add a more sensible
  default (like rebasing 'evil merges') in the future.

- Keep the existing behavior, but reword the docs to indicate that the
  "no value" form means "I want the default but don't care what it is",
  and an explicit value means "I want to use a particular mode and the
  rules of config vs CLI specificity should apply". Perhaps something
  like:

    When rebasing merges, there are two modes: `no-rebase-cousins` and
    `rebase-cousins`. If no mode is specified in either the config or
    CLI, it defaults to `no-rebase-cousins`.

   which I think is clearer that "rebase.merges=some-val" will win out
   over "--rebase-merges". What's nice is that we don't commit to a
   default value, so we can change it without having to update much of
   the wording here.

The code looks good, no real comments there.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=""
  2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-01 23:46     ` Glen Choo
  2023-03-02 10:07     ` Phillip Wood
  2023-03-02 18:02     ` Calvin Wan
  2 siblings, 0 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-01 23:46 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov
  Cc: Alex Henrie

Alex Henrie <alexhenrie24@gmail.com> writes:

> The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
> empty string argument) has been an undocumented synonym of
> --rebase-merges=no-rebase-cousins.

[...]

>  	if (rebase_merges) {
>  		if (!*rebase_merges)
> -			; /* default mode; do nothing */
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges=no-rebase-cousins "
> +				  "instead."));
>  		else if (!strcmp("rebase-cousins", rebase_merges))
>  			options.rebase_cousins = 1;
>  		else if (strcmp("no-rebase-cousins", rebase_merges))

As I mentioned in my review of patch 3/3, I think we might be better
served by saying that --rebase-merges="" is a synonym of --rebase-merges
(aka give me a sane default) instead of giving a specific value like
"no-rebase-cousins". This would be give us leeway to change the default
behavior in the future.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges
  2023-03-01 23:14   ` [PATCH v5 0/3] " Glen Choo
@ 2023-03-02  5:02     ` Alex Henrie
  2023-03-02  5:09       ` Alex Henrie
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-02  5:02 UTC (permalink / raw)
  To: Glen Choo
  Cc: Junio C Hamano, Elijah Newren, Calvin Wan, Jonathan Tan,
	Git mailing list

On Wed, Mar 1, 2023 at 4:14 PM Glen Choo <chooglen@google.com> wrote:
>
> Hi Alex! It was great to see you at Review Club today. If you'd like the
> notes, they are available at
>
> https://docs.google.com/document/d/14L8BAumGTpsXpjDY8VzZ4rRtpAjuGrFSRqn3stCuS_w
>
> though reviewers will send their feedback to the mailing list anyway, so
> don't feel compelled to address feedback that isn't on the mailing list
> :)

Hi Glenn,

Thanks for hosting the call today. I was impressed by the commitment
to having discussion and making decisions on the public mailing list,
"as though the call never happened". It was a pleasant change from
other open source projects I have contributed to where decisions are
sometimes made behind closed doors with little to no opportunity to
ask questions. Thanks again for making me think a little harder about
my patches; I expect to send a v6 by the end of the week.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 0/3] rebase: add a config option for --rebase-merges
  2023-03-02  5:02     ` Alex Henrie
@ 2023-03-02  5:09       ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-02  5:09 UTC (permalink / raw)
  To: Glen Choo
  Cc: Junio C Hamano, Elijah Newren, Calvin Wan, Jonathan Tan,
	Git mailing list

On Wed, Mar 1, 2023 at 10:02 PM Alex Henrie <alexhenrie24@gmail.com> wrote:
>
> Hi Glenn,

Typo: Glen. No matter how much I proofread, I seem to always notice
typos right after I send the email...

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-01 23:43     ` Glen Choo
@ 2023-03-02  9:37     ` Phillip Wood
  2023-03-04 23:24       ` Alex Henrie
  2023-03-02 18:02     ` Calvin Wan
  2 siblings, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-03-02  9:37 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, Glen Choo, Calvin Wan

Hi Alex

On 25/02/2023 18:03, Alex Henrie wrote:
> The purpose of the new option is to accommodate users who would like
> --rebase-merges to be on by default and to facilitate possibly turning
> on --rebase-merges by default without configuration in a future version
> of Git.
> 
> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>   Documentation/config/rebase.txt        | 10 ++++
>   Documentation/git-rebase.txt           |  3 +-
>   builtin/rebase.c                       | 79 ++++++++++++++++++--------
>   t/t3422-rebase-incompatible-options.sh | 12 ++++
>   t/t3430-rebase-merges.sh               | 63 ++++++++++++++++++++
>   5 files changed, 143 insertions(+), 24 deletions(-)
> 
> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
> index f19bd0e040..308baa9dbb 100644
> --- a/Documentation/config/rebase.txt
> +++ b/Documentation/config/rebase.txt
> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
>   
>   rebase.forkPoint::
>   	If set to false set `--no-fork-point` option by default.
> +
> +rebase.merges::
> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true is equivalent to `--rebase-merges` without an argument, setting to
> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
> +	`--rebase-merges` with that value as its argument, and setting to false
> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line without an argument overrides a `rebase.merges=false`
> +	configuration but does not override other values of `rebase.merge`.

I'm still not clear why the commandline doesn't override the config in 
all cases as is our usual practice. After all if the user has set 
rebase.merges then they don't need to pass --rebase-merges unless they 
want to override the config.

 > [...]
> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   				break;
>   
>   		if (i >= 0 || options.type == REBASE_APPLY) {
> -			if (is_merge(&options))
> -				die(_("apply options and merge options "
> -					  "cannot be used together"));
> -			else if (options.autosquash == -1 && options.config_autosquash == 1)
> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>   				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
> +				die(_("apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges"));

This is good, thanks for adding it

 > [...]
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index f50fbf1390..a825a77fb4 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -283,6 +283,69 @@ test_expect_success '--rebase-merges="" is deprecated' '
>   	grep deprecated actual
>   '
>   
> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b config-rebase-cousins main &&
> +	git rebase HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
> +	test_config rebase.merges no-rebase-cousins &&
> +	git checkout -b override-config-no-rebase-cousins E &&
> +	git rebase --no-rebase-merges C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b override-config-rebase-cousins main &&
> +	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	o | H
> +	|/
> +	o A
> +	EOF
> +'

I'm not sure this test adds much value, it is hard to see what kind of 
regression would allow the others to pass but not this one.

> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
> +	test_config rebase.merges false &&
> +	git checkout -b override-config-merges-false E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase --rebase-merges C &&
> +	test_cmp_rev HEAD $before

This test passes if the rebase does nothing, maybe pass --force and 
check the graph?


Best Wishes

Phillip

> +'
> +
> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b no-override-config-rebase-cousins main &&
> +	git rebase --rebase-merges HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
>   test_expect_success 'refs/rewritten/* is worktree-local' '
>   	git worktree add wt &&
>   	cat >wt/script-from-scratch <<-\EOF &&

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=""
  2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
  2023-03-01 23:46     ` Glen Choo
@ 2023-03-02 10:07     ` Phillip Wood
  2023-03-02 18:02     ` Calvin Wan
  2 siblings, 0 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-02 10:07 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov

Hi Alex

On 25/02/2023 18:03, Alex Henrie wrote:
> The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
> empty string argument) has been an undocumented synonym of
> --rebase-merges=no-rebase-cousins. Deprecate that syntax to avoid
> confusion when a rebase.merges config option is introduced, where
> rebase.merges="" will be equivalent to --no-rebase-merges.

I think deprecating this rather than making it an error is a good idea. 
I don't think we need the test though. The test suite is incredibly slow 
to run on windows (I'd heard people complain about it but it was not 
until I tried running it myself I realized just how diabolically slow it 
really is) and so it is important to balance adding tests to catch 
regression vs test run time. Tests that catch bugs in the rebase 
implementation are useful, ones like this just make everything slower 
which makes it harder to catch real regressions.

Best Wishes

Phillip

> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>   builtin/rebase.c         | 8 ++++++--
>   t/t3430-rebase-merges.sh | 5 +++++
>   2 files changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 6635f10d52..ccc13200b5 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
>   			N_("mode"),
>   			N_("try to rebase merges instead of skipping them"),
> -			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
> +			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
>   		OPT_BOOL(0, "fork-point", &options.fork_point,
>   			 N_("use 'merge-base --fork-point' to refine upstream")),
>   		OPT_STRING('s', "strategy", &options.strategy,
> @@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   
>   	if (rebase_merges) {
>   		if (!*rebase_merges)
> -			; /* default mode; do nothing */
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges=no-rebase-cousins "
> +				  "instead."));
>   		else if (!strcmp("rebase-cousins", rebase_merges))
>   			options.rebase_cousins = 1;
>   		else if (strcmp("no-rebase-cousins", rebase_merges))
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index d46d9545f2..f50fbf1390 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -278,6 +278,11 @@ test_expect_success 'do not rebase cousins unless asked for' '
>   	EOF
>   '
>   
> +test_expect_success '--rebase-merges="" is deprecated' '
> +	git rebase --rebase-merges="" HEAD^ 2>actual &&
> +	grep deprecated actual
> +'
> +
>   test_expect_success 'refs/rewritten/* is worktree-local' '
>   	git worktree add wt &&
>   	cat >wt/script-from-scratch <<-\EOF &&

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 2/3] rebase: deprecate --rebase-merges=""
  2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
  2023-03-01 23:46     ` Glen Choo
  2023-03-02 10:07     ` Phillip Wood
@ 2023-03-02 18:02     ` Calvin Wan
  2 siblings, 0 replies; 96+ messages in thread
From: Calvin Wan @ 2023-03-02 18:02 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Calvin Wan, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov

> -			; /* default mode; do nothing */
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges=no-rebase-cousins "
> +				  "instead."));

Just a small nit on the warning message. It should describe to the user
what behavior is happening here and presumably the user wanted
`--rebase-merges` with no argument. For example, "Defaulting to
--rebase-merges instead."

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-01 23:43     ` Glen Choo
  2023-03-02  9:37     ` Phillip Wood
@ 2023-03-02 18:02     ` Calvin Wan
  2023-03-04 23:24       ` Alex Henrie
  2 siblings, 1 reply; 96+ messages in thread
From: Calvin Wan @ 2023-03-02 18:02 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Calvin Wan, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov

> +
> +rebase.merges::
> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true is equivalent to `--rebase-merges` without an argument, setting to
> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
> +	`--rebase-merges` with that value as its argument, and setting to false
> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line without an argument overrides a `rebase.merges=false`
> +	configuration but does not override other values of `rebase.merge`.

I originally believed that having the boolean values as an option here
created unnecessary complexity but I was convinced during Review Club
that the tradeoff for a user who just wants to turn this on is fine. I'm
just wondering whether this option will possibly have additional values
in the future. If so, what would a user expect from setting this config
to true?

Small typo at the end s/rebase.merge/rebase.merges

> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index c98784a0d2..b02f9cbb8c 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
>  	by recreating the merge commits. Any resolved merge conflicts or
>  	manual amendments in these merge commits will have to be
>  	resolved/re-applied manually. `--no-rebase-merges` can be used to
> -	countermand a previous `--rebase-merges`.
> +	countermand both the `rebase.merges` config option and a previous
> +	`--rebase-merges`.
>  +
>  By default, or when `no-rebase-cousins` was specified, commits which do not
>  have `<upstream>` as direct ancestor will keep their original branch point,
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index ccc13200b5..ac096f27e1 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -123,6 +123,7 @@ struct rebase_options {
>  	int fork_point;
>  	int update_refs;
>  	int config_autosquash;
> +	int config_rebase_merges;
>  	int config_update_refs;
>  };
>  
> @@ -140,6 +141,8 @@ struct rebase_options {
>  		.allow_empty_message = 1,               \
>  		.autosquash = -1,                       \
>  		.config_autosquash = -1,                \
> +		.rebase_merges = -1,                    \
> +		.config_rebase_merges = -1,             \
>  		.update_refs = -1,                      \
>  		.config_update_refs = -1,               \
>  	}
> @@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)
>  	return status ? -1 : 0;
>  }
>  
> +static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
> +{
> +	if (!strcmp("no-rebase-cousins", value))
> +		options->rebase_cousins = 0;
> +	else if (!strcmp("rebase-cousins", value))
> +		options->rebase_cousins = 1;
> +	else
> +		die(_("Unknown rebase-merges mode: %s"), value);
> +}
> +
>  static int rebase_config(const char *var, const char *value, void *data)
>  {
>  	struct rebase_options *opts = data;
> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)
>  		return 0;
>  	}
>  
> +	if (!strcmp(var, "rebase.merges")) {
> +		opts->config_rebase_merges = git_parse_maybe_bool(value);
> +		if (opts->config_rebase_merges < 0) {
> +			opts->config_rebase_merges = 1;
> +			parse_rebase_merges_value(opts, value);
> +		}
> +		return 0;
> +	}
> +
>  	if (!strcmp(var, "rebase.updaterefs")) {
>  		opts->config_update_refs = git_config_bool(var, value);
>  		return 0;
> @@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
>  	return 0;
>  }
>  
> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
> +{
> +	struct rebase_options *options = opt->value;
> +
> +	options->rebase_merges = !unset;
> +
> +	if (arg) {
> +		if (!*arg) {
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges=no-rebase-cousins "
> +				  "instead."));
> +			arg = "no-rebase-cousins";
> +		}
> +		parse_rebase_merges_value(options, arg);
> +	}
> +
> +	return 0;
> +}
> +
>  static void NORETURN error_on_missing_default_upstream(void)
>  {
>  	struct branch *current_branch = branch_get(NULL);
> @@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	struct object_id branch_base;
>  	int ignore_whitespace = 0;
>  	const char *gpg_sign = NULL;
> -	const char *rebase_merges = NULL;
>  	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
>  	struct object_id squash_onto;
>  	char *squash_onto_name = NULL;
> @@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  			   &options.allow_empty_message,
>  			   N_("allow rebasing commits with empty messages"),
>  			   PARSE_OPT_HIDDEN),
> -		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
> -			N_("mode"),
> +		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
>  			N_("try to rebase merges instead of skipping them"),
> -			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
> +			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
>  		OPT_BOOL(0, "fork-point", &options.fork_point,
>  			 N_("use 'merge-base --fork-point' to refine upstream")),
>  		OPT_STRING('s', "strategy", &options.strategy,
> @@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	if (options.exec.nr)
>  		imply_merge(&options, "--exec");
>  
> -	if (rebase_merges) {
> -		if (!*rebase_merges)
> -			warning(_("--rebase-merges with an empty string "
> -				  "argument is deprecated and will stop "
> -				  "working in a future version of Git. Use "
> -				  "--rebase-merges=no-rebase-cousins "
> -				  "instead."));
> -		else if (!strcmp("rebase-cousins", rebase_merges))
> -			options.rebase_cousins = 1;
> -		else if (strcmp("no-rebase-cousins", rebase_merges))
> -			die(_("Unknown mode: %s"), rebase_merges);
> -		options.rebase_merges = 1;
> -		imply_merge(&options, "--rebase-merges");
> -	}
> -
>  	if (options.type == REBASE_APPLY) {
>  		if (ignore_whitespace)
>  			strvec_push(&options.git_am_opts,
> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  				break;
>  
>  		if (i >= 0 || options.type == REBASE_APPLY) {
> -			if (is_merge(&options))
> -				die(_("apply options and merge options "
> -					  "cannot be used together"));
> -			else if (options.autosquash == -1 && options.config_autosquash == 1)
> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>  				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
> +				die(_("apply options are incompatible with rebase.merges.  Consider adding --no-rebase-merges"));
>  			else if (options.update_refs == -1 && options.config_update_refs == 1)
>  				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
> +			else if (is_merge(&options))
> +				die(_("apply options and merge options "
> +					  "cannot be used together"));
>  			else
>  				options.type = REBASE_APPLY;
>  		}
> @@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
>  			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
>  
> +	if (options.rebase_merges == 1)
> +		imply_merge(&options, "--rebase-merges");
> +	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
> +				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
> +
>  	if (options.autosquash == 1)
>  		imply_merge(&options, "--autosquash");
>  	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
> index 4711b37a28..af93f13849 100755
> --- a/t/t3422-rebase-incompatible-options.sh
> +++ b/t/t3422-rebase-incompatible-options.sh
> @@ -101,6 +101,12 @@ test_rebase_am_only () {
>  		grep -e --no-autosquash err
>  	"
>  
> +	test_expect_success "$opt incompatible with rebase.merges" "
> +		git checkout B^0 &&
> +		test_must_fail git -c rebase.merges=true rebase $opt A 2>err &&
> +		grep -e --no-rebase-merges err
> +	"
> +

Missing an additional test here "$opt incompatible with --rebase-merges"
to match the test patterns from the other option/config combos.

>  	test_expect_success "$opt incompatible with rebase.updateRefs" "
>  		git checkout B^0 &&
>  		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
> @@ -113,6 +119,12 @@ test_rebase_am_only () {
>  		git -c rebase.autosquash=true rebase --no-autosquash $opt A
>  	"
>  
> +	test_expect_success "$opt okay with overridden rebase.merges" "
> +		test_when_finished \"git reset --hard B^0\" &&
> +		git checkout B^0 &&
> +		git -c rebase.merges=true rebase --no-rebase-merges $opt A
> +	"
> +
>  	test_expect_success "$opt okay with overridden rebase.updateRefs" "
>  		test_when_finished \"git reset --hard B^0\" &&
>  		git checkout B^0 &&
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index f50fbf1390..a825a77fb4 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -283,6 +283,69 @@ test_expect_success '--rebase-merges="" is deprecated' '
>  	grep deprecated actual
>  '
>  
> +test_expect_success 'rebase.merges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b config-rebase-cousins main &&
> +	git rebase HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
> +test_expect_success '--no-rebase-merges overrides rebase.merges=no-rebase-cousins' '
> +	test_config rebase.merges no-rebase-cousins &&
> +	git checkout -b override-config-no-rebase-cousins E &&
> +	git rebase --no-rebase-merges C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b override-config-rebase-cousins main &&
> +	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	o | H
> +	|/
> +	o A
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
> +	test_config rebase.merges false &&
> +	git checkout -b override-config-merges-false E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase --rebase-merges C &&
> +	test_cmp_rev HEAD $before
> +'
> +
> +test_expect_success '--rebase-merges does not override rebase.merges=rebase-cousins' '
> +	test_config rebase.merges rebase-cousins &&
> +	git checkout -b no-override-config-rebase-cousins main &&
> +	git rebase --rebase-merges HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
>  test_expect_success 'refs/rewritten/* is worktree-local' '
>  	git worktree add wt &&
>  	cat >wt/script-from-scratch <<-\EOF &&
> -- 
> 2.39.2
> 
> 
> 

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-02  9:37     ` Phillip Wood
@ 2023-03-04 23:24       ` Alex Henrie
  2023-03-07 16:23         ` Phillip Wood
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-04 23:24 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, Glen Choo, Calvin Wan

On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> On 25/02/2023 18:03, Alex Henrie wrote:

> > +rebase.merges::
> > +     Whether and how to set the `--rebase-merges` option by default. Can
> > +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> > +     true is equivalent to `--rebase-merges` without an argument, setting to
> > +     `rebase-cousins` or `no-rebase-cousins` is equivalent to
> > +     `--rebase-merges` with that value as its argument, and setting to false
> > +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> > +     command line without an argument overrides a `rebase.merges=false`
> > +     configuration but does not override other values of `rebase.merge`.
>
> I'm still not clear why the commandline doesn't override the config in
> all cases as is our usual practice. After all if the user has set
> rebase.merges then they don't need to pass --rebase-merges unless they
> want to override the config.

Given the current push to turn rebase-merges on by default, it seems
likely that rebase-cousins will also be turned on by default at some
point after that. There will be a warning about the default changing,
and we'll want to let users suppress that warning by setting
rebase.rebaseMerges=rebase-cousins. It would then be very confusing if
a --rebase-merges from some old alias continued to mean
--rebase-merges=no-rebase-cousins when the user expects it to start
behaving as though the default has already changed.

I will rephrase the documentation in v6 to make it more clear that the
absence of a specific value on the command line does not clobber a
specific value set in the configuration, as Glen suggested.

> > +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
> > +     test_config rebase.merges rebase-cousins &&
> > +     git checkout -b override-config-rebase-cousins main &&
> > +     git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
> > +     test_cmp_graph HEAD^.. <<-\EOF
> > +     *   Merge the topic branch '\''onebranch'\''
> > +     |\
> > +     | * D
> > +     | * G
> > +     o | H
> > +     |/
> > +     o A
> > +     EOF
> > +'
>
> I'm not sure this test adds much value, it is hard to see what kind of
> regression would allow the others to pass but not this one.

I was worried that I or someone else would forget to explicitly set
rebase_cousins to 0 when no-rebase-cousins is given on the command
line, assuming that it is already 0 because that is the default. The
test makes me feel better, but I am happy to remove it if you still
think it's overkill.

> > +test_expect_success '--rebase-merges overrides rebase.merges=false' '
> > +     test_config rebase.merges false &&
> > +     git checkout -b override-config-merges-false E &&
> > +     before="$(git rev-parse --verify HEAD)" &&
> > +     test_tick &&
> > +     git rebase --rebase-merges C &&
> > +     test_cmp_rev HEAD $before
>
> This test passes if the rebase does nothing, maybe pass --force and
> check the graph?

The rebase is supposed to do nothing here. Checking that the commit
hash is the same is just a quick way to check that the entire graph is
the same. What more would be checked by checking the graph instead of
the hash?

Thanks for the feedback,

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-02 18:02     ` Calvin Wan
@ 2023-03-04 23:24       ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-04 23:24 UTC (permalink / raw)
  To: Calvin Wan
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov

On Thu, Mar 2, 2023 at 11:02 AM Calvin Wan <calvinwan@google.com> wrote:

> > +rebase.merges::
> > +     Whether and how to set the `--rebase-merges` option by default. Can
> > +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> > +     true is equivalent to `--rebase-merges` without an argument, setting to
> > +     `rebase-cousins` or `no-rebase-cousins` is equivalent to
> > +     `--rebase-merges` with that value as its argument, and setting to false
> > +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> > +     command line without an argument overrides a `rebase.merges=false`
> > +     configuration but does not override other values of `rebase.merge`.
>
> I originally believed that having the boolean values as an option here
> created unnecessary complexity but I was convinced during Review Club
> that the tradeoff for a user who just wants to turn this on is fine. I'm
> just wondering whether this option will possibly have additional values
> in the future. If so, what would a user expect from setting this config
> to true?

"true" will always mean "rebase merges in the default way, I don't
care what it is". During a time period when the default is
transitioning, "true" means that the user will get a warning, but
there's nothing wrong with continuing to use "true" if they really
don't care.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
                     ` (3 preceding siblings ...)
  2023-03-01 23:14   ` [PATCH v5 0/3] " Glen Choo
@ 2023-03-05  5:07   ` Alex Henrie
  2023-03-05  5:07     ` [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                       ` (6 more replies)
  4 siblings, 7 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-05  5:07 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

This patch series introduces a rebase.rebaseMerges config option to
accommodate users who would like --rebase-merges to be on by default and
to facilitate turning on --rebase-merges by default without
configuration in a future version of Git. It also cleans up and
documents the behavior of the --rebase-merges command line option to
avoid confusion about how the config option and the command line option
interact.

Changes from v5:
- Add commit message note about --no-rebase-merges having always worked
- Add commit message note about the test for --no-rebase-merges being
  somewhat contrived
- Rephrase the documentation to avoid using the phrase "By default" with
  two different meanings, and in so doing clarify that --rebase-merges
  without an argument is not the same as --no-rebase-merges and not
  passing --rebase-merges is not the same as passing
  --rebase-merges=no-rebase-cousins
- Add commit message note about keeping --rebase-merges="" for now out
  of an abundance of caution
- Rephrase the warning about --rebase-merges="" to recommend
  --rebase-merges without an argument instead, and clarify that that
  will do the same thing
- Remove the test for --rebase-merges=""
- Rename the config option from rebase.merges to rebase.rebaseMerges and
  explain why in the commit message
- Add commit message note about why "true" is a valid option for
  rebase.rebaseMerges and why --rebase-merges without an argument does
  not clobber the mode specified in rebase.rebaseMerges
- Rephrase the documentation to clarify that --rebase-merges without an
  argument does not clobber the mode specified in rebase.rebaseMerges
- Add another test for incompatible options

Suggestions on v5 not incorporated in v6:
- Make --rebase-merges without an argument clobber the mode specified in
  rebase.rebaseMerges
- Remove the test for --rebase-merges=no-rebase-cousins overriding
  rebase.rebaseMerges=rebase-cousins
- In the tests, check the graph itself instead of checking that the
  graph has not changed by checking that the commit hash has not changed

Thanks to Glen, Calvin, Junio, Jonathan, Elijah, and Phillip for your
feedback on v5. As before, if you feel strongly about one of the
suggestions that I have not incorporated into v6, or if you see
something else amiss, let's continue the discussion.

Alex Henrie (3):
  rebase: add documentation and test for --no-rebase-merges
  rebase: deprecate --rebase-merges=""
  rebase: add a config option for --rebase-merges

 Documentation/config/rebase.txt        | 11 ++++
 Documentation/git-rebase.txt           | 19 ++++---
 builtin/rebase.c                       | 75 ++++++++++++++++++-------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 78 ++++++++++++++++++++++++++
 5 files changed, 174 insertions(+), 26 deletions(-)

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


^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
@ 2023-03-05  5:07     ` Alex Henrie
  2023-03-08 22:25       ` Sergey Organov
  2023-03-05  5:07     ` [PATCH v6 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
                       ` (5 subsequent siblings)
  6 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-05  5:07 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

As far as I can tell, --no-rebase-merges has always worked, but has
never been documented. It is especially important to document it before
a rebase.rebaseMerges option is introduced so that users know how to
override the config option on the command line. It's also important to
clarify that --rebase-merges without an argument is not the same as
--no-rebase-merges and not passing --rebase-merges is not the same as
passing --rebase-merges=no-rebase-cousins.

A test case is necessary to make sure that --no-rebase-merges keeps
working after its code is refactored in the following patches of this
series. The test case is a little contrived: It's unlikely that a user
would type both --rebase-merges and --no-rebase-merges at the same time.
However, if an alias is defined which includes --rebase-merges, the user
might decide to add --no-rebase-merges to countermand that part of the
alias but leave alone other flags set by the alias.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt | 18 +++++++++++-------
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..4e57a87624 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v6 2/3] rebase: deprecate --rebase-merges=""
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-05  5:07     ` [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-05  5:07     ` Alex Henrie
  2023-03-07 14:59       ` Phillip Wood
  2023-03-05  5:07     ` [PATCH v6 3/3] rebase: add a config option for --rebase-merges Alex Henrie
                       ` (4 subsequent siblings)
  6 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-05  5:07 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges without an argument. Deprecate that syntax to avoid
confusion when a rebase.rebaseMerges config option is introduced, where
rebase.rebaseMerges="" will be equivalent to --no-rebase-merges.

It is not likely that anyone is actually using this syntax, but just in
case, deprecate the empty string argument instead of dropping support
for it immediately.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6635f10d52..c36ddc0050 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (rebase_merges) {
 		if (!*rebase_merges)
-			; /* default mode; do nothing */
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
 		else if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-05  5:07     ` [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-03-05  5:07     ` [PATCH v6 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-05  5:07     ` Alex Henrie
  2023-03-07 14:56       ` Phillip Wood
  2023-03-08  0:02       ` Glen Choo
  2023-03-05 12:22     ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Sergey Organov
                       ` (3 subsequent siblings)
  6 siblings, 2 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-05  5:07 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate turning on
--rebase-merges by default without configuration in a future version of
Git.

Name the new option rebase.rebaseMerges, even though it is a little
redundant, for consistency with the name of the command line option and
to be clear when scrolling through values in the [rebase] section of
.gitconfig.

In the future, the default rebase-merges mode may change from
no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges
to the nonspecific value "true" for users who do not need or want to
care about the default changing in the future. Similarly, for users who
have --rebase-merges in an alias and want to get the future behavior
now, use the specific rebase-merges mode from the config if a specific
mode is not given on the command line.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt        | 11 ++++
 Documentation/git-rebase.txt           | 17 +++---
 builtin/rebase.c                       | 79 ++++++++++++++++++--------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 68 ++++++++++++++++++++++
 5 files changed, 161 insertions(+), 31 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..f7d3218b1d 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true is equivalent to `--rebase-merges` without an argument, setting to
+	`rebase-cousins` or `no-rebase-cousins` is equivalent to
+	`--rebase-merges` with that value as its argument, and setting to false
+	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line without an argument overrides a `rebase.rebaseMerges=false`
+	configuration, but the absence of a specific rebase-merges mode on the
+	command line does not counteract a specific mode set in the configuration.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4e57a87624..6ec86c9c6e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,16 +537,17 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.rebaseMerges` config option and a previous
+	`--rebase-merges`.
 +
 When rebasing merges, there are two modes: `rebase-cousins` and
-`no-rebase-cousins`. If the mode is not specified, it defaults to
-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
-`<upstream>` as direct ancestor will keep their original branch point, i.e.
-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
-option will keep their original ancestry by default. In `rebase-cousins` mode,
-such commits are instead rebased onto `<upstream>` (or `<onto>`, if
-specified).
+`no-rebase-cousins`. If the mode is not specified on the command line or in
+the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.
+In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
+ancestor will keep their original branch point, i.e. commits that would be
+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their
+original ancestry by default. In `rebase-cousins` mode, such commits are
+instead rebased onto `<upstream>` (or `<onto>`, if specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c36ddc0050..04f815e3d0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -123,6 +123,7 @@ struct rebase_options {
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -140,6 +141,8 @@ struct rebase_options {
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
 		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 	}
@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.rebasemerges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		}
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
+			return 0;
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			warning(_("--rebase-merges with an empty string "
-				  "argument is deprecated and will stop "
-				  "working in a future version of Git. Use "
-				  "--rebase-merges without an argument "
-				  "instead, which does the same thing."));
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				break;
 
 		if (i >= 0 || options.type == REBASE_APPLY) {
-			if (is_merge(&options))
-				die(_("apply options and merge options "
-					  "cannot be used together"));
-			else if (options.autosquash == -1 && options.config_autosquash == 1)
+			if (options.autosquash == -1 && options.config_autosquash == 1)
 				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
+			else if (is_merge(&options))
+				die(_("apply options and merge options "
+					  "cannot be used together"));
 			else
 				options.type = REBASE_APPLY;
 		}
@@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
 	if (options.autosquash == 1)
 		imply_merge(&options, "--autosquash");
 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..2eba00bdf5 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@ test_rebase_am_only () {
 		test_must_fail git rebase $opt --reapply-cherry-picks A
 	"
 
+	test_expect_success "$opt incompatible with --rebase-merges" "
+		git checkout B^0 &&
+		test_must_fail git rebase $opt --rebase-merges A
+	"
+
 	test_expect_success "$opt incompatible with --update-refs" "
 		git checkout B^0 &&
 		test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
 		grep -e --no-autosquash err
 	"
 
+	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+		git checkout B^0 &&
+		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
+	"
+
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
 		git checkout B^0 &&
 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
 	"
 
+	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+		test_when_finished \"git reset --hard B^0\" &&
+		git checkout B^0 &&
+		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+	"
+
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..aa75e192d1 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success '--rebase-merges="" is deprecated' '
+	git rebase --rebase-merges="" HEAD^ 2>actual &&
+	grep deprecated actual
+'
+
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+	test_config rebase.rebaseMerges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins main &&
+	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	o | H
+	|/
+	o A
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
+	test_config rebase.rebaseMerges false &&
+	git checkout -b override-config-merges-false E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b no-override-config-rebase-cousins main &&
+	git rebase --rebase-merges HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
                       ` (2 preceding siblings ...)
  2023-03-05  5:07     ` [PATCH v6 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-05 12:22     ` Sergey Organov
  2023-03-05 21:33       ` Alex Henrie
  2023-03-06 16:24     ` Phillip Wood
                       ` (2 subsequent siblings)
  6 siblings, 1 reply; 96+ messages in thread
From: Sergey Organov @ 2023-03-05 12:22 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

[...]

> - Rephrase the warning about --rebase-merges="" to recommend
>   --rebase-merges without an argument instead, and clarify that that
>   will do the same thing

Not a strong objection to the series as they are, but, provided options
with optional arguments are considered to be a bad practice in general
(unless something had recently changed), I'd like to vote for adding:

  --rebase-merges=on (≡ --reabase-merges="")

and for suggesting to use that one instead of --rebase-merges="" rather
than naked --rebase-merges.

It'd be best to actually fix --rebase-merges to always have an argument,
the more so as we have '-r' shortcut, but as this is unlikely to fly due
to backward compatibility considerations, this way we would at least
avoid promoting bad practices further.

If accepted, please consider to introduce

  --rebase-merges=off (≡ --no-rebase-merges)

as well for completeness.

BTW, that's how we settled upon in the implementation of --diff-merges,
so these two will then be mutually consistent.

Thanks,
-- Sergey Organov

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05 12:22     ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Sergey Organov
@ 2023-03-05 21:33       ` Alex Henrie
  2023-03-05 22:54         ` Sergey Organov
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-05 21:33 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> [...]
>
> > - Rephrase the warning about --rebase-merges="" to recommend
> >   --rebase-merges without an argument instead, and clarify that that
> >   will do the same thing
>
> Not a strong objection to the series as they are, but, provided options
> with optional arguments are considered to be a bad practice in general
> (unless something had recently changed), I'd like to vote for adding:
>
>   --rebase-merges=on (≡ --reabase-merges="")
>
> and for suggesting to use that one instead of --rebase-merges="" rather
> than naked --rebase-merges.
>
> It'd be best to actually fix --rebase-merges to always have an argument,
> the more so as we have '-r' shortcut, but as this is unlikely to fly due
> to backward compatibility considerations, this way we would at least
> avoid promoting bad practices further.
>
> If accepted, please consider to introduce
>
>   --rebase-merges=off (≡ --no-rebase-merges)
>
> as well for completeness.
>
> BTW, that's how we settled upon in the implementation of --diff-merges,
> so these two will then be mutually consistent.

I am curious as to why you say that flags with optional arguments are
considered bad practice. I don't see an advantage to adding
--rebase-merges=on and then telling users that they ought to always
type the extra three characters, even if we were designing the feature
from scratch. As it is, we're certainly not going to drop support for
--no-rebase-merges or --rebase-merges without an argument, so it seems
fine to advertise them to users. And if we do want to add support for
passing a boolean value as an argument to --rebase-merges, that can be
done after the rebase.rebaseMerges config option is added; it's not a
prerequisite.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05 21:33       ` Alex Henrie
@ 2023-03-05 22:54         ` Sergey Organov
  2023-03-06  0:02           ` Alex Henrie
  2023-03-06 17:19           ` Junio C Hamano
  0 siblings, 2 replies; 96+ messages in thread
From: Sergey Organov @ 2023-03-05 22:54 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

> On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:
>>
>> Alex Henrie <alexhenrie24@gmail.com> writes:
>>
>> [...]
>>
>> > - Rephrase the warning about --rebase-merges="" to recommend
>> >   --rebase-merges without an argument instead, and clarify that that
>> >   will do the same thing
>>
>> Not a strong objection to the series as they are, but, provided options
>> with optional arguments are considered to be a bad practice in general
>> (unless something had recently changed), I'd like to vote for adding:
>>
>>   --rebase-merges=on (≡ --reabase-merges="")
>>
>> and for suggesting to use that one instead of --rebase-merges="" rather
>> than naked --rebase-merges.
>>
>> It'd be best to actually fix --rebase-merges to always have an argument,
>> the more so as we have '-r' shortcut, but as this is unlikely to fly due
>> to backward compatibility considerations, this way we would at least
>> avoid promoting bad practices further.
>>
>> If accepted, please consider to introduce
>>
>>   --rebase-merges=off (≡ --no-rebase-merges)
>>
>> as well for completeness.
>>
>> BTW, that's how we settled upon in the implementation of --diff-merges,
>> so these two will then be mutually consistent.
>
> I am curious as to why you say that flags with optional arguments are
> considered bad practice.

As far as I can tell, the core problem with such options is that generic
options parsing code can't tell if in "--option value" "value" is an
argument to "--option", or separate argument, that in turn may lead to
very surprising behaviors and bugs.

I believe it's a common knowledge here. If I'm wrong, then the rest
simply doesn't make sense.

> I don't see an advantage to adding --rebase-merges=on and then telling
> users that they ought to always type the extra three characters, even
> if we were designing the feature from scratch.

The advantage is that we avoid optional arguments. That said, there is
'-r' that is 13 characters shorter than --rebase-merges, so another
option is to advertise that, provided you are still opposed to
"--rebase-merges=on".

> As it is, we're certainly not going to drop support for
> --no-rebase-merges

--no-rebase-merges is fine, as it has no optional arguments

> or --rebase-merges without an argument,

Yep, but that's a pity and the whole point of my comment: if we can't
fix it, at least don't promote it.

> so it seems fine to advertise them to users.

--no-rebase-merges is fine, but then you don't advertise it anyway.

> And if we do want to add support for passing a boolean value as an
> argument to --rebase-merges, that can be done after the
> rebase.rebaseMerges config option is added; it's not a prerequisite.

Yep, that's why I said at the beginning that this is not an objection to
the series as they are, rather a nitpick.

Thanks,
-- Sergey

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05 22:54         ` Sergey Organov
@ 2023-03-06  0:02           ` Alex Henrie
  2023-03-06 13:23             ` Sergey Organov
  2023-03-06 19:08             ` Junio C Hamano
  2023-03-06 17:19           ` Junio C Hamano
  1 sibling, 2 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-06  0:02 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

On Sun, Mar 5, 2023 at 3:54 PM Sergey Organov <sorganov@gmail.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > On Sun, Mar 5, 2023 at 5:22 AM Sergey Organov <sorganov@gmail.com> wrote:
> >>
> >> Alex Henrie <alexhenrie24@gmail.com> writes:
> >>
> >> [...]
> >>
> >> > - Rephrase the warning about --rebase-merges="" to recommend
> >> >   --rebase-merges without an argument instead, and clarify that that
> >> >   will do the same thing
> >>
> >> Not a strong objection to the series as they are, but, provided options
> >> with optional arguments are considered to be a bad practice in general
> >> (unless something had recently changed), I'd like to vote for adding:
> >>
> >>   --rebase-merges=on (≡ --reabase-merges="")
> >>
> >> and for suggesting to use that one instead of --rebase-merges="" rather
> >> than naked --rebase-merges.
> >>
> >> It'd be best to actually fix --rebase-merges to always have an argument,
> >> the more so as we have '-r' shortcut, but as this is unlikely to fly due
> >> to backward compatibility considerations, this way we would at least
> >> avoid promoting bad practices further.
> >>
> >> If accepted, please consider to introduce
> >>
> >>   --rebase-merges=off (≡ --no-rebase-merges)
> >>
> >> as well for completeness.
> >>
> >> BTW, that's how we settled upon in the implementation of --diff-merges,
> >> so these two will then be mutually consistent.
> >
> > I am curious as to why you say that flags with optional arguments are
> > considered bad practice.
>
> As far as I can tell, the core problem with such options is that generic
> options parsing code can't tell if in "--option value" "value" is an
> argument to "--option", or separate argument, that in turn may lead to
> very surprising behaviors and bugs.

Interesting, thank you for clarifying. I just tried it and it appears
that --rebase-merges requires an equals sign when it has an argument.
For example:

$ git rebase --rebase-merges=rebase-cousins
Current branch master is up to date.

$ git rebase --rebase-merges rebase-cousins
fatal: invalid upstream 'rebase-cousins'

So there is no ambiguity because if an argument to a flag is optional,
an equals sign is required.

Conversely, because the argument to --diff-merges is required, the
equals sign is optional.

I can see the argument (no pun intended) for avoiding optional
arguments to flags because it is confusing that '=' is required for
optional arguments but not for mandatory arguments, and it's easy to
forget which arguments are mandatory and which are optional.

However, --rebase-merges already has an optional argument. If we make
it look more like --diff-merges by accepting the value "on", it could
actually be even more confusing because users would be more likely to
try "--rebase-merges on", thinking that that will work because
"--diff-merges on" works.

Given the current situation, users are just going to have to learn
that in Git, optional arguments to flags must be preceded by an equals
sign. Maybe someday Git will make breaking changes to get rid of
optional arguments to flags, but for better or for worse, that day
will not come soon :-/

> > As it is, we're certainly not going to drop support for
> > --no-rebase-merges
>
> --no-rebase-merges is fine, as it has no optional arguments
>
> > or --rebase-merges without an argument,
>
> Yep, but that's a pity and the whole point of my comment: if we can't
> fix it, at least don't promote it.
>
> > so it seems fine to advertise them to users.
>
> --no-rebase-merges is fine, but then you don't advertise it anyway.

I am not sure what you mean by this. The first patch of the series
adds documentation and a test for --no-rebase-merges, so I am
advertising it. Or are you saying that I /should/ advertise neither
--no-rebase-merges nor --rebase-merges without an argument, because
you think --rebase-merges=off and --rebase-merges=on would be better?

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-06  0:02           ` Alex Henrie
@ 2023-03-06 13:23             ` Sergey Organov
  2023-03-06 19:08             ` Junio C Hamano
  1 sibling, 0 replies; 96+ messages in thread
From: Sergey Organov @ 2023-03-06 13:23 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

[...]

>> > so it seems fine to advertise them to users.
>>
>> --no-rebase-merges is fine, but then you don't advertise it anyway.
>
> I am not sure what you mean by this. The first patch of the series
> adds documentation and a test for --no-rebase-merges, so I am
> advertising it.

Ah, yes, you do it there, and that's fine with me.

I meant only the part where you suggest --rebase-merges instead of
--rebase-merges="". I have no nitpicks about any other parts of the
series.

> Or are you saying that I /should/ advertise neither --no-rebase-merges
> nor --rebase-merges without an argument, because you think
> --rebase-merges=off and --rebase-merges=on would be better?

No, --no-rebase-merges is fine as far as I'm concerned.

Thanks,
-- Sergey

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
                       ` (3 preceding siblings ...)
  2023-03-05 12:22     ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Sergey Organov
@ 2023-03-06 16:24     ` Phillip Wood
  2023-03-06 17:36       ` Alex Henrie
  2023-03-08  0:13     ` Glen Choo
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
  6 siblings, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-03-06 16:24 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, chooglen, calvinwan, jonathantanmy

Hi Alex

On 05/03/2023 05:07, Alex Henrie wrote:
> This patch series introduces a rebase.rebaseMerges config option to
> accommodate users who would like --rebase-merges to be on by default and
> to facilitate turning on --rebase-merges by default without
> configuration in a future version of Git. It also cleans up and
> documents the behavior of the --rebase-merges command line option to
> avoid confusion about how the config option and the command line option
> interact.
> 
> Changes from v5:
> - Add commit message note about --no-rebase-merges having always worked
> - Add commit message note about the test for --no-rebase-merges being
>    somewhat contrived
> - Rephrase the documentation to avoid using the phrase "By default" with
>    two different meanings, and in so doing clarify that --rebase-merges
>    without an argument is not the same as --no-rebase-merges and not
>    passing --rebase-merges is not the same as passing
>    --rebase-merges=no-rebase-cousins
> - Add commit message note about keeping --rebase-merges="" for now out
>    of an abundance of caution
> - Rephrase the warning about --rebase-merges="" to recommend
>    --rebase-merges without an argument instead, and clarify that that
>    will do the same thing
> - Remove the test for --rebase-merges=""
> - Rename the config option from rebase.merges to rebase.rebaseMerges and
>    explain why in the commit message
> - Add commit message note about why "true" is a valid option for
>    rebase.rebaseMerges and why --rebase-merges without an argument does
>    not clobber the mode specified in rebase.rebaseMerges
> - Rephrase the documentation to clarify that --rebase-merges without an
>    argument does not clobber the mode specified in rebase.rebaseMerges
> - Add another test for incompatible options
> 
> Suggestions on v5 not incorporated in v6:

Thanks for adding this, it is really helpful to see what did not change 
as well as what did change. It is also helpful to briefly explain why 
you disagree with the suggestions so others can understand why you 
decided not to make these changes.

> - Make --rebase-merges without an argument clobber the mode specified in
>    rebase.rebaseMerges

I'm still confused as to why we want the config value to change the 
behavior of --rebase-merges. Is it for "git pull --rebase=merges"? If 
that's the case then I think a better approach is for pull to parse 
rebase.merges and pass the appropriate argument to rebase. That way we 
don't break the expectation that command line arguments override config 
values.

> - Remove the test for --rebase-merges=no-rebase-cousins overriding
>    rebase.rebaseMerges=rebase-cousins
> - In the tests, check the graph itself instead of checking that the
>    graph has not changed by checking that the commit hash has not changed

I'm not sure what value the existing test has if it only checks that 
HEAD is unchanged after the rebase. It could be unchanged because the 
rebase fast-forwarded or because it did nothing.

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05 22:54         ` Sergey Organov
  2023-03-06  0:02           ` Alex Henrie
@ 2023-03-06 17:19           ` Junio C Hamano
  1 sibling, 0 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-03-06 17:19 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Alex Henrie, git, tao, newren, phillip.wood123,
	Johannes.Schindelin, chooglen, calvinwan, jonathantanmy

Sergey Organov <sorganov@gmail.com> writes:

>> I am curious as to why you say that flags with optional arguments are
>> considered bad practice.
>
> As far as I can tell, the core problem with such options is that generic
> options parsing code can't tell if in "--option value" "value" is an
> argument to "--option", or separate argument, that in turn may lead to
> very surprising behaviors and bugs.

Another one and half reasons are

 * Can there be two or more such options used on a single command
   line?  Unless we limit the command line and say "such an option
   can appear only at the end of options and without any non option
   arguments" (the latter is what you said above), we'd end up with
   a system that cannot be explained to casual users.

 * What about short-form of option?  Is "-abc" asking for three
   options "-a", "-b", and "-c"?  Or is it "-a" option that takes
   optional argument "bc"?

There are some advices we give in "git help cli" to our users, which
we shouldn't have to have given if we rejected flags with optional
arguments in our commands.

Thanks.



    

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-06 16:24     ` Phillip Wood
@ 2023-03-06 17:36       ` Alex Henrie
  2023-03-07 15:07         ` Phillip Wood
  0 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-06 17:36 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy

On Mon, Mar 6, 2023 at 9:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> On 05/03/2023 05:07, Alex Henrie wrote:

> > Suggestions on v5 not incorporated in v6:
>
> Thanks for adding this, it is really helpful to see what did not change
> as well as what did change. It is also helpful to briefly explain why
> you disagree with the suggestions so others can understand why you
> decided not to make these changes.
>
> > - Make --rebase-merges without an argument clobber the mode specified in
> >    rebase.rebaseMerges
>
> I'm still confused as to why we want the config value to change the
> behavior of --rebase-merges. Is it for "git pull --rebase=merges"? If
> that's the case then I think a better approach is for pull to parse
> rebase.merges and pass the appropriate argument to rebase. That way we
> don't break the expectation that command line arguments override config
> values.
>
> > - Remove the test for --rebase-merges=no-rebase-cousins overriding
> >    rebase.rebaseMerges=rebase-cousins
> > - In the tests, check the graph itself instead of checking that the
> >    graph has not changed by checking that the commit hash has not changed
>
> I'm not sure what value the existing test has if it only checks that
> HEAD is unchanged after the rebase. It could be unchanged because the
> rebase fast-forwarded or because it did nothing.

Please have a look at the email I sent before sending v6:
https://lore.kernel.org/git/CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com/

In that email I tried to explain why I didn't incorporate your three
suggestions on v5. Please let me know if it still isn't clear.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-06  0:02           ` Alex Henrie
  2023-03-06 13:23             ` Sergey Organov
@ 2023-03-06 19:08             ` Junio C Hamano
  1 sibling, 0 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-03-06 19:08 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Sergey Organov, git, tao, newren, phillip.wood123,
	Johannes.Schindelin, chooglen, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

> Interesting, thank you for clarifying. I just tried it and it appears
> that --rebase-merges requires an equals sign when it has an argument.
> For example:
>
> $ git rebase --rebase-merges=rebase-cousins
> Current branch master is up to date.
>
> $ git rebase --rebase-merges rebase-cousins
> fatal: invalid upstream 'rebase-cousins'
>
> So there is no ambiguity because if an argument to a flag is optional,
> an equals sign is required.

Exactly.

It is not a good excuse that users can express something
unambiguously to the machinery once they know they need to use '=',
when they are so accustomed to giving values to ordinary options
without.  This is why options with optional value is considered a
bad UI element, because the way "--opt val" is interpreted for them
is different from everybody else.  And it burdens the users by
forcing them to _know_ which ones are with optional value.

Since it is an existing UI breakage, as long as the series is not
making it worse or harder to fix in the future, it is fine, though.


^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-05  5:07     ` [PATCH v6 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-07 14:56       ` Phillip Wood
  2023-03-07 18:32         ` Junio C Hamano
  2023-03-08  0:09         ` Glen Choo
  2023-03-08  0:02       ` Glen Choo
  1 sibling, 2 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-07 14:56 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, chooglen, calvinwan, jonathantanmy

Hi Alex

On 05/03/2023 05:07, Alex Henrie wrote:
> The purpose of the new option is to accommodate users who would like
> --rebase-merges to be on by default and to facilitate turning on
> --rebase-merges by default without configuration in a future version of
> Git.
> 
> Name the new option rebase.rebaseMerges, even though it is a little
> redundant, for consistency with the name of the command line option and
> to be clear when scrolling through values in the [rebase] section of
> .gitconfig.
> 
> In the future, the default rebase-merges mode may change from
> no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges
> to the nonspecific value "true" for users who do not need or want to
> care about the default changing in the future. Similarly, for users who
> have --rebase-merges in an alias and want to get the future behavior
> now, use the specific rebase-merges mode from the config if a specific
> mode is not given on the command line.
> 
> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>   Documentation/config/rebase.txt        | 11 ++++
>   Documentation/git-rebase.txt           | 17 +++---
>   builtin/rebase.c                       | 79 ++++++++++++++++++--------
>   t/t3422-rebase-incompatible-options.sh | 17 ++++++
>   t/t3430-rebase-merges.sh               | 68 ++++++++++++++++++++++
>   5 files changed, 161 insertions(+), 31 deletions(-)
> 
> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
> index f19bd0e040..f7d3218b1d 100644
> --- a/Documentation/config/rebase.txt
> +++ b/Documentation/config/rebase.txt
> @@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::
>   
>   rebase.forkPoint::
>   	If set to false set `--no-fork-point` option by default.
> +
> +rebase.rebaseMerges::
> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true is equivalent to `--rebase-merges` without an argument,

This is a bit picky but how can rebase.rebaseMerges=true be equivalent 
to --rebase-merges without an argument when the behavior of 
--rebase-merges without an argument depends on the value of 
rebase.rebaseMerges?

> setting to
> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
> +	`--rebase-merges` with that value as its argument, and setting to false
> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line without an argument overrides a `rebase.rebaseMerges=false`
> +	configuration, but the absence of a specific rebase-merges mode on the
> +	command line does not counteract a specific mode set in the configuration.

I may not agree the with the design choice but the documentation here 
and below is very clear about the behavior of --rebase-merges without an 
argument which is good.

> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index 4e57a87624..6ec86c9c6e 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -537,16 +537,17 @@ See also INCOMPATIBLE OPTIONS below.
>   	by recreating the merge commits. Any resolved merge conflicts or
>   	manual amendments in these merge commits will have to be
>   	resolved/re-applied manually. `--no-rebase-merges` can be used to
> -	countermand a previous `--rebase-merges`.
> +	countermand both the `rebase.rebaseMerges` config option and a previous
> +	`--rebase-merges`.
>   +
>   When rebasing merges, there are two modes: `rebase-cousins` and
> -`no-rebase-cousins`. If the mode is not specified, it defaults to
> -`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
> -`<upstream>` as direct ancestor will keep their original branch point, i.e.
> -commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
> -option will keep their original ancestry by default. In `rebase-cousins` mode,
> -such commits are instead rebased onto `<upstream>` (or `<onto>`, if
> -specified).
> +`no-rebase-cousins`. If the mode is not specified on the command line or in
> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.
> +In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
> +ancestor will keep their original branch point, i.e. commits that would be
> +excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their
> +original ancestry by default. In `rebase-cousins` mode, such commits are
> +instead rebased onto `<upstream>` (or `<onto>`, if specified).
 >[...]
>   static int rebase_config(const char *var, const char *value, void *data)
>   {
>   	struct rebase_options *opts = data;
> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)
>   		return 0;
>   	}
>   
> +	if (!strcmp(var, "rebase.rebasemerges")) {
> +		opts->config_rebase_merges = git_parse_maybe_bool(value);
> +		if (opts->config_rebase_merges < 0) {
> +			opts->config_rebase_merges = 1;
> +			parse_rebase_merges_value(opts, value);
> +		}

I think we need

	} else {
		opts->rebase_cousins = 0;
	}

here. Otherwise if rebase.rebaseMerges is set twice we wont follow the 
usual "last config wins" convention. For example

	[rebase]
		rebaseMerges=rebase-cousins
		rebaseMerges=true

will result in us unexpectedly rebasing cousins

The rest of the patch looks fine

Best Wishes

Phillip

> +		return 0;
> +	}
> +
>   	if (!strcmp(var, "rebase.updaterefs")) {
>   		opts->config_update_refs = git_config_bool(var, value);
>   		return 0;
> @@ -980,6 +1002,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
>   	return 0;
>   }
>   
> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
> +{
> +	struct rebase_options *options = opt->value;
> +
> +	options->rebase_merges = !unset;
> +
> +	if (arg) {
> +		if (!*arg) {
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges without an argument "
> +				  "instead, which does the same thing."));
> +			return 0;
> +		}
> +		parse_rebase_merges_value(options, arg);
> +	}
> +
> +	return 0;
> +}
> +
>   static void NORETURN error_on_missing_default_upstream(void)
>   {
>   	struct branch *current_branch = branch_get(NULL);
> @@ -1035,7 +1078,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	struct object_id branch_base;
>   	int ignore_whitespace = 0;
>   	const char *gpg_sign = NULL;
> -	const char *rebase_merges = NULL;
>   	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
>   	struct object_id squash_onto;
>   	char *squash_onto_name = NULL;
> @@ -1137,10 +1179,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   			   &options.allow_empty_message,
>   			   N_("allow rebasing commits with empty messages"),
>   			   PARSE_OPT_HIDDEN),
> -		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
> -			N_("mode"),
> +		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
>   			N_("try to rebase merges instead of skipping them"),
> -			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
> +			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
>   		OPT_BOOL(0, "fork-point", &options.fork_point,
>   			 N_("use 'merge-base --fork-point' to refine upstream")),
>   		OPT_STRING('s', "strategy", &options.strategy,
> @@ -1436,21 +1477,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	if (options.exec.nr)
>   		imply_merge(&options, "--exec");
>   
> -	if (rebase_merges) {
> -		if (!*rebase_merges)
> -			warning(_("--rebase-merges with an empty string "
> -				  "argument is deprecated and will stop "
> -				  "working in a future version of Git. Use "
> -				  "--rebase-merges without an argument "
> -				  "instead, which does the same thing."));
> -		else if (!strcmp("rebase-cousins", rebase_merges))
> -			options.rebase_cousins = 1;
> -		else if (strcmp("no-rebase-cousins", rebase_merges))
> -			die(_("Unknown mode: %s"), rebase_merges);
> -		options.rebase_merges = 1;
> -		imply_merge(&options, "--rebase-merges");
> -	}
> -
>   	if (options.type == REBASE_APPLY) {
>   		if (ignore_whitespace)
>   			strvec_push(&options.git_am_opts,
> @@ -1513,13 +1539,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   				break;
>   
>   		if (i >= 0 || options.type == REBASE_APPLY) {
> -			if (is_merge(&options))
> -				die(_("apply options and merge options "
> -					  "cannot be used together"));
> -			else if (options.autosquash == -1 && options.config_autosquash == 1)
> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>   				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
> +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
>   			else if (options.update_refs == -1 && options.config_update_refs == 1)
>   				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
> +			else if (is_merge(&options))
> +				die(_("apply options and merge options "
> +					  "cannot be used together"));
>   			else
>   				options.type = REBASE_APPLY;
>   		}
> @@ -1530,6 +1558,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
>   			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
>   
> +	if (options.rebase_merges == 1)
> +		imply_merge(&options, "--rebase-merges");
> +	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
> +				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
> +
>   	if (options.autosquash == 1)
>   		imply_merge(&options, "--autosquash");
>   	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
> index 4711b37a28..2eba00bdf5 100755
> --- a/t/t3422-rebase-incompatible-options.sh
> +++ b/t/t3422-rebase-incompatible-options.sh
> @@ -85,6 +85,11 @@ test_rebase_am_only () {
>   		test_must_fail git rebase $opt --reapply-cherry-picks A
>   	"
>   
> +	test_expect_success "$opt incompatible with --rebase-merges" "
> +		git checkout B^0 &&
> +		test_must_fail git rebase $opt --rebase-merges A
> +	"
> +
>   	test_expect_success "$opt incompatible with --update-refs" "
>   		git checkout B^0 &&
>   		test_must_fail git rebase $opt --update-refs A
> @@ -101,6 +106,12 @@ test_rebase_am_only () {
>   		grep -e --no-autosquash err
>   	"
>   
> +	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
> +		git checkout B^0 &&
> +		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
> +		grep -e --no-rebase-merges err
> +	"
> +
>   	test_expect_success "$opt incompatible with rebase.updateRefs" "
>   		git checkout B^0 &&
>   		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
> @@ -113,6 +124,12 @@ test_rebase_am_only () {
>   		git -c rebase.autosquash=true rebase --no-autosquash $opt A
>   	"
>   
> +	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
> +		test_when_finished \"git reset --hard B^0\" &&
> +		git checkout B^0 &&
> +		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
> +	"
> +
>   	test_expect_success "$opt okay with overridden rebase.updateRefs" "
>   		test_when_finished \"git reset --hard B^0\" &&
>   		git checkout B^0 &&
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index d46d9545f2..aa75e192d1 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '
>   	EOF
>   '
>   
> +test_expect_success '--rebase-merges="" is deprecated' '
> +	git rebase --rebase-merges="" HEAD^ 2>actual &&
> +	grep deprecated actual
> +'
> +
> +test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
> +	test_config rebase.rebaseMerges rebase-cousins &&
> +	git checkout -b config-rebase-cousins main &&
> +	git rebase HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
> +test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
> +	test_config rebase.rebaseMerges no-rebase-cousins &&
> +	git checkout -b override-config-no-rebase-cousins E &&
> +	git rebase --no-rebase-merges C &&
> +	test_cmp_graph C.. <<-\EOF
> +	* B
> +	* D
> +	o C
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '
> +	test_config rebase.rebaseMerges rebase-cousins &&
> +	git checkout -b override-config-rebase-cousins main &&
> +	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	o | H
> +	|/
> +	o A
> +	EOF
> +'
> +
> +test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
> +	test_config rebase.rebaseMerges false &&
> +	git checkout -b override-config-merges-false E &&
> +	before="$(git rev-parse --verify HEAD)" &&
> +	test_tick &&
> +	git rebase --rebase-merges C &&
> +	test_cmp_rev HEAD $before
> +'
> +
> +test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '
> +	test_config rebase.rebaseMerges rebase-cousins &&
> +	git checkout -b no-override-config-rebase-cousins main &&
> +	git rebase --rebase-merges HEAD^ &&
> +	test_cmp_graph HEAD^.. <<-\EOF
> +	*   Merge the topic branch '\''onebranch'\''
> +	|\
> +	| * D
> +	| * G
> +	|/
> +	o H
> +	EOF
> +'
> +
>   test_expect_success 'refs/rewritten/* is worktree-local' '
>   	git worktree add wt &&
>   	cat >wt/script-from-scratch <<-\EOF &&

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 2/3] rebase: deprecate --rebase-merges=""
  2023-03-05  5:07     ` [PATCH v6 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-07 14:59       ` Phillip Wood
  0 siblings, 0 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-07 14:59 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, chooglen, calvinwan, jonathantanmy

Hi Alex

On 05/03/2023 05:07, Alex Henrie wrote:
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 6635f10d52..c36ddc0050 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
>   			N_("mode"),
>   			N_("try to rebase merges instead of skipping them"),
> -			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
> +			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},

I've just realized there is a subtle behavior change here. If the user 
passes "--rebase-merges=rebase-cousins --rebase-merges" we used to 
rebase cousins but now we wont. The next patch goes back to the old 
behavior so I don't think we need to worry about it.

Best Wishes

Phillip

>   		OPT_BOOL(0, "fork-point", &options.fork_point,
>   			 N_("use 'merge-base --fork-point' to refine upstream")),
>   		OPT_STRING('s', "strategy", &options.strategy,
> @@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   
>   	if (rebase_merges) {
>   		if (!*rebase_merges)
> -			; /* default mode; do nothing */
> +			warning(_("--rebase-merges with an empty string "
> +				  "argument is deprecated and will stop "
> +				  "working in a future version of Git. Use "
> +				  "--rebase-merges without an argument "
> +				  "instead, which does the same thing."));
>   		else if (!strcmp("rebase-cousins", rebase_merges))
>   			options.rebase_cousins = 1;
>   		else if (strcmp("no-rebase-cousins", rebase_merges))

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-06 17:36       ` Alex Henrie
@ 2023-03-07 15:07         ` Phillip Wood
  0 siblings, 0 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-07 15:07 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy

Hi Alex

On 06/03/2023 17:36, Alex Henrie wrote:
> On Mon, Mar 6, 2023 at 9:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> Please have a look at the email I sent before sending v6:
> https://lore.kernel.org/git/CAMMLpeRfD+81fsEtvKFvVepPpwYm0_-AD=QHMwhoc+LtiXpavw@mail.gmail.com/

Thanks for the link, I'd missed that email.

> In that email I tried to explain why I didn't incorporate your three
> suggestions on v5. Please let me know if it still isn't clear.

I'll take a look in the next couple of days and let you know

Thanks

Phillip

> -Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-04 23:24       ` Alex Henrie
@ 2023-03-07 16:23         ` Phillip Wood
  2023-03-12 20:57           ` Alex Henrie
  0 siblings, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-03-07 16:23 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, Glen Choo, Calvin Wan

Hi Alex

On 04/03/2023 23:24, Alex Henrie wrote:
> On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> 
>> On 25/02/2023 18:03, Alex Henrie wrote:
> 
>>> +rebase.merges::
>>> +     Whether and how to set the `--rebase-merges` option by default. Can
>>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
>>> +     true is equivalent to `--rebase-merges` without an argument, setting to
>>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to
>>> +     `--rebase-merges` with that value as its argument, and setting to false
>>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
>>> +     command line without an argument overrides a `rebase.merges=false`
>>> +     configuration but does not override other values of `rebase.merge`.
>>
>> I'm still not clear why the commandline doesn't override the config in
>> all cases as is our usual practice. After all if the user has set
>> rebase.merges then they don't need to pass --rebase-merges unless they
>> want to override the config.
> 
> Given the current push to turn rebase-merges on by default, it seems
> likely that rebase-cousins will also be turned on by default at some
> point after that.

It is good to try and future proof things but this seems rather 
hypothetical. I don't really see how the choice of whether 
--rebase-merges is turned on by default is related to the choice of 
whether or not to rebase cousins by default. It is worth noting that the 
default was changed to from rebase-cousins to no-rebase-cousins early in 
the development of --rebase-merges[1] as it was felt to be less surprising.

> There will be a warning about the default changing,
> and we'll want to let users suppress that warning by setting
> rebase.rebaseMerges=rebase-cousins. It would then be very confusing if
> a --rebase-merges from some old alias continued to mean
> --rebase-merges=no-rebase-cousins when the user expects it to start
> behaving as though the default has already changed.

But aren't you breaking those aliases now when 
rebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It 
seems like we're breaking things now to avoid a hypothetical future 
change breaking them which does not seem like the right trade off to me.

It also does not fit with the way other optional arguments interact with 
their associated config setting. For example "git branch/checkout/switch 
--track" and branch.autoSetupMerge. If the optional argument to --track 
is omitted it defaults to "direct" irrespective of the config.

[1] 
https://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/

> I will rephrase the documentation in v6 to make it more clear that the
> absence of a specific value on the command line does not clobber a
> specific value set in the configuration, as Glen suggested.

The documentation in v6 is certainly quite clear on this point.

>>> +test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.merges=rebase-cousins' '
>>> +     test_config rebase.merges rebase-cousins &&
>>> +     git checkout -b override-config-rebase-cousins main &&
>>> +     git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
>>> +     test_cmp_graph HEAD^.. <<-\EOF
>>> +     *   Merge the topic branch '\''onebranch'\''
>>> +     |\
>>> +     | * D
>>> +     | * G
>>> +     o | H
>>> +     |/
>>> +     o A
>>> +     EOF
>>> +'
>>
>> I'm not sure this test adds much value, it is hard to see what kind of
>> regression would allow the others to pass but not this one.
> 
> I was worried that I or someone else would forget to explicitly set
> rebase_cousins to 0 when no-rebase-cousins is given on the command
> line, assuming that it is already 0 because that is the default. The
> test makes me feel better, but I am happy to remove it if you still
> think it's overkill.

Given we're using the same code to parse the command line argument and 
the config setting and we have a test for 
rebase.rebaseMerges=no-rebase-cousins I think we could drop it.

>>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
>>> +     test_config rebase.merges false &&
>>> +     git checkout -b override-config-merges-false E &&
>>> +     before="$(git rev-parse --verify HEAD)" &&
>>> +     test_tick &&
>>> +     git rebase --rebase-merges C &&
>>> +     test_cmp_rev HEAD $before
>>
>> This test passes if the rebase does nothing, maybe pass --force and
>> check the graph?
> 
> The rebase is supposed to do nothing here.

It's not doing nothing though - it is rebasing the branch, it just 
happens that everything fast-forwards so HEAD ends up unchanged. My 
point is that this test should verify the branch has been rebased. Maybe 
you could check the reflog message for HEAD@{0} is

	rebase (finish): returning to refs/heads/override-config-merges-false

> Checking that the commit
> hash is the same is just a quick way to check that the entire graph is
> the same. What more would be checked by checking the graph instead of
> the hash?

By using --force and checking the graph you check that the rebase 
actually happened.

Thanks for working on this

Phillip

> Thanks for the feedback,
> 
> -Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-07 14:56       ` Phillip Wood
@ 2023-03-07 18:32         ` Junio C Hamano
  2023-03-12 21:01           ` Alex Henrie
  2023-03-08  0:09         ` Glen Choo
  1 sibling, 1 reply; 96+ messages in thread
From: Junio C Hamano @ 2023-03-07 18:32 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Alex Henrie, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy

Phillip Wood <phillip.wood123@gmail.com> writes:

>> +rebase.rebaseMerges::
>> +	Whether and how to set the `--rebase-merges` option by default. Can
>> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
>> +	true is equivalent to `--rebase-merges` without an argument,
>
> This is a bit picky but how can rebase.rebaseMerges=true be equivalent
> to --rebase-merges without an argument when the behavior of
> --rebase-merges without an argument depends on the value of
> rebase.rebaseMerges?

True.  I think the configuration is meant to give (when set to
something other than Boolean) the default value to the option
"--rebase-merges" that is given without value, so setting to false
should be a no-op (a command line option would override it if given,
and if there is no command line option, --rebase-merges is not used
by default), setting it to a specific value between cousin choices
would give --rebase-merges=<value> unless --no-rebase-merges is
given, but setting it to true here makes the result undefined,
unless the built-in default between cousin choices is described
here.

"Setting to true is equivalent to setting to no-rebase-cousins" and
"Setting to false is a no-op but accepted only for completeness",
perhaps?

>> setting to
>> +	`rebase-cousins` or `no-rebase-cousins` is equivalent to
>> +	`--rebase-merges` with that value as its argument, and setting to false
>> +	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
>> +	command line without an argument overrides a `rebase.rebaseMerges=false`
>> +	configuration, but the absence of a specific rebase-merges mode on the
>> +	command line does not counteract a specific mode set in the configuration.
>
> I may not agree the with the design choice but the documentation here
> and below is very clear about the behavior of --rebase-merges without
> an argument which is good.

Is it?  rebase.rebaseMerges=true does not give "a specific mode set
in the configuration", so it still is unclear what --rebase-merges
should do in that case.  Unless what it means to set it to true is
described, as you pointed out above, that is.

>> +`no-rebase-cousins`. If the mode is not specified on the command line or in
>> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.

This side could describe what setting it to "true" means, but it is
a separate page so it would be more friendly to readers to cover it
on both pages.

> I think we need
>
> 	} else {
> 		opts->rebase_cousins = 0;
> 	}
>
> here. Otherwise if rebase.rebaseMerges is set twice we wont follow the
> usual "last config wins" convention. For example
>
> 	[rebase]
> 		rebaseMerges=rebase-cousins
> 		rebaseMerges=true
>
> will result in us unexpectedly rebasing cousins

Thanks for a careful review.


^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-05  5:07     ` [PATCH v6 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-07 14:56       ` Phillip Wood
@ 2023-03-08  0:02       ` Glen Choo
  2023-03-12 21:03         ` Alex Henrie
  2023-03-15  2:52         ` Alex Henrie
  1 sibling, 2 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-08  0:02 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, calvinwan, jonathantanmy
  Cc: Alex Henrie

Alex Henrie <alexhenrie24@gmail.com> writes:

> The purpose of the new option is to accommodate users who would like
> --rebase-merges to be on by default and to facilitate turning on
> --rebase-merges by default without configuration in a future version of
> Git.
>
> Name the new option rebase.rebaseMerges, even though it is a little
> redundant, for consistency with the name of the command line option and
> to be clear when scrolling through values in the [rebase] section of
> .gitconfig.

This rationale makes sense to me.

> In the future, the default rebase-merges mode may change from
> no-rebase-cousins to rebase-cousins.

I suspect a more likely future would be that the default is to rebase
'evil' merges instead of trying to recreate merge commits, but of
course, the important thing is that we promote the default, not what the
default will be...

>                                      Support setting rebase.rebaseMerges
> to the nonspecific value "true" for users who do not need or want to
> care about the default changing in the future. Similarly, for users who
> have --rebase-merges in an alias and want to get the future behavior
> now, use the specific rebase-merges mode from the config if a specific
> mode is not given on the command line.

so this rationale makes sense to me too :)

> @@ -278,6 +278,74 @@ test_expect_success 'do not rebase cousins unless asked for' '
>  	EOF
>  '
>  
> +test_expect_success '--rebase-merges="" is deprecated' '
> +	git rebase --rebase-merges="" HEAD^ 2>actual &&
> +	grep deprecated actual
> +'

I believe this used to be on 2/3, i.e.

  https://lore.kernel.org/git/20230225180325.796624-3-alexhenrie24@gmail.com/

but your cover letter suggests that it was removed. Mechanical error?

The rest of changes look good (besides what others have spotted).

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-07 14:56       ` Phillip Wood
  2023-03-07 18:32         ` Junio C Hamano
@ 2023-03-08  0:09         ` Glen Choo
  1 sibling, 0 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-08  0:09 UTC (permalink / raw)
  To: phillip.wood, Alex Henrie, git, tao, gitster, newren,
	phillip.wood123, Johannes.Schindelin, sorganov, calvinwan,
	jonathantanmy

Phillip Wood <phillip.wood123@gmail.com> writes:

>> @@ -800,6 +813,15 @@ static int rebase_config(const char *var, const char *value, void *data)
>>   		return 0;
>>   	}
>>   
>> +	if (!strcmp(var, "rebase.rebasemerges")) {
>> +		opts->config_rebase_merges = git_parse_maybe_bool(value);
>> +		if (opts->config_rebase_merges < 0) {
>> +			opts->config_rebase_merges = 1;
>> +			parse_rebase_merges_value(opts, value);
>> +		}
>
> I think we need
>
> 	} else {
> 		opts->rebase_cousins = 0;
> 	}
>
> here. Otherwise if rebase.rebaseMerges is set twice we wont follow the 
> usual "last config wins" convention. For example
>
> 	[rebase]
> 		rebaseMerges=rebase-cousins
> 		rebaseMerges=true
>
> will result in us unexpectedly rebasing cousins

Ah, good catch.

An alternative might be to use the "lookup" functions (e.g.
repo_config_get_maybe_bool()), which already implement "last one wins"
semantics. It's a bigger change, but it may be easier for future readers
to understand. I don't have a strong opinion on which is the 'better'
approach.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
                       ` (4 preceding siblings ...)
  2023-03-06 16:24     ` Phillip Wood
@ 2023-03-08  0:13     ` Glen Choo
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
  6 siblings, 0 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-08  0:13 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, calvinwan, jonathantanmy
  Cc: Alex Henrie

Alex Henrie <alexhenrie24@gmail.com> writes:

> Changes from v5:
> - Add commit message note about --no-rebase-merges having always worked
> - Add commit message note about the test for --no-rebase-merges being
>   somewhat contrived
> - Rephrase the documentation to avoid using the phrase "By default" with
>   two different meanings, and in so doing clarify that --rebase-merges
>   without an argument is not the same as --no-rebase-merges and not
>   passing --rebase-merges is not the same as passing
>   --rebase-merges=no-rebase-cousins
> - Add commit message note about keeping --rebase-merges="" for now out
>   of an abundance of caution
> - Rephrase the warning about --rebase-merges="" to recommend
>   --rebase-merges without an argument instead, and clarify that that
>   will do the same thing
> - Remove the test for --rebase-merges=""
> - Rename the config option from rebase.merges to rebase.rebaseMerges and
>   explain why in the commit message
> - Add commit message note about why "true" is a valid option for
>   rebase.rebaseMerges and why --rebase-merges without an argument does
>   not clobber the mode specified in rebase.rebaseMerges
> - Rephrase the documentation to clarify that --rebase-merges without an
>   argument does not clobber the mode specified in rebase.rebaseMerges
> - Add another test for incompatible options

This version addresses all of the concerns I had with the previous
version. Thanks!

Besides the concerns that other reviewers raised and a possible
mechanical error, I don't have any outstanding concerns. I'd be happy
to see this merged when those are resolved :)

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-03-05  5:07     ` [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-08 22:25       ` Sergey Organov
  0 siblings, 0 replies; 96+ messages in thread
From: Sergey Organov @ 2023-03-08 22:25 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	chooglen, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

[...]

> +When rebasing merges, there are two modes: `rebase-cousins` and
> +`no-rebase-cousins`. If the mode is not specified, it defaults to
> +`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
> +`<upstream>` as direct ancestor will keep their original branch point, i.e.

I realize this is in fact unchanged from the original, so is not exactly
material of these series, but what is the meaning of "direct ancestor"?
Is it just "parent"?

> +commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
> +option will keep their original ancestry by default.

Excluded when --ancestry-path is applied to what commit, exactly? To the
commit being considered for rebase, or to the <upstream>, or to the
'fork_point'?

Please notice that rebase claims to operate either on <upstream>..HEAD or
'fork_point'..HEAD range, and --ancestry-path without arguments applies
to the left commit of the range when used in "git log".

Looks like some clarifications are needed here, even though maybe not in
these series?

Thanks,
-- Sergey Organov

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-07 16:23         ` Phillip Wood
@ 2023-03-12 20:57           ` Alex Henrie
  2023-03-13 14:20             ` Phillip Wood
                               ` (2 more replies)
  0 siblings, 3 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 20:57 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, Glen Choo, Calvin Wan

On Tue, Mar 7, 2023 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> On 04/03/2023 23:24, Alex Henrie wrote:
> > On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >
> >> On 25/02/2023 18:03, Alex Henrie wrote:
> >
> >>> +rebase.merges::
> >>> +     Whether and how to set the `--rebase-merges` option by default. Can
> >>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> >>> +     true is equivalent to `--rebase-merges` without an argument, setting to
> >>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to
> >>> +     `--rebase-merges` with that value as its argument, and setting to false
> >>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> >>> +     command line without an argument overrides a `rebase.merges=false`
> >>> +     configuration but does not override other values of `rebase.merge`.
> >>
> >> I'm still not clear why the commandline doesn't override the config in
> >> all cases as is our usual practice. After all if the user has set
> >> rebase.merges then they don't need to pass --rebase-merges unless they
> >> want to override the config.
> >
> > Given the current push to turn rebase-merges on by default, it seems
> > likely that rebase-cousins will also be turned on by default at some
> > point after that.
>
> It is good to try and future proof things but this seems rather
> hypothetical. I don't really see how the choice of whether
> --rebase-merges is turned on by default is related to the choice of
> whether or not to rebase cousins by default. It is worth noting that the
> default was changed to from rebase-cousins to no-rebase-cousins early in
> the development of --rebase-merges[1] as it was felt to be less surprising.

> [1]
> https://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/

Thank you for sharing that link. Even though I got the tests right, I
got confused and started thinking that rebase-cousins was a more
thorough version of rebase-merges. In fact, they do opposite things:
rebase-merges tries to preserve the graph and rebase-cousins
intentionally restructures the graph. In my opinion using the word
"rebase" in the names of both options was another unfortunate UI
decision, but I understand the difference now.

> > There will be a warning about the default changing,
> > and we'll want to let users suppress that warning by setting
> > rebase.rebaseMerges=rebase-cousins. It would then be very confusing if
> > a --rebase-merges from some old alias continued to mean
> > --rebase-merges=no-rebase-cousins when the user expects it to start
> > behaving as though the default has already changed.
>
> But aren't you breaking those aliases now when
> rebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It
> seems like we're breaking things now to avoid a hypothetical future
> change breaking them which does not seem like the right trade off to me.
>
> It also does not fit with the way other optional arguments interact with
> their associated config setting. For example "git branch/checkout/switch
> --track" and branch.autoSetupMerge. If the optional argument to --track
> is omitted it defaults to "direct" irrespective of the config.

What I really don't want is to paint ourselves into a corner. You're
right that it's unlikely that the default will ever change from
no-rebase-cousins to rebase-cousins; I was mistaken. However, Glen
thinks that in the future we might have some kind of
rebase-evil-merges mode as well, and that that might become the
default. If we don't let the rebase.rebaseMerges config value control
the default behavior of --rebase-merges without an argument on the
command line, we would have to introduce a separate config option for
the transition, which would be ugly.

More voices would be helpful here. Does anyone else have an opinion on
how likely it is that the default rebase-merges mode will change in
the future? Or on whether rebase.rebaseMerges should be allowed to
affect --rebase-merges in order to facilitate such a change?

> >>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
> >>> +     test_config rebase.merges false &&
> >>> +     git checkout -b override-config-merges-false E &&
> >>> +     before="$(git rev-parse --verify HEAD)" &&
> >>> +     test_tick &&
> >>> +     git rebase --rebase-merges C &&
> >>> +     test_cmp_rev HEAD $before
> >>
> >> This test passes if the rebase does nothing, maybe pass --force and
> >> check the graph?
> >
> > The rebase is supposed to do nothing here.
>
> It's not doing nothing though - it is rebasing the branch, it just
> happens that everything fast-forwards so HEAD ends up unchanged. My
> point is that this test should verify the branch has been rebased. Maybe
> you could check the reflog message for HEAD@{0} is
>
>         rebase (finish): returning to refs/heads/override-config-merges-false
>
> > Checking that the commit
> > hash is the same is just a quick way to check that the entire graph is
> > the same. What more would be checked by checking the graph instead of
> > the hash?
>
> By using --force and checking the graph you check that the rebase
> actually happened.

I got the impression that people like that not checking the graph
itself (or the reflog) makes the tests more concise, but I don't care
much either way. For what it's worth, the way I did it matches the
existing tests in the file. If you can find at least one other person
who thinks that it ought to change for this patch series to be
accepted, and no one else objects, I'll change it.

> Thanks for working on this

You're welcome; hopefully we can get the remaining details ironed out quickly.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-07 18:32         ` Junio C Hamano
@ 2023-03-12 21:01           ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:01 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy

On Tue, Mar 7, 2023 at 11:32 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> >> +rebase.rebaseMerges::
> >> +    Whether and how to set the `--rebase-merges` option by default. Can
> >> +    be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> >> +    true is equivalent to `--rebase-merges` without an argument,
> >
> > This is a bit picky but how can rebase.rebaseMerges=true be equivalent
> > to --rebase-merges without an argument when the behavior of
> > --rebase-merges without an argument depends on the value of
> > rebase.rebaseMerges?
>
> True.  I think the configuration is meant to give (when set to
> something other than Boolean) the default value to the option
> "--rebase-merges" that is given without value, so setting to false
> should be a no-op (a command line option would override it if given,
> and if there is no command line option, --rebase-merges is not used
> by default), setting it to a specific value between cousin choices
> would give --rebase-merges=<value> unless --no-rebase-merges is
> given, but setting it to true here makes the result undefined,
> unless the built-in default between cousin choices is described
> here.
>
> "Setting to true is equivalent to setting to no-rebase-cousins" and
> "Setting to false is a no-op but accepted only for completeness",
> perhaps?

A false in the local configuration overrides a true in the global
configuration, so I wouldn't call "false" a no-op. But it would be
fine to say that "true" is equivalent to no-rebase-cousins (and then
change the documentation for "true" if and when the default changes in
the future).

> >> +`no-rebase-cousins`. If the mode is not specified on the command line or in
> >> +the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.
>
> This side could describe what setting it to "true" means, but it is
> a separate page so it would be more friendly to readers to cover it
> on both pages.

The longer the documentation is, the less likely people are to read
any of it. I don't really want to repeat the detailed explanation that
is in git-config.txt, but I see that other places in git-rebase.txt
link to git-config.txt, and having a link seems like a good idea. Fair
enough?

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-08  0:02       ` Glen Choo
@ 2023-03-12 21:03         ` Alex Henrie
  2023-03-15  2:52         ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:03 UTC (permalink / raw)
  To: Glen Choo
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

On Tue, Mar 7, 2023 at 5:02 PM Glen Choo <chooglen@google.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:

> > +test_expect_success '--rebase-merges="" is deprecated' '
> > +     git rebase --rebase-merges="" HEAD^ 2>actual &&
> > +     grep deprecated actual
> > +'
>
> I believe this used to be on 2/3, i.e.
>
>   https://lore.kernel.org/git/20230225180325.796624-3-alexhenrie24@gmail.com/
>
> but your cover letter suggests that it was removed. Mechanical error?

I removed it from the second patch but accidentally added it back in
the third patch. Thanks for catching that.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v7 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
                       ` (5 preceding siblings ...)
  2023-03-08  0:13     ` Glen Choo
@ 2023-03-12 21:04     ` Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                         ` (3 more replies)
  6 siblings, 4 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:04 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

This patch series introduces a rebase.rebaseMerges config option to
accommodate users who would like --rebase-merges to be on by default and
to facilitate turning on --rebase-merges by default without
configuration in a future version of Git. It also cleans up and
documents the behavior of the --rebase-merges command line option to
avoid confusion about how the config option and the command line option
interact.

Changes from v6:
- Don't say that the default rebase-merges mode will likely change to
  rebase-cousins (although it might change to something else)
- In git-config.txt, say that rebase.rebaseMerges=true is equivalent
  to --rebase-merges=no-rebase-cousins
- Add a link from the revised paragraph in git-rebase.txt to the
  corresponding new section in git-config.txt
- Clear rebase_cousins if rebase.rebaseMerges is set to true after
  having been set to rebase-cousins or no-rebase-cousins
- Actually remove the test for --rebase-merges=""
- Remove the test for --rebase-merges=no-rebase-cousins overriding
  rebase.rebaseMerges=rebase-cousins

Suggestions on v6 not incorporated in v7:
- Make --rebase-merges without an argument clobber the mode specified in
  rebase.rebaseMerges
- In the tests, pass --force and check the graph itself or the reflog
  instead of checking that the graph has not changed by checking that
  the commit hash has not changed
- Add --rebase-merges=on as a synonym of
  --rebase-merges=no-rebase-cousins and --rebase-merges=off as a synonym
  of --no-rebase-merges
- Rewrite the documentation for rebase-cousins to more clearly explain
  the difference between rebase-cousins and no-rebase-cousins

Thanks to Phillip, Junio, Glen, and Sergey for your feedback on v6.

Alex Henrie (3):
  rebase: add documentation and test for --no-rebase-merges
  rebase: deprecate --rebase-merges=""
  rebase: add a config option for --rebase-merges

 Documentation/config/rebase.txt        | 11 ++++
 Documentation/git-rebase.txt           | 20 ++++---
 builtin/rebase.c                       | 76 +++++++++++++++++++-------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 58 ++++++++++++++++++++
 5 files changed, 156 insertions(+), 26 deletions(-)

Range-diff against v6:
1:  bf08c03ba7 = 1:  3aee0c2277 rebase: add documentation and test for --no-rebase-merges
2:  26f98b8400 = 2:  e57843d8b5 rebase: deprecate --rebase-merges=""
3:  402365256c ! 3:  b0c1a4dcb2 rebase: add a config option for --rebase-merges
    @@ Commit message
         .gitconfig.
     
         In the future, the default rebase-merges mode may change from
    -    no-rebase-cousins to rebase-cousins. Support setting rebase.rebaseMerges
    -    to the nonspecific value "true" for users who do not need or want to
    -    care about the default changing in the future. Similarly, for users who
    -    have --rebase-merges in an alias and want to get the future behavior
    -    now, use the specific rebase-merges mode from the config if a specific
    -    mode is not given on the command line.
    +    no-rebase-cousins to some other mode that doesn't exist yet. Support
    +    setting rebase.rebaseMerges to the nonspecific value "true" for users
    +    who do not need or want to care about the default changing in the
    +    future. Similarly, for users who have --rebase-merges in an alias and
    +    want to get the future behavior now, use the specific rebase-merges mode
    +    from the config if a specific mode is not given on the command line.
     
         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
     
    @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::
     +rebase.rebaseMerges::
     +	Whether and how to set the `--rebase-merges` option by default. Can
     +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
    -+	true is equivalent to `--rebase-merges` without an argument, setting to
    -+	`rebase-cousins` or `no-rebase-cousins` is equivalent to
    -+	`--rebase-merges` with that value as its argument, and setting to false
    -+	is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
    ++	true or to `no-rebase-cousins` is equivalent to
    ++	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
    ++	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
    ++	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
     +	command line without an argument overrides a `rebase.rebaseMerges=false`
     +	configuration, but the absence of a specific rebase-merges mode on the
     +	command line does not counteract a specific mode set in the configuration.
    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
     -such commits are instead rebased onto `<upstream>` (or `<onto>`, if
     -specified).
     +`no-rebase-cousins`. If the mode is not specified on the command line or in
    -+the `rebase.rebaseMerges` config option, it defaults to `no-rebase-cousins`.
    -+In `no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
    ++the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or
    ++"CONFIGURATION" below), it defaults to `no-rebase-cousins`. In
    ++`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
     +ancestor will keep their original branch point, i.e. commits that would be
     +excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their
     +original ancestry by default. In `rebase-cousins` mode, such commits are
    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
     +		if (opts->config_rebase_merges < 0) {
     +			opts->config_rebase_merges = 1;
     +			parse_rebase_merges_value(opts, value);
    -+		}
    ++		} else
    ++			opts->rebase_cousins = 0;
     +		return 0;
     +	}
     +
    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske
      	EOF
      '
      
    -+test_expect_success '--rebase-merges="" is deprecated' '
    -+	git rebase --rebase-merges="" HEAD^ 2>actual &&
    -+	grep deprecated actual
    -+'
    -+
     +test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
     +	test_config rebase.rebaseMerges rebase-cousins &&
     +	git checkout -b config-rebase-cousins main &&
    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske
     +	EOF
     +'
     +
    -+test_expect_success '--rebase-merges=no-rebase-cousins overrides rebase.rebaseMerges=rebase-cousins' '
    -+	test_config rebase.rebaseMerges rebase-cousins &&
    -+	git checkout -b override-config-rebase-cousins main &&
    -+	git rebase --rebase-merges=no-rebase-cousins HEAD^ &&
    -+	test_cmp_graph HEAD^.. <<-\EOF
    -+	*   Merge the topic branch '\''onebranch'\''
    -+	|\
    -+	| * D
    -+	| * G
    -+	o | H
    -+	|/
    -+	o A
    -+	EOF
    -+'
    -+
     +test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
     +	test_config rebase.rebaseMerges false &&
     +	git checkout -b override-config-merges-false E &&
-- 
2.39.2


^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
@ 2023-03-12 21:04       ` Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:04 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

As far as I can tell, --no-rebase-merges has always worked, but has
never been documented. It is especially important to document it before
a rebase.rebaseMerges option is introduced so that users know how to
override the config option on the command line. It's also important to
clarify that --rebase-merges without an argument is not the same as
--no-rebase-merges and not passing --rebase-merges is not the same as
passing --rebase-merges=no-rebase-cousins.

A test case is necessary to make sure that --no-rebase-merges keeps
working after its code is refactored in the following patches of this
series. The test case is a little contrived: It's unlikely that a user
would type both --rebase-merges and --no-rebase-merges at the same time.
However, if an alias is defined which includes --rebase-merges, the user
might decide to add --no-rebase-merges to countermand that part of the
alias but leave alone other flags set by the alias.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt | 18 +++++++++++-------
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..4e57a87624 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v7 2/3] rebase: deprecate --rebase-merges=""
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-12 21:04       ` Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:04 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges without an argument. Deprecate that syntax to avoid
confusion when a rebase.rebaseMerges config option is introduced, where
rebase.rebaseMerges="" will be equivalent to --no-rebase-merges.

It is not likely that anyone is actually using this syntax, but just in
case, deprecate the empty string argument instead of dropping support
for it immediately.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6635f10d52..c36ddc0050 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1140,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1438,7 +1438,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (rebase_merges) {
 		if (!*rebase_merges)
-			; /* default mode; do nothing */
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
 		else if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v7 3/3] rebase: add a config option for --rebase-merges
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-03-12 21:04       ` [PATCH v7 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-12 21:04       ` Alex Henrie
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-12 21:04 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate turning on
--rebase-merges by default without configuration in a future version of
Git.

Name the new option rebase.rebaseMerges, even though it is a little
redundant, for consistency with the name of the command line option and
to be clear when scrolling through values in the [rebase] section of
.gitconfig.

In the future, the default rebase-merges mode may change from
no-rebase-cousins to some other mode that doesn't exist yet. Support
setting rebase.rebaseMerges to the nonspecific value "true" for users
who do not need or want to care about the default changing in the
future. Similarly, for users who have --rebase-merges in an alias and
want to get the future behavior now, use the specific rebase-merges mode
from the config if a specific mode is not given on the command line.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt        | 11 ++++
 Documentation/git-rebase.txt           | 18 +++---
 builtin/rebase.c                       | 80 ++++++++++++++++++--------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 48 ++++++++++++++++
 5 files changed, 143 insertions(+), 31 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..98e8f193d4 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,14 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true or to `no-rebase-cousins` is equivalent to
+	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line without an argument overrides a `rebase.rebaseMerges=false`
+	configuration, but the absence of a specific rebase-merges mode on the
+	command line does not counteract a specific mode set in the configuration.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4e57a87624..7ff02f474b 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,16 +537,18 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.rebaseMerges` config option and a previous
+	`--rebase-merges`.
 +
 When rebasing merges, there are two modes: `rebase-cousins` and
-`no-rebase-cousins`. If the mode is not specified, it defaults to
-`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
-`<upstream>` as direct ancestor will keep their original branch point, i.e.
-commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
-option will keep their original ancestry by default. In `rebase-cousins` mode,
-such commits are instead rebased onto `<upstream>` (or `<onto>`, if
-specified).
+`no-rebase-cousins`. If the mode is not specified on the command line or in
+the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or
+"CONFIGURATION" below), it defaults to `no-rebase-cousins`. In
+`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
+ancestor will keep their original branch point, i.e. commits that would be
+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their
+original ancestry by default. In `rebase-cousins` mode, such commits are
+instead rebased onto `<upstream>` (or `<onto>`, if specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c36ddc0050..593db9e53f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -123,6 +123,7 @@ struct rebase_options {
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -140,6 +141,8 @@ struct rebase_options {
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
 		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 	}
@@ -771,6 +774,16 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -800,6 +813,16 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.rebasemerges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		} else
+			opts->rebase_cousins = 0;
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -980,6 +1003,27 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
+			return 0;
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1035,7 +1079,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1137,10 +1180,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1436,21 +1478,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			warning(_("--rebase-merges with an empty string "
-				  "argument is deprecated and will stop "
-				  "working in a future version of Git. Use "
-				  "--rebase-merges without an argument "
-				  "instead, which does the same thing."));
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1513,13 +1540,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				break;
 
 		if (i >= 0 || options.type == REBASE_APPLY) {
-			if (is_merge(&options))
-				die(_("apply options and merge options "
-					  "cannot be used together"));
-			else if (options.autosquash == -1 && options.config_autosquash == 1)
+			if (options.autosquash == -1 && options.config_autosquash == 1)
 				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
+			else if (is_merge(&options))
+				die(_("apply options and merge options "
+					  "cannot be used together"));
 			else
 				options.type = REBASE_APPLY;
 		}
@@ -1530,6 +1559,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
 	if (options.autosquash == 1)
 		imply_merge(&options, "--autosquash");
 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..2eba00bdf5 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@ test_rebase_am_only () {
 		test_must_fail git rebase $opt --reapply-cherry-picks A
 	"
 
+	test_expect_success "$opt incompatible with --rebase-merges" "
+		git checkout B^0 &&
+		test_must_fail git rebase $opt --rebase-merges A
+	"
+
 	test_expect_success "$opt incompatible with --update-refs" "
 		git checkout B^0 &&
 		test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
 		grep -e --no-autosquash err
 	"
 
+	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+		git checkout B^0 &&
+		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
+	"
+
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
 		git checkout B^0 &&
 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
 	"
 
+	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+		test_when_finished \"git reset --hard B^0\" &&
+		git checkout B^0 &&
+		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+	"
+
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..a8d9297224 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,54 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+	test_config rebase.rebaseMerges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
+	test_config rebase.rebaseMerges false &&
+	git checkout -b override-config-merges-false E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b no-override-config-rebase-cousins main &&
+	git rebase --rebase-merges HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-12 20:57           ` Alex Henrie
@ 2023-03-13 14:20             ` Phillip Wood
  2023-03-13 16:12               ` Felipe Contreras
  2023-03-13 19:46             ` Junio C Hamano
  2023-03-24 14:47             ` About replaying "evil" merges... " Johannes Schindelin
  2 siblings, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-03-13 14:20 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, Glen Choo, Calvin Wan

Hi Alex

On 12/03/2023 20:57, Alex Henrie wrote:
> On Tue, Mar 7, 2023 at 9:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> 
>> On 04/03/2023 23:24, Alex Henrie wrote:
>>> On Thu, Mar 2, 2023 at 2:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>>
>>>> On 25/02/2023 18:03, Alex Henrie wrote:
>>>
>>>>> +rebase.merges::
>>>>> +     Whether and how to set the `--rebase-merges` option by default. Can
>>>>> +     be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
>>>>> +     true is equivalent to `--rebase-merges` without an argument, setting to
>>>>> +     `rebase-cousins` or `no-rebase-cousins` is equivalent to
>>>>> +     `--rebase-merges` with that value as its argument, and setting to false
>>>>> +     is equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
>>>>> +     command line without an argument overrides a `rebase.merges=false`
>>>>> +     configuration but does not override other values of `rebase.merge`.
>>>>
>>>> I'm still not clear why the commandline doesn't override the config in
>>>> all cases as is our usual practice. After all if the user has set
>>>> rebase.merges then they don't need to pass --rebase-merges unless they
>>>> want to override the config.
>>>
>>> Given the current push to turn rebase-merges on by default, it seems
>>> likely that rebase-cousins will also be turned on by default at some
>>> point after that.
>>
>> It is good to try and future proof things but this seems rather
>> hypothetical. I don't really see how the choice of whether
>> --rebase-merges is turned on by default is related to the choice of
>> whether or not to rebase cousins by default. It is worth noting that the
>> default was changed to from rebase-cousins to no-rebase-cousins early in
>> the development of --rebase-merges[1] as it was felt to be less surprising.
> 
>> [1]
>> https://lore.kernel.org/git/nycvar.QRO.7.76.6.1801292251240.35@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/
> 
> Thank you for sharing that link. Even though I got the tests right, I
> got confused and started thinking that rebase-cousins was a more
> thorough version of rebase-merges. In fact, they do opposite things:
> rebase-merges tries to preserve the graph and rebase-cousins
> intentionally restructures the graph. In my opinion using the word
> "rebase" in the names of both options was another unfortunate UI
> decision, but I understand the difference now.
> 
>>> There will be a warning about the default changing,
>>> and we'll want to let users suppress that warning by setting
>>> rebase.rebaseMerges=rebase-cousins. It would then be very confusing if
>>> a --rebase-merges from some old alias continued to mean
>>> --rebase-merges=no-rebase-cousins when the user expects it to start
>>> behaving as though the default has already changed.
>>
>> But aren't you breaking those aliases now when
>> rebase.rebaseMerges=rebase-cousins? That's what I'm objecting to. It
>> seems like we're breaking things now to avoid a hypothetical future
>> change breaking them which does not seem like the right trade off to me.
>>
>> It also does not fit with the way other optional arguments interact with
>> their associated config setting. For example "git branch/checkout/switch
>> --track" and branch.autoSetupMerge. If the optional argument to --track
>> is omitted it defaults to "direct" irrespective of the config.
 >
> What I really don't want is to paint ourselves into a corner.

Nor do I but we already seem to be in some kind of corner as what you're 
proposing breaks the status quo and the existing convention that command 
line options override config settings.

> You're
> right that it's unlikely that the default will ever change from
> no-rebase-cousins to rebase-cousins; I was mistaken. However, Glen
> thinks that in the future we might have some kind of
> rebase-evil-merges mode as well, and that that might become the
> default. If we don't let the rebase.rebaseMerges config value control
> the default behavior of --rebase-merges without an argument on the
> command line, we would have to introduce a separate config option for
> the transition, which would be ugly.

I'm optimistic that Elijah's work on rebasing evil merges will allow us 
to improve --rebase-merges. I expect that we'll enable it by default 
once it is merged. I do not think that we should add another argument 
for --rebase-merges to disable it though as that would be orthogonal to 
"rebase-cousins" and "no-rebase-cousins" which control what commits get 
rebased not how merges are rebased. If users want to disable the better 
rebasing of merges we'll need to add a --remerge option or something 
like that.

> More voices would be helpful here. Does anyone else have an opinion on
> how likely it is that the default rebase-merges mode will change in
> the future? Or on whether rebase.rebaseMerges should be allowed to
> affect --rebase-merges in order to facilitate such a change?

Getting other's views would indeed be helpful

Best Wishes

Phillip


>>>>> +test_expect_success '--rebase-merges overrides rebase.merges=false' '
>>>>> +     test_config rebase.merges false &&
>>>>> +     git checkout -b override-config-merges-false E &&
>>>>> +     before="$(git rev-parse --verify HEAD)" &&
>>>>> +     test_tick &&
>>>>> +     git rebase --rebase-merges C &&
>>>>> +     test_cmp_rev HEAD $before
>>>>
>>>> This test passes if the rebase does nothing, maybe pass --force and
>>>> check the graph?
>>>
>>> The rebase is supposed to do nothing here.
>>
>> It's not doing nothing though - it is rebasing the branch, it just
>> happens that everything fast-forwards so HEAD ends up unchanged. My
>> point is that this test should verify the branch has been rebased. Maybe
>> you could check the reflog message for HEAD@{0} is
>>
>>          rebase (finish): returning to refs/heads/override-config-merges-false
>>
>>> Checking that the commit
>>> hash is the same is just a quick way to check that the entire graph is
>>> the same. What more would be checked by checking the graph instead of
>>> the hash?
>>
>> By using --force and checking the graph you check that the rebase
>> actually happened.
> 
> I got the impression that people like that not checking the graph
> itself (or the reflog) makes the tests more concise, but I don't care
> much either way. For what it's worth, the way I did it matches the
> existing tests in the file. If you can find at least one other person
> who thinks that it ought to change for this patch series to be
> accepted, and no one else objects, I'll change it.
> 
>> Thanks for working on this
> 
> You're welcome; hopefully we can get the remaining details ironed out quickly.
> 
> -Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-13 14:20             ` Phillip Wood
@ 2023-03-13 16:12               ` Felipe Contreras
  0 siblings, 0 replies; 96+ messages in thread
From: Felipe Contreras @ 2023-03-13 16:12 UTC (permalink / raw)
  To: phillip.wood
  Cc: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, Glen Choo, Calvin Wan

On Mon, Mar 13, 2023 at 9:38 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 12/03/2023 20:57, Alex Henrie wrote:

> > More voices would be helpful here. Does anyone else have an opinion on
> > how likely it is that the default rebase-merges mode will change in
> > the future? Or on whether rebase.rebaseMerges should be allowed to
> > affect --rebase-merges in order to facilitate such a change?
>
> Getting other's views would indeed be helpful

My view is that we shouldn't make assumptions about what is going to
happen in the future. Most of the UI design warts of Git come
precisely from the fact that somebody in the past did not think
something in the future would happen, and when it does the excuse to
not do what's sensible in the UI is "it's too late now".

I would vote for whatever is more flexible to whatever happens in the
future, not something that seems to make sense now, but adds yet more
inertia and later on becomes yet another wart in the list of "oh, if
only we had considered that back in 2023".

Cheers.

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-12 20:57           ` Alex Henrie
  2023-03-13 14:20             ` Phillip Wood
@ 2023-03-13 19:46             ` Junio C Hamano
  2023-03-24 14:47             ` About replaying "evil" merges... " Johannes Schindelin
  2 siblings, 0 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-03-13 19:46 UTC (permalink / raw)
  To: Alex Henrie
  Cc: phillip.wood, git, tao, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, Glen Choo, Calvin Wan

Alex Henrie <alexhenrie24@gmail.com> writes:

> What I really don't want is to paint ourselves into a corner. You're
> right that it's unlikely that the default will ever change from
> no-rebase-cousins to rebase-cousins; I was mistaken. However, Glen
> thinks that in the future we might have some kind of
> rebase-evil-merges mode as well, and that that might become the
> default. If we don't let the rebase.rebaseMerges config value control
> the default behavior of --rebase-merges without an argument on the
> command line, we would have to introduce a separate config option for
> the transition, which would be ugly.
>
> More voices would be helpful here. Does anyone else have an opinion on
> how likely it is that the default rebase-merges mode will change in
> the future? Or on whether rebase.rebaseMerges should be allowed to
> affect --rebase-merges in order to facilitate such a change?

Any such "opinion" about the future, or the belief that we can
somehow predict one, would be wrong anyway.  So I am not sure
soliciting more voices to look into crystal balls would help all
that much.  Having said that...

The default rebase-merges behaviour may be improved, but changing it
in a totally backward incompatible way _without_ a carefully prepared
transition strategy will be very unlikely, simply because existing
end users would not allow us to.  I do not offhand know if the
configuration variable(s) you propose would serve as a good mechanism
to help such transition.

Thanks.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-08  0:02       ` Glen Choo
  2023-03-12 21:03         ` Alex Henrie
@ 2023-03-15  2:52         ` Alex Henrie
  2023-03-16 17:32           ` Glen Choo
  1 sibling, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-15  2:52 UTC (permalink / raw)
  To: Glen Choo
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

On Tue, Mar 7, 2023 at 5:02 PM Glen Choo <chooglen@google.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:

> > In the future, the default rebase-merges mode may change from
> > no-rebase-cousins to rebase-cousins.
>
> I suspect a more likely future would be that the default is to rebase
> 'evil' merges instead of trying to recreate merge commits, but of
> course, the important thing is that we promote the default, not what the
> default will be...

Glen, do you have any more thoughts? At this point, the only thing
that's keeping me from implementing Phillip's request to make
--rebase-merges without an argument clobber rebase.rebaseMerges is
your suspicion that we might need to change the default rebase-merges
mode in the future, and I assume that we would want to use the
rebase.rebaseMerges config option to facilitate the transition.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-15  2:52         ` Alex Henrie
@ 2023-03-16 17:32           ` Glen Choo
  2023-03-16 18:11             ` Felipe Contreras
  2023-03-16 20:27             ` Alex Henrie
  0 siblings, 2 replies; 96+ messages in thread
From: Glen Choo @ 2023-03-16 17:32 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

>> > In the future, the default rebase-merges mode may change from
>> > no-rebase-cousins to rebase-cousins.
>>
>> I suspect a more likely future would be that the default is to rebase
>> 'evil' merges instead of trying to recreate merge commits, but of
>> course, the important thing is that we promote the default, not what the
>> default will be...
>
> Glen, do you have any more thoughts? At this point, the only thing
> that's keeping me from implementing Phillip's request to make
> --rebase-merges without an argument clobber rebase.rebaseMerges is
> your suspicion that we might need to change the default rebase-merges
> mode in the future, and I assume that we would want to use the
> rebase.rebaseMerges config option to facilitate the transition.

(Sorry for the late reply)

Ah, I don't really have more thoughts on the matter. I am fairly
confident that we would _like_ to change the default to rebase 'evil'
merges, but I don't know how likely that will be.

Perhaps it would help to enumerate the rules to see if it is too
confusing or not?

The behaviors we can tweak are:

- Whether to rebase merges or not (true, false, specified mode, or
  default)
- What mode to use when rebasing merges (specified mode or default)

And the sources are either CLI or config, with CLI always overriding
config.

Should we rebase a merge?

- If neither CLI or config tells us whether or not to rebase a merge,
  default to "don't rebase merges".
- If one of CLI or config tells us whether or not to rebase a merge,
  respect it.
- If both CLI or config tell us whether or not to rebase a merge,
  respect CLI and ignore config.

What mode should we use?

- If neither CLI or config tells us what mode to use, default to
  "no-rebase-cousins" (or whatever default we decide).
- If one of CLI or config tells us what mode to use, respect it.
- If both CLI or config tell us what mode to use, respect CLI and ignore
  config.

If users cleanly separate the two concepts, I think it is quite clear.
(I'm not advocating for this approach, but) e.g. if we pretend that each
behavior were configured separately, like:

--[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]

I don't think there would be any confusion. (Having --rebase-merges-mode
be a no-op without --rebase-merges is probably even more confusing to
users, plus this would break backwards compatibility, so I don't think
this is a good idea at all.)

Your doc patch explains the rules pretty clearly, but perhaps it doesn't
explain this mental model clearly enough, hence the confusion. If we
don't find a good way to communicate this (I think it is clear, but
other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's
suggestion to have "--rebase-merges" override
"rebase.rebaseMerges='specific-mode'".

>
> -Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 17:32           ` Glen Choo
@ 2023-03-16 18:11             ` Felipe Contreras
  2023-03-16 22:46               ` Glen Choo
  2023-03-16 20:27             ` Alex Henrie
  1 sibling, 1 reply; 96+ messages in thread
From: Felipe Contreras @ 2023-03-16 18:11 UTC (permalink / raw)
  To: Glen Choo
  Cc: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, calvinwan, jonathantanmy

On Thu, Mar 16, 2023 at 11:57 AM Glen Choo <chooglen@google.com> wrote:

> If users cleanly separate the two concepts, I think it is quite clear.
> (I'm not advocating for this approach, but) e.g. if we pretend that each
> behavior were configured separately, like:
>
> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]
>
> I don't think there would be any confusion.

Not being conversant with these options I agree the above isn't confusing.

> (Having --rebase-merges-mode
> be a no-op without --rebase-merges is probably even more confusing to
> users, plus this would break backwards compatibility, so I don't think
> this is a good idea at all.)

I don't find it confusing. And how would it break backwards
compatibility if --rebase-merges-mode doesn't exist now?

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 17:32           ` Glen Choo
  2023-03-16 18:11             ` Felipe Contreras
@ 2023-03-16 20:27             ` Alex Henrie
  2023-03-16 22:39               ` Glen Choo
                                 ` (2 more replies)
  1 sibling, 3 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-16 20:27 UTC (permalink / raw)
  To: Glen Choo
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

On Thu, Mar 16, 2023 at 11:32 AM Glen Choo <chooglen@google.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> >> > In the future, the default rebase-merges mode may change from
> >> > no-rebase-cousins to rebase-cousins.
> >>
> >> I suspect a more likely future would be that the default is to rebase
> >> 'evil' merges instead of trying to recreate merge commits, but of
> >> course, the important thing is that we promote the default, not what the
> >> default will be...
> >
> > Glen, do you have any more thoughts? At this point, the only thing
> > that's keeping me from implementing Phillip's request to make
> > --rebase-merges without an argument clobber rebase.rebaseMerges is
> > your suspicion that we might need to change the default rebase-merges
> > mode in the future, and I assume that we would want to use the
> > rebase.rebaseMerges config option to facilitate the transition.
>
> (Sorry for the late reply)
>
> Ah, I don't really have more thoughts on the matter. I am fairly
> confident that we would _like_ to change the default to rebase 'evil'
> merges, but I don't know how likely that will be.
>
> Perhaps it would help to enumerate the rules to see if it is too
> confusing or not?
>
> The behaviors we can tweak are:
>
> - Whether to rebase merges or not (true, false, specified mode, or
>   default)
> - What mode to use when rebasing merges (specified mode or default)
>
> And the sources are either CLI or config, with CLI always overriding
> config.
>
> Should we rebase a merge?
>
> - If neither CLI or config tells us whether or not to rebase a merge,
>   default to "don't rebase merges".
> - If one of CLI or config tells us whether or not to rebase a merge,
>   respect it.
> - If both CLI or config tell us whether or not to rebase a merge,
>   respect CLI and ignore config.
>
> What mode should we use?
>
> - If neither CLI or config tells us what mode to use, default to
>   "no-rebase-cousins" (or whatever default we decide).
> - If one of CLI or config tells us what mode to use, respect it.
> - If both CLI or config tell us what mode to use, respect CLI and ignore
>   config.
>
> If users cleanly separate the two concepts, I think it is quite clear.
> (I'm not advocating for this approach, but) e.g. if we pretend that each
> behavior were configured separately, like:
>
> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]
>
> I don't think there would be any confusion. (Having --rebase-merges-mode
> be a no-op without --rebase-merges is probably even more confusing to
> users, plus this would break backwards compatibility, so I don't think
> this is a good idea at all.)
>
> Your doc patch explains the rules pretty clearly, but perhaps it doesn't
> explain this mental model clearly enough, hence the confusion. If we
> don't find a good way to communicate this (I think it is clear, but
> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's
> suggestion to have "--rebase-merges" override
> "rebase.rebaseMerges='specific-mode'".

I got the impression that everyone, including Phillip,[1] already
agrees that the proposed documentation is clear about the interaction
between the config option and the command line option. However, it is
a little weird when you consider that other flags with optional
arguments, like `git branch --track`, unconditionally override their
corresponding config options.[2]

Let me ask a different but related question: If we add a
rebase-evil-merges mode, do you think that would be orthogonal to the
rebase-cousins mode?

-Alex

[1] https://lore.kernel.org/git/7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk/
[2] https://lore.kernel.org/git/5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk/

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 20:27             ` Alex Henrie
@ 2023-03-16 22:39               ` Glen Choo
  2023-03-18  5:59                 ` Alex Henrie
  2023-03-24 15:05               ` Johannes Schindelin
  2023-03-25 16:59               ` Sergey Organov
  2 siblings, 1 reply; 96+ messages in thread
From: Glen Choo @ 2023-03-16 22:39 UTC (permalink / raw)
  To: Alex Henrie
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

>> Your doc patch explains the rules pretty clearly, but perhaps it doesn't
>> explain this mental model clearly enough, hence the confusion. If we
>> don't find a good way to communicate this (I think it is clear, but
>> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's
>> suggestion to have "--rebase-merges" override
>> "rebase.rebaseMerges='specific-mode'".
>
> I got the impression that everyone, including Phillip,[1] already
> agrees that the proposed documentation is clear about the interaction
> between the config option and the command line option. However, it is
> a little weird when you consider that other flags with optional
> arguments, like `git branch --track`, unconditionally override their
> corresponding config options.[2]

Ah, I didn't consider other options like `git branch --track`. I haven't
looked into what is the norm, but I think we should follow it (whatever
it is).

If other reviewers have a strong idea of what this norm is, I am happy
to defer to them. If not, I can look into it given some time.

> Let me ask a different but related question: If we add a
> rebase-evil-merges mode, do you think that would be orthogonal to the
> rebase-cousins mode?

I am not an expert on this, so perhaps my opinion isn't that important
;)

My understanding is that `[no-]rebase-cousins` affects which commits get
rebased and onto where, whereas `rebase-evil-merges` would affect how
the merge commits are generated (by rebasing the evil or by simply
recreating the merges). From that perspective, it seems like yes, the
two would be orthogonal.

Hm. Maybe this means that we'd be introducing a new option, and that my
hunch that we would change the default to `rebase-evil-merges` is more
wrong than I expected.

Though I guess this doesn't really affects what we do with the CLI
options _now_, since the discussion is about what we do about defaults,
and what the default is is quite immaterial.

>
> -Alex
>
> [1] https://lore.kernel.org/git/7cf19017-518b-245e-aea2-5dee55f88276@dunelm.org.uk/
> [2] https://lore.kernel.org/git/5551d67b-3021-8cfc-53b5-318f223ded6d@dunelm.org.uk/

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 18:11             ` Felipe Contreras
@ 2023-03-16 22:46               ` Glen Choo
  2023-03-16 23:48                 ` Felipe Contreras
  0 siblings, 1 reply; 96+ messages in thread
From: Glen Choo @ 2023-03-16 22:46 UTC (permalink / raw)
  To: Felipe Contreras
  Cc: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, calvinwan, jonathantanmy

Felipe Contreras <felipe.contreras@gmail.com> writes:

>> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]
>>
>> I don't think there would be any confusion.
>> [...]
>> (Having --rebase-merges-mode
>> be a no-op without --rebase-merges is probably even more confusing to
>> users, plus this would break backwards compatibility, so I don't think
>> this is a good idea at all.)
>
> I don't find it confusing. And how would it break backwards
> compatibility if --rebase-merges-mode doesn't exist now?

I meant that for the above example to work, we would need to have
`--[no-]rebase-merges` as a boolean that says whether we rebase merges at
all, and `--rebase-merges-mode=[whatever]` would tell us what mode to
use _if_ we were rebasing merges. This means that
`--rebase-merges=not-a-boolean` would become invalid.

We were in this position before, actually, with `git log -p -m`. The
conclusion on a recent series [1] is that having such a no-op flag was
probably a mistake (and unfortunately, one that is extremely hard to fix
due to backwards compatibility requirements), so introducing more no-op
flags is probably a bad idea :)

[1] https://lore.kernel.org/git/871qn5pyez.fsf@osv.gnss.ru

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 22:46               ` Glen Choo
@ 2023-03-16 23:48                 ` Felipe Contreras
  0 siblings, 0 replies; 96+ messages in thread
From: Felipe Contreras @ 2023-03-16 23:48 UTC (permalink / raw)
  To: Glen Choo
  Cc: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, calvinwan, jonathantanmy

On Thu, Mar 16, 2023 at 4:46 PM Glen Choo <chooglen@google.com> wrote:
>
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
> >> --[no-]rebase-merges [--rebase-merges-mode=(rebase-cousins|no-rebase-cousins)]
> >>
> >> I don't think there would be any confusion.
> >> [...]
> >> (Having --rebase-merges-mode
> >> be a no-op without --rebase-merges is probably even more confusing to
> >> users, plus this would break backwards compatibility, so I don't think
> >> this is a good idea at all.)
> >
> > I don't find it confusing. And how would it break backwards
> > compatibility if --rebase-merges-mode doesn't exist now?
>
> I meant that for the above example to work, we would need to have
> `--[no-]rebase-merges` as a boolean that says whether we rebase merges at
> all, and `--rebase-merges-mode=[whatever]` would tell us what mode to
> use _if_ we were rebasing merges.

No, we don't need --no-rebase-merges for --rebase-merges-mode to work.
It would be nice, but not necessary.

> This means that
> `--rebase-merges=not-a-boolean` would become invalid.

No, it would not become invalid, that's yet another decision to make.
`--rebase-merges=mode` could become synonymous with `--rebase-merges
--rebase-merges-mode=mode`. A shortcut.

Because it's redundant it could be deprecated in the future, but not
necessarily invalid.

> We were in this position before, actually, with `git log -p -m`. The
> conclusion on a recent series [1] is that having such a no-op flag was
> probably a mistake (and unfortunately, one that is extremely hard to fix
> due to backwards compatibility requirements),

No, `git log -m` was a mistake *only* if -p isn't turned on as well,
which is an interface wart that could be fixed today.

> so introducing more no-op flags is probably a bad idea :)

Whether --rebase-merges-mode turns on --rebase-merges by default is a
decision that could be made in the future. Just like `git log -m`
turning on `-p` by default is a decision that could be made in the
future (and probably should).

---

Either way: introducing a new option cannot possibly break backwards
compatibility.

Cheers.

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 22:39               ` Glen Choo
@ 2023-03-18  5:59                 ` Alex Henrie
  0 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-18  5:59 UTC (permalink / raw)
  To: Glen Choo
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, calvinwan, jonathantanmy

On Thu, Mar 16, 2023 at 4:39 PM Glen Choo <chooglen@google.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> >> Your doc patch explains the rules pretty clearly, but perhaps it doesn't
> >> explain this mental model clearly enough, hence the confusion. If we
> >> don't find a good way to communicate this (I think it is clear, but
> >> other reviewers seem yet unconvinced), I wouldn't mind taking Phillip's
> >> suggestion to have "--rebase-merges" override
> >> "rebase.rebaseMerges='specific-mode'".
> >
> > I got the impression that everyone, including Phillip,[1] already
> > agrees that the proposed documentation is clear about the interaction
> > between the config option and the command line option. However, it is
> > a little weird when you consider that other flags with optional
> > arguments, like `git branch --track`, unconditionally override their
> > corresponding config options.[2]
>
> Ah, I didn't consider other options like `git branch --track`. I haven't
> looked into what is the norm, but I think we should follow it (whatever
> it is).
>
> If other reviewers have a strong idea of what this norm is, I am happy
> to defer to them. If not, I can look into it given some time.
>
> > Let me ask a different but related question: If we add a
> > rebase-evil-merges mode, do you think that would be orthogonal to the
> > rebase-cousins mode?
>
> I am not an expert on this, so perhaps my opinion isn't that important
> ;)
>
> My understanding is that `[no-]rebase-cousins` affects which commits get
> rebased and onto where, whereas `rebase-evil-merges` would affect how
> the merge commits are generated (by rebasing the evil or by simply
> recreating the merges). From that perspective, it seems like yes, the
> two would be orthogonal.
>
> Hm. Maybe this means that we'd be introducing a new option, and that my
> hunch that we would change the default to `rebase-evil-merges` is more
> wrong than I expected.
>
> Though I guess this doesn't really affects what we do with the CLI
> options _now_, since the discussion is about what we do about defaults,
> and what the default is is quite immaterial.

I looked through the code and documentation and found 12 good examples
of command line flags with an optional argument that always override
their corresponding config options whether or not the optional
argument is given:

git -c branch.autoSetupMerge=inherit branch --track mynewbranch

git -c commit.gpgSign=mykey commit --gpg-sign

git -c core.sharedRepository=0666 init --shared .

git -c diff.ignoreSubmodules=untracked diff --ignore-submodules

git -c diff.submodule=diff diff --submodule

git -c format.attach=myboundary format-patch --attach HEAD^

git -c format.from='Dang Git <dang@example.org>' format-patch --from HEAD^

git -c format.thread=deep format-patch --thread HEAD~3

git -c gc.pruneExpire=1.week.ago gc --prune

git -c log.decorate=full log --decorate

git -c merge.log=1 merge --log mytopicbranch

git -c pull.rebase=interactive pull --rebase

I found 6 other similar examples where the config option is a
yes/no/auto trilean, but I don't think those are good examples because
it makes total sense for a command line flag without an argument to
override a nonspecific "auto" value in the configuration:

git -c color.diff=auto diff --color | less

git -c color.grep=auto grep --color . | less

git -c color.showbranch=auto show-branch --color master mytopicbranch | less

git -c color.ui=auto log --color | less

git -c fetch.recurseSubmodules=on-demand fetch --recurse-submodules

git -c push.gpgSign=if-asked push --signed

I found 1 good example where the config option does make a difference
if the optional argument is omitted:

git -c diff.colorMoved=plain diff --color-moved

And I found 1 other similar example, but it's not good a example
because the config option doesn't do anything unless the command line
flag is specified:

git -c diff.dirstat=lines diff --dirstat

Given 12 good examples versus 1 good counterexample, I would say that
the established convention is for the command line flag to always
override the config option. Please let me know if there are other
examples that I missed.

It's worth noting that the documentation for `git format-patch
--thread` explicitly says that format.thread takes precedence over
--thread without an argument, but that is not correct: When I tested
it, the command line argument always took precedence.

Another problem is that the documentation for `git pack-objects` does
not even mention that --unpack-unreachable has an optional argument,
and it says that the argument to --cruft-expiration is required when
it is not.

I'm not going to fix all the documentation problems, at least not
right now. However, in order to follow the established convention for
flags with optional arguments, and because we probably aren't going to
use rebase.rebaseMerges to facilitate a future change of defaults, I
will redo the patch so that --rebase-merges without an argument takes
precedence over rebase.rebaseMerges.

Thanks for the feedback. I feel more confident about the way forward now.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v8 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
                         ` (2 preceding siblings ...)
  2023-03-12 21:04       ` [PATCH v7 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-20  5:59       ` Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                           ` (3 more replies)
  3 siblings, 4 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-20  5:59 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

This patch series introduces a rebase.rebaseMerges config option to
accommodate users who would like --rebase-merges to be on by default and
to facilitate turning on --rebase-merges by default without
configuration in a future version of Git. It also cleans up and
documents the behavior of the --rebase-merges command line option to
avoid confusion about how the config option and the command line option
interact.

Changes from v7:
- Make --rebase-merges without an argument clobber the mode specified in
  rebase.rebaseMerges
- Replace the test for --rebase-merges not overriding
  rebase.rebaseMerges=rebase-cousins and the test for --rebase-merges
  overriding rebase.rebaseMerges=false with a single test that
  --rebase-merges does override rebase.rebaseMerges=rebase-cousins

Thanks to Phillip, Felipe, Junio, and Glen for your feedback on v7.

Alex Henrie (3):
  rebase: add documentation and test for --no-rebase-merges
  rebase: deprecate --rebase-merges=""
  rebase: add a config option for --rebase-merges

 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           | 19 ++++---
 builtin/rebase.c                       | 77 +++++++++++++++++++-------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 44 +++++++++++++++
 5 files changed, 141 insertions(+), 26 deletions(-)

Range-diff against v7:
1:  3aee0c2277 = 1:  09fb7c1b74 rebase: add documentation and test for --no-rebase-merges
2:  e57843d8b5 = 2:  a846716a4a rebase: deprecate --rebase-merges=""
3:  b0c1a4dcb2 ! 3:  b12a3610ba rebase: add a config option for --rebase-merges
    @@ Commit message
         to be clear when scrolling through values in the [rebase] section of
         .gitconfig.
     
    -    In the future, the default rebase-merges mode may change from
    -    no-rebase-cousins to some other mode that doesn't exist yet. Support
    -    setting rebase.rebaseMerges to the nonspecific value "true" for users
    -    who do not need or want to care about the default changing in the
    -    future. Similarly, for users who have --rebase-merges in an alias and
    -    want to get the future behavior now, use the specific rebase-merges mode
    -    from the config if a specific mode is not given on the command line.
    +    Support setting rebase.rebaseMerges to the nonspecific value "true" for
    +    users who don't need to or don't want to learn about the difference
    +    between rebase-cousins and no-rebase-cousins.
    +
    +    Make --rebase-merges without an argument on the command line override
    +    any value of rebase.rebaseMerges in the configuration, for consistency
    +    with other command line flags with optional arguments that have an
    +    associated config option.
     
         Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
     
    @@ Documentation/config/rebase.txt: rebase.rescheduleFailedExec::
     +	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
     +	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
     +	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
    -+	command line without an argument overrides a `rebase.rebaseMerges=false`
    -+	configuration, but the absence of a specific rebase-merges mode on the
    -+	command line does not counteract a specific mode set in the configuration.
    ++	command line, with or without an argument, overrides any
    ++	`rebase.rebaseMerges` configuration.
     
      ## Documentation/git-rebase.txt ##
     @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
    @@ Documentation/git-rebase.txt: See also INCOMPATIBLE OPTIONS below.
     +	`--rebase-merges`.
      +
      When rebasing merges, there are two modes: `rebase-cousins` and
    --`no-rebase-cousins`. If the mode is not specified, it defaults to
    --`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
    --`<upstream>` as direct ancestor will keep their original branch point, i.e.
    --commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
    --option will keep their original ancestry by default. In `rebase-cousins` mode,
    --such commits are instead rebased onto `<upstream>` (or `<onto>`, if
    --specified).
    -+`no-rebase-cousins`. If the mode is not specified on the command line or in
    -+the `rebase.rebaseMerges` config option (see linkgit:git-config[1] or
    -+"CONFIGURATION" below), it defaults to `no-rebase-cousins`. In
    -+`no-rebase-cousins` mode, commits which do not have `<upstream>` as direct
    -+ancestor will keep their original branch point, i.e. commits that would be
    -+excluded by linkgit:git-log[1]'s `--ancestry-path` option will keep their
    -+original ancestry by default. In `rebase-cousins` mode, such commits are
    -+instead rebased onto `<upstream>` (or `<onto>`, if specified).
    - +
    - It is currently only possible to recreate the merge commits using the
    - `ort` merge strategy; different merge strategies can be used only via
    + `no-rebase-cousins`. If the mode is not specified, it defaults to
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: struct rebase_options {
    @@ builtin/rebase.c: static int parse_opt_empty(const struct option *opt, const cha
     +	struct rebase_options *options = opt->value;
     +
     +	options->rebase_merges = !unset;
    ++	options->rebase_cousins = 0;
     +
     +	if (arg) {
     +		if (!*arg) {
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
     -					  "cannot be used together"));
     -			else if (options.autosquash == -1 && options.config_autosquash == 1)
     +			if (options.autosquash == -1 && options.config_autosquash == 1)
    - 				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
    + 				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
     +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
     +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
      			else if (options.update_refs == -1 && options.config_update_refs == 1)
    @@ t/t3430-rebase-merges.sh: test_expect_success 'do not rebase cousins unless aske
     +	EOF
     +'
     +
    -+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=false' '
    -+	test_config rebase.rebaseMerges false &&
    -+	git checkout -b override-config-merges-false E &&
    ++test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
    ++	test_config rebase.rebaseMerges rebase-cousins &&
    ++	git checkout -b override-config-rebase-cousins E &&
     +	before="$(git rev-parse --verify HEAD)" &&
     +	test_tick &&
     +	git rebase --rebase-merges C &&
     +	test_cmp_rev HEAD $before
     +'
    -+
    -+test_expect_success '--rebase-merges does not override rebase.rebaseMerges=rebase-cousins' '
    -+	test_config rebase.rebaseMerges rebase-cousins &&
    -+	git checkout -b no-override-config-rebase-cousins main &&
    -+	git rebase --rebase-merges HEAD^ &&
    -+	test_cmp_graph HEAD^.. <<-\EOF
    -+	*   Merge the topic branch '\''onebranch'\''
    -+	|\
    -+	| * D
    -+	| * G
    -+	|/
    -+	o H
    -+	EOF
    -+'
     +
      test_expect_success 'refs/rewritten/* is worktree-local' '
      	git worktree add wt &&
-- 
2.40.0


^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
@ 2023-03-20  5:59         ` Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
                           ` (2 subsequent siblings)
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-20  5:59 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

As far as I can tell, --no-rebase-merges has always worked, but has
never been documented. It is especially important to document it before
a rebase.rebaseMerges option is introduced so that users know how to
override the config option on the command line. It's also important to
clarify that --rebase-merges without an argument is not the same as
--no-rebase-merges and not passing --rebase-merges is not the same as
passing --rebase-merges=no-rebase-cousins.

A test case is necessary to make sure that --no-rebase-merges keeps
working after its code is refactored in the following patches of this
series. The test case is a little contrived: It's unlikely that a user
would type both --rebase-merges and --no-rebase-merges at the same time.
However, if an alias is defined which includes --rebase-merges, the user
might decide to add --no-rebase-merges to countermand that part of the
alias but leave alone other flags set by the alias.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt | 18 +++++++++++-------
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..4e57a87624 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v8 2/3] rebase: deprecate --rebase-merges=""
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-20  5:59         ` Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-20  5:59 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges without an argument. Deprecate that syntax to avoid
confusion when a rebase.rebaseMerges config option is introduced, where
rebase.rebaseMerges="" will be equivalent to --no-rebase-merges.

It is not likely that anyone is actually using this syntax, but just in
case, deprecate the empty string argument instead of dropping support
for it immediately.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index dd31d5ab91..4b3f29a449 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1141,7 +1141,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1439,7 +1439,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (rebase_merges) {
 		if (!*rebase_merges)
-			; /* default mode; do nothing */
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
 		else if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-03-20  5:59         ` [PATCH v8 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-20  5:59         ` Alex Henrie
  2023-03-22 16:54           ` Phillip Wood
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
  3 siblings, 1 reply; 96+ messages in thread
From: Alex Henrie @ 2023-03-20  5:59 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate turning on
--rebase-merges by default without configuration in a future version of
Git.

Name the new option rebase.rebaseMerges, even though it is a little
redundant, for consistency with the name of the command line option and
to be clear when scrolling through values in the [rebase] section of
.gitconfig.

Support setting rebase.rebaseMerges to the nonspecific value "true" for
users who don't need to or don't want to learn about the difference
between rebase-cousins and no-rebase-cousins.

Make --rebase-merges without an argument on the command line override
any value of rebase.rebaseMerges in the configuration, for consistency
with other command line flags with optional arguments that have an
associated config option.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           |  3 +-
 builtin/rebase.c                       | 81 ++++++++++++++++++--------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 34 +++++++++++
 5 files changed, 121 insertions(+), 24 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..afaf6dad99 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true or to `no-rebase-cousins` is equivalent to
+	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line, with or without an argument, overrides any
+	`rebase.rebaseMerges` configuration.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4e57a87624..e7b39ad244 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.rebaseMerges` config option and a previous
+	`--rebase-merges`.
 +
 When rebasing merges, there are two modes: `rebase-cousins` and
 `no-rebase-cousins`. If the mode is not specified, it defaults to
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4b3f29a449..fd284e24ab 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -124,6 +124,7 @@ struct rebase_options {
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -141,6 +142,8 @@ struct rebase_options {
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
 		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 	}
@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -801,6 +814,16 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.rebasemerges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		} else
+			opts->rebase_cousins = 0;
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -981,6 +1004,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+	options->rebase_cousins = 0;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
+			return 0;
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1036,7 +1081,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1138,10 +1182,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1437,21 +1480,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			warning(_("--rebase-merges with an empty string "
-				  "argument is deprecated and will stop "
-				  "working in a future version of Git. Use "
-				  "--rebase-merges without an argument "
-				  "instead, which does the same thing."));
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				break;
 
 		if (i >= 0 || options.type == REBASE_APPLY) {
-			if (is_merge(&options))
-				die(_("apply options and merge options "
-					  "cannot be used together"));
-			else if (options.autosquash == -1 && options.config_autosquash == 1)
+			if (options.autosquash == -1 && options.config_autosquash == 1)
 				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
+			else if (is_merge(&options))
+				die(_("apply options and merge options "
+					  "cannot be used together"));
 			else
 				options.type = REBASE_APPLY;
 		}
@@ -1531,6 +1561,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
 	if (options.autosquash == 1)
 		imply_merge(&options, "--autosquash");
 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..2eba00bdf5 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@ test_rebase_am_only () {
 		test_must_fail git rebase $opt --reapply-cherry-picks A
 	"
 
+	test_expect_success "$opt incompatible with --rebase-merges" "
+		git checkout B^0 &&
+		test_must_fail git rebase $opt --rebase-merges A
+	"
+
 	test_expect_success "$opt incompatible with --update-refs" "
 		git checkout B^0 &&
 		test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
 		grep -e --no-autosquash err
 	"
 
+	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+		git checkout B^0 &&
+		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
+	"
+
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
 		git checkout B^0 &&
 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
 	"
 
+	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+		test_when_finished \"git reset --hard B^0\" &&
+		git checkout B^0 &&
+		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+	"
+
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..f03599c63b 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+	test_config rebase.rebaseMerges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-20  5:59         ` [PATCH v8 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-22 16:54           ` Phillip Wood
  2023-03-23 18:45             ` Junio C Hamano
  2023-03-25  5:21             ` Alex Henrie
  0 siblings, 2 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-22 16:54 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, chooglen, calvinwan, jonathantanmy,
	felipe.contreras

Hi Alex

On 20/03/2023 05:59, Alex Henrie wrote:
> The purpose of the new option is to accommodate users who would like
> --rebase-merges to be on by default and to facilitate turning on
> --rebase-merges by default without configuration in a future version of
> Git.
> 
> Name the new option rebase.rebaseMerges, even though it is a little
> redundant, for consistency with the name of the command line option and
> to be clear when scrolling through values in the [rebase] section of
> .gitconfig.
> 
> Support setting rebase.rebaseMerges to the nonspecific value "true" for
> users who don't need to or don't want to learn about the difference
> between rebase-cousins and no-rebase-cousins.
> 
> Make --rebase-merges without an argument on the command line override
> any value of rebase.rebaseMerges in the configuration, for consistency
> with other command line flags with optional arguments that have an
> associated config option.

Hurray! Thanks for re-rolling. I've left a couple of comments below, I 
had hoped this would be the final version but I've realized that the way 
you've rearranged the error handling for apply and merge options is not 
a good idea (see below). Apart from that I'm happy.

> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>   Documentation/config/rebase.txt        | 10 ++++
>   Documentation/git-rebase.txt           |  3 +-
>   builtin/rebase.c                       | 81 ++++++++++++++++++--------
>   t/t3422-rebase-incompatible-options.sh | 17 ++++++
>   t/t3430-rebase-merges.sh               | 34 +++++++++++
>   5 files changed, 121 insertions(+), 24 deletions(-)
> 
> diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
> index f19bd0e040..afaf6dad99 100644
> --- a/Documentation/config/rebase.txt
> +++ b/Documentation/config/rebase.txt
> @@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
>   
>   rebase.forkPoint::
>   	If set to false set `--no-fork-point` option by default.
> +
> +rebase.rebaseMerges::
> +	Whether and how to set the `--rebase-merges` option by default. Can
> +	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
> +	true or to `no-rebase-cousins` is equivalent to
> +	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
> +	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
> +	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
> +	command line, with or without an argument, overrides any
> +	`rebase.rebaseMerges` configuration.

Sounds good

> [...]   
> +	if (!strcmp(var, "rebase.rebasemerges")) {
> +		opts->config_rebase_merges = git_parse_maybe_bool(value);
> +		if (opts->config_rebase_merges < 0) {
> +			opts->config_rebase_merges = 1;
> +			parse_rebase_merges_value(opts, value);
> +		} else
> +			opts->rebase_cousins = 0;

The "else" clause should have braces because the "if" cause requires 
them (see Documentation/CodingGuidelines). I don't think it is worth 
re-rolling just for this though.

> [...]   
> +static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
> +{
> +	struct rebase_options *options = opt->value;
> +
> +	options->rebase_merges = !unset;
> +	options->rebase_cousins = 0;

This makes --rebase-merges without an option override 
rebase.rebaseMerges - good.

 > [...]
> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   				break;
>   
>   		if (i >= 0 || options.type == REBASE_APPLY) {
> -			if (is_merge(&options))
> -				die(_("apply options and merge options "
> -					  "cannot be used together"));

This isn't a new change but having thought about it I'm not sure moving 
this code is a good idea. If the user runs

	git -c rebase.updateRefs=true rebase --whitespace=fix --exec "make test"

then instead of getting an message saying that they cannot use apply and 
merge options together they'll get a message suggesting they pass 
--no-update-refs which wont fix the problem for them.

> -			else if (options.autosquash == -1 && options.config_autosquash == 1)
> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>   				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
> +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
>   			else if (options.update_refs == -1 && options.config_update_refs == 1)
>   				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
> +			else if (is_merge(&options))
> +				die(_("apply options and merge options "
> +					  "cannot be used together"));
>   			else
>   				options.type = REBASE_APPLY;
>   		}

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-22 16:54           ` Phillip Wood
@ 2023-03-23 18:45             ` Junio C Hamano
  2023-03-24 14:52               ` Phillip Wood
  2023-03-25  5:23               ` Alex Henrie
  2023-03-25  5:21             ` Alex Henrie
  1 sibling, 2 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-03-23 18:45 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Alex Henrie, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy, felipe.contreras

Phillip Wood <phillip.wood123@gmail.com> writes:

>> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>>   				break;
>>     		if (i >= 0 || options.type == REBASE_APPLY) {
>> -			if (is_merge(&options))
>> -				die(_("apply options and merge options "
>> -					  "cannot be used together"));
>
> This isn't a new change but having thought about it I'm not sure
> moving this code is a good idea. If the user runs
>
> 	git -c rebase.updateRefs=true rebase --whitespace=fix --exec "make test"
>
> then instead of getting an message saying that they cannot use apply
> and merge options together they'll get a message suggesting they pass
> --no-update-refs which wont fix the problem for them.

Hmph.  Instead of dying here, ...

>> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>>   				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
>> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
>> +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
>>   			else if (options.update_refs == -1 && options.config_update_refs == 1)
>>   				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));

... we get caught here, and the next one is not triggered.

>> +			else if (is_merge(&options))
>> +				die(_("apply options and merge options "
>> +					  "cannot be used together"));
>>   			else
>>   				options.type = REBASE_APPLY;

What's the reason why "cannot be used together" is moved to the last
in the chain?

The first two new conditions in this chain try to catch an
invocation with some apply-specific command line option
(e.g. "--whitespace=fix") when used with configuration variables
specific to the merge-backend (e.g. "rebase.merges") and suggest
overriding the configuration from the command line, and I suspect
that the motivation behind this change is that their error messages
are more specific than the generic "apply and merge do not mix".

But are these two new errors and hints universally a good suggestion
to make?  I actually think a command line option forcing us to use
the apply backend should simply silently ignore (aka "override")
configuration variables that take effect only when we are using the
merge-backend.  "git rebase --whitespace=fix --rebase-merges",
giving both from the command line, is making conflicting and
unsatisfyable request, and it is worth giving an error message.  

But if you configure rebase.autosquash only because you want to it
to be in effect for your invocations of "git rebase -i", you
shouldn't have to override it from the command line when you say
"git rebase" or "git rebase --whitespace=fix", should you?

Thanks.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* About replaying "evil" merges... Re: [PATCH v5 3/3] rebase: add a config option for --rebase-merges
  2023-03-12 20:57           ` Alex Henrie
  2023-03-13 14:20             ` Phillip Wood
  2023-03-13 19:46             ` Junio C Hamano
@ 2023-03-24 14:47             ` Johannes Schindelin
  2 siblings, 0 replies; 96+ messages in thread
From: Johannes Schindelin @ 2023-03-24 14:47 UTC (permalink / raw)
  To: Alex Henrie
  Cc: phillip.wood, git, tao, gitster, newren, phillip.wood123,
	sorganov, Glen Choo, Calvin Wan

Hi Alex,

Heads up: This mail is totally a tangent and is _not_ intended to distract
from the patch series.

On Sun, 12 Mar 2023, Alex Henrie wrote:

> [...] Glen thinks that in the future we might have some kind of
> rebase-evil-merges mode as well, and that that might become the default.

While it probably won't be the default, like, ever, I can well imagine
that there would be a good opportunity to add a config setting to make it
the default _for that user_.

About implementing that mode... I recently picked up a repair item from my
ever-growing TODO list to brush up that "replay merge conflict resolutions
and amendments" idea of Phillip's that I had implemented already four
years ago (only to find out that it unfortunately caused a lot of nested
merge conflicts when using it on Git for Windows' branch thicket; I tried
my best to add a test case demonstrating the problematic behavior).

While I had to stop working on it again, I at least left a rebased version
at https://github.com/gitgitgadget/git/pull/1491 (in case anybody is
interested where I wanted to take this PR, I left comments describing the
changes I had planned on looking into).

I mention this PR here in this thread not so much to derail the review, or
even to distract from this patch series (I really detest when that happens
in reviews of my own patches, so please accept my apology in advance
should that happen here, it is _really_ not my intention), but as a point
in favor of Glen's suspicion that we might get such a mode in the future
to replay evil merges or merges with resolved conflicts.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-23 18:45             ` Junio C Hamano
@ 2023-03-24 14:52               ` Phillip Wood
  2023-03-25  5:23               ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Phillip Wood @ 2023-03-24 14:52 UTC (permalink / raw)
  To: Junio C Hamano, Phillip Wood
  Cc: Alex Henrie, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy, felipe.contreras

Hi Junio

On 23/03/2023 18:45, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
> 
>>> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>>>    				break;
>>>      		if (i >= 0 || options.type == REBASE_APPLY) {
>>> -			if (is_merge(&options))
>>> -				die(_("apply options and merge options "
>>> -					  "cannot be used together"));
>>
>> This isn't a new change but having thought about it I'm not sure
>> moving this code is a good idea. If the user runs
>>
>> 	git -c rebase.updateRefs=true rebase --whitespace=fix --exec "make test"
>>
>> then instead of getting an message saying that they cannot use apply
>> and merge options together they'll get a message suggesting they pass
>> --no-update-refs which wont fix the problem for them.
> 
> Hmph.  Instead of dying here, ...
> 
>>> +			if (options.autosquash == -1 && options.config_autosquash == 1)
>>>    				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
>>> +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
>>> +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
>>>    			else if (options.update_refs == -1 && options.config_update_refs == 1)
>>>    				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
> 
> ... we get caught here, and the next one is not triggered.
> 
>>> +			else if (is_merge(&options))
>>> +				die(_("apply options and merge options "
>>> +					  "cannot be used together"));
>>>    			else
>>>    				options.type = REBASE_APPLY;
> 
> What's the reason why "cannot be used together" is moved to the last
> in the chain?
> 
> The first two new conditions in this chain try to catch an
> invocation with some apply-specific command line option
> (e.g. "--whitespace=fix") when used with configuration variables
> specific to the merge-backend (e.g. "rebase.merges") and suggest
> overriding the configuration from the command line, and I suspect
> that the motivation behind this change is that their error messages
> are more specific than the generic "apply and merge do not mix".
> 
> But are these two new errors and hints universally a good suggestion
> to make?  I actually think a command line option forcing us to use
> the apply backend should simply silently ignore (aka "override")
> configuration variables that take effect only when we are using the
> merge-backend.  "git rebase --whitespace=fix --rebase-merges",
> giving both from the command line, is making conflicting and
> unsatisfyable request, and it is worth giving an error message.
> 
> But if you configure rebase.autosquash only because you want to it
> to be in effect for your invocations of "git rebase -i", you
> shouldn't have to override it from the command line when you say
> "git rebase" or "git rebase --whitespace=fix", should you?

I think there are two conflicting viewpoints here which depend on what 
one thinks the user wants when they set these configuration variables. 
As I understand it Elijah's thinking was that if the user set 
rebase.autosquash they'd be surprised when their fixup commits were not 
squashed by

	git rebase --whitespace=fix

and that's why his patch series changed things to error out.

The other point of view is that the user expects that these 
configuration variables to apply only when they are using the "merge" 
backend and so we should not error out.

Personally I lean a little more towards the latter but I don't feel that 
strongly about it and can see an argument for having either behavior. I 
do think we should leave the ordering alone though so the user gets a 
useful error message in the case of "git rebase --exec 'make test' 
--whitespace=fix"

Best Wishes

Phillip

> Thanks.

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 20:27             ` Alex Henrie
  2023-03-16 22:39               ` Glen Choo
@ 2023-03-24 15:05               ` Johannes Schindelin
  2023-03-25 16:59               ` Sergey Organov
  2 siblings, 0 replies; 96+ messages in thread
From: Johannes Schindelin @ 2023-03-24 15:05 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Glen Choo, git, tao, gitster, newren, phillip.wood123, sorganov,
	calvinwan, jonathantanmy

Hi Alex,

On Thu, 16 Mar 2023, Alex Henrie wrote:

> If we add a rebase-evil-merges mode, do you think that would be
> orthogonal to the rebase-cousins mode?

Those two concepts are very much orthogonal.

The rebase-evil-merges mode needs to change the way the `merge` command
operates, from "forget everything but the commit message of the original
merge commit" to "do try to figure out what amendments were made to the
original merge and replay them".

Whether cousins are rebased or not has everything to do with the `reset`
command, though.

Elsewhere in the thread, you mentioned a suspicion that `rebase-cousins`
should become the default. However, that would be a radical shift from the
spirit of the `git rebase [-r] <onto>` command: If `<onto>` is reachable
from `HEAD`, the idea is that an unchanged rebase script will produce the
identical commit topology. This is in line with a regular (non-`-r`)
rebase as long as there are no merge commits between `<onto>` and `HEAD`.
I am not sure that such a radical shift could ever be an easy sell.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-22 16:54           ` Phillip Wood
  2023-03-23 18:45             ` Junio C Hamano
@ 2023-03-25  5:21             ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-25  5:21 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras

On Wed, Mar 22, 2023 at 10:54 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> Hurray! Thanks for re-rolling.

Thanks for making sure that we got the UI right!

> On 20/03/2023 05:59, Alex Henrie wrote:

> > +     if (!strcmp(var, "rebase.rebasemerges")) {
> > +             opts->config_rebase_merges = git_parse_maybe_bool(value);
> > +             if (opts->config_rebase_merges < 0) {
> > +                     opts->config_rebase_merges = 1;
> > +                     parse_rebase_merges_value(opts, value);
> > +             } else
> > +                     opts->rebase_cousins = 0;
>
> The "else" clause should have braces because the "if" cause requires
> them (see Documentation/CodingGuidelines). I don't think it is worth
> re-rolling just for this though.

Thanks for pointing out that documentation. I'm going to make a v9
anyway, and I'll add the braces then. By the way, actions speak louder
than words: While writing the patch, I found several examples of the
braces being omitted in cases like this in other places in rebase.c,
so I assumed that that was the preferred style here. If you want to
encourage people to follow the CodingGuidelines document, the best way
would be to clean up the existing code to conform to it.

-Alex

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v8 3/3] rebase: add a config option for --rebase-merges
  2023-03-23 18:45             ` Junio C Hamano
  2023-03-24 14:52               ` Phillip Wood
@ 2023-03-25  5:23               ` Alex Henrie
  1 sibling, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-25  5:23 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy, felipe.contreras

On Thu, Mar 23, 2023 at 12:45 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> >> @@ -1514,13 +1542,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> >>                              break;
> >>              if (i >= 0 || options.type == REBASE_APPLY) {
> >> -                    if (is_merge(&options))
> >> -                            die(_("apply options and merge options "
> >> -                                      "cannot be used together"));
> >
> > This isn't a new change but having thought about it I'm not sure
> > moving this code is a good idea. If the user runs
> >
> >       git -c rebase.updateRefs=true rebase --whitespace=fix --exec "make test"
> >
> > then instead of getting an message saying that they cannot use apply
> > and merge options together they'll get a message suggesting they pass
> > --no-update-refs which wont fix the problem for them.
>
> Hmph.  Instead of dying here, ...
>
> >> +                    if (options.autosquash == -1 && options.config_autosquash == 1)
> >>                              die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
> >> +                    else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
> >> +                            die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
> >>                      else if (options.update_refs == -1 && options.config_update_refs == 1)
> >>                              die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
>
> ... we get caught here, and the next one is not triggered.
>
> >> +                    else if (is_merge(&options))
> >> +                            die(_("apply options and merge options "
> >> +                                      "cannot be used together"));
> >>                      else
> >>                              options.type = REBASE_APPLY;
>
> What's the reason why "cannot be used together" is moved to the last
> in the chain?
>
> The first two new conditions in this chain try to catch an
> invocation with some apply-specific command line option
> (e.g. "--whitespace=fix") when used with configuration variables
> specific to the merge-backend (e.g. "rebase.merges") and suggest
> overriding the configuration from the command line, and I suspect
> that the motivation behind this change is that their error messages
> are more specific than the generic "apply and merge do not mix".

Phillip specifically asked for `git -c rebase.rebaseMerges=true rebase
--whitespace=fix` to print "error: apply options are incompatible with
rebase.rebaseMerges. Consider adding --no-rebase-merges".[1] I could
have sworn that "if (is_merge(&options))" had to be after "if
(options.rebase_merges == -1 && options.config_rebase_merges == 1)" to
make that happen, but now it works without that change. I must have
been debugging with some intermediate version that still had
'imply_merge(&options, "--rebase-merges")' before this if chain. I'll
send a v9 that puts "if (is_merge(&options))" back at the top.

Thanks for your attention to detail!

-Alex

[1] https://lore.kernel.org/git/7ba8a92d-8c94-5226-5416-6ed3a8e2e389@dunelm.org.uk/

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v6 3/3] rebase: add a config option for --rebase-merges
  2023-03-16 20:27             ` Alex Henrie
  2023-03-16 22:39               ` Glen Choo
  2023-03-24 15:05               ` Johannes Schindelin
@ 2023-03-25 16:59               ` Sergey Organov
  2 siblings, 0 replies; 96+ messages in thread
From: Sergey Organov @ 2023-03-25 16:59 UTC (permalink / raw)
  To: Alex Henrie
  Cc: Glen Choo, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, calvinwan, jonathantanmy

Alex Henrie <alexhenrie24@gmail.com> writes:

[...]

> Let me ask a different but related question: If we add a
> rebase-evil-merges mode, do you think that would be orthogonal to the
> rebase-cousins mode?

This is the question that I considered as well. To me they look
orthogonal, and I'm afraid we will have hard times extending
--rebase-merges gracefully in a backward-compatible manner.

The set:

--rebase-merges=remerge
--rebase-merges=rebase
--rebase-merges=off

looks natural to me, but "cousins" that affect what is to be rebased (as
opposed to how, if at all), will apparently get in the way. Besides, I
recently ran into similar problem with --diff-merges (when|how)
dichotomy, and there was no elegant solution found, even though a
working one has been implemented, and then there were some discussions
around.

Also, I'm not quite sure that these "cousins" options make no sense when
we in fact flatten history (--no-rebase-merges case). To me it looks
like there should have been separate --[no-]rebase-cousins option,
provided we do need this choice in the first place, but I definitely
might be wrong here as well.

As a side note, it sounds both unfair and confusing to use
"rebase-evil-merges" term to describe a mode that will actually rebase
(all the) merges, as neither a merge needs to be 'evil' to be the object
of rebase operation, nor dedicated mode is necessarily needed to rebase
specifically 'evil' merges (whatever they actually are.)

Thanks,
-- Sergey Organov

^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
                           ` (2 preceding siblings ...)
  2023-03-20  5:59         ` [PATCH v8 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-26  3:06         ` Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
                             ` (3 more replies)
  3 siblings, 4 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-26  3:06 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

This patch series introduces a rebase.rebaseMerges config option to
accommodate users who would like --rebase-merges to be on by default and
to facilitate turning on --rebase-merges by default without
configuration in a future version of Git. It also cleans up and
documents the behavior of the --rebase-merges command line option to
avoid confusion about how the config option and the command line option
interact.

Changes from v8:
- Add braces around one-line else clause
- Remove unnecessary change to error message priority

Thanks to Phillip, Junio, Johannes and Sergey for your feedback on v8.

Alex Henrie (3):
  rebase: add documentation and test for --no-rebase-merges
  rebase: deprecate --rebase-merges=""
  rebase: add a config option for --rebase-merges

 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           | 19 ++++---
 builtin/rebase.c                       | 70 ++++++++++++++++++++------
 t/t3422-rebase-incompatible-options.sh | 17 +++++++
 t/t3430-rebase-merges.sh               | 44 ++++++++++++++++
 5 files changed, 138 insertions(+), 22 deletions(-)

Range-diff against v8:
1:  09fb7c1b74 = 1:  a22b9d0da2 rebase: add documentation and test for --no-rebase-merges
2:  a846716a4a = 2:  112fee4833 rebase: deprecate --rebase-merges=""
3:  b12a3610ba ! 3:  868899cd6d rebase: add a config option for --rebase-merges
    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
     +		if (opts->config_rebase_merges < 0) {
     +			opts->config_rebase_merges = 1;
     +			parse_rebase_merges_value(opts, value);
    -+		} else
    ++		} else {
     +			opts->rebase_cousins = 0;
    ++		}
     +		return 0;
     +	}
     +
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      		if (ignore_whitespace)
      			strvec_push(&options.git_am_opts,
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 				break;
    - 
    - 		if (i >= 0 || options.type == REBASE_APPLY) {
    --			if (is_merge(&options))
    --				die(_("apply options and merge options "
    --					  "cannot be used together"));
    --			else if (options.autosquash == -1 && options.config_autosquash == 1)
    -+			if (options.autosquash == -1 && options.config_autosquash == 1)
    + 					  "cannot be used together"));
    + 			else if (options.autosquash == -1 && options.config_autosquash == 1)
      				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
     +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
     +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
      			else if (options.update_refs == -1 && options.config_update_refs == 1)
      				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
    -+			else if (is_merge(&options))
    -+				die(_("apply options and merge options "
    -+					  "cannot be used together"));
      			else
    - 				options.type = REBASE_APPLY;
    - 		}
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
      	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
      			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
-- 
2.40.0


^ permalink raw reply	[flat|nested] 96+ messages in thread

* [PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
@ 2023-03-26  3:06           ` Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-26  3:06 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

As far as I can tell, --no-rebase-merges has always worked, but has
never been documented. It is especially important to document it before
a rebase.rebaseMerges option is introduced so that users know how to
override the config option on the command line. It's also important to
clarify that --rebase-merges without an argument is not the same as
--no-rebase-merges and not passing --rebase-merges is not the same as
passing --rebase-merges=no-rebase-cousins.

A test case is necessary to make sure that --no-rebase-merges keeps
working after its code is refactored in the following patches of this
series. The test case is a little contrived: It's unlikely that a user
would type both --rebase-merges and --no-rebase-merges at the same time.
However, if an alias is defined which includes --rebase-merges, the user
might decide to add --no-rebase-merges to countermand that part of the
alias but leave alone other flags set by the alias.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/git-rebase.txt | 18 +++++++++++-------
 t/t3430-rebase-merges.sh     | 10 ++++++++++
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..4e57a87624 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,20 +529,24 @@ See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand a previous `--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..d46d9545f2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v9 2/3] rebase: deprecate --rebase-merges=""
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
@ 2023-03-26  3:06           ` Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 3/3] rebase: add a config option for --rebase-merges Alex Henrie
  2023-03-26 15:12           ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Phillip Wood
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-26  3:06 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

The unusual syntax --rebase-merges="" (that is, --rebase-merges with an
empty string argument) has been an undocumented synonym of
--rebase-merges without an argument. Deprecate that syntax to avoid
confusion when a rebase.rebaseMerges config option is introduced, where
rebase.rebaseMerges="" will be equivalent to --no-rebase-merges.

It is not likely that anyone is actually using this syntax, but just in
case, deprecate the empty string argument instead of dropping support
for it immediately.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 builtin/rebase.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5b7b908b66..9448f7d87f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1141,7 +1141,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1439,7 +1439,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (rebase_merges) {
 		if (!*rebase_merges)
-			; /* default mode; do nothing */
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
 		else if (!strcmp("rebase-cousins", rebase_merges))
 			options.rebase_cousins = 1;
 		else if (strcmp("no-rebase-cousins", rebase_merges))
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* [PATCH v9 3/3] rebase: add a config option for --rebase-merges
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
  2023-03-26  3:06           ` [PATCH v9 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
@ 2023-03-26  3:06           ` Alex Henrie
  2023-03-26 15:12           ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Phillip Wood
  3 siblings, 0 replies; 96+ messages in thread
From: Alex Henrie @ 2023-03-26  3:06 UTC (permalink / raw)
  To: git, tao, gitster, newren, phillip.wood123, Johannes.Schindelin,
	sorganov, chooglen, calvinwan, jonathantanmy, felipe.contreras
  Cc: Alex Henrie

The purpose of the new option is to accommodate users who would like
--rebase-merges to be on by default and to facilitate turning on
--rebase-merges by default without configuration in a future version of
Git.

Name the new option rebase.rebaseMerges, even though it is a little
redundant, for consistency with the name of the command line option and
to be clear when scrolling through values in the [rebase] section of
.gitconfig.

Support setting rebase.rebaseMerges to the nonspecific value "true" for
users who don't need to or don't want to learn about the difference
between rebase-cousins and no-rebase-cousins.

Make --rebase-merges without an argument on the command line override
any value of rebase.rebaseMerges in the configuration, for consistency
with other command line flags with optional arguments that have an
associated config option.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 Documentation/config/rebase.txt        | 10 ++++
 Documentation/git-rebase.txt           |  3 +-
 builtin/rebase.c                       | 74 +++++++++++++++++++-------
 t/t3422-rebase-incompatible-options.sh | 17 ++++++
 t/t3430-rebase-merges.sh               | 34 ++++++++++++
 5 files changed, 118 insertions(+), 20 deletions(-)

diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..afaf6dad99 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true or to `no-rebase-cousins` is equivalent to
+	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line, with or without an argument, overrides any
+	`rebase.rebaseMerges` configuration.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4e57a87624..e7b39ad244 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -537,7 +537,8 @@ See also INCOMPATIBLE OPTIONS below.
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
 	resolved/re-applied manually. `--no-rebase-merges` can be used to
-	countermand a previous `--rebase-merges`.
+	countermand both the `rebase.rebaseMerges` config option and a previous
+	`--rebase-merges`.
 +
 When rebasing merges, there are two modes: `rebase-cousins` and
 `no-rebase-cousins`. If the mode is not specified, it defaults to
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 9448f7d87f..beebeb8f52 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -124,6 +124,7 @@ struct rebase_options {
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -141,6 +142,8 @@ struct rebase_options {
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
 		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 	}
@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.rebasemerges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		} else {
+			opts->rebase_cousins = 0;
+		}
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+	options->rebase_cousins = 0;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
+			return 0;
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1036,7 +1082,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)"no-rebase-cousins"},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
@@ -1437,21 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			warning(_("--rebase-merges with an empty string "
-				  "argument is deprecated and will stop "
-				  "working in a future version of Git. Use "
-				  "--rebase-merges without an argument "
-				  "instead, which does the same thing."));
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1519,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 					  "cannot be used together"));
 			else if (options.autosquash == -1 && options.config_autosquash == 1)
 				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
 			else
@@ -1531,6 +1562,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
 	if (options.autosquash == 1)
 		imply_merge(&options, "--autosquash");
 	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..2eba00bdf5 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@ test_rebase_am_only () {
 		test_must_fail git rebase $opt --reapply-cherry-picks A
 	"
 
+	test_expect_success "$opt incompatible with --rebase-merges" "
+		git checkout B^0 &&
+		test_must_fail git rebase $opt --rebase-merges A
+	"
+
 	test_expect_success "$opt incompatible with --update-refs" "
 		git checkout B^0 &&
 		test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
 		grep -e --no-autosquash err
 	"
 
+	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+		git checkout B^0 &&
+		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
+	"
+
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
 		git checkout B^0 &&
 		test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
 		git -c rebase.autosquash=true rebase --no-autosquash $opt A
 	"
 
+	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+		test_when_finished \"git reset --hard B^0\" &&
+		git checkout B^0 &&
+		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+	"
+
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index d46d9545f2..f03599c63b 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -278,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
 	EOF
 '
 
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+	test_config rebase.rebaseMerges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 96+ messages in thread

* Re: [PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
                             ` (2 preceding siblings ...)
  2023-03-26  3:06           ` [PATCH v9 3/3] rebase: add a config option for --rebase-merges Alex Henrie
@ 2023-03-26 15:12           ` Phillip Wood
  2023-03-27 16:33             ` Junio C Hamano
  3 siblings, 1 reply; 96+ messages in thread
From: Phillip Wood @ 2023-03-26 15:12 UTC (permalink / raw)
  To: Alex Henrie, git, tao, gitster, newren, phillip.wood123,
	Johannes.Schindelin, sorganov, chooglen, calvinwan, jonathantanmy,
	felipe.contreras

Hi Alex

On 26/03/2023 04:06, Alex Henrie wrote:
> This patch series introduces a rebase.rebaseMerges config option to
> accommodate users who would like --rebase-merges to be on by default and
> to facilitate turning on --rebase-merges by default without
> configuration in a future version of Git. It also cleans up and
> documents the behavior of the --rebase-merges command line option to
> avoid confusion about how the config option and the command line option
> interact.
> 
> Changes from v8:
> - Add braces around one-line else clause
> - Remove unnecessary change to error message priority

The range-diff looks good to me. This iteration addresses all of my 
outstanding concerns.

Thanks

Phillip

> Thanks to Phillip, Junio, Johannes and Sergey for your feedback on v8.
> 
> Alex Henrie (3):
>    rebase: add documentation and test for --no-rebase-merges
>    rebase: deprecate --rebase-merges=""
>    rebase: add a config option for --rebase-merges
> 
>   Documentation/config/rebase.txt        | 10 ++++
>   Documentation/git-rebase.txt           | 19 ++++---
>   builtin/rebase.c                       | 70 ++++++++++++++++++++------
>   t/t3422-rebase-incompatible-options.sh | 17 +++++++
>   t/t3430-rebase-merges.sh               | 44 ++++++++++++++++
>   5 files changed, 138 insertions(+), 22 deletions(-)
> 
> Range-diff against v8:
> 1:  09fb7c1b74 = 1:  a22b9d0da2 rebase: add documentation and test for --no-rebase-merges
> 2:  a846716a4a = 2:  112fee4833 rebase: deprecate --rebase-merges=""
> 3:  b12a3610ba ! 3:  868899cd6d rebase: add a config option for --rebase-merges
>      @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
>       +		if (opts->config_rebase_merges < 0) {
>       +			opts->config_rebase_merges = 1;
>       +			parse_rebase_merges_value(opts, value);
>      -+		} else
>      ++		} else {
>       +			opts->rebase_cousins = 0;
>      ++		}
>       +		return 0;
>       +	}
>       +
>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
>        		if (ignore_whitespace)
>        			strvec_push(&options.git_am_opts,
>       @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
>      - 				break;
>      -
>      - 		if (i >= 0 || options.type == REBASE_APPLY) {
>      --			if (is_merge(&options))
>      --				die(_("apply options and merge options "
>      --					  "cannot be used together"));
>      --			else if (options.autosquash == -1 && options.config_autosquash == 1)
>      -+			if (options.autosquash == -1 && options.config_autosquash == 1)
>      + 					  "cannot be used together"));
>      + 			else if (options.autosquash == -1 && options.config_autosquash == 1)
>        				die(_("apply options are incompatible with rebase.autoSquash.  Consider adding --no-autosquash"));
>       +			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
>       +				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
>        			else if (options.update_refs == -1 && options.config_update_refs == 1)
>        				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
>      -+			else if (is_merge(&options))
>      -+				die(_("apply options and merge options "
>      -+					  "cannot be used together"));
>        			else
>      - 				options.type = REBASE_APPLY;
>      - 		}
>       @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
>        	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
>        			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);

^ permalink raw reply	[flat|nested] 96+ messages in thread

* Re: [PATCH v9 0/3] rebase: document, clean up, and introduce a config option for --rebase-merges
  2023-03-26 15:12           ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Phillip Wood
@ 2023-03-27 16:33             ` Junio C Hamano
  0 siblings, 0 replies; 96+ messages in thread
From: Junio C Hamano @ 2023-03-27 16:33 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Alex Henrie, git, tao, newren, Johannes.Schindelin, sorganov,
	chooglen, calvinwan, jonathantanmy, felipe.contreras

Phillip Wood <phillip.wood123@gmail.com> writes:

> Hi Alex
>
> On 26/03/2023 04:06, Alex Henrie wrote:
>> This patch series introduces a rebase.rebaseMerges config option to
>> accommodate users who would like --rebase-merges to be on by default and
>> to facilitate turning on --rebase-merges by default without
>> configuration in a future version of Git. It also cleans up and
>> documents the behavior of the --rebase-merges command line option to
>> avoid confusion about how the config option and the command line option
>> interact.
>> Changes from v8:
>> - Add braces around one-line else clause
>> - Remove unnecessary change to error message priority
>
> The range-diff looks good to me. This iteration addresses all of my
> outstanding concerns.

Thanks, both.  Let's mark the topic as ready for 'next', then.

^ permalink raw reply	[flat|nested] 96+ messages in thread

end of thread, other threads:[~2023-03-27 16:33 UTC | newest]

Thread overview: 96+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-23  5:34 [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-02-23  5:34 ` [PATCH v4 2/3] rebase: stop accepting --rebase-merges="" Alex Henrie
2023-02-24 13:54   ` Johannes Schindelin
2023-02-24 17:20     ` Junio C Hamano
2023-02-24 17:50       ` Alex Henrie
2023-02-24 18:08         ` Junio C Hamano
2023-02-24 18:23           ` Alex Henrie
2023-02-24 18:40             ` Junio C Hamano
2023-02-24 18:55               ` Alex Henrie
2023-02-24 19:13                 ` Junio C Hamano
2023-02-24 19:24                   ` Alex Henrie
2023-02-24 19:24                   ` Phillip Wood
2023-02-24 19:56                     ` Alex Henrie
2023-02-23  5:34 ` [PATCH v4 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-02-24 13:53   ` Johannes Schindelin
2023-02-24 17:49     ` Alex Henrie
2023-02-24 14:55   ` Phillip Wood
2023-02-24 17:51     ` Alex Henrie
2023-02-23 17:28 ` [PATCH v4 1/3] rebase: add documentation and test for --no-rebase-merges Junio C Hamano
2023-02-24 13:57   ` Johannes Schindelin
2023-02-24 19:16     ` Junio C Hamano
2023-02-25 18:09     ` Alex Henrie
2023-02-25 18:03 ` [PATCH v5 0/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-02-25 18:03   ` [PATCH v5 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-03-01 23:23     ` Glen Choo
2023-02-25 18:03   ` [PATCH v5 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
2023-03-01 23:46     ` Glen Choo
2023-03-02 10:07     ` Phillip Wood
2023-03-02 18:02     ` Calvin Wan
2023-02-25 18:03   ` [PATCH v5 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-03-01 23:43     ` Glen Choo
2023-03-02  9:37     ` Phillip Wood
2023-03-04 23:24       ` Alex Henrie
2023-03-07 16:23         ` Phillip Wood
2023-03-12 20:57           ` Alex Henrie
2023-03-13 14:20             ` Phillip Wood
2023-03-13 16:12               ` Felipe Contreras
2023-03-13 19:46             ` Junio C Hamano
2023-03-24 14:47             ` About replaying "evil" merges... " Johannes Schindelin
2023-03-02 18:02     ` Calvin Wan
2023-03-04 23:24       ` Alex Henrie
2023-03-01 23:14   ` [PATCH v5 0/3] " Glen Choo
2023-03-02  5:02     ` Alex Henrie
2023-03-02  5:09       ` Alex Henrie
2023-03-05  5:07   ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Alex Henrie
2023-03-05  5:07     ` [PATCH v6 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-03-08 22:25       ` Sergey Organov
2023-03-05  5:07     ` [PATCH v6 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
2023-03-07 14:59       ` Phillip Wood
2023-03-05  5:07     ` [PATCH v6 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-03-07 14:56       ` Phillip Wood
2023-03-07 18:32         ` Junio C Hamano
2023-03-12 21:01           ` Alex Henrie
2023-03-08  0:09         ` Glen Choo
2023-03-08  0:02       ` Glen Choo
2023-03-12 21:03         ` Alex Henrie
2023-03-15  2:52         ` Alex Henrie
2023-03-16 17:32           ` Glen Choo
2023-03-16 18:11             ` Felipe Contreras
2023-03-16 22:46               ` Glen Choo
2023-03-16 23:48                 ` Felipe Contreras
2023-03-16 20:27             ` Alex Henrie
2023-03-16 22:39               ` Glen Choo
2023-03-18  5:59                 ` Alex Henrie
2023-03-24 15:05               ` Johannes Schindelin
2023-03-25 16:59               ` Sergey Organov
2023-03-05 12:22     ` [PATCH v6 0/3] rebase: document, clean up, and introduce " Sergey Organov
2023-03-05 21:33       ` Alex Henrie
2023-03-05 22:54         ` Sergey Organov
2023-03-06  0:02           ` Alex Henrie
2023-03-06 13:23             ` Sergey Organov
2023-03-06 19:08             ` Junio C Hamano
2023-03-06 17:19           ` Junio C Hamano
2023-03-06 16:24     ` Phillip Wood
2023-03-06 17:36       ` Alex Henrie
2023-03-07 15:07         ` Phillip Wood
2023-03-08  0:13     ` Glen Choo
2023-03-12 21:04     ` [PATCH v7 " Alex Henrie
2023-03-12 21:04       ` [PATCH v7 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-03-12 21:04       ` [PATCH v7 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
2023-03-12 21:04       ` [PATCH v7 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-03-20  5:59       ` [PATCH v8 0/3] rebase: document, clean up, and introduce " Alex Henrie
2023-03-20  5:59         ` [PATCH v8 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-03-20  5:59         ` [PATCH v8 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
2023-03-20  5:59         ` [PATCH v8 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-03-22 16:54           ` Phillip Wood
2023-03-23 18:45             ` Junio C Hamano
2023-03-24 14:52               ` Phillip Wood
2023-03-25  5:23               ` Alex Henrie
2023-03-25  5:21             ` Alex Henrie
2023-03-26  3:06         ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Alex Henrie
2023-03-26  3:06           ` [PATCH v9 1/3] rebase: add documentation and test for --no-rebase-merges Alex Henrie
2023-03-26  3:06           ` [PATCH v9 2/3] rebase: deprecate --rebase-merges="" Alex Henrie
2023-03-26  3:06           ` [PATCH v9 3/3] rebase: add a config option for --rebase-merges Alex Henrie
2023-03-26 15:12           ` [PATCH v9 0/3] rebase: document, clean up, and introduce " Phillip Wood
2023-03-27 16:33             ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).