git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v5 0/3] rebase: add a config option for --rebase-merges

From
Alex Henrie <alexhenrie24@gmail.com>
Date
Feb 25, 2023, 18:03 UTC
Message-ID
<20230225180325.796624-1-alexhenrie24@gmail.com>
In-Reply-To
<20230223053410.644503-1-alexhenrie24@gmail.com>
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
Previous: Alex HenrieNext: Alex Henrie
Message 23 of 96 in “rebase: add documentation and test for --no-rebase-merges”
  1. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Feb 23, 2023
  2. 2/3 rebase: stop accepting --rebase-merges=""Alex Henrie, Feb 23, 2023
  3. Johannes SchindelinFeb 24, 2023
  4. Junio C HamanoFeb 24, 2023
  5. Alex HenrieFeb 24, 2023
  6. Junio C HamanoFeb 24, 2023
  7. Alex HenrieFeb 24, 2023
  8. Junio C HamanoFeb 24, 2023
  9. Alex HenrieFeb 24, 2023
  10. Junio C HamanoFeb 24, 2023
  11. Alex HenrieFeb 24, 2023
  12. Phillip WoodFeb 24, 2023
  13. Alex HenrieFeb 24, 2023
  14. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Feb 23, 2023
  15. Johannes SchindelinFeb 24, 2023
  16. Alex HenrieFeb 24, 2023
  17. Phillip WoodFeb 24, 2023
  18. Alex HenrieFeb 24, 2023
  19. Junio C HamanoFeb 23, 2023
  20. Johannes SchindelinFeb 24, 2023
  21. Junio C HamanoFeb 24, 2023
  22. Alex HenrieFeb 25, 2023
  23. 0/3 rebase: add a config option for --rebase-mergesAlex Henrie, Feb 25, 2023
  24. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Feb 25, 2023
  25. Glen ChooMar 1, 2023
  26. 2/3 rebase: deprecate --rebase-merges=""Alex Henrie, Feb 25, 2023
  27. Glen ChooMar 1, 2023
  28. Phillip WoodMar 2, 2023
  29. Calvin WanMar 2, 2023
  30. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Feb 25, 2023
  31. Glen ChooMar 1, 2023
  32. Phillip WoodMar 2, 2023
  33. Alex HenrieMar 4, 2023
  34. Phillip WoodMar 7, 2023
  35. Alex HenrieMar 12, 2023
  36. Phillip WoodMar 13, 2023
  37. Felipe ContrerasMar 13, 2023
  38. Junio C HamanoMar 13, 2023
  39. About replaying "evil" merges... Re: [PATCH v5 3/3] rebase: add a config option for --rebase-mergesJohannes Schindelin, Mar 24, 2023
  40. Calvin WanMar 2, 2023
  41. Alex HenrieMar 4, 2023
  42. Glen ChooMar 1, 2023
  43. Alex HenrieMar 2, 2023
  44. Alex HenrieMar 2, 2023
  45. 0/3 rebase: document, clean up, and introduce a config option for --rebase-mergesAlex Henrie, Mar 5, 2023
  46. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Mar 5, 2023
  47. Phillip WoodMar 7, 2023
  48. Junio C HamanoMar 7, 2023
  49. Alex HenrieMar 12, 2023
  50. Glen ChooMar 8, 2023
  51. Glen ChooMar 8, 2023
  52. Alex HenrieMar 12, 2023
  53. Alex HenrieMar 15, 2023
  54. Glen ChooMar 16, 2023
  55. Felipe ContrerasMar 16, 2023
  56. Glen ChooMar 16, 2023
  57. Felipe ContrerasMar 16, 2023
  58. Alex HenrieMar 16, 2023
  59. Glen ChooMar 16, 2023
  60. Alex HenrieMar 18, 2023
  61. Johannes SchindelinMar 24, 2023
  62. Sergey OrganovMar 25, 2023
  63. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Mar 5, 2023
  64. Sergey OrganovMar 8, 2023
  65. 2/3 rebase: deprecate --rebase-merges=""Alex Henrie, Mar 5, 2023
  66. Phillip WoodMar 7, 2023
  67. Sergey OrganovMar 5, 2023
  68. Alex HenrieMar 5, 2023
  69. Sergey OrganovMar 5, 2023
  70. Alex HenrieMar 6, 2023
  71. Sergey OrganovMar 6, 2023
  72. Junio C HamanoMar 6, 2023
  73. Junio C HamanoMar 6, 2023
  74. Phillip WoodMar 6, 2023
  75. Alex HenrieMar 6, 2023
  76. Phillip WoodMar 7, 2023
  77. Glen ChooMar 8, 2023
  78. 0/3 rebase: document, clean up, and introduce a config option for --rebase-mergesAlex Henrie, Mar 12, 2023
  79. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Mar 12, 2023
  80. 2/3 rebase: deprecate --rebase-merges=""Alex Henrie, Mar 12, 2023
  81. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Mar 12, 2023
  82. 0/3 rebase: document, clean up, and introduce a config option for --rebase-mergesAlex Henrie, Mar 20, 2023
  83. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Mar 20, 2023
  84. 2/3 rebase: deprecate --rebase-merges=""Alex Henrie, Mar 20, 2023
  85. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Mar 20, 2023
  86. Phillip WoodMar 22, 2023
  87. Junio C HamanoMar 23, 2023
  88. Phillip WoodMar 24, 2023
  89. Alex HenrieMar 25, 2023
  90. Alex HenrieMar 25, 2023
  91. 0/3 rebase: document, clean up, and introduce a config option for --rebase-mergesAlex Henrie, Mar 26, 2023
  92. 1/3 rebase: add documentation and test for --no-rebase-mergesAlex Henrie, Mar 26, 2023
  93. 2/3 rebase: deprecate --rebase-merges=""Alex Henrie, Mar 26, 2023
  94. 3/3 rebase: add a config option for --rebase-mergesAlex Henrie, Mar 26, 2023
  95. Phillip WoodMar 26, 2023
  96. Junio C HamanoMar 27, 2023

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.