{"thread":{"id":"66724","subject":"[PATCH] merge-ort: accept rename settings given without a value","startedAt":"2026-10-31T10:06:42Z","lastAt":"2026-11-01T00:52:51Z","messageCount":2,"participants":["Thomas Bachem via GitGitGadget","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"557001","messageId":"pull.2284.git.1793441202854.gitgitgadget@gmail.com","threadId":"66724","inReplyTo":null,"subject":"[PATCH] merge-ort: accept rename settings given without a value","fromName":"Thomas Bachem via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-10-31T10:06:42Z","receivedAt":"2026-10-31T10:06:42Z","isPatch":true,"sender":{"key":"mail@thomasbachem.com","avatar":"https://gravatar.com/avatar/e9bc28172fe03ca6777b6d330a8503511d84bde14ce0e84c962d2b3b39ed3db1?d=mp&s=160"},"body":"From: Thomas Bachem <mail@thomasbachem.com>\n\n\"git -c merge.renames merge\" fails with \"missing value for\n'merge.renames'\", although git(1) says that \"git -c foo.bar\" sets\nfoo.bar to true. The same happens with diff.renames and\nmerge.directoryRenames, and with a setting written without\n\"= <value>\" in a config file, which git-config(1) also documents as\ntrue. Every command that merges through merge-ort fails this way.\n\nmerge-ort reads these settings with repo_config_get_string(), which\nrejects a missing value. Read them with repo_config_get_value()\ninstead. git_config_rename() and git_parse_maybe_bool() already take\na missing value as true.\n\nAssisted-by: Claude Fable 5.1\nSigned-off-by: Thomas Bachem <mail@thomasbachem.com>\n---\n    merge-ort: accept rename settings given without a value\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2284%2Fthomasbachem%2Fmerge-ort-valueless-renames-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2284/thomasbachem/merge-ort-valueless-renames-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2284\n\n merge-ort.c                               | 20 ++++++++------------\n t/t6423-merge-rename-directories.sh       | 17 +++++++++++++++++\n t/t6434-merge-recursive-rename-options.sh | 12 ++++++++++++\n 3 files changed, 37 insertions(+), 12 deletions(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex c410a5d353..24ddd97696 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -5453,31 +5453,27 @@ void merge_incore_recursive(struct merge_options *opt,\n static void merge_recursive_config(struct merge_options *opt, int ui)\n {\n \tchar *value = NULL;\n+\tconst char *v;\n \tint renormalize = 0;\n \trepo_config_get_int(opt->repo, \"merge.verbosity\", &opt->verbosity);\n \trepo_config_get_int(opt->repo, \"diff.renamelimit\", &opt->rename_limit);\n \trepo_config_get_int(opt->repo, \"merge.renamelimit\", &opt->rename_limit);\n \trepo_config_get_bool(opt->repo, \"merge.renormalize\", &renormalize);\n \topt->renormalize = renormalize;\n-\tif (!repo_config_get_string(opt->repo, \"diff.renames\", &value)) {\n-\t\topt->detect_renames = git_config_rename(\"diff.renames\", value);\n-\t\tfree(value);\n-\t}\n-\tif (!repo_config_get_string(opt->repo, \"merge.renames\", &value)) {\n-\t\topt->detect_renames = git_config_rename(\"merge.renames\", value);\n-\t\tfree(value);\n-\t}\n-\tif (!repo_config_get_string(opt->repo, \"merge.directoryrenames\", &value)) {\n-\t\tint boolval = git_parse_maybe_bool(value);\n+\tif (!repo_config_get_value(opt->repo, \"diff.renames\", &v))\n+\t\topt->detect_renames = git_config_rename(\"diff.renames\", v);\n+\tif (!repo_config_get_value(opt->repo, \"merge.renames\", &v))\n+\t\topt->detect_renames = git_config_rename(\"merge.renames\", v);\n+\tif (!repo_config_get_value(opt->repo, \"merge.directoryrenames\", &v)) {\n+\t\tint boolval = git_parse_maybe_bool(v);\n \t\tif (0 <= boolval) {\n \t\t\topt->detect_directory_renames = boolval ?\n \t\t\t\tMERGE_DIRECTORY_RENAMES_TRUE :\n \t\t\t\tMERGE_DIRECTORY_RENAMES_NONE;\n-\t\t} else if (!strcasecmp(value, \"conflict\")) {\n+\t\t} else if (!strcasecmp(v, \"conflict\")) {\n \t\t\topt->detect_directory_renames =\n \t\t\t\tMERGE_DIRECTORY_RENAMES_CONFLICT;\n \t\t} /* avoid erroring on values from future versions of git */\n-\t\tfree(value);\n \t}\n \tif (ui) {\n \t\tif (!repo_config_get_string(opt->repo, \"diff.algorithm\", &value)) {\ndiff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh\nindex 24d4c5447b..da145c150c 100755\n--- a/t/t6423-merge-rename-directories.sh\n+++ b/t/t6423-merge-rename-directories.sh\n@@ -97,6 +97,23 @@ test_expect_success '1a: Simple directory rename detection' '\n \t)\n '\n \n+test_expect_success '1a (valueless): merge.directoryRenames without a value means true' '\n+\tmkdir valueless &&\n+\t(\n+\t\tcd valueless &&\n+\t\ttest_setup_1a &&\n+\t\tcd 1a &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames merge -s recursive B^0 &&\n+\n+\t\tgit rev-parse >actual HEAD:y/d HEAD:y/e/f &&\n+\t\tgit rev-parse >expect B:z/d B:z/e/f &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n # Testcase 1b, Merge a directory with another\n #   Commit O: z/{b,c},   y/d\n #   Commit A: z/{b,c,e}, y/d\ndiff --git a/t/t6434-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh\nindex 5a6f74839c..427de2ba95 100755\n--- a/t/t6434-merge-recursive-rename-options.sh\n+++ b/t/t6434-merge-recursive-rename-options.sh\n@@ -332,4 +332,16 @@ test_expect_success 'merge.renames overrides diff.renames' '\n \t$check_50\n '\n \n+test_expect_success 'diff.renames without a value means true' '\n+\tgit read-tree --reset -u HEAD &&\n+\ttest_must_fail git -c diff.renames merge-recursive $tail &&\n+\t$check_50\n+'\n+\n+test_expect_success 'merge.renames without a value means true' '\n+\tgit read-tree --reset -u HEAD &&\n+\ttest_must_fail git -c diff.renames=false -c merge.renames merge-recursive $tail &&\n+\t$check_50\n+'\n+\n test_done\n\nbase-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd\n-- \ngitgitgadget\n\n"},{"id":"557050","messageId":"xmqqjymx2x0c.fsf@gitster.g","threadId":"66724","inReplyTo":"pull.2284.git.1793441202854.gitgitgadget@gmail.com","subject":"Re: [PATCH] merge-ort: accept rename settings given without a value","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-11-01T00:52:51Z","receivedAt":"2026-11-01T00:52:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Thomas Bachem via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Thomas Bachem <mail@thomasbachem.com>\n>\n> \"git -c merge.renames merge\" fails with \"missing value for\n> 'merge.renames'\", although git(1) says that \"git -c foo.bar\" sets\n> foo.bar to true. The same happens with diff.renames and\n> merge.directoryRenames, and with a setting written without\n> \"= <value>\" in a config file, which git-config(1) also documents as\n> true. Every command that merges through merge-ort fails this way.\n\nSearching the list archive for \"valueless true\" tells us that we had\ncorrected a handful of instances of config_get_string() with a call\nto config_get_value() in the past half year.\n\nAnd this patch seems to be in line with the other efforts to fix the\nsame misuse.\n\n> +\tif (!repo_config_get_value(opt->repo, \"diff.renames\", &v))\n> +\t\topt->detect_renames = git_config_rename(\"diff.renames\", v);\n> +\tif (!repo_config_get_value(opt->repo, \"merge.renames\", &v))\n> +\t\topt->detect_renames = git_config_rename(\"merge.renames\", v);\n> +\tif (!repo_config_get_value(opt->repo, \"merge.directoryrenames\", &v)) {\n> +\t\tint boolval = git_parse_maybe_bool(v);\n>  \t\tif (0 <= boolval) {\n>  \t\t\topt->detect_directory_renames = boolval ?\n>  \t\t\t\tMERGE_DIRECTORY_RENAMES_TRUE :\n>  \t\t\t\tMERGE_DIRECTORY_RENAMES_NONE;\n> -\t\t} else if (!strcasecmp(value, \"conflict\")) {\n> +\t\t} else if (!strcasecmp(v, \"conflict\")) {\n>  \t\t\topt->detect_directory_renames =\n>  \t\t\t\tMERGE_DIRECTORY_RENAMES_CONFLICT;\n>  \t\t} /* avoid erroring on values from future versions of git */\n> -\t\tfree(value);\n>  \t}\n\nOK.  Since get_value(), unlike get_string(), lets us borrow the\nvalue as \"const char *\", once we used to drive git_config_rename() or\ncompared with fixed string to turn it into an enum value, we can simply\nforget without calling free(), which is simpler.\n\nNicely done.\n\n"}]}