{"thread":{"id":"61452","subject":"[PATCH 00/21] builtin/config: remove global state","startedAt":"2024-05-10T11:24:30Z","lastAt":"2024-05-15T06:43:16Z","messageCount":81,"participants":["Patrick Steinhardt","Junio C Hamano","Kyle Lippincott","Eric Sunshine","Taylor Blau"],"isPatch":true,"patchVersion":1,"patchTotal":21},"messages":[{"id":"494438","messageId":"cover.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":null,"subject":"[PATCH 00/21] builtin/config: remove global state","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:23Z","receivedAt":"2024-05-10T11:24:30Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nwe have quite a lot of global state in git-config(1). For one, this\nglobal state is used to track options passed by the user. And second,\nthere is a lot of global state that is really only used to pass data\nbetween caller and callbacks.\n\nThis global state makes it quite hard to understand interactions at\ntimes. This only became harder now with the introduction of subcommands,\nwhere a whole lot less of the global state is even relevant in the first\nplace. As an example, this global state has made it quite easy for a bug\nto sneak into the new subcommands where `check_write()` was called\nbefore the global data it access is initialized (see patch 6).\n\nThis patch series refactors the code such that we have no more global\nstate in \"builtin/config.c\". It's rather long, but most of the patches\nare really quite trivial and only move code around. So overall, I think\nit shouldn't be too bad to review this. But please, let me know if you\ndisagree and I'll happily split this series into two.\n\nThe patch series depends on 7b91d310ce (builtin/config: display\nsubcommand help, 2024-05-06).\n\nThanks!\n\nPatrick\n\nPatrick Steinhardt (21):\n  builtin/config: stop printing full usage on misuse\n  builtin/config: move legacy mode into its own function\n  builtin/config: move subcommand options into `cmd_config()`\n  builtin/config: move legacy options into `cmd_config()`\n  builtin/config: move actions into `cmd_config_actions()`\n  builtin/config: check for writeability after source is set up\n  config: make the config source const\n  builtin/config: refactor functions to have common exit paths\n  builtin/config: move location options into local variables\n  builtin/config: move display options into local variables\n  builtin/config: move type options into display options\n  builtin/config: move default value into display options\n  builtin/config: move `respect_includes_opt` into location options\n  builtin/config: convert `do_not_match` to a local variable\n  builtin/config: convert `value_pattern` to a local variable\n  builtin/config: convert `regexp` to a local variable\n  builtin/config: convert `key_regexp` to a local variable\n  builtin/config: convert `key` to a local variable\n  builtin/config: track \"fixed value\" option via flags only\n  builtin/config: convert flags to a local variable\n  builtin/config: pass data between callbacks via local variables\n\n builtin/config.c  | 964 +++++++++++++++++++++++++---------------------\n config.c          |   4 +-\n config.h          |   2 +-\n t/t1300-config.sh |   9 +-\n 4 files changed, 546 insertions(+), 433 deletions(-)\n\n-- \n2.45.GIT\n\n"},{"id":"494439","messageId":"0ba76281267b324dd7fe8094132dbce11e9f2182.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 01/21] builtin/config: stop printing full usage on misuse","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:28Z","receivedAt":"2024-05-10T11:24:32Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When invoking git-config(1) with a wrong set of arguments we end up\ncalling `usage_builtin_config()` after printing an error message that\nsays what was wrong. As that function ends up printing the full list of\noptions, which is quite long, the actual error message will be buried by\na wall of text. This makes it really hard to figure out what exactly\ncaused the error.\n\nFurthermore, now that we have recently introduced subcommands, the usage\ninformation may actually be misleading as we unconditionally print\noptions of the subcommand-less mode.\n\nFix both of these issues by just not printing the options at all\nanymore. Instead, we call `usage()` that makes us report in a single\nline what has gone wrong. This should be way more discoverable for our\nusers and addresses the inconsistency.\n\nFurthermore, this change allow us to inline the options into the\nrespective functions that use them to parse the command line.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 28 +++++++++++-----------------\n t/t1300-config.sh |  3 ++-\n 2 files changed, 13 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 80aa9d8a66..3a71d3253f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,8 +125,6 @@ static const char *comment_arg;\n \t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n \tPARSE_OPT_NONEG, option_parse_type, (i) }\n \n-static NORETURN void usage_builtin_config(void);\n-\n static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n@@ -171,7 +169,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t * --type=int'.\n \t\t */\n \t\terror(_(\"only one type at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \t*to_type = new_type;\n \n@@ -187,7 +185,7 @@ static void check_argc(int argc, int min, int max)\n \telse\n \t\terror(_(\"wrong number of arguments, should be from %d to %d\"),\n \t\t      min, max);\n-\tusage_builtin_config();\n+\texit(129);\n }\n \n static void show_config_origin(const struct key_value_info *kvi,\n@@ -672,7 +670,7 @@ static void handle_config_location(const char *prefix)\n \t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n@@ -802,11 +800,6 @@ static struct option builtin_config_options[] = {\n \tOPT_END(),\n };\n \n-static NORETURN void usage_builtin_config(void)\n-{\n-\tusage_with_options(builtin_config_usage, builtin_config_options);\n-}\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1110,7 +1103,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (actions == 0)\n@@ -1119,30 +1112,31 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcase 2: actions = ACTION_SET; break;\n \t\tcase 3: actions = ACTION_SET_ALL; break;\n \t\tdefault:\n-\t\t\tusage_builtin_config();\n+\t\t\terror(_(\"no action specified\"));\n+\t\t\texit(129);\n \t\t}\n \tif (omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (comment_arg &&\n \t    !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {\n \t\terror(_(\"--comment is only applicable to add/set/replace operations\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \t/* check usage of --fixed-value */\n@@ -1175,7 +1169,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\tif (!allowed_usage) {\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\t\t\tusage_builtin_config();\n+\t\t\texit(129);\n \t\t}\n \n \t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f3c4d28e06..d90a69b29f 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -596,7 +596,8 @@ test_expect_success 'get bool variable with empty value' '\n \n test_expect_success 'no arguments, but no crash' '\n \ttest_must_fail git config >output 2>&1 &&\n-\ttest_grep usage output\n+\techo \"error: no action specified\" >expect &&\n+\ttest_cmp expect output\n '\n \n cat > .git/config << EOF\n-- \n2.45.GIT\n\n"},{"id":"494440","messageId":"663e1f74f82b8c1acdd0c35ad87736c071eafb33.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 02/21] builtin/config: move legacy mode into its own function","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:33Z","receivedAt":"2024-05-10T11:24:37Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In `cmd_config()` we first try to parse the provided arguments as\nsubcommands and, if this is successful, call the respective functions\nof that subcommand. Otherwise we continue with the \"legacy\" mode that\nuses implicit actions and/or flags.\n\nDisentangle this by moving the legacy mode into its own function. This\nallows us to move the options into the respective functions and clearly\nseparates concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 43 ++++++++++++++++++++++++-------------------\n 1 file changed, 24 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3a71d3253f..f6c7e7a082 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -1069,31 +1069,13 @@ static struct option builtin_subcommand_options[] = {\n \tOPT_END(),\n };\n \n-int cmd_config(int argc, const char **argv, const char *prefix)\n+static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n-\t/*\n-\t * This is somewhat hacky: we first parse the command line while\n-\t * keeping all args intact in order to determine whether a subcommand\n-\t * has been specified. If so, we re-parse it a second time, but this\n-\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n-\t * line in case no subcommand was given, which would otherwise confuse\n-\t * us when parsing the legacy-style modes that don't use subcommands.\n-\t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\t\treturn subcommand(argc, argv, prefix);\n-\t}\n-\n \targc = parse_options(argc, argv, prefix, builtin_config_options,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -1306,3 +1288,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tfree(value);\n \treturn ret;\n }\n+\n+int cmd_config(int argc, const char **argv, const char *prefix)\n+{\n+\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\n+\t/*\n+\t * This is somewhat hacky: we first parse the command line while\n+\t * keeping all args intact in order to determine whether a subcommand\n+\t * has been specified. If so, we re-parse it a second time, but this\n+\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n+\t * line in case no subcommand was given, which would otherwise confuse\n+\t * us when parsing the legacy-style modes that don't use subcommands.\n+\t */\n+\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\tif (subcommand) {\n+\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\t\treturn subcommand(argc, argv, prefix);\n+\t}\n+\n+\treturn cmd_config_actions(argc, argv, prefix);\n+}\n-- \n2.45.GIT\n\n"},{"id":"494441","messageId":"1239c151d08448f5782cdd12c5cf10fd5d2719d4.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 03/21] builtin/config: move subcommand options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:37Z","receivedAt":"2024-05-10T11:24:41Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the subcommand options as well as the `subcommand` variable into\n`cmd_config()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f6c7e7a082..58c8b0abda 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -75,7 +75,6 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static parse_opt_subcommand_fn *subcommand;\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n@@ -1058,17 +1057,6 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \treturn show_editor();\n }\n \n-static struct option builtin_subcommand_options[] = {\n-\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n-\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n-\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n-\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n-\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n-\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n-\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n@@ -1291,6 +1279,18 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n int cmd_config(int argc, const char **argv, const char *prefix)\n {\n+\tparse_opt_subcommand_fn *subcommand = NULL;\n+\tstruct option subcommand_opts[] = {\n+\t\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n+\t\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n+\t\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n+\t\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n+\t\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n+\t\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n+\t\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n+\t\tOPT_END(),\n+\t};\n+\n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n \t/*\n@@ -1301,10 +1301,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t * line in case no subcommand was given, which would otherwise confuse\n \t * us when parsing the legacy-style modes that don't use subcommands.\n \t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \t\treturn subcommand(argc, argv, prefix);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494442","messageId":"82964510c557a398e2713744ec75bba3ba5edb25.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 04/21] builtin/config: move legacy options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:42Z","receivedAt":"2024-05-10T11:24:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the legacy options as well some of the variables it references into\n`cmd_config_action()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 ++++++++++++++++++++++++------------------------\n 1 file changed, 30 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 58c8b0abda..e9956574fe 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -78,7 +78,7 @@ static char term = '\\n';\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n-static int actions, type;\n+static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n@@ -86,7 +86,6 @@ static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n-static const char *comment_arg;\n \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -772,33 +771,6 @@ static void handle_nul(void) {\n \tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n \n-static struct option builtin_config_options[] = {\n-\tCONFIG_LOCATION_OPTIONS,\n-\tOPT_GROUP(N_(\"Action\")),\n-\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n-\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n-\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n-\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n-\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n-\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n-\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n-\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n-\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n-\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n-\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n-\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n-\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n-\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\tCONFIG_TYPE_OPTIONS,\n-\tCONFIG_DISPLAY_OPTIONS,\n-\tOPT_GROUP(N_(\"Other\")),\n-\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n-\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1059,12 +1031,40 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tconst char *comment_arg = NULL;\n+\tint actions = 0;\n+\tstruct option opts[] = {\n+\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Action\")),\n+\t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n+\t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n+\t\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n+\t\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n+\t\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n+\t\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n+\t\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n+\t\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n+\t\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n+\t\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n+\t\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n+\t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n+\t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n+\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Other\")),\n+\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n+\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_END(),\n+\t};\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\targc = parse_options(argc, argv, prefix, builtin_config_options,\n+\targc = parse_options(argc, argv, prefix, opts,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-- \n2.45.GIT\n\n"},{"id":"494443","messageId":"2e308393edb0c34593e78603b43bee8d3a45dd8a.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 05/21] builtin/config: move actions into `cmd_config_actions()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:47Z","receivedAt":"2024-05-10T11:24:51Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We only use actions in the legacy mode. Convert them to an enum and move\nthem into `cmd_config_actions()` to clearly demonstrate that they are\nnot used anywhere else.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 44 +++++++++++++++++++-------------------------\n 1 file changed, 19 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e9956574fe..8f3342b593 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -87,30 +87,6 @@ static int show_origin;\n static int show_scope;\n static int fixed_value;\n \n-#define ACTION_GET (1<<0)\n-#define ACTION_GET_ALL (1<<1)\n-#define ACTION_GET_REGEXP (1<<2)\n-#define ACTION_REPLACE_ALL (1<<3)\n-#define ACTION_ADD (1<<4)\n-#define ACTION_UNSET (1<<5)\n-#define ACTION_UNSET_ALL (1<<6)\n-#define ACTION_RENAME_SECTION (1<<7)\n-#define ACTION_REMOVE_SECTION (1<<8)\n-#define ACTION_LIST (1<<9)\n-#define ACTION_EDIT (1<<10)\n-#define ACTION_SET (1<<11)\n-#define ACTION_SET_ALL (1<<12)\n-#define ACTION_GET_COLOR (1<<13)\n-#define ACTION_GET_COLORBOOL (1<<14)\n-#define ACTION_GET_URLMATCH (1<<15)\n-\n-/*\n- * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n- * one line of output and which should therefore be paged.\n- */\n-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n-\t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -1031,6 +1007,24 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tenum {\n+\t\tACTION_GET = (1<<0),\n+\t\tACTION_GET_ALL = (1<<1),\n+\t\tACTION_GET_REGEXP = (1<<2),\n+\t\tACTION_REPLACE_ALL = (1<<3),\n+\t\tACTION_ADD = (1<<4),\n+\t\tACTION_UNSET = (1<<5),\n+\t\tACTION_UNSET_ALL = (1<<6),\n+\t\tACTION_RENAME_SECTION = (1<<7),\n+\t\tACTION_REMOVE_SECTION = (1<<8),\n+\t\tACTION_LIST = (1<<9),\n+\t\tACTION_EDIT = (1<<10),\n+\t\tACTION_SET = (1<<11),\n+\t\tACTION_SET_ALL = (1<<12),\n+\t\tACTION_GET_COLOR = (1<<13),\n+\t\tACTION_GET_COLORBOOL = (1<<14),\n+\t\tACTION_GET_URLMATCH = (1<<15),\n+\t};\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1147,7 +1141,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\tif (actions & PAGING_ACTIONS)\n+\tif (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n \t\tsetup_auto_pager(\"config\", 1);\n \n \tif (actions == ACTION_LIST) {\n-- \n2.45.GIT\n\n"},{"id":"494444","messageId":"edfd7caa39ab990faf5b49a6c572a612a35dbdd5.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 06/21] builtin/config: check for writeability after source is set up","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:52Z","receivedAt":"2024-05-10T11:24:57Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `check_write()` function verifies that we do not try to write to a\nconfig source that cannot be written to, like for example stdin. But\nwhile the new subcommands do call this function, they do so before\ncalling `handle_config_location()`. Consequently, we only end up\nchecking the default config location for writeability, not the location\nthat was actually specified by the caller of git-config(1).\n\nFix this by calling `check_write()` after `handle_config_location()`. We\nwill further clarify the relationship between those two functions in a\nsubsequent commit where we remove the global state that both implicitly\nrely on.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 10 +++++-----\n t/t1300-config.sh |  6 ++++++\n 2 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8f3342b593..9295a2f737 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n@@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n@@ -891,13 +891,13 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n@@ -918,10 +918,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n@@ -943,10 +943,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n@@ -997,10 +997,10 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n-\tcheck_write();\n \tcheck_argc(argc, 0, 0);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \treturn show_editor();\n }\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex d90a69b29f..9de2d95f06 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2835,6 +2835,12 @@ test_expect_success 'specifying multiple modes causes failure' '\n \ttest_cmp expect err\n '\n \n+test_expect_success 'writing to stdin is rejected' '\n+\techo \"fatal: writing to stdin is not supported\" >expect &&\n+\ttest_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&\n+\ttest_cmp expect err\n+'\n+\n done\n \n test_done\n-- \n2.45.GIT\n\n"},{"id":"494445","messageId":"bfba68aa1e0b19fa16a2cfc5dd04df28ef337505.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 07/21] config: make the config source const","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:24:57Z","receivedAt":"2024-05-10T11:25:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `struct git_config_source` passed to `config_with_options()` is\nnever modified. Let's mark it as `const` to clarify.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n config.c | 4 ++--\n config.h | 2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 13cf9eeb16..bc0c005039 100644\n--- a/config.c\n+++ b/config.c\n@@ -125,7 +125,7 @@ struct config_include_data {\n \tconfig_fn_t fn;\n \tvoid *data;\n \tconst struct config_options *opts;\n-\tstruct git_config_source *config_source;\n+\tconst struct git_config_source *config_source;\n \tstruct repository *repo;\n \n \t/*\n@@ -2105,7 +2105,7 @@ static int do_git_config_sequence(const struct config_options *opts,\n }\n \n int config_with_options(config_fn_t fn, void *data,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts)\n {\ndiff --git a/config.h b/config.h\nindex db8b608064..e66c84520b 100644\n--- a/config.h\n+++ b/config.h\n@@ -232,7 +232,7 @@ void git_config(config_fn_t fn, void *);\n  * sets `opts.respect_includes` to `1` by default.\n  */\n int config_with_options(config_fn_t fn, void *,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts);\n \n-- \n2.45.GIT\n\n"},{"id":"494446","messageId":"6bff0410e99d59de8cc302f6b788af851b5e005a.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 08/21] builtin/config: refactor functions to have common exit paths","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:02Z","receivedAt":"2024-05-10T11:25:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Refactor functions to have a single exit path. This will make it easier\nin subsequent commits to add common cleanup code.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 38 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9295a2f737..9b90eda328 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -798,6 +798,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -816,8 +817,11 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\treturn get_urlmatch(argv[0], url);\n-\treturn get_value(argv[0], value_pattern, flags);\n+\t\tret = get_urlmatch(argv[0], url);\n+\telse\n+\t\tret = get_value(argv[0], value_pattern, flags);\n+\n+\treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n@@ -888,6 +892,7 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -900,12 +905,14 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, value_pattern,\n-\t\t\t\t\t\t\t      NULL, flags);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n+\t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\treturn git_config_set_in_file_gently(given_config_source.file, argv[0],\n-\t\t\t\t\t\t     NULL, NULL);\n+\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\t\t\t\t\t    NULL, NULL);\n+\n+\treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n@@ -926,11 +933,13 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n@@ -951,11 +960,13 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int show_editor(void)\n@@ -1195,41 +1206,41 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\treturn get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t\t      NULL, flags);\n+\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\treturn git_config_set_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t     argv[0], NULL, NULL);\n+\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t      NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tcheck_write();\n@@ -1237,7 +1248,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1249,7 +1260,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1263,9 +1274,10 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\treturn get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(argv[0], argc == 2);\n \t}\n \n+out:\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494447","messageId":"a96c122280ad025f94c1343cca85c67c7c617c8f.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 09/21] builtin/config: move location options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:07Z","receivedAt":"2024-05-10T11:25:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The location options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 311 ++++++++++++++++++++++++++---------------------\n 1 file changed, 174 insertions(+), 137 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9b90eda328..5346a782d0 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -62,6 +62,25 @@ static const char *const builtin_config_edit_usage[] = {\n \tNULL\n };\n \n+#define CONFIG_LOCATION_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Config file location\")), \\\n+\tOPT_BOOL(0, \"global\", &opts.use_global_config, N_(\"use global config file\")), \\\n+\tOPT_BOOL(0, \"system\", &opts.use_system_config, N_(\"use system config file\")), \\\n+\tOPT_BOOL(0, \"local\", &opts.use_local_config, N_(\"use repository config file\")), \\\n+\tOPT_BOOL(0, \"worktree\", &opts.use_worktree_config, N_(\"use per-worktree config file\")), \\\n+\tOPT_STRING('f', \"file\", &opts.source.file, N_(\"file\"), N_(\"use given config file\")), \\\n+\tOPT_STRING(0, \"blob\", &opts.source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n+\n+struct config_location_options {\n+\tstruct git_config_source source;\n+\tstruct config_options options;\n+\tint use_global_config;\n+\tint use_system_config;\n+\tint use_local_config;\n+\tint use_worktree_config;\n+};\n+#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n@@ -75,14 +94,10 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static int use_global_config, use_system_config, use_local_config;\n-static int use_worktree_config;\n-static struct git_config_source given_config_source;\n static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n-static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n@@ -298,7 +313,8 @@ static int collect_config(const char *key_, const char *value_,\n \treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n }\n \n-static int get_value(const char *key_, const char *regex_, unsigned flags)\n+static int get_value(const struct config_location_options *opts,\n+\t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -353,8 +369,8 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)\n \t}\n \n \tconfig_with_options(collect_config, &values,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!values.nr && default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n@@ -464,14 +480,15 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const char *var, const char *def_color)\n+static void get_color(const struct config_location_options *opts,\n+\t\t      const char *var, const char *def_color)\n {\n \tget_color_slot = var;\n \tget_color_found = 0;\n \tparsed_color[0] = '\\0';\n \tconfig_with_options(git_get_color_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!get_color_found && def_color) {\n \t\tif (color_parse(def_color, parsed_color) < 0)\n@@ -497,15 +514,16 @@ static int git_get_colorbool_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_colorbool(const char *var, int print)\n+static int get_colorbool(const struct config_location_options *opts,\n+\t\t\t const char *var, int print)\n {\n \tget_colorbool_slot = var;\n \tget_colorbool_found = -1;\n \tget_diff_color_found = -1;\n \tget_color_ui_found = -1;\n \tconfig_with_options(git_get_colorbool_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (get_colorbool_found < 0) {\n \t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n@@ -527,15 +545,15 @@ static int get_colorbool(const char *var, int print)\n \t\treturn get_colorbool_found ? 0 : 1;\n }\n \n-static void check_write(void)\n+static void check_write(const struct git_config_source *source)\n {\n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!source->file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n \n-\tif (given_config_source.use_stdin)\n+\tif (source->use_stdin)\n \t\tdie(_(\"writing to stdin is not supported\"));\n \n-\tif (given_config_source.blob)\n+\tif (source->blob)\n \t\tdie(_(\"writing config blobs is not supported\"));\n }\n \n@@ -572,7 +590,8 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_urlmatch(const char *var, const char *url)\n+static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n@@ -599,8 +618,8 @@ static int get_urlmatch(const char *var, const char *url)\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tret = !values.nr;\n \n@@ -638,34 +657,40 @@ static char *default_user_config(void)\n \treturn strbuf_detach(&buf, NULL);\n }\n \n-static void handle_config_location(const char *prefix)\n+static void location_options_init(struct config_location_options *opts,\n+\t\t\t\t  const char *prefix)\n {\n-\tif (use_global_config + use_system_config + use_local_config +\n-\t    use_worktree_config +\n-\t    !!given_config_source.file + !!given_config_source.blob > 1) {\n+\tif (!opts->source.file)\n+\t\topts->source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\telse\n+\t\topts->source.file = xstrdup(opts->source.file);\n+\n+\tif (opts->use_global_config + opts->use_system_config +\n+\t    opts->use_local_config + opts->use_worktree_config +\n+\t    !!opts->source.file + !!opts->source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n-\t\tif (use_local_config)\n+\t\tif (opts->use_local_config)\n \t\t\tdie(_(\"--local can only be used inside a git repository\"));\n-\t\tif (given_config_source.blob)\n+\t\tif (opts->source.blob)\n \t\t\tdie(_(\"--blob can only be used inside a git repository\"));\n-\t\tif (use_worktree_config)\n+\t\tif (opts->use_worktree_config)\n \t\t\tdie(_(\"--worktree can only be used inside a git repository\"));\n \t}\n \n-\tif (given_config_source.file &&\n-\t\t\t!strcmp(given_config_source.file, \"-\")) {\n-\t\tgiven_config_source.file = NULL;\n-\t\tgiven_config_source.use_stdin = 1;\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\tif (opts->source.file &&\n+\t\t\t!strcmp(opts->source.file, \"-\")) {\n+\t\topts->source.file = NULL;\n+\t\topts->source.use_stdin = 1;\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (use_global_config) {\n-\t\tgiven_config_source.file = git_global_config();\n-\t\tif (!given_config_source.file)\n+\tif (opts->use_global_config) {\n+\t\topts->source.file = xstrdup_or_null(git_global_config());\n+\t\tif (!opts->source.file)\n \t\t\t/*\n \t\t\t * It is unknown if HOME/.gitconfig exists, so\n \t\t\t * we do not know if we should write to XDG\n@@ -673,17 +698,17 @@ static void handle_config_location(const char *prefix)\n \t\t\t * is set and points at a sane location.\n \t\t\t */\n \t\t\tdie(_(\"$HOME not set\"));\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_GLOBAL;\n-\t} else if (use_system_config) {\n-\t\tgiven_config_source.file = git_system_config();\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_SYSTEM;\n-\t} else if (use_local_config) {\n-\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n-\t} else if (use_worktree_config) {\n+\t\topts->source.scope = CONFIG_SCOPE_GLOBAL;\n+\t} else if (opts->use_system_config) {\n+\t\topts->source.file = xstrdup_or_null(git_system_config());\n+\t\topts->source.scope = CONFIG_SCOPE_SYSTEM;\n+\t} else if (opts->use_local_config) {\n+\t\topts->source.file = xstrdup_or_null(git_pathdup(\"config\"));\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n+\t} else if (opts->use_worktree_config) {\n \t\tstruct worktree **worktrees = get_worktrees();\n \t\tif (the_repository->repository_format_worktree_config)\n-\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\t\topts->source.file = git_pathdup(\"config.worktree\");\n \t\telse if (worktrees[0] && worktrees[1])\n \t\t\tdie(_(\"--worktree cannot be used with multiple \"\n \t\t\t      \"working trees unless the config\\n\"\n@@ -691,28 +716,33 @@ static void handle_config_location(const char *prefix)\n \t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n \t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n \t\telse\n-\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n+\t\t\topts->source.file = git_pathdup(\"config\");\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n \t\tfree_worktrees(worktrees);\n-\t} else if (given_config_source.file) {\n-\t\tif (!is_absolute_path(given_config_source.file) && prefix)\n-\t\t\tgiven_config_source.file =\n-\t\t\t\tprefix_filename(prefix, given_config_source.file);\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n-\t} else if (given_config_source.blob) {\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.file) {\n+\t\tif (!is_absolute_path(opts->source.file) && prefix)\n+\t\t\topts->source.file =\n+\t\t\t\tprefix_filename(prefix, opts->source.file);\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.blob) {\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n \tif (respect_includes_opt == -1)\n-\t\tconfig_options.respect_includes = !given_config_source.file;\n+\t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\tconfig_options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = respect_includes_opt;\n \tif (startup_info->have_repository) {\n-\t\tconfig_options.commondir = get_git_common_dir();\n-\t\tconfig_options.git_dir = get_git_dir();\n+\t\topts->options.commondir = get_git_common_dir();\n+\t\topts->options.git_dir = get_git_dir();\n \t}\n }\n \n+static void location_options_release(struct config_location_options *opts)\n+{\n+\tfree((char *) opts->source.file);\n+}\n+\n static void handle_nul(void) {\n \tif (end_nul) {\n \t\tterm = '\\0';\n@@ -721,15 +751,6 @@ static void handle_nul(void) {\n \t}\n }\n \n-#define CONFIG_LOCATION_OPTIONS \\\n-\tOPT_GROUP(N_(\"Config file location\")), \\\n-\tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")), \\\n-\tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")), \\\n-\tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")), \\\n-\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")), \\\n-\tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")), \\\n-\tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n-\n #define CONFIG_TYPE_OPTIONS \\\n \tOPT_GROUP(N_(\"Type\")), \\\n \tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n@@ -749,8 +770,9 @@ static void handle_nul(void) {\n \n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS,\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -760,30 +782,32 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t&config_options) < 0) {\n-\t\tif (given_config_source.file)\n+\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t&location_opts.options) < 0) {\n+\t\tif (location_opts.source.file)\n \t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t  given_config_source.file);\n+\t\t\t\t  location_opts.source.file);\n \t\telse\n \t\t\tdie(_(\"error processing config file(s)\"));\n \t}\n \n+\tlocation_options_release(&location_opts);\n \treturn 0;\n }\n \n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n@@ -811,26 +835,28 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, argv[0], url);\n \telse\n-\t\tret = get_value(argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n \tint flags = 0, append = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n@@ -858,23 +884,24 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, value_pattern,\n \t\t\t\t\t\t\t     comment, flags);\n \t} else {\n-\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t    argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -882,10 +909,11 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -901,24 +929,26 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n \t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0],\n \t\t\t\t\t\t    NULL, NULL);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -927,10 +957,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 2, 2);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -939,13 +969,15 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -954,10 +986,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 1, 1);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -966,24 +998,25 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n-static int show_editor(void)\n+static int show_editor(struct config_location_options *opts)\n {\n \tchar *config_file;\n \n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!opts->source.file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n-\tif (given_config_source.use_stdin)\n+\tif (opts->source.use_stdin)\n \t\tdie(_(\"editing stdin is not supported\"));\n-\tif (given_config_source.blob)\n+\tif (opts->source.blob)\n \t\tdie(_(\"editing blobs is not supported\"));\n \tgit_config(git_default_config, NULL);\n-\tconfig_file = given_config_source.file ?\n-\t\t\txstrdup(given_config_source.file) :\n+\tconfig_file = opts->source.file ?\n+\t\t\txstrdup(opts->source.file) :\n \t\t\tgit_pathdup(\"config\");\n-\tif (use_global_config) {\n+\tif (opts->use_global_config) {\n \t\tint fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);\n \t\tif (fd >= 0) {\n \t\t\tchar *content = default_user_config();\n@@ -1002,18 +1035,22 @@ static int show_editor(void)\n \n static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\treturn show_editor();\n+\tret = show_editor(&location_opts);\n+\tlocation_options_release(&location_opts);\n+\treturn ret;\n }\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n@@ -1036,10 +1073,11 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_COLORBOOL = (1<<14),\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n \t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n \t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n@@ -1073,7 +1111,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n@@ -1158,94 +1196,94 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t\t&config_options) < 0) {\n-\t\t\tif (given_config_source.file)\n+\t\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t\t&location_opts.options) < 0) {\n+\t\t\tif (location_opts.source.file)\n \t\t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t\t  given_config_source.file);\n+\t\t\t\t\t  location_opts.source.file);\n \t\t\telse\n \t\t\t\tdie(_(\"error processing config file(s)\"));\n \t\t}\n \t}\n \telse if (actions == ACTION_EDIT) {\n-\t\tret = show_editor();\n+\t\tret = show_editor(&location_opts);\n \t}\n \telse if (actions == ACTION_SET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \telse if (actions == ACTION_SET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1255,9 +1293,9 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\tret = 0;\n \t}\n \telse if (actions == ACTION_REMOVE_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 1);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1268,16 +1306,17 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(argv[0], argv[1]);\n+\t\tget_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\tret = get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(&location_opts, argv[0], argc == 2);\n \t}\n \n out:\n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -1297,8 +1336,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n \t/*\n \t * This is somewhat hacky: we first parse the command line while\n \t * keeping all args intact in order to determine whether a subcommand\n-- \n2.45.GIT\n\n"},{"id":"494448","messageId":"06c1e08fc49ff5fbf5346ae9b20df045bdb5e7ff.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 10/21] builtin/config: move display options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:12Z","receivedAt":"2024-05-10T11:25:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The display options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 171 ++++++++++++++++++++++++++++-------------------\n 1 file changed, 101 insertions(+), 70 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5346a782d0..7089692c4b 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -81,25 +81,42 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_DISPLAY_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Display options\")), \\\n+\tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n+\tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n+\tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n+\tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\n+struct config_display_options {\n+\tint end_nul;\n+\tint omit_values;\n+\tint show_origin;\n+\tint show_scope;\n+\tint show_keys;\n+\t/* Populated via `display_options_init()`. */\n+\tint term;\n+\tint delim;\n+\tint key_delim;\n+};\n+#define CONFIG_DISPLAY_OPTIONS_INIT { \\\n+\t.term = '\\n', \\\n+\t.delim = '=', \\\n+\t.key_delim = ' ', \\\n+}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n static regex_t *regexp;\n-static int show_keys;\n-static int omit_values;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char delim = '=';\n-static char key_delim = ' ';\n-static char term = '\\n';\n \n static int type;\n static char *default_value;\n-static int end_nul;\n static int respect_includes_opt = -1;\n-static int show_origin;\n-static int show_scope;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -177,24 +194,26 @@ static void check_argc(int argc, int min, int max)\n \texit(129);\n }\n \n-static void show_config_origin(const struct key_value_info *kvi,\n+static void show_config_origin(const struct config_display_options *opts,\n+\t\t\t       const struct key_value_info *kvi,\n \t\t\t       struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \n \tstrbuf_addstr(buf, config_origin_type_name(kvi->origin_type));\n \tstrbuf_addch(buf, ':');\n-\tif (end_nul)\n+\tif (opts->end_nul)\n \t\tstrbuf_addstr(buf, kvi->filename ? kvi->filename : \"\");\n \telse\n \t\tquote_c_style(kvi->filename ? kvi->filename : \"\", buf, NULL, 0);\n \tstrbuf_addch(buf, term);\n }\n \n-static void show_config_scope(const struct key_value_info *kvi,\n+static void show_config_scope(const struct config_display_options *opts,\n+\t\t\t      const struct key_value_info *kvi,\n \t\t\t      struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \tconst char *scope = config_scope_name(kvi->scope);\n \n \tstrbuf_addstr(buf, N_(scope));\n@@ -203,24 +222,25 @@ static void show_config_scope(const struct key_value_info *kvi,\n \n static int show_all_config(const char *key_, const char *value_,\n \t\t\t   const struct config_context *ctx,\n-\t\t\t   void *cb UNUSED)\n+\t\t\t   void *cb)\n {\n+\tconst struct config_display_options *opts = cb;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (show_origin || show_scope) {\n+\tif (opts->show_origin || opts->show_scope) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tif (show_scope)\n-\t\t\tshow_config_scope(kvi, &buf);\n-\t\tif (show_origin)\n-\t\t\tshow_config_origin(kvi, &buf);\n+\t\tif (opts->show_scope)\n+\t\t\tshow_config_scope(opts, kvi, &buf);\n+\t\tif (opts->show_origin)\n+\t\t\tshow_config_origin(opts, kvi, &buf);\n \t\t/* Use fwrite as \"buf\" can contain \\0's if \"end_null\" is set. */\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n \t\tstrbuf_release(&buf);\n \t}\n-\tif (!omit_values && value_)\n-\t\tprintf(\"%s%c%s%c\", key_, delim, value_, term);\n+\tif (!opts->omit_values && value_)\n+\t\tprintf(\"%s%c%s%c\", key_, opts->delim, value_, opts->term);\n \telse\n-\t\tprintf(\"%s%c\", key_, term);\n+\t\tprintf(\"%s%c\", key_, opts->term);\n \treturn 0;\n }\n \n@@ -230,18 +250,19 @@ struct strbuf_list {\n \tint alloc;\n };\n \n-static int format_config(struct strbuf *buf, const char *key_,\n+static int format_config(const struct config_display_options *opts,\n+\t\t\t struct strbuf *buf, const char *key_,\n \t\t\t const char *value_, const struct key_value_info *kvi)\n {\n-\tif (show_scope)\n-\t\tshow_config_scope(kvi, buf);\n-\tif (show_origin)\n-\t\tshow_config_origin(kvi, buf);\n-\tif (show_keys)\n+\tif (opts->show_scope)\n+\t\tshow_config_scope(opts, kvi, buf);\n+\tif (opts->show_origin)\n+\t\tshow_config_origin(opts, kvi, buf);\n+\tif (opts->show_keys)\n \t\tstrbuf_addstr(buf, key_);\n-\tif (!omit_values) {\n-\t\tif (show_keys)\n-\t\t\tstrbuf_addch(buf, key_delim);\n+\tif (!opts->omit_values) {\n+\t\tif (opts->show_keys)\n+\t\t\tstrbuf_addch(buf, opts->key_delim);\n \n \t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n@@ -283,18 +304,24 @@ static int format_config(struct strbuf *buf, const char *key_,\n \t\t\tstrbuf_addstr(buf, value_);\n \t\t} else {\n \t\t\t/* Just show the key name; back out delimiter */\n-\t\t\tif (show_keys)\n+\t\t\tif (opts->show_keys)\n \t\t\t\tstrbuf_setlen(buf, buf->len - 1);\n \t\t}\n \t}\n-\tstrbuf_addch(buf, term);\n+\tstrbuf_addch(buf, opts->term);\n \treturn 0;\n }\n \n+struct collect_config_data {\n+\tconst struct config_display_options *display_opts;\n+\tstruct strbuf_list *values;\n+};\n+\n static int collect_config(const char *key_, const char *value_,\n \t\t\t  const struct config_context *ctx, void *cb)\n {\n-\tstruct strbuf_list *values = cb;\n+\tstruct collect_config_data *data = cb;\n+\tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n \tif (!use_key_regexp && strcmp(key_, key))\n@@ -310,14 +337,20 @@ static int collect_config(const char *key_, const char *value_,\n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n \tstrbuf_init(&values->items[values->nr], 0);\n \n-\treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n+\treturn format_config(data->display_opts, &values->items[values->nr++],\n+\t\t\t     key_, value_, kvi);\n }\n \n static int get_value(const struct config_location_options *opts,\n+\t\t     const struct config_display_options *display_opts,\n \t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n+\tstruct collect_config_data data = {\n+\t\t.display_opts = display_opts,\n+\t\t.values = &values,\n+\t};\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -368,7 +401,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t}\n \t}\n \n-\tconfig_with_options(collect_config, &values,\n+\tconfig_with_options(collect_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n@@ -380,7 +413,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n \t\t\t\tdefault_value);\n \t}\n@@ -591,10 +624,12 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n }\n \n static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst struct config_display_options *_display_opts,\n \t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n+\tstruct config_display_options display_opts = *_display_opts;\n \tstruct string_list_item *item;\n \tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n \tstruct string_list values = STRING_LIST_INIT_DUP;\n@@ -611,10 +646,10 @@ static int get_urlmatch(const struct config_location_options *opts,\n \tif (section_tail) {\n \t\t*section_tail = '\\0';\n \t\tconfig.key = section_tail + 1;\n-\t\tshow_keys = 0;\n+\t\tdisplay_opts.show_keys = 0;\n \t} else {\n \t\tconfig.key = NULL;\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n@@ -627,7 +662,7 @@ static int get_urlmatch(const struct config_location_options *opts,\n \t\tstruct urlmatch_current_candidate_value *matched = item->util;\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tformat_config(&buf, item->string,\n+\t\tformat_config(&display_opts, &buf, item->string,\n \t\t\t      matched->value_is_null ? NULL : matched->value.buf,\n \t\t\t      &matched->kvi);\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n@@ -743,11 +778,12 @@ static void location_options_release(struct config_location_options *opts)\n \tfree((char *) opts->source.file);\n }\n \n-static void handle_nul(void) {\n-\tif (end_nul) {\n-\t\tterm = '\\0';\n-\t\tdelim = '\\n';\n-\t\tkey_delim = '\\n';\n+static void display_options_init(struct config_display_options *opts)\n+{\n+\tif (opts->end_nul) {\n+\t\topts->term = '\\0';\n+\t\topts->delim = '\\n';\n+\t\topts->key_delim = '\\n';\n \t}\n }\n \n@@ -761,19 +797,13 @@ static void handle_nul(void) {\n \tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n \tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n \n-#define CONFIG_DISPLAY_OPTIONS \\\n-\tOPT_GROUP(N_(\"Display options\")), \\\n-\tOPT_BOOL('z', \"null\", &end_nul, N_(\"terminate values with NUL byte\")), \\\n-\tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")), \\\n-\tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n-\tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n@@ -783,11 +813,11 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \tcheck_argc(argc, 0, 0);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n-\tif (config_with_options(show_all_config, NULL,\n+\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t&location_opts.options) < 0) {\n \t\tif (location_opts.source.file)\n@@ -804,6 +834,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n@@ -815,8 +846,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n-\t\tOPT_BOOL(0, \"show-names\", &show_keys, N_(\"show config keys in addition to their values\")),\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n@@ -836,14 +866,14 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(&location_opts, argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1074,6 +1104,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1094,7 +1125,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_TYPE_OPTIONS,\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n@@ -1112,7 +1143,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n@@ -1128,13 +1159,13 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"no action specified\"));\n \t\t\texit(129);\n \t\t}\n-\tif (omit_values &&\n+\tif (display_opts.omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n \t\texit(129);\n \t}\n \n-\tif (show_origin && !(actions &\n+\tif (display_opts.show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n@@ -1195,7 +1226,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n-\t\tif (config_with_options(show_all_config, NULL,\n+\t\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t\t&location_opts.options) < 0) {\n \t\t\tif (location_opts.source.file)\n@@ -1244,23 +1275,23 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write(&location_opts.source);\n-- \n2.45.GIT\n\n"},{"id":"494449","messageId":"96108976627fbb4b650156538441e2f3f5a8a490.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 11/21] builtin/config: move type options into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:17Z","receivedAt":"2024-05-10T11:25:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The type options are tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 +++++++++++++++++++++++-------------------------\n 1 file changed, 29 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 7089692c4b..1f864f71c4 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -81,13 +81,24 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_TYPE_OPTIONS(type) \\\n+\tOPT_GROUP(N_(\"Type\")), \\\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n+\n #define CONFIG_DISPLAY_OPTIONS(opts) \\\n \tOPT_GROUP(N_(\"Display options\")), \\\n \tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n \tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n \tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n-\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\")), \\\n+\tCONFIG_TYPE_OPTIONS(opts.type)\n \n struct config_display_options {\n \tint end_nul;\n@@ -95,6 +106,7 @@ struct config_display_options {\n \tint show_origin;\n \tint show_scope;\n \tint show_keys;\n+\tint type;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -113,8 +125,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-\n-static int type;\n static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n@@ -264,13 +274,13 @@ static int format_config(const struct config_display_options *opts,\n \t\tif (opts->show_keys)\n \t\t\tstrbuf_addch(buf, opts->key_delim);\n \n-\t\tif (type == TYPE_INT)\n+\t\tif (opts->type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\", kvi));\n-\t\telse if (type == TYPE_BOOL)\n+\t\telse if (opts->type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (type == TYPE_BOOL_OR_INT) {\n+\t\telse if (opts->type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, kvi,\n \t\t\t\t\t\t   &is_bool);\n@@ -278,24 +288,24 @@ static int format_config(const struct config_display_options *opts,\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (type == TYPE_BOOL_OR_STR) {\n+\t\t} else if (opts->type == TYPE_BOOL_OR_STR) {\n \t\t\tint v = git_parse_maybe_bool(value_);\n \t\t\tif (v < 0)\n \t\t\t\tstrbuf_addstr(buf, value_);\n \t\t\telse\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n-\t\t} else if (type == TYPE_PATH) {\n+\t\t} else if (opts->type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (type == TYPE_EXPIRY_DATE) {\n+\t\t} else if (opts->type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addf(buf, \"%\"PRItime, t);\n-\t\t} else if (type == TYPE_COLOR) {\n+\t\t} else if (opts->type == TYPE_COLOR) {\n \t\t\tchar v[COLOR_MAXLEN];\n \t\t\tif (git_config_color(v, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -443,7 +453,7 @@ static int get_value(const struct config_location_options *opts,\n }\n \n static char *normalize_value(const char *key, const char *value,\n-\t\t\t     struct key_value_info *kvi)\n+\t\t\t     int type, struct key_value_info *kvi)\n {\n \tif (!value)\n \t\treturn NULL;\n@@ -787,16 +797,6 @@ static void display_options_init(struct config_display_options *opts)\n \t}\n }\n \n-#define CONFIG_TYPE_OPTIONS \\\n-\tOPT_GROUP(N_(\"Type\")), \\\n-\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n-\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n-\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n-\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n@@ -839,7 +839,6 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tint flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n \t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n@@ -884,10 +883,10 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n-\tint flags = 0, append = 0;\n+\tint flags = 0, append = 0, type = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_TYPE_OPTIONS(type),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -917,7 +916,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tcheck_write(&location_opts.source);\n \n-\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\tvalue = normalize_value(argv[0], argv[1], type, &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n@@ -1124,7 +1123,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n@@ -1145,7 +1143,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n \t\texit(129);\n \t}\n@@ -1242,7 +1240,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n@@ -1251,7 +1249,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n@@ -1259,7 +1257,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n@@ -1268,7 +1266,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n-- \n2.45.GIT\n\n"},{"id":"494450","messageId":"eb7946286177058405946d520708f32fbef76e84.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 12/21] builtin/config: move default value into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:22Z","receivedAt":"2024-05-10T11:25:26Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The default value is tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 1f864f71c4..eca5179f5f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -107,6 +107,7 @@ struct config_display_options {\n \tint show_scope;\n \tint show_keys;\n \tint type;\n+\tchar *default_value;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -125,7 +126,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n \n@@ -415,7 +415,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!values.nr && default_value) {\n+\tif (!values.nr && display_opts->default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n \t\tstruct strbuf *item;\n \n@@ -423,9 +423,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_,\n+\t\t\t\t  display_opts->default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n-\t\t\t\tdefault_value);\n+\t\t\t    display_opts->default_value);\n \t}\n \n \tret = !values.nr;\n@@ -848,7 +849,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -859,7 +861,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (default_value && (do_all || url))\n+\tif (display_opts.default_value && (do_all || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n@@ -1125,7 +1127,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -1170,7 +1173,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\texit(129);\n \t}\n \n-\tif (default_value && !(actions & ACTION_GET)) {\n+\tif (display_opts.default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n \t\texit(129);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494451","messageId":"b9ebfbd667d0ff0d500dc1088a4352d666fc8570.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 13/21] builtin/config: move `respect_includes_opt` into location options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:27Z","receivedAt":"2024-05-10T11:25:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The variable tracking whether or not we want to honor includes is\ntracked via a global variable. Move it into the location options\ninstead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 ++++++++++++-------\n 1 file changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex eca5179f5f..d11d0b4784 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -78,8 +78,11 @@ struct config_location_options {\n \tint use_system_config;\n \tint use_local_config;\n \tint use_worktree_config;\n+\tint respect_includes_opt;\n };\n-#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+#define CONFIG_LOCATION_OPTIONS_INIT { \\\n+\t.respect_includes_opt = -1, \\\n+}\n \n #define CONFIG_TYPE_OPTIONS(type) \\\n \tOPT_GROUP(N_(\"Type\")), \\\n@@ -126,7 +129,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static int respect_includes_opt = -1;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -774,10 +776,10 @@ static void location_options_init(struct config_location_options *opts,\n \t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (respect_includes_opt == -1)\n+\tif (opts->respect_includes_opt == -1)\n \t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\topts->options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = opts->respect_includes_opt;\n \tif (startup_info->have_repository) {\n \t\topts->options.commondir = get_git_common_dir();\n \t\topts->options.git_dir = get_git_dir();\n@@ -806,7 +808,8 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \n@@ -848,7 +851,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n@@ -1131,7 +1135,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-- \n2.45.GIT\n\n"},{"id":"494452","messageId":"2b40b784fe146b4d17de9accd4afc53144c93812.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 14/21] builtin/config: convert `do_not_match` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:32Z","receivedAt":"2024-05-10T11:25:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `do_not_match` variable is used by the `format_config()` callback as\nan indicator whteher or not the passed regular expression is negated. It\nis only ever set up by its only caller, `collect_config()` and can thus\neasily be moved into the `collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex d11d0b4784..9d5d8c8df3 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -128,7 +128,6 @@ static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n-static int do_not_match;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -327,6 +326,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tint do_not_match;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -343,7 +343,7 @@ static int collect_config(const char *key_, const char *value_,\n \tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n-\t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -400,7 +400,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tvalue_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n-\t\t\tdo_not_match = 1;\n+\t\t\tdata.do_not_match = 1;\n \t\t\tregex_++;\n \t\t}\n \n-- \n2.45.GIT\n\n"},{"id":"494453","messageId":"71d1b7a51b0908c50661b975677061b8121b92cc.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 15/21] builtin/config: convert `value_pattern` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:36Z","receivedAt":"2024-05-10T11:25:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `value_pattern` variable is used by the `format_config()` callback\nwhen `CONFIG_FLAGS_FIXED_VALUE` is used. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9d5d8c8df3..a681097fbd 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n@@ -326,6 +325,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tconst char *value_pattern;\n \tint do_not_match;\n };\n \n@@ -340,7 +340,7 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n+\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n \t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -397,7 +397,7 @@ static int get_value(const struct config_location_options *opts,\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-\t\tvalue_pattern = regex_;\n+\t\tdata.value_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdata.do_not_match = 1;\n-- \n2.45.GIT\n\n"},{"id":"494454","messageId":"c3b340f1193fdf76064b6eec2e3bad591a709267.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 16/21] builtin/config: convert `regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:41Z","receivedAt":"2024-05-10T11:25:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `regexp` variable is used by the `format_config()` callback when\n`CONFIG_FLAGS_FIXED_VALUE` is not set. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex a681097fbd..fe15f1ee77 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -326,6 +325,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tregex_t *regexp;\n \tint do_not_match;\n };\n \n@@ -342,8 +342,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n-\tif (regexp != NULL &&\n-\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\tif (data->regexp &&\n+\t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -404,10 +404,10 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tregex_++;\n \t\t}\n \n-\t\tregexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(regexp, regex_, REG_EXTENDED)) {\n+\t\tdata.regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.regexp, regex_, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid pattern: %s\"), regex_);\n-\t\t\tFREE_AND_NULL(regexp);\n+\t\t\tFREE_AND_NULL(data.regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -447,9 +447,9 @@ static int get_value(const struct config_location_options *opts,\n \t\tregfree(key_regexp);\n \t\tfree(key_regexp);\n \t}\n-\tif (regexp) {\n-\t\tregfree(regexp);\n-\t\tfree(regexp);\n+\tif (data.regexp) {\n+\t\tregfree(data.regexp);\n+\t\tfree(data.regexp);\n \t}\n \n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494455","messageId":"835cc0acfb2bf027f67edb1ea537674f5236f453.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 17/21] builtin/config: convert `key_regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:46Z","receivedAt":"2024-05-10T11:25:50Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key_regexp` variable is used by the `format_config()` callback when\n`use_key_regexp` is set. It is only ever set up by its only caller,\n`collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex fe15f1ee77..1c6d9c734c 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -123,7 +123,6 @@ struct config_display_options {\n }\n \n static char *key;\n-static regex_t *key_regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -326,6 +325,7 @@ struct collect_config_data {\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n \tregex_t *regexp;\n+\tregex_t *key_regexp;\n \tint do_not_match;\n };\n \n@@ -338,7 +338,7 @@ static int collect_config(const char *key_, const char *value_,\n \n \tif (!use_key_regexp && strcmp(key_, key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n+\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n@@ -382,10 +382,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tfor (tl = key; *tl && *tl != '.'; tl++)\n \t\t\t*tl = tolower(*tl);\n \n-\t\tkey_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(key_regexp, key, REG_EXTENDED)) {\n+\t\tdata.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.key_regexp, key, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid key pattern: %s\"), key_);\n-\t\t\tFREE_AND_NULL(key_regexp);\n+\t\t\tFREE_AND_NULL(data.key_regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -443,9 +443,9 @@ static int get_value(const struct config_location_options *opts,\n \n free_strings:\n \tfree(key);\n-\tif (key_regexp) {\n-\t\tregfree(key_regexp);\n-\t\tfree(key_regexp);\n+\tif (data.key_regexp) {\n+\t\tregfree(data.key_regexp);\n+\t\tfree(data.key_regexp);\n \t}\n \tif (data.regexp) {\n \t\tregfree(data.regexp);\n-- \n2.45.GIT\n\n"},{"id":"494456","messageId":"2aee7ec5d8dcd301decec75c6d0fd49da700ac3a.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 18/21] builtin/config: convert `key` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:51Z","receivedAt":"2024-05-10T11:25:55Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key` variable is used by the `get_value()` function for two\npurposes:\n\n  - It is used to store the result of `git_config_parse_key()`, which si\n    then passed on to `collect_config()`.\n\n  - It is used as a store to convert the provided key to an\n    all-lowercase key when `use_key_regexp` is set.\n\nBoth of these cases don't warrant a global variable at all. In the\nformer case we can pass the key via `struct collect_config_data`. And in\nthe latter case we really only want to have it as a temporary local\nvariable such that we can free associated memory.\n\nRefactor the code accordingly to reduce our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 1c6d9c734c..8cedf67a4f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -122,7 +122,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static char *key;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -324,6 +323,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tconst char *key;\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n@@ -336,7 +336,7 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, key))\n+\tif (!use_key_regexp && strcmp(key_, data->key))\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n@@ -363,6 +363,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n \t};\n+\tchar *key = NULL;\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -394,6 +395,8 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tret = CONFIG_INVALID_KEY;\n \t\t\tgoto free_strings;\n \t\t}\n+\n+\t\tdata.key = key;\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-- \n2.45.GIT\n\n"},{"id":"494457","messageId":"625216a7741b64b63717c72fd3c906b04c256bbf.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 19/21] builtin/config: track \"fixed value\" option via flags only","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:25:56Z","receivedAt":"2024-05-10T11:26:00Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We track the \"fixed value\" option via two separate bits: once via the\nglobal variable `fixed_value`, and once via the CONFIG_FLAGS_FIXED_VALUE\nbit in `flags`. This is confusing and may easily lead to issues when one\nis not aware that this is tracked via two separate mechanisms.\n\nRefactor the code to use the flag exclusively. We already pass it to all\nthe require callsites anyway, except for `collect_config()`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8cedf67a4f..e6bc283751 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static int use_key_regexp;\n static int do_all;\n-static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n@@ -327,6 +326,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned flags;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -340,7 +340,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n+\tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n+\t    strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (data->regexp &&\n \t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -362,6 +363,7 @@ static int get_value(const struct config_location_options *opts,\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n@@ -1115,6 +1117,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n+\tunsigned flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n@@ -1137,13 +1140,12 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n \t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-\tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n@@ -1193,7 +1195,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \n \t/* check usage of --fixed-value */\n-\tif (fixed_value) {\n+\tif (flags & CONFIG_FLAGS_FIXED_VALUE) {\n \t\tint allowed_usage = 0;\n \n \t\tswitch (actions) {\n@@ -1224,8 +1226,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n \t\t\texit(129);\n \t\t}\n-\n-\t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\n \t}\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n-- \n2.45.GIT\n\n"},{"id":"494458","messageId":"05254d512b890b4422a72f0fa17794a237a5bf8f.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 20/21] builtin/config: convert flags to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:26:02Z","receivedAt":"2024-05-10T11:26:06Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Both the `do_all` and `use_key_regexp` bits essentially act like flags\nto `get_value()`. Let's convert them to actual flags so that we can get\nrid of the last two remaining global variables that track options.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 48 +++++++++++++++++++++++++++++-------------------\n 1 file changed, 29 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e6bc283751..bbb6864453 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -122,9 +122,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static int use_key_regexp;\n-static int do_all;\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -318,6 +315,9 @@ static int format_config(const struct config_display_options *opts,\n \treturn 0;\n }\n \n+#define GET_VALUE_ALL        (1 << 0)\n+#define GET_VALUE_KEY_REGEXP (1 << 1)\n+\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n@@ -326,6 +326,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned get_value_flags;\n \tunsigned flags;\n };\n \n@@ -336,9 +337,11 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, data->key))\n+\tif (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    strcmp(key_, data->key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n+\tif ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n \t    strcmp(data->value_pattern, (value_?value_:\"\")))\n@@ -356,19 +359,21 @@ static int collect_config(const char *key_, const char *value_,\n \n static int get_value(const struct config_location_options *opts,\n \t\t     const struct config_display_options *display_opts,\n-\t\t     const char *key_, const char *regex_, unsigned flags)\n+\t\t     const char *key_, const char *regex_,\n+\t\t     unsigned get_value_flags, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.get_value_flags = get_value_flags,\n \t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n \n-\tif (use_key_regexp) {\n+\tif (get_value_flags & GET_VALUE_KEY_REGEXP) {\n \t\tchar *tl;\n \n \t\t/*\n@@ -440,7 +445,7 @@ static int get_value(const struct config_location_options *opts,\n \n \tfor (i = 0; i < values.nr; i++) {\n \t\tstruct strbuf *buf = values.items + i;\n-\t\tif (do_all || i == values.nr - 1)\n+\t\tif ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)\n \t\t\tfwrite(buf->buf, 1, buf->len, stdout);\n \t\tstrbuf_release(buf);\n \t}\n@@ -846,11 +851,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n+\tunsigned get_value_flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter options\")),\n-\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n-\t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n+\t\tOPT_BIT(0, \"all\", &get_value_flags, N_(\"return all values for multi-valued config options\"), GET_VALUE_ALL),\n+\t\tOPT_BIT(0, \"regexp\", &get_value_flags, N_(\"interpret the name as a regular expression\"), GET_VALUE_KEY_REGEXP),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n@@ -870,9 +876,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (display_opts.default_value && (do_all || url))\n+\tif (display_opts.default_value &&\n+\t    ((get_value_flags & GET_VALUE_ALL) || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n-\tif (url && (do_all || use_key_regexp || value_pattern))\n+\tif (url && ((get_value_flags & GET_VALUE_ALL) ||\n+\t\t    (get_value_flags & GET_VALUE_KEY_REGEXP) ||\n+\t\t    value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n@@ -883,7 +892,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n+\t\t\t\tget_value_flags, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1284,19 +1294,19 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\t0, flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL, flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tdisplay_opts.show_keys = 1;\n-\t\tuse_key_regexp = 1;\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-- \n2.45.GIT\n\n"},{"id":"494459","messageId":"3a5f059789ae550002e08155faacd4bb3998a489.1715339393.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH 21/21] builtin/config: pass data between callbacks via local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:26:07Z","receivedAt":"2024-05-10T11:26:10Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We use several global variables to pass data between callers and\ncallbacks in `get_color()` and `get_colorbool()`. Convert those to use\ncallback data structures instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 90 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 52 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex bbb6864453..127bc097b2 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -517,21 +517,24 @@ static char *normalize_value(const char *key, const char *value,\n \tBUG(\"cannot normalize type %d\", type);\n }\n \n-static int get_color_found;\n-static const char *get_color_slot;\n-static const char *get_colorbool_slot;\n-static char parsed_color[COLOR_MAXLEN];\n+struct get_color_config_data {\n+\tint get_color_found;\n+\tconst char *get_color_slot;\n+\tchar parsed_color[COLOR_MAXLEN];\n+};\n \n static int git_get_color_config(const char *var, const char *value,\n \t\t\t\tconst struct config_context *ctx UNUSED,\n-\t\t\t\tvoid *cb UNUSED)\n+\t\t\t\tvoid *cb)\n {\n-\tif (!strcmp(var, get_color_slot)) {\n+\tstruct get_color_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_color_slot)) {\n \t\tif (!value)\n \t\t\tconfig_error_nonbool(var);\n-\t\tif (color_parse(value, parsed_color) < 0)\n+\t\tif (color_parse(value, data->parsed_color) < 0)\n \t\t\treturn -1;\n-\t\tget_color_found = 1;\n+\t\tdata->get_color_found = 1;\n \t}\n \treturn 0;\n }\n@@ -539,66 +542,77 @@ static int git_get_color_config(const char *var, const char *value,\n static void get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n-\tget_color_slot = var;\n-\tget_color_found = 0;\n-\tparsed_color[0] = '\\0';\n-\tconfig_with_options(git_get_color_config, NULL,\n+\tstruct get_color_config_data data = {\n+\t\t.get_color_slot = var,\n+\t\t.parsed_color[0] = '\\0',\n+\t};\n+\n+\tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!get_color_found && def_color) {\n-\t\tif (color_parse(def_color, parsed_color) < 0)\n+\tif (!data.get_color_found && def_color) {\n+\t\tif (color_parse(def_color, data.parsed_color) < 0)\n \t\t\tdie(_(\"unable to parse default color value\"));\n \t}\n \n-\tfputs(parsed_color, stdout);\n+\tfputs(data.parsed_color, stdout);\n }\n \n-static int get_colorbool_found;\n-static int get_diff_color_found;\n-static int get_color_ui_found;\n+struct get_colorbool_config_data {\n+\tint get_colorbool_found;\n+\tint get_diff_color_found;\n+\tint get_color_ui_found;\n+\tconst char *get_colorbool_slot;\n+};\n+\n static int git_get_colorbool_config(const char *var, const char *value,\n \t\t\t\t    const struct config_context *ctx UNUSED,\n-\t\t\t\t    void *data UNUSED)\n+\t\t\t\t    void *cb)\n {\n-\tif (!strcmp(var, get_colorbool_slot))\n-\t\tget_colorbool_found = git_config_colorbool(var, value);\n+\tstruct get_colorbool_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_colorbool_slot))\n+\t\tdata->get_colorbool_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"diff.color\"))\n-\t\tget_diff_color_found = git_config_colorbool(var, value);\n+\t\tdata->get_diff_color_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"color.ui\"))\n-\t\tget_color_ui_found = git_config_colorbool(var, value);\n+\t\tdata->get_color_ui_found = git_config_colorbool(var, value);\n \treturn 0;\n }\n \n static int get_colorbool(const struct config_location_options *opts,\n \t\t\t const char *var, int print)\n {\n-\tget_colorbool_slot = var;\n-\tget_colorbool_found = -1;\n-\tget_diff_color_found = -1;\n-\tget_color_ui_found = -1;\n-\tconfig_with_options(git_get_colorbool_config, NULL,\n+\tstruct get_colorbool_config_data data = {\n+\t\t.get_colorbool_slot = var,\n+\t\t.get_colorbool_found = -1,\n+\t\t.get_diff_color_found = -1,\n+\t\t.get_color_ui_found = -1,\n+\t};\n+\n+\tconfig_with_options(git_get_colorbool_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (get_colorbool_found < 0) {\n-\t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n-\t\t\tget_colorbool_found = get_diff_color_found;\n-\t\tif (get_colorbool_found < 0)\n-\t\t\tget_colorbool_found = get_color_ui_found;\n+\tif (data.get_colorbool_found < 0) {\n+\t\tif (!strcmp(data.get_colorbool_slot, \"color.diff\"))\n+\t\t\tdata.get_colorbool_found = data.get_diff_color_found;\n+\t\tif (data.get_colorbool_found < 0)\n+\t\t\tdata.get_colorbool_found = data.get_color_ui_found;\n \t}\n \n-\tif (get_colorbool_found < 0)\n+\tif (data.get_colorbool_found < 0)\n \t\t/* default value if none found in config */\n-\t\tget_colorbool_found = GIT_COLOR_AUTO;\n+\t\tdata.get_colorbool_found = GIT_COLOR_AUTO;\n \n-\tget_colorbool_found = want_color(get_colorbool_found);\n+\tdata.get_colorbool_found = want_color(data.get_colorbool_found);\n \n \tif (print) {\n-\t\tprintf(\"%s\\n\", get_colorbool_found ? \"true\" : \"false\");\n+\t\tprintf(\"%s\\n\", data.get_colorbool_found ? \"true\" : \"false\");\n \t\treturn 0;\n \t} else\n-\t\treturn get_colorbool_found ? 0 : 1;\n+\t\treturn data.get_colorbool_found ? 0 : 1;\n }\n \n static void check_write(const struct git_config_source *source)\n-- \n2.45.GIT\n\n"},{"id":"494460","messageId":"Zj4GLqRQCfOrmz6d@tanuki","threadId":"61452","inReplyTo":"a96c122280ad025f94c1343cca85c67c7c617c8f.1715339393.git.ps@pks.im","subject":"Re: [PATCH 09/21] builtin/config: move location options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-10T11:34:06Z","receivedAt":"2024-05-10T11:34:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, May 10, 2024 at 01:25:07PM +0200, Patrick Steinhardt wrote:\n> @@ -638,34 +657,40 @@ static char *default_user_config(void)\n[snip]\n> -\tif (use_global_config) {\n> -\t\tgiven_config_source.file = git_global_config();\n> -\t\tif (!given_config_source.file)\n> +\tif (opts->use_global_config) {\n> +\t\topts->source.file = xstrdup_or_null(git_global_config());\n> +\t\tif (!opts->source.file)\n\nThis needs the following on top to plug a memory leak -- I didn't\nrealize that `git_global_config()` transfers ownership of the string to\nthe caller already.\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 127bc097b2..30a49d07d7 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -761,7 +761,7 @@ static void location_options_init(struct config_location_options *opts,\n        }\n\n        if (opts->use_global_config) {\n-               opts->source.file = xstrdup_or_null(git_global_config());\n+               opts->source.file = git_global_config();\n                if (!opts->source.file)\n                        /*\n                         * It is unknown if HOME/.gitconfig exists, so\n\nWill fix in v2.\n\nPatrick\n"},{"id":"494483","messageId":"xmqq4jb55zwb.fsf@gitster.g","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"Re: [PATCH 00/21] builtin/config: remove global state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-10T17:40:20Z","receivedAt":"2024-05-10T17:40:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> we have quite a lot of global state in git-config(1). For one, this\n> global state is used to track options passed by the user. And second,\n> there is a lot of global state that is really only used to pass data\n> between caller and callbacks.\n\nLong overdue.  It is very nice to see this happen finally.  \n\nEven though I somehow feel that the clean-up should have done before\nthe \"subcommand\" series, it may probably not worth the churn to flip\nthe order of the series around.\n\nWill take a look.\n"},{"id":"494506","messageId":"CAO_smVjXjW6pM-bWbQmCWxVi8LLZ2iB2p6RYhJhtc529cgDxHA@mail.gmail.com","threadId":"61452","inReplyTo":"2e308393edb0c34593e78603b43bee8d3a45dd8a.1715339393.git.ps@pks.im","subject":"Re: [PATCH 05/21] builtin/config: move actions into `cmd_config_actions()`","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-05-10T20:42:49Z","receivedAt":"2024-05-10T20:43:08Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Fri, May 10, 2024 at 4:26 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> We only use actions in the legacy mode. Convert them to an enum and move\n> them into `cmd_config_actions()` to clearly demonstrate that they are\n> not used anywhere else.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/config.c | 44 +++++++++++++++++++-------------------------\n>  1 file changed, 19 insertions(+), 25 deletions(-)\n>\n> diff --git a/builtin/config.c b/builtin/config.c\n> index e9956574fe..8f3342b593 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -87,30 +87,6 @@ static int show_origin;\n>  static int show_scope;\n>  static int fixed_value;\n>\n> -#define ACTION_GET (1<<0)\n> -#define ACTION_GET_ALL (1<<1)\n> -#define ACTION_GET_REGEXP (1<<2)\n> -#define ACTION_REPLACE_ALL (1<<3)\n> -#define ACTION_ADD (1<<4)\n> -#define ACTION_UNSET (1<<5)\n> -#define ACTION_UNSET_ALL (1<<6)\n> -#define ACTION_RENAME_SECTION (1<<7)\n> -#define ACTION_REMOVE_SECTION (1<<8)\n> -#define ACTION_LIST (1<<9)\n> -#define ACTION_EDIT (1<<10)\n> -#define ACTION_SET (1<<11)\n> -#define ACTION_SET_ALL (1<<12)\n> -#define ACTION_GET_COLOR (1<<13)\n> -#define ACTION_GET_COLORBOOL (1<<14)\n> -#define ACTION_GET_URLMATCH (1<<15)\n> -\n> -/*\n> - * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n> - * one line of output and which should therefore be paged.\n> - */\n\nWe lost this comment when inlining this. Not sure if that was\nintentional (comment deemed to be of low value) or accidental. I think\nas someone unfamiliar with this logic that the comment would be\nhelpful to keep - it describes _why_ those items are checked for (vs.\nneeding to infer it from the action that's taken when there's a\nmatch).\n\n> -#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n> -                       ACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n> -\n>  #define TYPE_BOOL              1\n>  #define TYPE_INT               2\n>  #define TYPE_BOOL_OR_INT       3\n> @@ -1031,6 +1007,24 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n>\n>  static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n>  {\n> +       enum {\n> +               ACTION_GET = (1<<0),\n> +               ACTION_GET_ALL = (1<<1),\n> +               ACTION_GET_REGEXP = (1<<2),\n> +               ACTION_REPLACE_ALL = (1<<3),\n> +               ACTION_ADD = (1<<4),\n> +               ACTION_UNSET = (1<<5),\n> +               ACTION_UNSET_ALL = (1<<6),\n> +               ACTION_RENAME_SECTION = (1<<7),\n> +               ACTION_REMOVE_SECTION = (1<<8),\n> +               ACTION_LIST = (1<<9),\n> +               ACTION_EDIT = (1<<10),\n> +               ACTION_SET = (1<<11),\n> +               ACTION_SET_ALL = (1<<12),\n> +               ACTION_GET_COLOR = (1<<13),\n> +               ACTION_GET_COLORBOOL = (1<<14),\n> +               ACTION_GET_URLMATCH = (1<<15),\n> +       };\n>         const char *comment_arg = NULL;\n>         int actions = 0;\n>         struct option opts[] = {\n> @@ -1147,7 +1141,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n>\n>         comment = git_config_prepare_comment_string(comment_arg);\n>\n> -       if (actions & PAGING_ACTIONS)\n> +       if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n>                 setup_auto_pager(\"config\", 1);\n>\n>         if (actions == ACTION_LIST) {\n> --\n> 2.45.GIT\n>\n"},{"id":"494507","messageId":"CAO_smVhahWTvH4ycDGoRd2WeUnbNs=8Ukq5L17gRYn9kdE-zjA@mail.gmail.com","threadId":"61452","inReplyTo":"edfd7caa39ab990faf5b49a6c572a612a35dbdd5.1715339393.git.ps@pks.im","subject":"Re: [PATCH 06/21] builtin/config: check for writeability after source is set up","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-05-10T20:46:43Z","receivedAt":"2024-05-10T20:47:02Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Fri, May 10, 2024 at 4:26 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> The `check_write()` function verifies that we do not try to write to a\n> config source that cannot be written to, like for example stdin. But\n> while the new subcommands do call this function, they do so before\n> calling `handle_config_location()`. Consequently, we only end up\n> checking the default config location for writeability, not the location\n> that was actually specified by the caller of git-config(1).\n>\n> Fix this by calling `check_write()` after `handle_config_location()`. We\n> will further clarify the relationship between those two functions in a\n> subsequent commit where we remove the global state that both implicitly\n> rely on.\n\nMinor nit/question since I'm still pretty inexperienced w/ the project norms:\nSince this is a bug fix/behavior change, can we reorder the series so\nthis comes before (or after) the rest of the cleanups? I'm assuming\nthis fix would be something that could stand alone in its own series\neven if we weren't making the other changes.\n\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/config.c  | 10 +++++-----\n>  t/t1300-config.sh |  6 ++++++\n>  2 files changed, 11 insertions(+), 5 deletions(-)\n>\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 8f3342b593..9295a2f737 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n>\n>         argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n>                              PARSE_OPT_STOP_AT_NON_OPTION);\n> -       check_write();\n>         check_argc(argc, 2, 2);\n>\n>         if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n> @@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n>         comment = git_config_prepare_comment_string(comment_arg);\n>\n>         handle_config_location(prefix);\n> +       check_write();\n>\n>         value = normalize_value(argv[0], argv[1], &default_kvi);\n>\n> @@ -891,13 +891,13 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n>\n>         argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n>                              PARSE_OPT_STOP_AT_NON_OPTION);\n> -       check_write();\n>         check_argc(argc, 1, 1);\n>\n>         if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n>                 die(_(\"--fixed-value only applies with 'value-pattern'\"));\n>\n>         handle_config_location(prefix);\n> +       check_write();\n>\n>         if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n>                 return git_config_set_multivar_in_file_gently(given_config_source.file,\n> @@ -918,10 +918,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n>\n>         argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,\n>                              PARSE_OPT_STOP_AT_NON_OPTION);\n> -       check_write();\n>         check_argc(argc, 2, 2);\n>\n>         handle_config_location(prefix);\n> +       check_write();\n>\n>         ret = git_config_rename_section_in_file(given_config_source.file,\n>                                                 argv[0], argv[1]);\n> @@ -943,10 +943,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n>\n>         argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,\n>                              PARSE_OPT_STOP_AT_NON_OPTION);\n> -       check_write();\n>         check_argc(argc, 1, 1);\n>\n>         handle_config_location(prefix);\n> +       check_write();\n>\n>         ret = git_config_rename_section_in_file(given_config_source.file,\n>                                                 argv[0], NULL);\n> @@ -997,10 +997,10 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n>         };\n>\n>         argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n> -       check_write();\n>         check_argc(argc, 0, 0);\n>\n>         handle_config_location(prefix);\n> +       check_write();\n>\n>         return show_editor();\n>  }\n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n> index d90a69b29f..9de2d95f06 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> @@ -2835,6 +2835,12 @@ test_expect_success 'specifying multiple modes causes failure' '\n>         test_cmp expect err\n>  '\n>\n> +test_expect_success 'writing to stdin is rejected' '\n> +       echo \"fatal: writing to stdin is not supported\" >expect &&\n> +       test_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&\n> +       test_cmp expect err\n> +'\n> +\n>  done\n>\n>  test_done\n> --\n> 2.45.GIT\n>\n"},{"id":"494527","messageId":"CAO_smVgBASXOGkGFvB8Nu38SxxRR1VBcrSOg9km2SVGN459_pA@mail.gmail.com","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"Re: [PATCH 00/21] builtin/config: remove global state","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-05-10T23:08:46Z","receivedAt":"2024-05-10T23:09:04Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Fri, May 10, 2024 at 4:26 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> Hi,\n>\n> we have quite a lot of global state in git-config(1). For one, this\n> global state is used to track options passed by the user. And second,\n> there is a lot of global state that is really only used to pass data\n> between caller and callbacks.\n>\n> This global state makes it quite hard to understand interactions at\n> times. This only became harder now with the introduction of subcommands,\n> where a whole lot less of the global state is even relevant in the first\n> place. As an example, this global state has made it quite easy for a bug\n> to sneak into the new subcommands where `check_write()` was called\n> before the global data it access is initialized (see patch 6).\n>\n> This patch series refactors the code such that we have no more global\n> state in \"builtin/config.c\". It's rather long, but most of the patches\n> are really quite trivial and only move code around. So overall, I think\n> it shouldn't be too bad to review this. But please, let me know if you\n> disagree and I'll happily split this series into two.\n>\n> The patch series depends on 7b91d310ce (builtin/config: display\n> subcommand help, 2024-05-06).\n>\n> Thanks!\n>\n> Patrick\n>\n> Patrick Steinhardt (21):\n>   builtin/config: stop printing full usage on misuse\n>   builtin/config: move legacy mode into its own function\n>   builtin/config: move subcommand options into `cmd_config()`\n>   builtin/config: move legacy options into `cmd_config()`\n>   builtin/config: move actions into `cmd_config_actions()`\n>   builtin/config: check for writeability after source is set up\n>   config: make the config source const\n>   builtin/config: refactor functions to have common exit paths\n>   builtin/config: move location options into local variables\n>   builtin/config: move display options into local variables\n>   builtin/config: move type options into display options\n>   builtin/config: move default value into display options\n>   builtin/config: move `respect_includes_opt` into location options\n>   builtin/config: convert `do_not_match` to a local variable\n>   builtin/config: convert `value_pattern` to a local variable\n>   builtin/config: convert `regexp` to a local variable\n>   builtin/config: convert `key_regexp` to a local variable\n>   builtin/config: convert `key` to a local variable\n>   builtin/config: track \"fixed value\" option via flags only\n>   builtin/config: convert flags to a local variable\n>   builtin/config: pass data between callbacks via local variables\n>\n>  builtin/config.c  | 964 +++++++++++++++++++++++++---------------------\n>  config.c          |   4 +-\n>  config.h          |   2 +-\n>  t/t1300-config.sh |   9 +-\n>  4 files changed, 546 insertions(+), 433 deletions(-)\n\nLeft a couple small comments, but looks good to me. Thanks!\n\n>\n> --\n> 2.45.GIT\n>\n"},{"id":"494541","messageId":"CAPig+cT9ZiZCzPL682Y8aos_=Ptq=doV9UCQd3M4c4kr6qzhvA@mail.gmail.com","threadId":"61452","inReplyTo":"2b40b784fe146b4d17de9accd4afc53144c93812.1715339393.git.ps@pks.im","subject":"Re: [PATCH 14/21] builtin/config: convert `do_not_match` to a local variable","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-05-11T16:42:39Z","receivedAt":"2024-05-11T16:42:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, May 11, 2024 at 5:25 AM Patrick Steinhardt <ps@pks.im> wrote:\n> The `do_not_match` variable is used by the `format_config()` callback as\n> an indicator whteher or not the passed regular expression is negated. It\n> is only ever set up by its only caller, `collect_config()` and can thus\n> easily be moved into the `collect_config_data` structure.\n\ns/whteher/whether/\n\n> Do so to remove our reliance on global state.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n"},{"id":"494543","messageId":"CAPig+cSf1BcAYKk4Mg4Nn8TV6L_VRQg0tXvUcNykFcnwdiFf3w@mail.gmail.com","threadId":"61452","inReplyTo":"625216a7741b64b63717c72fd3c906b04c256bbf.1715339393.git.ps@pks.im","subject":"Re: [PATCH 19/21] builtin/config: track \"fixed value\" option via flags only","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-05-11T16:52:05Z","receivedAt":"2024-05-11T16:52:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, May 11, 2024 at 8:11 AM Patrick Steinhardt <ps@pks.im> wrote:\n> We track the \"fixed value\" option via two separate bits: once via the\n> global variable `fixed_value`, and once via the CONFIG_FLAGS_FIXED_VALUE\n> bit in `flags`. This is confusing and may easily lead to issues when one\n> is not aware that this is tracked via two separate mechanisms.\n>\n> Refactor the code to use the flag exclusively. We already pass it to all\n> the require callsites anyway, except for `collect_config()`.\n\ns/require/required/\n\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n"},{"id":"494605","messageId":"ZkHpe12KCaBv3AO6@tanuki","threadId":"61452","inReplyTo":"CAO_smVjXjW6pM-bWbQmCWxVi8LLZ2iB2p6RYhJhtc529cgDxHA@mail.gmail.com","subject":"Re: [PATCH 05/21] builtin/config: move actions into `cmd_config_actions()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:20:43Z","receivedAt":"2024-05-13T10:20:48Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, May 10, 2024 at 01:42:49PM -0700, Kyle Lippincott wrote:\n> On Fri, May 10, 2024 at 4:26 AM Patrick Steinhardt <ps@pks.im> wrote:\n[snip]\n> > -/*\n> > - * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n> > - * one line of output and which should therefore be paged.\n> > - */\n> \n> We lost this comment when inlining this. Not sure if that was\n> intentional (comment deemed to be of low value) or accidental. I think\n> as someone unfamiliar with this logic that the comment would be\n> helpful to keep - it describes _why_ those items are checked for (vs.\n> needing to infer it from the action that's taken when there's a\n> match).\n\nIt was intentional because I found the comment to be somewhat redundant.\nBut I can also see that it does provide a bit more context than the code\nitself does, so I'll add it back in.\n\nPatrick\n"},{"id":"494606","messageId":"ZkHplkpUfySw_hUn@tanuki","threadId":"61452","inReplyTo":"CAO_smVhahWTvH4ycDGoRd2WeUnbNs=8Ukq5L17gRYn9kdE-zjA@mail.gmail.com","subject":"Re: [PATCH 06/21] builtin/config: check for writeability after source is set up","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:21:10Z","receivedAt":"2024-05-13T10:21:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, May 10, 2024 at 01:46:43PM -0700, Kyle Lippincott wrote:\n> On Fri, May 10, 2024 at 4:26 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > The `check_write()` function verifies that we do not try to write to a\n> > config source that cannot be written to, like for example stdin. But\n> > while the new subcommands do call this function, they do so before\n> > calling `handle_config_location()`. Consequently, we only end up\n> > checking the default config location for writeability, not the location\n> > that was actually specified by the caller of git-config(1).\n> >\n> > Fix this by calling `check_write()` after `handle_config_location()`. We\n> > will further clarify the relationship between those two functions in a\n> > subsequent commit where we remove the global state that both implicitly\n> > rely on.\n> \n> Minor nit/question since I'm still pretty inexperienced w/ the project norms:\n> Since this is a bug fix/behavior change, can we reorder the series so\n> this comes before (or after) the rest of the cleanups? I'm assuming\n> this fix would be something that could stand alone in its own series\n> even if we weren't making the other changes.\n\nYeah, it can indeed stand on its own. The reason why I moved it into the\nmiddle of the series is so that the subsequent patch will immediately\nrefactor the reason why this bug was able to sneak in, namely the\nimplicit dependency on a global variable. I thus lean towards keeping\nthe order as-is, also because the patch itself can be cleanly\ncherry-picked on top of ps/config-subcommands regardless of the order.\n\nPatrick\n"},{"id":"494607","messageId":"cover.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH v2 00/21] builtin/config: remove global state","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:21:58Z","receivedAt":"2024-05-13T10:22:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis is the second version of my patch series that removes global state\nfrom \"builtin/config.c\". Changes compared to v1:\n\n  - Reinstated a comment in patch 5.\n\n  - Fixed a memory leak in patch 9.\n\n  - A couple of commit message fixes.\n\nThe series continues to build on top of ps/config-subcommands.\n\nThanks!\n\nPatrick\n\nPatrick Steinhardt (21):\n  builtin/config: stop printing full usage on misuse\n  builtin/config: move legacy mode into its own function\n  builtin/config: move subcommand options into `cmd_config()`\n  builtin/config: move legacy options into `cmd_config()`\n  builtin/config: move actions into `cmd_config_actions()`\n  builtin/config: check for writeability after source is set up\n  config: make the config source const\n  builtin/config: refactor functions to have common exit paths\n  builtin/config: move location options into local variables\n  builtin/config: move display options into local variables\n  builtin/config: move type options into display options\n  builtin/config: move default value into display options\n  builtin/config: move `respect_includes_opt` into location options\n  builtin/config: convert `do_not_match` to a local variable\n  builtin/config: convert `value_pattern` to a local variable\n  builtin/config: convert `regexp` to a local variable\n  builtin/config: convert `key_regexp` to a local variable\n  builtin/config: convert `key` to a local variable\n  builtin/config: track \"fixed value\" option via flags only\n  builtin/config: convert flags to a local variable\n  builtin/config: pass data between callbacks via local variables\n\n builtin/config.c  | 968 ++++++++++++++++++++++++++--------------------\n config.c          |   4 +-\n config.h          |   2 +-\n t/t1300-config.sh |   9 +-\n 4 files changed, 550 insertions(+), 433 deletions(-)\n\nRange-diff against v1:\n 1:  0ba7628126 =  1:  0ba7628126 builtin/config: stop printing full usage on misuse\n 2:  663e1f74f8 =  2:  663e1f74f8 builtin/config: move legacy mode into its own function\n 3:  1239c151d0 =  3:  1239c151d0 builtin/config: move subcommand options into `cmd_config()`\n 4:  82964510c5 =  4:  82964510c5 builtin/config: move legacy options into `cmd_config()`\n 5:  2e308393ed !  5:  0a6ecae2cc builtin/config: move actions into `cmd_config_actions()`\n    @@ builtin/config.c: static int cmd_config_actions(int argc, const char **argv, con\n      \tcomment = git_config_prepare_comment_string(comment_arg);\n      \n     -\tif (actions & PAGING_ACTIONS)\n    ++\t/*\n    ++\t * The following actions may produce more than one line of output and\n    ++\t * should therefore be paged.\n    ++\t */\n     +\tif (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n      \t\tsetup_auto_pager(\"config\", 1);\n      \n 6:  edfd7caa39 =  6:  7ab99a27c1 builtin/config: check for writeability after source is set up\n 7:  bfba68aa1e =  7:  1460d3a36c config: make the config source const\n 8:  6bff0410e9 =  8:  018ed0226b builtin/config: refactor functions to have common exit paths\n 9:  a96c122280 !  9:  b5d43b6f85 builtin/config: move location options into local variables\n    @@ builtin/config.c: static char *default_user_config(void)\n     -\t\tgiven_config_source.file = git_global_config();\n     -\t\tif (!given_config_source.file)\n     +\tif (opts->use_global_config) {\n    -+\t\topts->source.file = xstrdup_or_null(git_global_config());\n    ++\t\topts->source.file = git_global_config();\n     +\t\tif (!opts->source.file)\n      \t\t\t/*\n      \t\t\t * It is unknown if HOME/.gitconfig exists, so\n10:  06c1e08fc4 = 10:  d66e14af30 builtin/config: move display options into local variables\n11:  9610897662 = 11:  63436c3416 builtin/config: move type options into display options\n12:  eb79462861 = 12:  106b8ac8a2 builtin/config: move default value into display options\n13:  b9ebfbd667 = 13:  8a6b555b58 builtin/config: move `respect_includes_opt` into location options\n14:  2b40b784fe ! 14:  0dd22bf51a builtin/config: convert `do_not_match` to a local variable\n    @@ Commit message\n         builtin/config: convert `do_not_match` to a local variable\n     \n         The `do_not_match` variable is used by the `format_config()` callback as\n    -    an indicator whteher or not the passed regular expression is negated. It\n    +    an indicator whether or not the passed regular expression is negated. It\n         is only ever set up by its only caller, `collect_config()` and can thus\n         easily be moved into the `collect_config_data` structure.\n     \n15:  71d1b7a51b = 15:  b656951f0c builtin/config: convert `value_pattern` to a local variable\n16:  c3b340f119 = 16:  b56a07bda0 builtin/config: convert `regexp` to a local variable\n17:  835cc0acfb = 17:  323cb05120 builtin/config: convert `key_regexp` to a local variable\n18:  2aee7ec5d8 ! 18:  e972e63be8 builtin/config: convert `key` to a local variable\n    @@ Commit message\n         The `key` variable is used by the `get_value()` function for two\n         purposes:\n     \n    -      - It is used to store the result of `git_config_parse_key()`, which si\n    +      - It is used to store the result of `git_config_parse_key()`, which is\n             then passed on to `collect_config()`.\n     \n           - It is used as a store to convert the provided key to an\n             all-lowercase key when `use_key_regexp` is set.\n     \n    -    Both of these cases don't warrant a global variable at all. In the\n    -    former case we can pass the key via `struct collect_config_data`. And in\n    -    the latter case we really only want to have it as a temporary local\n    -    variable such that we can free associated memory.\n    +    Neither of these cases warrant a global variable at all. In the former\n    +    case we can pass the key via `struct collect_config_data`. And in the\n    +    latter case we really only want to have it as a temporary local variable\n    +    such that we can free associated memory.\n     \n         Refactor the code accordingly to reduce our reliance on global state.\n     \n19:  625216a774 ! 19:  d83c3d085e builtin/config: track \"fixed value\" option via flags only\n    @@ Commit message\n         is not aware that this is tracked via two separate mechanisms.\n     \n         Refactor the code to use the flag exclusively. We already pass it to all\n    -    the require callsites anyway, except for `collect_config()`.\n    +    the required callsites anyway, except for `collect_config()`.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n20:  05254d512b = 20:  294bcd96a4 builtin/config: convert flags to a local variable\n21:  3a5f059789 = 21:  0496b958e2 builtin/config: pass data between callbacks via local variables\n-- \n2.45.GIT\n\n"},{"id":"494608","messageId":"0ba76281267b324dd7fe8094132dbce11e9f2182.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 01/21] builtin/config: stop printing full usage on misuse","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:03Z","receivedAt":"2024-05-13T10:22:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When invoking git-config(1) with a wrong set of arguments we end up\ncalling `usage_builtin_config()` after printing an error message that\nsays what was wrong. As that function ends up printing the full list of\noptions, which is quite long, the actual error message will be buried by\na wall of text. This makes it really hard to figure out what exactly\ncaused the error.\n\nFurthermore, now that we have recently introduced subcommands, the usage\ninformation may actually be misleading as we unconditionally print\noptions of the subcommand-less mode.\n\nFix both of these issues by just not printing the options at all\nanymore. Instead, we call `usage()` that makes us report in a single\nline what has gone wrong. This should be way more discoverable for our\nusers and addresses the inconsistency.\n\nFurthermore, this change allow us to inline the options into the\nrespective functions that use them to parse the command line.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 28 +++++++++++-----------------\n t/t1300-config.sh |  3 ++-\n 2 files changed, 13 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 80aa9d8a66..3a71d3253f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,8 +125,6 @@ static const char *comment_arg;\n \t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n \tPARSE_OPT_NONEG, option_parse_type, (i) }\n \n-static NORETURN void usage_builtin_config(void);\n-\n static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n@@ -171,7 +169,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t * --type=int'.\n \t\t */\n \t\terror(_(\"only one type at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \t*to_type = new_type;\n \n@@ -187,7 +185,7 @@ static void check_argc(int argc, int min, int max)\n \telse\n \t\terror(_(\"wrong number of arguments, should be from %d to %d\"),\n \t\t      min, max);\n-\tusage_builtin_config();\n+\texit(129);\n }\n \n static void show_config_origin(const struct key_value_info *kvi,\n@@ -672,7 +670,7 @@ static void handle_config_location(const char *prefix)\n \t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n@@ -802,11 +800,6 @@ static struct option builtin_config_options[] = {\n \tOPT_END(),\n };\n \n-static NORETURN void usage_builtin_config(void)\n-{\n-\tusage_with_options(builtin_config_usage, builtin_config_options);\n-}\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1110,7 +1103,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (actions == 0)\n@@ -1119,30 +1112,31 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcase 2: actions = ACTION_SET; break;\n \t\tcase 3: actions = ACTION_SET_ALL; break;\n \t\tdefault:\n-\t\t\tusage_builtin_config();\n+\t\t\terror(_(\"no action specified\"));\n+\t\t\texit(129);\n \t\t}\n \tif (omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (comment_arg &&\n \t    !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {\n \t\terror(_(\"--comment is only applicable to add/set/replace operations\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \t/* check usage of --fixed-value */\n@@ -1175,7 +1169,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\tif (!allowed_usage) {\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\t\t\tusage_builtin_config();\n+\t\t\texit(129);\n \t\t}\n \n \t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f3c4d28e06..d90a69b29f 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -596,7 +596,8 @@ test_expect_success 'get bool variable with empty value' '\n \n test_expect_success 'no arguments, but no crash' '\n \ttest_must_fail git config >output 2>&1 &&\n-\ttest_grep usage output\n+\techo \"error: no action specified\" >expect &&\n+\ttest_cmp expect output\n '\n \n cat > .git/config << EOF\n-- \n2.45.GIT\n\n"},{"id":"494609","messageId":"663e1f74f82b8c1acdd0c35ad87736c071eafb33.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 02/21] builtin/config: move legacy mode into its own function","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:08Z","receivedAt":"2024-05-13T10:22:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In `cmd_config()` we first try to parse the provided arguments as\nsubcommands and, if this is successful, call the respective functions\nof that subcommand. Otherwise we continue with the \"legacy\" mode that\nuses implicit actions and/or flags.\n\nDisentangle this by moving the legacy mode into its own function. This\nallows us to move the options into the respective functions and clearly\nseparates concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 43 ++++++++++++++++++++++++-------------------\n 1 file changed, 24 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3a71d3253f..f6c7e7a082 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -1069,31 +1069,13 @@ static struct option builtin_subcommand_options[] = {\n \tOPT_END(),\n };\n \n-int cmd_config(int argc, const char **argv, const char *prefix)\n+static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n-\t/*\n-\t * This is somewhat hacky: we first parse the command line while\n-\t * keeping all args intact in order to determine whether a subcommand\n-\t * has been specified. If so, we re-parse it a second time, but this\n-\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n-\t * line in case no subcommand was given, which would otherwise confuse\n-\t * us when parsing the legacy-style modes that don't use subcommands.\n-\t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\t\treturn subcommand(argc, argv, prefix);\n-\t}\n-\n \targc = parse_options(argc, argv, prefix, builtin_config_options,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -1306,3 +1288,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tfree(value);\n \treturn ret;\n }\n+\n+int cmd_config(int argc, const char **argv, const char *prefix)\n+{\n+\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\n+\t/*\n+\t * This is somewhat hacky: we first parse the command line while\n+\t * keeping all args intact in order to determine whether a subcommand\n+\t * has been specified. If so, we re-parse it a second time, but this\n+\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n+\t * line in case no subcommand was given, which would otherwise confuse\n+\t * us when parsing the legacy-style modes that don't use subcommands.\n+\t */\n+\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\tif (subcommand) {\n+\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\t\treturn subcommand(argc, argv, prefix);\n+\t}\n+\n+\treturn cmd_config_actions(argc, argv, prefix);\n+}\n-- \n2.45.GIT\n\n"},{"id":"494610","messageId":"1239c151d08448f5782cdd12c5cf10fd5d2719d4.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 03/21] builtin/config: move subcommand options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:13Z","receivedAt":"2024-05-13T10:22:17Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the subcommand options as well as the `subcommand` variable into\n`cmd_config()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f6c7e7a082..58c8b0abda 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -75,7 +75,6 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static parse_opt_subcommand_fn *subcommand;\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n@@ -1058,17 +1057,6 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \treturn show_editor();\n }\n \n-static struct option builtin_subcommand_options[] = {\n-\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n-\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n-\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n-\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n-\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n-\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n-\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n@@ -1291,6 +1279,18 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n int cmd_config(int argc, const char **argv, const char *prefix)\n {\n+\tparse_opt_subcommand_fn *subcommand = NULL;\n+\tstruct option subcommand_opts[] = {\n+\t\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n+\t\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n+\t\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n+\t\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n+\t\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n+\t\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n+\t\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n+\t\tOPT_END(),\n+\t};\n+\n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n \t/*\n@@ -1301,10 +1301,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t * line in case no subcommand was given, which would otherwise confuse\n \t * us when parsing the legacy-style modes that don't use subcommands.\n \t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \t\treturn subcommand(argc, argv, prefix);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494611","messageId":"82964510c557a398e2713744ec75bba3ba5edb25.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 04/21] builtin/config: move legacy options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:18Z","receivedAt":"2024-05-13T10:22:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the legacy options as well some of the variables it references into\n`cmd_config_action()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 ++++++++++++++++++++++++------------------------\n 1 file changed, 30 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 58c8b0abda..e9956574fe 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -78,7 +78,7 @@ static char term = '\\n';\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n-static int actions, type;\n+static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n@@ -86,7 +86,6 @@ static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n-static const char *comment_arg;\n \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -772,33 +771,6 @@ static void handle_nul(void) {\n \tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n \n-static struct option builtin_config_options[] = {\n-\tCONFIG_LOCATION_OPTIONS,\n-\tOPT_GROUP(N_(\"Action\")),\n-\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n-\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n-\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n-\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n-\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n-\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n-\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n-\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n-\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n-\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n-\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n-\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n-\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n-\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\tCONFIG_TYPE_OPTIONS,\n-\tCONFIG_DISPLAY_OPTIONS,\n-\tOPT_GROUP(N_(\"Other\")),\n-\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n-\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1059,12 +1031,40 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tconst char *comment_arg = NULL;\n+\tint actions = 0;\n+\tstruct option opts[] = {\n+\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Action\")),\n+\t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n+\t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n+\t\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n+\t\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n+\t\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n+\t\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n+\t\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n+\t\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n+\t\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n+\t\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n+\t\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n+\t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n+\t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n+\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Other\")),\n+\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n+\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_END(),\n+\t};\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\targc = parse_options(argc, argv, prefix, builtin_config_options,\n+\targc = parse_options(argc, argv, prefix, opts,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-- \n2.45.GIT\n\n"},{"id":"494612","messageId":"0a6ecae2ccc55789bce3f9c42900b3f41c1cb06a.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 05/21] builtin/config: move actions into `cmd_config_actions()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:23Z","receivedAt":"2024-05-13T10:22:28Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We only use actions in the legacy mode. Convert them to an enum and move\nthem into `cmd_config_actions()` to clearly demonstrate that they are\nnot used anywhere else.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 48 +++++++++++++++++++++++-------------------------\n 1 file changed, 23 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e9956574fe..0842e4f198 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -87,30 +87,6 @@ static int show_origin;\n static int show_scope;\n static int fixed_value;\n \n-#define ACTION_GET (1<<0)\n-#define ACTION_GET_ALL (1<<1)\n-#define ACTION_GET_REGEXP (1<<2)\n-#define ACTION_REPLACE_ALL (1<<3)\n-#define ACTION_ADD (1<<4)\n-#define ACTION_UNSET (1<<5)\n-#define ACTION_UNSET_ALL (1<<6)\n-#define ACTION_RENAME_SECTION (1<<7)\n-#define ACTION_REMOVE_SECTION (1<<8)\n-#define ACTION_LIST (1<<9)\n-#define ACTION_EDIT (1<<10)\n-#define ACTION_SET (1<<11)\n-#define ACTION_SET_ALL (1<<12)\n-#define ACTION_GET_COLOR (1<<13)\n-#define ACTION_GET_COLORBOOL (1<<14)\n-#define ACTION_GET_URLMATCH (1<<15)\n-\n-/*\n- * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n- * one line of output and which should therefore be paged.\n- */\n-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n-\t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -1031,6 +1007,24 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tenum {\n+\t\tACTION_GET = (1<<0),\n+\t\tACTION_GET_ALL = (1<<1),\n+\t\tACTION_GET_REGEXP = (1<<2),\n+\t\tACTION_REPLACE_ALL = (1<<3),\n+\t\tACTION_ADD = (1<<4),\n+\t\tACTION_UNSET = (1<<5),\n+\t\tACTION_UNSET_ALL = (1<<6),\n+\t\tACTION_RENAME_SECTION = (1<<7),\n+\t\tACTION_REMOVE_SECTION = (1<<8),\n+\t\tACTION_LIST = (1<<9),\n+\t\tACTION_EDIT = (1<<10),\n+\t\tACTION_SET = (1<<11),\n+\t\tACTION_SET_ALL = (1<<12),\n+\t\tACTION_GET_COLOR = (1<<13),\n+\t\tACTION_GET_COLORBOOL = (1<<14),\n+\t\tACTION_GET_URLMATCH = (1<<15),\n+\t};\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1147,7 +1141,11 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\tif (actions & PAGING_ACTIONS)\n+\t/*\n+\t * The following actions may produce more than one line of output and\n+\t * should therefore be paged.\n+\t */\n+\tif (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n \t\tsetup_auto_pager(\"config\", 1);\n \n \tif (actions == ACTION_LIST) {\n-- \n2.45.GIT\n\n"},{"id":"494613","messageId":"7ab99a27c16718ad4dff1f7862e80c52b48c3812.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 06/21] builtin/config: check for writeability after source is set up","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:28Z","receivedAt":"2024-05-13T10:22:33Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `check_write()` function verifies that we do not try to write to a\nconfig source that cannot be written to, like for example stdin. But\nwhile the new subcommands do call this function, they do so before\ncalling `handle_config_location()`. Consequently, we only end up\nchecking the default config location for writeability, not the location\nthat was actually specified by the caller of git-config(1).\n\nFix this by calling `check_write()` after `handle_config_location()`. We\nwill further clarify the relationship between those two functions in a\nsubsequent commit where we remove the global state that both implicitly\nrely on.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 10 +++++-----\n t/t1300-config.sh |  6 ++++++\n 2 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 0842e4f198..9866d1a055 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n@@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n@@ -891,13 +891,13 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n@@ -918,10 +918,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n@@ -943,10 +943,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n@@ -997,10 +997,10 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n-\tcheck_write();\n \tcheck_argc(argc, 0, 0);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \treturn show_editor();\n }\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex d90a69b29f..9de2d95f06 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2835,6 +2835,12 @@ test_expect_success 'specifying multiple modes causes failure' '\n \ttest_cmp expect err\n '\n \n+test_expect_success 'writing to stdin is rejected' '\n+\techo \"fatal: writing to stdin is not supported\" >expect &&\n+\ttest_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&\n+\ttest_cmp expect err\n+'\n+\n done\n \n test_done\n-- \n2.45.GIT\n\n"},{"id":"494614","messageId":"1460d3a36c4f5357310164114e23ba32f3098802.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 07/21] config: make the config source const","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:32Z","receivedAt":"2024-05-13T10:22:37Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `struct git_config_source` passed to `config_with_options()` is\nnever modified. Let's mark it as `const` to clarify.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n config.c | 4 ++--\n config.h | 2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 13cf9eeb16..bc0c005039 100644\n--- a/config.c\n+++ b/config.c\n@@ -125,7 +125,7 @@ struct config_include_data {\n \tconfig_fn_t fn;\n \tvoid *data;\n \tconst struct config_options *opts;\n-\tstruct git_config_source *config_source;\n+\tconst struct git_config_source *config_source;\n \tstruct repository *repo;\n \n \t/*\n@@ -2105,7 +2105,7 @@ static int do_git_config_sequence(const struct config_options *opts,\n }\n \n int config_with_options(config_fn_t fn, void *data,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts)\n {\ndiff --git a/config.h b/config.h\nindex db8b608064..e66c84520b 100644\n--- a/config.h\n+++ b/config.h\n@@ -232,7 +232,7 @@ void git_config(config_fn_t fn, void *);\n  * sets `opts.respect_includes` to `1` by default.\n  */\n int config_with_options(config_fn_t fn, void *,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts);\n \n-- \n2.45.GIT\n\n"},{"id":"494615","messageId":"018ed0226ba670616c44db20a35d456188cf0604.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 08/21] builtin/config: refactor functions to have common exit paths","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:38Z","receivedAt":"2024-05-13T10:22:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Refactor functions to have a single exit path. This will make it easier\nin subsequent commits to add common cleanup code.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 38 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9866d1a055..155564b832 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -798,6 +798,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -816,8 +817,11 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\treturn get_urlmatch(argv[0], url);\n-\treturn get_value(argv[0], value_pattern, flags);\n+\t\tret = get_urlmatch(argv[0], url);\n+\telse\n+\t\tret = get_value(argv[0], value_pattern, flags);\n+\n+\treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n@@ -888,6 +892,7 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -900,12 +905,14 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, value_pattern,\n-\t\t\t\t\t\t\t      NULL, flags);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n+\t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\treturn git_config_set_in_file_gently(given_config_source.file, argv[0],\n-\t\t\t\t\t\t     NULL, NULL);\n+\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\t\t\t\t\t    NULL, NULL);\n+\n+\treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n@@ -926,11 +933,13 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n@@ -951,11 +960,13 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int show_editor(void)\n@@ -1199,41 +1210,41 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\treturn get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t\t      NULL, flags);\n+\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\treturn git_config_set_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t     argv[0], NULL, NULL);\n+\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t      NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tcheck_write();\n@@ -1241,7 +1252,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1253,7 +1264,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1267,9 +1278,10 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\treturn get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(argv[0], argc == 2);\n \t}\n \n+out:\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494616","messageId":"b5d43b6f852d99021236b187ad016dfe7b5f3d09.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 09/21] builtin/config: move location options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:43Z","receivedAt":"2024-05-13T10:22:48Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The location options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 311 ++++++++++++++++++++++++++---------------------\n 1 file changed, 174 insertions(+), 137 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 155564b832..c309497e23 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -62,6 +62,25 @@ static const char *const builtin_config_edit_usage[] = {\n \tNULL\n };\n \n+#define CONFIG_LOCATION_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Config file location\")), \\\n+\tOPT_BOOL(0, \"global\", &opts.use_global_config, N_(\"use global config file\")), \\\n+\tOPT_BOOL(0, \"system\", &opts.use_system_config, N_(\"use system config file\")), \\\n+\tOPT_BOOL(0, \"local\", &opts.use_local_config, N_(\"use repository config file\")), \\\n+\tOPT_BOOL(0, \"worktree\", &opts.use_worktree_config, N_(\"use per-worktree config file\")), \\\n+\tOPT_STRING('f', \"file\", &opts.source.file, N_(\"file\"), N_(\"use given config file\")), \\\n+\tOPT_STRING(0, \"blob\", &opts.source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n+\n+struct config_location_options {\n+\tstruct git_config_source source;\n+\tstruct config_options options;\n+\tint use_global_config;\n+\tint use_system_config;\n+\tint use_local_config;\n+\tint use_worktree_config;\n+};\n+#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n@@ -75,14 +94,10 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static int use_global_config, use_system_config, use_local_config;\n-static int use_worktree_config;\n-static struct git_config_source given_config_source;\n static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n-static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n@@ -298,7 +313,8 @@ static int collect_config(const char *key_, const char *value_,\n \treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n }\n \n-static int get_value(const char *key_, const char *regex_, unsigned flags)\n+static int get_value(const struct config_location_options *opts,\n+\t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -353,8 +369,8 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)\n \t}\n \n \tconfig_with_options(collect_config, &values,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!values.nr && default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n@@ -464,14 +480,15 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const char *var, const char *def_color)\n+static void get_color(const struct config_location_options *opts,\n+\t\t      const char *var, const char *def_color)\n {\n \tget_color_slot = var;\n \tget_color_found = 0;\n \tparsed_color[0] = '\\0';\n \tconfig_with_options(git_get_color_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!get_color_found && def_color) {\n \t\tif (color_parse(def_color, parsed_color) < 0)\n@@ -497,15 +514,16 @@ static int git_get_colorbool_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_colorbool(const char *var, int print)\n+static int get_colorbool(const struct config_location_options *opts,\n+\t\t\t const char *var, int print)\n {\n \tget_colorbool_slot = var;\n \tget_colorbool_found = -1;\n \tget_diff_color_found = -1;\n \tget_color_ui_found = -1;\n \tconfig_with_options(git_get_colorbool_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (get_colorbool_found < 0) {\n \t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n@@ -527,15 +545,15 @@ static int get_colorbool(const char *var, int print)\n \t\treturn get_colorbool_found ? 0 : 1;\n }\n \n-static void check_write(void)\n+static void check_write(const struct git_config_source *source)\n {\n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!source->file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n \n-\tif (given_config_source.use_stdin)\n+\tif (source->use_stdin)\n \t\tdie(_(\"writing to stdin is not supported\"));\n \n-\tif (given_config_source.blob)\n+\tif (source->blob)\n \t\tdie(_(\"writing config blobs is not supported\"));\n }\n \n@@ -572,7 +590,8 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_urlmatch(const char *var, const char *url)\n+static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n@@ -599,8 +618,8 @@ static int get_urlmatch(const char *var, const char *url)\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tret = !values.nr;\n \n@@ -638,34 +657,40 @@ static char *default_user_config(void)\n \treturn strbuf_detach(&buf, NULL);\n }\n \n-static void handle_config_location(const char *prefix)\n+static void location_options_init(struct config_location_options *opts,\n+\t\t\t\t  const char *prefix)\n {\n-\tif (use_global_config + use_system_config + use_local_config +\n-\t    use_worktree_config +\n-\t    !!given_config_source.file + !!given_config_source.blob > 1) {\n+\tif (!opts->source.file)\n+\t\topts->source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\telse\n+\t\topts->source.file = xstrdup(opts->source.file);\n+\n+\tif (opts->use_global_config + opts->use_system_config +\n+\t    opts->use_local_config + opts->use_worktree_config +\n+\t    !!opts->source.file + !!opts->source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n-\t\tif (use_local_config)\n+\t\tif (opts->use_local_config)\n \t\t\tdie(_(\"--local can only be used inside a git repository\"));\n-\t\tif (given_config_source.blob)\n+\t\tif (opts->source.blob)\n \t\t\tdie(_(\"--blob can only be used inside a git repository\"));\n-\t\tif (use_worktree_config)\n+\t\tif (opts->use_worktree_config)\n \t\t\tdie(_(\"--worktree can only be used inside a git repository\"));\n \t}\n \n-\tif (given_config_source.file &&\n-\t\t\t!strcmp(given_config_source.file, \"-\")) {\n-\t\tgiven_config_source.file = NULL;\n-\t\tgiven_config_source.use_stdin = 1;\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\tif (opts->source.file &&\n+\t\t\t!strcmp(opts->source.file, \"-\")) {\n+\t\topts->source.file = NULL;\n+\t\topts->source.use_stdin = 1;\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (use_global_config) {\n-\t\tgiven_config_source.file = git_global_config();\n-\t\tif (!given_config_source.file)\n+\tif (opts->use_global_config) {\n+\t\topts->source.file = git_global_config();\n+\t\tif (!opts->source.file)\n \t\t\t/*\n \t\t\t * It is unknown if HOME/.gitconfig exists, so\n \t\t\t * we do not know if we should write to XDG\n@@ -673,17 +698,17 @@ static void handle_config_location(const char *prefix)\n \t\t\t * is set and points at a sane location.\n \t\t\t */\n \t\t\tdie(_(\"$HOME not set\"));\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_GLOBAL;\n-\t} else if (use_system_config) {\n-\t\tgiven_config_source.file = git_system_config();\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_SYSTEM;\n-\t} else if (use_local_config) {\n-\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n-\t} else if (use_worktree_config) {\n+\t\topts->source.scope = CONFIG_SCOPE_GLOBAL;\n+\t} else if (opts->use_system_config) {\n+\t\topts->source.file = xstrdup_or_null(git_system_config());\n+\t\topts->source.scope = CONFIG_SCOPE_SYSTEM;\n+\t} else if (opts->use_local_config) {\n+\t\topts->source.file = xstrdup_or_null(git_pathdup(\"config\"));\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n+\t} else if (opts->use_worktree_config) {\n \t\tstruct worktree **worktrees = get_worktrees();\n \t\tif (the_repository->repository_format_worktree_config)\n-\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\t\topts->source.file = git_pathdup(\"config.worktree\");\n \t\telse if (worktrees[0] && worktrees[1])\n \t\t\tdie(_(\"--worktree cannot be used with multiple \"\n \t\t\t      \"working trees unless the config\\n\"\n@@ -691,28 +716,33 @@ static void handle_config_location(const char *prefix)\n \t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n \t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n \t\telse\n-\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n+\t\t\topts->source.file = git_pathdup(\"config\");\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n \t\tfree_worktrees(worktrees);\n-\t} else if (given_config_source.file) {\n-\t\tif (!is_absolute_path(given_config_source.file) && prefix)\n-\t\t\tgiven_config_source.file =\n-\t\t\t\tprefix_filename(prefix, given_config_source.file);\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n-\t} else if (given_config_source.blob) {\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.file) {\n+\t\tif (!is_absolute_path(opts->source.file) && prefix)\n+\t\t\topts->source.file =\n+\t\t\t\tprefix_filename(prefix, opts->source.file);\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.blob) {\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n \tif (respect_includes_opt == -1)\n-\t\tconfig_options.respect_includes = !given_config_source.file;\n+\t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\tconfig_options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = respect_includes_opt;\n \tif (startup_info->have_repository) {\n-\t\tconfig_options.commondir = get_git_common_dir();\n-\t\tconfig_options.git_dir = get_git_dir();\n+\t\topts->options.commondir = get_git_common_dir();\n+\t\topts->options.git_dir = get_git_dir();\n \t}\n }\n \n+static void location_options_release(struct config_location_options *opts)\n+{\n+\tfree((char *) opts->source.file);\n+}\n+\n static void handle_nul(void) {\n \tif (end_nul) {\n \t\tterm = '\\0';\n@@ -721,15 +751,6 @@ static void handle_nul(void) {\n \t}\n }\n \n-#define CONFIG_LOCATION_OPTIONS \\\n-\tOPT_GROUP(N_(\"Config file location\")), \\\n-\tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")), \\\n-\tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")), \\\n-\tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")), \\\n-\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")), \\\n-\tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")), \\\n-\tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n-\n #define CONFIG_TYPE_OPTIONS \\\n \tOPT_GROUP(N_(\"Type\")), \\\n \tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n@@ -749,8 +770,9 @@ static void handle_nul(void) {\n \n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS,\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -760,30 +782,32 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t&config_options) < 0) {\n-\t\tif (given_config_source.file)\n+\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t&location_opts.options) < 0) {\n+\t\tif (location_opts.source.file)\n \t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t  given_config_source.file);\n+\t\t\t\t  location_opts.source.file);\n \t\telse\n \t\t\tdie(_(\"error processing config file(s)\"));\n \t}\n \n+\tlocation_options_release(&location_opts);\n \treturn 0;\n }\n \n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n@@ -811,26 +835,28 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, argv[0], url);\n \telse\n-\t\tret = get_value(argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n \tint flags = 0, append = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n@@ -858,23 +884,24 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, value_pattern,\n \t\t\t\t\t\t\t     comment, flags);\n \t} else {\n-\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t    argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -882,10 +909,11 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -901,24 +929,26 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n \t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0],\n \t\t\t\t\t\t    NULL, NULL);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -927,10 +957,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 2, 2);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -939,13 +969,15 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -954,10 +986,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 1, 1);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -966,24 +998,25 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n-static int show_editor(void)\n+static int show_editor(struct config_location_options *opts)\n {\n \tchar *config_file;\n \n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!opts->source.file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n-\tif (given_config_source.use_stdin)\n+\tif (opts->source.use_stdin)\n \t\tdie(_(\"editing stdin is not supported\"));\n-\tif (given_config_source.blob)\n+\tif (opts->source.blob)\n \t\tdie(_(\"editing blobs is not supported\"));\n \tgit_config(git_default_config, NULL);\n-\tconfig_file = given_config_source.file ?\n-\t\t\txstrdup(given_config_source.file) :\n+\tconfig_file = opts->source.file ?\n+\t\t\txstrdup(opts->source.file) :\n \t\t\tgit_pathdup(\"config\");\n-\tif (use_global_config) {\n+\tif (opts->use_global_config) {\n \t\tint fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);\n \t\tif (fd >= 0) {\n \t\t\tchar *content = default_user_config();\n@@ -1002,18 +1035,22 @@ static int show_editor(void)\n \n static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\treturn show_editor();\n+\tret = show_editor(&location_opts);\n+\tlocation_options_release(&location_opts);\n+\treturn ret;\n }\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n@@ -1036,10 +1073,11 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_COLORBOOL = (1<<14),\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n \t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n \t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n@@ -1073,7 +1111,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n@@ -1162,94 +1200,94 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t\t&config_options) < 0) {\n-\t\t\tif (given_config_source.file)\n+\t\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t\t&location_opts.options) < 0) {\n+\t\t\tif (location_opts.source.file)\n \t\t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t\t  given_config_source.file);\n+\t\t\t\t\t  location_opts.source.file);\n \t\t\telse\n \t\t\t\tdie(_(\"error processing config file(s)\"));\n \t\t}\n \t}\n \telse if (actions == ACTION_EDIT) {\n-\t\tret = show_editor();\n+\t\tret = show_editor(&location_opts);\n \t}\n \telse if (actions == ACTION_SET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \telse if (actions == ACTION_SET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1259,9 +1297,9 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\tret = 0;\n \t}\n \telse if (actions == ACTION_REMOVE_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 1);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1272,16 +1310,17 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(argv[0], argv[1]);\n+\t\tget_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\tret = get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(&location_opts, argv[0], argc == 2);\n \t}\n \n out:\n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -1301,8 +1340,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n \t/*\n \t * This is somewhat hacky: we first parse the command line while\n \t * keeping all args intact in order to determine whether a subcommand\n-- \n2.45.GIT\n\n"},{"id":"494617","messageId":"d66e14af30062a0d5acfb51d02cdc072f8336f2f.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 10/21] builtin/config: move display options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:49Z","receivedAt":"2024-05-13T10:22:54Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The display options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 171 ++++++++++++++++++++++++++++-------------------\n 1 file changed, 101 insertions(+), 70 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex c309497e23..24ad8e7476 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -81,25 +81,42 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_DISPLAY_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Display options\")), \\\n+\tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n+\tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n+\tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n+\tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\n+struct config_display_options {\n+\tint end_nul;\n+\tint omit_values;\n+\tint show_origin;\n+\tint show_scope;\n+\tint show_keys;\n+\t/* Populated via `display_options_init()`. */\n+\tint term;\n+\tint delim;\n+\tint key_delim;\n+};\n+#define CONFIG_DISPLAY_OPTIONS_INIT { \\\n+\t.term = '\\n', \\\n+\t.delim = '=', \\\n+\t.key_delim = ' ', \\\n+}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n static regex_t *regexp;\n-static int show_keys;\n-static int omit_values;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char delim = '=';\n-static char key_delim = ' ';\n-static char term = '\\n';\n \n static int type;\n static char *default_value;\n-static int end_nul;\n static int respect_includes_opt = -1;\n-static int show_origin;\n-static int show_scope;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -177,24 +194,26 @@ static void check_argc(int argc, int min, int max)\n \texit(129);\n }\n \n-static void show_config_origin(const struct key_value_info *kvi,\n+static void show_config_origin(const struct config_display_options *opts,\n+\t\t\t       const struct key_value_info *kvi,\n \t\t\t       struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \n \tstrbuf_addstr(buf, config_origin_type_name(kvi->origin_type));\n \tstrbuf_addch(buf, ':');\n-\tif (end_nul)\n+\tif (opts->end_nul)\n \t\tstrbuf_addstr(buf, kvi->filename ? kvi->filename : \"\");\n \telse\n \t\tquote_c_style(kvi->filename ? kvi->filename : \"\", buf, NULL, 0);\n \tstrbuf_addch(buf, term);\n }\n \n-static void show_config_scope(const struct key_value_info *kvi,\n+static void show_config_scope(const struct config_display_options *opts,\n+\t\t\t      const struct key_value_info *kvi,\n \t\t\t      struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \tconst char *scope = config_scope_name(kvi->scope);\n \n \tstrbuf_addstr(buf, N_(scope));\n@@ -203,24 +222,25 @@ static void show_config_scope(const struct key_value_info *kvi,\n \n static int show_all_config(const char *key_, const char *value_,\n \t\t\t   const struct config_context *ctx,\n-\t\t\t   void *cb UNUSED)\n+\t\t\t   void *cb)\n {\n+\tconst struct config_display_options *opts = cb;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (show_origin || show_scope) {\n+\tif (opts->show_origin || opts->show_scope) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tif (show_scope)\n-\t\t\tshow_config_scope(kvi, &buf);\n-\t\tif (show_origin)\n-\t\t\tshow_config_origin(kvi, &buf);\n+\t\tif (opts->show_scope)\n+\t\t\tshow_config_scope(opts, kvi, &buf);\n+\t\tif (opts->show_origin)\n+\t\t\tshow_config_origin(opts, kvi, &buf);\n \t\t/* Use fwrite as \"buf\" can contain \\0's if \"end_null\" is set. */\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n \t\tstrbuf_release(&buf);\n \t}\n-\tif (!omit_values && value_)\n-\t\tprintf(\"%s%c%s%c\", key_, delim, value_, term);\n+\tif (!opts->omit_values && value_)\n+\t\tprintf(\"%s%c%s%c\", key_, opts->delim, value_, opts->term);\n \telse\n-\t\tprintf(\"%s%c\", key_, term);\n+\t\tprintf(\"%s%c\", key_, opts->term);\n \treturn 0;\n }\n \n@@ -230,18 +250,19 @@ struct strbuf_list {\n \tint alloc;\n };\n \n-static int format_config(struct strbuf *buf, const char *key_,\n+static int format_config(const struct config_display_options *opts,\n+\t\t\t struct strbuf *buf, const char *key_,\n \t\t\t const char *value_, const struct key_value_info *kvi)\n {\n-\tif (show_scope)\n-\t\tshow_config_scope(kvi, buf);\n-\tif (show_origin)\n-\t\tshow_config_origin(kvi, buf);\n-\tif (show_keys)\n+\tif (opts->show_scope)\n+\t\tshow_config_scope(opts, kvi, buf);\n+\tif (opts->show_origin)\n+\t\tshow_config_origin(opts, kvi, buf);\n+\tif (opts->show_keys)\n \t\tstrbuf_addstr(buf, key_);\n-\tif (!omit_values) {\n-\t\tif (show_keys)\n-\t\t\tstrbuf_addch(buf, key_delim);\n+\tif (!opts->omit_values) {\n+\t\tif (opts->show_keys)\n+\t\t\tstrbuf_addch(buf, opts->key_delim);\n \n \t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n@@ -283,18 +304,24 @@ static int format_config(struct strbuf *buf, const char *key_,\n \t\t\tstrbuf_addstr(buf, value_);\n \t\t} else {\n \t\t\t/* Just show the key name; back out delimiter */\n-\t\t\tif (show_keys)\n+\t\t\tif (opts->show_keys)\n \t\t\t\tstrbuf_setlen(buf, buf->len - 1);\n \t\t}\n \t}\n-\tstrbuf_addch(buf, term);\n+\tstrbuf_addch(buf, opts->term);\n \treturn 0;\n }\n \n+struct collect_config_data {\n+\tconst struct config_display_options *display_opts;\n+\tstruct strbuf_list *values;\n+};\n+\n static int collect_config(const char *key_, const char *value_,\n \t\t\t  const struct config_context *ctx, void *cb)\n {\n-\tstruct strbuf_list *values = cb;\n+\tstruct collect_config_data *data = cb;\n+\tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n \tif (!use_key_regexp && strcmp(key_, key))\n@@ -310,14 +337,20 @@ static int collect_config(const char *key_, const char *value_,\n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n \tstrbuf_init(&values->items[values->nr], 0);\n \n-\treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n+\treturn format_config(data->display_opts, &values->items[values->nr++],\n+\t\t\t     key_, value_, kvi);\n }\n \n static int get_value(const struct config_location_options *opts,\n+\t\t     const struct config_display_options *display_opts,\n \t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n+\tstruct collect_config_data data = {\n+\t\t.display_opts = display_opts,\n+\t\t.values = &values,\n+\t};\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -368,7 +401,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t}\n \t}\n \n-\tconfig_with_options(collect_config, &values,\n+\tconfig_with_options(collect_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n@@ -380,7 +413,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n \t\t\t\tdefault_value);\n \t}\n@@ -591,10 +624,12 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n }\n \n static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst struct config_display_options *_display_opts,\n \t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n+\tstruct config_display_options display_opts = *_display_opts;\n \tstruct string_list_item *item;\n \tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n \tstruct string_list values = STRING_LIST_INIT_DUP;\n@@ -611,10 +646,10 @@ static int get_urlmatch(const struct config_location_options *opts,\n \tif (section_tail) {\n \t\t*section_tail = '\\0';\n \t\tconfig.key = section_tail + 1;\n-\t\tshow_keys = 0;\n+\t\tdisplay_opts.show_keys = 0;\n \t} else {\n \t\tconfig.key = NULL;\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n@@ -627,7 +662,7 @@ static int get_urlmatch(const struct config_location_options *opts,\n \t\tstruct urlmatch_current_candidate_value *matched = item->util;\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tformat_config(&buf, item->string,\n+\t\tformat_config(&display_opts, &buf, item->string,\n \t\t\t      matched->value_is_null ? NULL : matched->value.buf,\n \t\t\t      &matched->kvi);\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n@@ -743,11 +778,12 @@ static void location_options_release(struct config_location_options *opts)\n \tfree((char *) opts->source.file);\n }\n \n-static void handle_nul(void) {\n-\tif (end_nul) {\n-\t\tterm = '\\0';\n-\t\tdelim = '\\n';\n-\t\tkey_delim = '\\n';\n+static void display_options_init(struct config_display_options *opts)\n+{\n+\tif (opts->end_nul) {\n+\t\topts->term = '\\0';\n+\t\topts->delim = '\\n';\n+\t\topts->key_delim = '\\n';\n \t}\n }\n \n@@ -761,19 +797,13 @@ static void handle_nul(void) {\n \tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n \tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n \n-#define CONFIG_DISPLAY_OPTIONS \\\n-\tOPT_GROUP(N_(\"Display options\")), \\\n-\tOPT_BOOL('z', \"null\", &end_nul, N_(\"terminate values with NUL byte\")), \\\n-\tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")), \\\n-\tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n-\tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n@@ -783,11 +813,11 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \tcheck_argc(argc, 0, 0);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n-\tif (config_with_options(show_all_config, NULL,\n+\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t&location_opts.options) < 0) {\n \t\tif (location_opts.source.file)\n@@ -804,6 +834,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n@@ -815,8 +846,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n-\t\tOPT_BOOL(0, \"show-names\", &show_keys, N_(\"show config keys in addition to their values\")),\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n@@ -836,14 +866,14 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(&location_opts, argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1074,6 +1104,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1094,7 +1125,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_TYPE_OPTIONS,\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n@@ -1112,7 +1143,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n@@ -1128,13 +1159,13 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"no action specified\"));\n \t\t\texit(129);\n \t\t}\n-\tif (omit_values &&\n+\tif (display_opts.omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n \t\texit(129);\n \t}\n \n-\tif (show_origin && !(actions &\n+\tif (display_opts.show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n@@ -1199,7 +1230,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n-\t\tif (config_with_options(show_all_config, NULL,\n+\t\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t\t&location_opts.options) < 0) {\n \t\t\tif (location_opts.source.file)\n@@ -1248,23 +1279,23 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write(&location_opts.source);\n-- \n2.45.GIT\n\n"},{"id":"494618","messageId":"63436c34165ee6744cfa15aea5c53adab361d07c.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 11/21] builtin/config: move type options into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:22:54Z","receivedAt":"2024-05-13T10:22:59Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The type options are tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 +++++++++++++++++++++++-------------------------\n 1 file changed, 29 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 24ad8e7476..6d2597f7c2 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -81,13 +81,24 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_TYPE_OPTIONS(type) \\\n+\tOPT_GROUP(N_(\"Type\")), \\\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n+\n #define CONFIG_DISPLAY_OPTIONS(opts) \\\n \tOPT_GROUP(N_(\"Display options\")), \\\n \tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n \tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n \tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n-\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\")), \\\n+\tCONFIG_TYPE_OPTIONS(opts.type)\n \n struct config_display_options {\n \tint end_nul;\n@@ -95,6 +106,7 @@ struct config_display_options {\n \tint show_origin;\n \tint show_scope;\n \tint show_keys;\n+\tint type;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -113,8 +125,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-\n-static int type;\n static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n@@ -264,13 +274,13 @@ static int format_config(const struct config_display_options *opts,\n \t\tif (opts->show_keys)\n \t\t\tstrbuf_addch(buf, opts->key_delim);\n \n-\t\tif (type == TYPE_INT)\n+\t\tif (opts->type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\", kvi));\n-\t\telse if (type == TYPE_BOOL)\n+\t\telse if (opts->type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (type == TYPE_BOOL_OR_INT) {\n+\t\telse if (opts->type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, kvi,\n \t\t\t\t\t\t   &is_bool);\n@@ -278,24 +288,24 @@ static int format_config(const struct config_display_options *opts,\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (type == TYPE_BOOL_OR_STR) {\n+\t\t} else if (opts->type == TYPE_BOOL_OR_STR) {\n \t\t\tint v = git_parse_maybe_bool(value_);\n \t\t\tif (v < 0)\n \t\t\t\tstrbuf_addstr(buf, value_);\n \t\t\telse\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n-\t\t} else if (type == TYPE_PATH) {\n+\t\t} else if (opts->type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (type == TYPE_EXPIRY_DATE) {\n+\t\t} else if (opts->type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addf(buf, \"%\"PRItime, t);\n-\t\t} else if (type == TYPE_COLOR) {\n+\t\t} else if (opts->type == TYPE_COLOR) {\n \t\t\tchar v[COLOR_MAXLEN];\n \t\t\tif (git_config_color(v, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -443,7 +453,7 @@ static int get_value(const struct config_location_options *opts,\n }\n \n static char *normalize_value(const char *key, const char *value,\n-\t\t\t     struct key_value_info *kvi)\n+\t\t\t     int type, struct key_value_info *kvi)\n {\n \tif (!value)\n \t\treturn NULL;\n@@ -787,16 +797,6 @@ static void display_options_init(struct config_display_options *opts)\n \t}\n }\n \n-#define CONFIG_TYPE_OPTIONS \\\n-\tOPT_GROUP(N_(\"Type\")), \\\n-\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n-\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n-\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n-\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n@@ -839,7 +839,6 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tint flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n \t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n@@ -884,10 +883,10 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n-\tint flags = 0, append = 0;\n+\tint flags = 0, append = 0, type = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_TYPE_OPTIONS(type),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -917,7 +916,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tcheck_write(&location_opts.source);\n \n-\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\tvalue = normalize_value(argv[0], argv[1], type, &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n@@ -1124,7 +1123,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n@@ -1145,7 +1143,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n \t\texit(129);\n \t}\n@@ -1246,7 +1244,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n@@ -1255,7 +1253,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n@@ -1263,7 +1261,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n@@ -1272,7 +1270,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n-- \n2.45.GIT\n\n"},{"id":"494619","messageId":"106b8ac8a2292b9852c6fda23a796787b6e72ca4.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 12/21] builtin/config: move default value into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:00Z","receivedAt":"2024-05-13T10:23:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The default value is tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 6d2597f7c2..dcccb89037 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -107,6 +107,7 @@ struct config_display_options {\n \tint show_scope;\n \tint show_keys;\n \tint type;\n+\tchar *default_value;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -125,7 +126,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n \n@@ -415,7 +415,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!values.nr && default_value) {\n+\tif (!values.nr && display_opts->default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n \t\tstruct strbuf *item;\n \n@@ -423,9 +423,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_,\n+\t\t\t\t  display_opts->default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n-\t\t\t\tdefault_value);\n+\t\t\t    display_opts->default_value);\n \t}\n \n \tret = !values.nr;\n@@ -848,7 +849,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -859,7 +861,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (default_value && (do_all || url))\n+\tif (display_opts.default_value && (do_all || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n@@ -1125,7 +1127,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -1170,7 +1173,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\texit(129);\n \t}\n \n-\tif (default_value && !(actions & ACTION_GET)) {\n+\tif (display_opts.default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n \t\texit(129);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494620","messageId":"8a6b555b58ef4db5d7fe7e427fb4d6403f228095.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 13/21] builtin/config: move `respect_includes_opt` into location options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:05Z","receivedAt":"2024-05-13T10:23:10Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The variable tracking whether or not we want to honor includes is\ntracked via a global variable. Move it into the location options\ninstead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 ++++++++++++-------\n 1 file changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex dcccb89037..3424d602f2 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -78,8 +78,11 @@ struct config_location_options {\n \tint use_system_config;\n \tint use_local_config;\n \tint use_worktree_config;\n+\tint respect_includes_opt;\n };\n-#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+#define CONFIG_LOCATION_OPTIONS_INIT { \\\n+\t.respect_includes_opt = -1, \\\n+}\n \n #define CONFIG_TYPE_OPTIONS(type) \\\n \tOPT_GROUP(N_(\"Type\")), \\\n@@ -126,7 +129,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static int respect_includes_opt = -1;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -774,10 +776,10 @@ static void location_options_init(struct config_location_options *opts,\n \t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (respect_includes_opt == -1)\n+\tif (opts->respect_includes_opt == -1)\n \t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\topts->options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = opts->respect_includes_opt;\n \tif (startup_info->have_repository) {\n \t\topts->options.commondir = get_git_common_dir();\n \t\topts->options.git_dir = get_git_dir();\n@@ -806,7 +808,8 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \n@@ -848,7 +851,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n@@ -1131,7 +1135,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-- \n2.45.GIT\n\n"},{"id":"494621","messageId":"0dd22bf51a4e16161b3704295b8026b8e32a835b.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 14/21] builtin/config: convert `do_not_match` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:10Z","receivedAt":"2024-05-13T10:23:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `do_not_match` variable is used by the `format_config()` callback as\nan indicator whether or not the passed regular expression is negated. It\nis only ever set up by its only caller, `collect_config()` and can thus\neasily be moved into the `collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3424d602f2..16d92ab176 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -128,7 +128,6 @@ static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n-static int do_not_match;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -327,6 +326,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tint do_not_match;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -343,7 +343,7 @@ static int collect_config(const char *key_, const char *value_,\n \tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n-\t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -400,7 +400,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tvalue_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n-\t\t\tdo_not_match = 1;\n+\t\t\tdata.do_not_match = 1;\n \t\t\tregex_++;\n \t\t}\n \n-- \n2.45.GIT\n\n"},{"id":"494622","messageId":"b656951f0c7bb169169fc8eba77aab101940ab9c.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 15/21] builtin/config: convert `value_pattern` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:15Z","receivedAt":"2024-05-13T10:23:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `value_pattern` variable is used by the `format_config()` callback\nwhen `CONFIG_FLAGS_FIXED_VALUE` is used. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 16d92ab176..a17e274ece 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n@@ -326,6 +325,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tconst char *value_pattern;\n \tint do_not_match;\n };\n \n@@ -340,7 +340,7 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n+\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n \t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -397,7 +397,7 @@ static int get_value(const struct config_location_options *opts,\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-\t\tvalue_pattern = regex_;\n+\t\tdata.value_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdata.do_not_match = 1;\n-- \n2.45.GIT\n\n"},{"id":"494623","messageId":"b56a07bda0be898c2b999c63fd844cc0ee7d420a.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 16/21] builtin/config: convert `regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:20Z","receivedAt":"2024-05-13T10:23:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `regexp` variable is used by the `format_config()` callback when\n`CONFIG_FLAGS_FIXED_VALUE` is not set. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex a17e274ece..1909de22fc 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -326,6 +325,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tregex_t *regexp;\n \tint do_not_match;\n };\n \n@@ -342,8 +342,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n-\tif (regexp != NULL &&\n-\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\tif (data->regexp &&\n+\t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -404,10 +404,10 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tregex_++;\n \t\t}\n \n-\t\tregexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(regexp, regex_, REG_EXTENDED)) {\n+\t\tdata.regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.regexp, regex_, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid pattern: %s\"), regex_);\n-\t\t\tFREE_AND_NULL(regexp);\n+\t\t\tFREE_AND_NULL(data.regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -447,9 +447,9 @@ static int get_value(const struct config_location_options *opts,\n \t\tregfree(key_regexp);\n \t\tfree(key_regexp);\n \t}\n-\tif (regexp) {\n-\t\tregfree(regexp);\n-\t\tfree(regexp);\n+\tif (data.regexp) {\n+\t\tregfree(data.regexp);\n+\t\tfree(data.regexp);\n \t}\n \n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494624","messageId":"323cb051205fdde86eb3e996d0a9fc089780eaee.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 17/21] builtin/config: convert `key_regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:25Z","receivedAt":"2024-05-13T10:23:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key_regexp` variable is used by the `format_config()` callback when\n`use_key_regexp` is set. It is only ever set up by its only caller,\n`collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 1909de22fc..8fc9ee20a3 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -123,7 +123,6 @@ struct config_display_options {\n }\n \n static char *key;\n-static regex_t *key_regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -326,6 +325,7 @@ struct collect_config_data {\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n \tregex_t *regexp;\n+\tregex_t *key_regexp;\n \tint do_not_match;\n };\n \n@@ -338,7 +338,7 @@ static int collect_config(const char *key_, const char *value_,\n \n \tif (!use_key_regexp && strcmp(key_, key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n+\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n@@ -382,10 +382,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tfor (tl = key; *tl && *tl != '.'; tl++)\n \t\t\t*tl = tolower(*tl);\n \n-\t\tkey_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(key_regexp, key, REG_EXTENDED)) {\n+\t\tdata.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.key_regexp, key, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid key pattern: %s\"), key_);\n-\t\t\tFREE_AND_NULL(key_regexp);\n+\t\t\tFREE_AND_NULL(data.key_regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -443,9 +443,9 @@ static int get_value(const struct config_location_options *opts,\n \n free_strings:\n \tfree(key);\n-\tif (key_regexp) {\n-\t\tregfree(key_regexp);\n-\t\tfree(key_regexp);\n+\tif (data.key_regexp) {\n+\t\tregfree(data.key_regexp);\n+\t\tfree(data.key_regexp);\n \t}\n \tif (data.regexp) {\n \t\tregfree(data.regexp);\n-- \n2.45.GIT\n\n"},{"id":"494625","messageId":"e972e63be88b75285eb22be56c2ff48747d78510.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 18/21] builtin/config: convert `key` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:30Z","receivedAt":"2024-05-13T10:23:35Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key` variable is used by the `get_value()` function for two\npurposes:\n\n  - It is used to store the result of `git_config_parse_key()`, which is\n    then passed on to `collect_config()`.\n\n  - It is used as a store to convert the provided key to an\n    all-lowercase key when `use_key_regexp` is set.\n\nNeither of these cases warrant a global variable at all. In the former\ncase we can pass the key via `struct collect_config_data`. And in the\nlatter case we really only want to have it as a temporary local variable\nsuch that we can free associated memory.\n\nRefactor the code accordingly to reduce our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8fc9ee20a3..5737574b25 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -122,7 +122,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static char *key;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -324,6 +323,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tconst char *key;\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n@@ -336,7 +336,7 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, key))\n+\tif (!use_key_regexp && strcmp(key_, data->key))\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n@@ -363,6 +363,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n \t};\n+\tchar *key = NULL;\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -394,6 +395,8 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tret = CONFIG_INVALID_KEY;\n \t\t\tgoto free_strings;\n \t\t}\n+\n+\t\tdata.key = key;\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-- \n2.45.GIT\n\n"},{"id":"494626","messageId":"d83c3d085e4c1dee9501949c296e0ed782ae4436.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 19/21] builtin/config: track \"fixed value\" option via flags only","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:35Z","receivedAt":"2024-05-13T10:23:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We track the \"fixed value\" option via two separate bits: once via the\nglobal variable `fixed_value`, and once via the CONFIG_FLAGS_FIXED_VALUE\nbit in `flags`. This is confusing and may easily lead to issues when one\nis not aware that this is tracked via two separate mechanisms.\n\nRefactor the code to use the flag exclusively. We already pass it to all\nthe required callsites anyway, except for `collect_config()`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5737574b25..2ca23a35d2 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n \n static int use_key_regexp;\n static int do_all;\n-static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n@@ -327,6 +326,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned flags;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -340,7 +340,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n+\tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n+\t    strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (data->regexp &&\n \t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -362,6 +363,7 @@ static int get_value(const struct config_location_options *opts,\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n@@ -1115,6 +1117,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n+\tunsigned flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n@@ -1137,13 +1140,12 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n \t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-\tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n@@ -1193,7 +1195,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \n \t/* check usage of --fixed-value */\n-\tif (fixed_value) {\n+\tif (flags & CONFIG_FLAGS_FIXED_VALUE) {\n \t\tint allowed_usage = 0;\n \n \t\tswitch (actions) {\n@@ -1224,8 +1226,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n \t\t\texit(129);\n \t\t}\n-\n-\t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\n \t}\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n-- \n2.45.GIT\n\n"},{"id":"494627","messageId":"294bcd96a4ec1d27e58a5faf22255eb60d303fc6.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 20/21] builtin/config: convert flags to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:40Z","receivedAt":"2024-05-13T10:23:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Both the `do_all` and `use_key_regexp` bits essentially act like flags\nto `get_value()`. Let's convert them to actual flags so that we can get\nrid of the last two remaining global variables that track options.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 48 +++++++++++++++++++++++++++++-------------------\n 1 file changed, 29 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 2ca23a35d2..2ab7d24b62 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -122,9 +122,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static int use_key_regexp;\n-static int do_all;\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -318,6 +315,9 @@ static int format_config(const struct config_display_options *opts,\n \treturn 0;\n }\n \n+#define GET_VALUE_ALL        (1 << 0)\n+#define GET_VALUE_KEY_REGEXP (1 << 1)\n+\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n@@ -326,6 +326,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned get_value_flags;\n \tunsigned flags;\n };\n \n@@ -336,9 +337,11 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, data->key))\n+\tif (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    strcmp(key_, data->key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n+\tif ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n \t    strcmp(data->value_pattern, (value_?value_:\"\")))\n@@ -356,19 +359,21 @@ static int collect_config(const char *key_, const char *value_,\n \n static int get_value(const struct config_location_options *opts,\n \t\t     const struct config_display_options *display_opts,\n-\t\t     const char *key_, const char *regex_, unsigned flags)\n+\t\t     const char *key_, const char *regex_,\n+\t\t     unsigned get_value_flags, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.get_value_flags = get_value_flags,\n \t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n \n-\tif (use_key_regexp) {\n+\tif (get_value_flags & GET_VALUE_KEY_REGEXP) {\n \t\tchar *tl;\n \n \t\t/*\n@@ -440,7 +445,7 @@ static int get_value(const struct config_location_options *opts,\n \n \tfor (i = 0; i < values.nr; i++) {\n \t\tstruct strbuf *buf = values.items + i;\n-\t\tif (do_all || i == values.nr - 1)\n+\t\tif ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)\n \t\t\tfwrite(buf->buf, 1, buf->len, stdout);\n \t\tstrbuf_release(buf);\n \t}\n@@ -846,11 +851,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n+\tunsigned get_value_flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter options\")),\n-\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n-\t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n+\t\tOPT_BIT(0, \"all\", &get_value_flags, N_(\"return all values for multi-valued config options\"), GET_VALUE_ALL),\n+\t\tOPT_BIT(0, \"regexp\", &get_value_flags, N_(\"interpret the name as a regular expression\"), GET_VALUE_KEY_REGEXP),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n@@ -870,9 +876,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (display_opts.default_value && (do_all || url))\n+\tif (display_opts.default_value &&\n+\t    ((get_value_flags & GET_VALUE_ALL) || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n-\tif (url && (do_all || use_key_regexp || value_pattern))\n+\tif (url && ((get_value_flags & GET_VALUE_ALL) ||\n+\t\t    (get_value_flags & GET_VALUE_KEY_REGEXP) ||\n+\t\t    value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n@@ -883,7 +892,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n+\t\t\t\tget_value_flags, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1288,19 +1298,19 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\t0, flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL, flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tdisplay_opts.show_keys = 1;\n-\t\tuse_key_regexp = 1;\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-- \n2.45.GIT\n\n"},{"id":"494628","messageId":"0496b958e24a4d0191821d3f6c8c0d2028f36452.1715595550.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"[PATCH v2 21/21] builtin/config: pass data between callbacks via local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-13T10:23:45Z","receivedAt":"2024-05-13T10:23:50Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We use several global variables to pass data between callers and\ncallbacks in `get_color()` and `get_colorbool()`. Convert those to use\ncallback data structures instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 90 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 52 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 2ab7d24b62..945a1fd82d 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -517,21 +517,24 @@ static char *normalize_value(const char *key, const char *value,\n \tBUG(\"cannot normalize type %d\", type);\n }\n \n-static int get_color_found;\n-static const char *get_color_slot;\n-static const char *get_colorbool_slot;\n-static char parsed_color[COLOR_MAXLEN];\n+struct get_color_config_data {\n+\tint get_color_found;\n+\tconst char *get_color_slot;\n+\tchar parsed_color[COLOR_MAXLEN];\n+};\n \n static int git_get_color_config(const char *var, const char *value,\n \t\t\t\tconst struct config_context *ctx UNUSED,\n-\t\t\t\tvoid *cb UNUSED)\n+\t\t\t\tvoid *cb)\n {\n-\tif (!strcmp(var, get_color_slot)) {\n+\tstruct get_color_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_color_slot)) {\n \t\tif (!value)\n \t\t\tconfig_error_nonbool(var);\n-\t\tif (color_parse(value, parsed_color) < 0)\n+\t\tif (color_parse(value, data->parsed_color) < 0)\n \t\t\treturn -1;\n-\t\tget_color_found = 1;\n+\t\tdata->get_color_found = 1;\n \t}\n \treturn 0;\n }\n@@ -539,66 +542,77 @@ static int git_get_color_config(const char *var, const char *value,\n static void get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n-\tget_color_slot = var;\n-\tget_color_found = 0;\n-\tparsed_color[0] = '\\0';\n-\tconfig_with_options(git_get_color_config, NULL,\n+\tstruct get_color_config_data data = {\n+\t\t.get_color_slot = var,\n+\t\t.parsed_color[0] = '\\0',\n+\t};\n+\n+\tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!get_color_found && def_color) {\n-\t\tif (color_parse(def_color, parsed_color) < 0)\n+\tif (!data.get_color_found && def_color) {\n+\t\tif (color_parse(def_color, data.parsed_color) < 0)\n \t\t\tdie(_(\"unable to parse default color value\"));\n \t}\n \n-\tfputs(parsed_color, stdout);\n+\tfputs(data.parsed_color, stdout);\n }\n \n-static int get_colorbool_found;\n-static int get_diff_color_found;\n-static int get_color_ui_found;\n+struct get_colorbool_config_data {\n+\tint get_colorbool_found;\n+\tint get_diff_color_found;\n+\tint get_color_ui_found;\n+\tconst char *get_colorbool_slot;\n+};\n+\n static int git_get_colorbool_config(const char *var, const char *value,\n \t\t\t\t    const struct config_context *ctx UNUSED,\n-\t\t\t\t    void *data UNUSED)\n+\t\t\t\t    void *cb)\n {\n-\tif (!strcmp(var, get_colorbool_slot))\n-\t\tget_colorbool_found = git_config_colorbool(var, value);\n+\tstruct get_colorbool_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_colorbool_slot))\n+\t\tdata->get_colorbool_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"diff.color\"))\n-\t\tget_diff_color_found = git_config_colorbool(var, value);\n+\t\tdata->get_diff_color_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"color.ui\"))\n-\t\tget_color_ui_found = git_config_colorbool(var, value);\n+\t\tdata->get_color_ui_found = git_config_colorbool(var, value);\n \treturn 0;\n }\n \n static int get_colorbool(const struct config_location_options *opts,\n \t\t\t const char *var, int print)\n {\n-\tget_colorbool_slot = var;\n-\tget_colorbool_found = -1;\n-\tget_diff_color_found = -1;\n-\tget_color_ui_found = -1;\n-\tconfig_with_options(git_get_colorbool_config, NULL,\n+\tstruct get_colorbool_config_data data = {\n+\t\t.get_colorbool_slot = var,\n+\t\t.get_colorbool_found = -1,\n+\t\t.get_diff_color_found = -1,\n+\t\t.get_color_ui_found = -1,\n+\t};\n+\n+\tconfig_with_options(git_get_colorbool_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (get_colorbool_found < 0) {\n-\t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n-\t\t\tget_colorbool_found = get_diff_color_found;\n-\t\tif (get_colorbool_found < 0)\n-\t\t\tget_colorbool_found = get_color_ui_found;\n+\tif (data.get_colorbool_found < 0) {\n+\t\tif (!strcmp(data.get_colorbool_slot, \"color.diff\"))\n+\t\t\tdata.get_colorbool_found = data.get_diff_color_found;\n+\t\tif (data.get_colorbool_found < 0)\n+\t\t\tdata.get_colorbool_found = data.get_color_ui_found;\n \t}\n \n-\tif (get_colorbool_found < 0)\n+\tif (data.get_colorbool_found < 0)\n \t\t/* default value if none found in config */\n-\t\tget_colorbool_found = GIT_COLOR_AUTO;\n+\t\tdata.get_colorbool_found = GIT_COLOR_AUTO;\n \n-\tget_colorbool_found = want_color(get_colorbool_found);\n+\tdata.get_colorbool_found = want_color(data.get_colorbool_found);\n \n \tif (print) {\n-\t\tprintf(\"%s\\n\", get_colorbool_found ? \"true\" : \"false\");\n+\t\tprintf(\"%s\\n\", data.get_colorbool_found ? \"true\" : \"false\");\n \t\treturn 0;\n \t} else\n-\t\treturn get_colorbool_found ? 0 : 1;\n+\t\treturn data.get_colorbool_found ? 0 : 1;\n }\n \n static void check_write(const struct git_config_source *source)\n-- \n2.45.GIT\n\n"},{"id":"494670","messageId":"CAO_smVijhZYjuZA+Q_1-XNLDC5AkFVf1SMwFyE3f6UCTrrmgZg@mail.gmail.com","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"Re: [PATCH v2 00/21] builtin/config: remove global state","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-05-13T21:57:22Z","receivedAt":"2024-05-13T21:57:41Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Mon, May 13, 2024 at 3:22 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> Hi,\n>\n> this is the second version of my patch series that removes global state\n> from \"builtin/config.c\". Changes compared to v1:\n>\n>   - Reinstated a comment in patch 5.\n>\n>   - Fixed a memory leak in patch 9.\n>\n>   - A couple of commit message fixes.\n>\n> The series continues to build on top of ps/config-subcommands.\n>\n> Thanks!\n>\n> Patrick\n>\n> Patrick Steinhardt (21):\n>   builtin/config: stop printing full usage on misuse\n>   builtin/config: move legacy mode into its own function\n>   builtin/config: move subcommand options into `cmd_config()`\n>   builtin/config: move legacy options into `cmd_config()`\n>   builtin/config: move actions into `cmd_config_actions()`\n>   builtin/config: check for writeability after source is set up\n>   config: make the config source const\n>   builtin/config: refactor functions to have common exit paths\n>   builtin/config: move location options into local variables\n>   builtin/config: move display options into local variables\n>   builtin/config: move type options into display options\n>   builtin/config: move default value into display options\n>   builtin/config: move `respect_includes_opt` into location options\n>   builtin/config: convert `do_not_match` to a local variable\n>   builtin/config: convert `value_pattern` to a local variable\n>   builtin/config: convert `regexp` to a local variable\n>   builtin/config: convert `key_regexp` to a local variable\n>   builtin/config: convert `key` to a local variable\n>   builtin/config: track \"fixed value\" option via flags only\n>   builtin/config: convert flags to a local variable\n>   builtin/config: pass data between callbacks via local variables\n>\n>  builtin/config.c  | 968 ++++++++++++++++++++++++++--------------------\n>  config.c          |   4 +-\n>  config.h          |   2 +-\n>  t/t1300-config.sh |   9 +-\n>  4 files changed, 550 insertions(+), 433 deletions(-)\n>\n> Range-diff against v1:\n\nLooks good to me\n\n>  1:  0ba7628126 =  1:  0ba7628126 builtin/config: stop printing full usage on misuse\n>  2:  663e1f74f8 =  2:  663e1f74f8 builtin/config: move legacy mode into its own function\n>  3:  1239c151d0 =  3:  1239c151d0 builtin/config: move subcommand options into `cmd_config()`\n>  4:  82964510c5 =  4:  82964510c5 builtin/config: move legacy options into `cmd_config()`\n>  5:  2e308393ed !  5:  0a6ecae2cc builtin/config: move actions into `cmd_config_actions()`\n>     @@ builtin/config.c: static int cmd_config_actions(int argc, const char **argv, con\n>         comment = git_config_prepare_comment_string(comment_arg);\n>\n>      -  if (actions & PAGING_ACTIONS)\n>     ++  /*\n>     ++   * The following actions may produce more than one line of output and\n>     ++   * should therefore be paged.\n>     ++   */\n>      +  if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n>                 setup_auto_pager(\"config\", 1);\n>\n>  6:  edfd7caa39 =  6:  7ab99a27c1 builtin/config: check for writeability after source is set up\n>  7:  bfba68aa1e =  7:  1460d3a36c config: make the config source const\n>  8:  6bff0410e9 =  8:  018ed0226b builtin/config: refactor functions to have common exit paths\n>  9:  a96c122280 !  9:  b5d43b6f85 builtin/config: move location options into local variables\n>     @@ builtin/config.c: static char *default_user_config(void)\n>      -          given_config_source.file = git_global_config();\n>      -          if (!given_config_source.file)\n>      +  if (opts->use_global_config) {\n>     -+          opts->source.file = xstrdup_or_null(git_global_config());\n>     ++          opts->source.file = git_global_config();\n>      +          if (!opts->source.file)\n>                         /*\n>                          * It is unknown if HOME/.gitconfig exists, so\n> 10:  06c1e08fc4 = 10:  d66e14af30 builtin/config: move display options into local variables\n> 11:  9610897662 = 11:  63436c3416 builtin/config: move type options into display options\n> 12:  eb79462861 = 12:  106b8ac8a2 builtin/config: move default value into display options\n> 13:  b9ebfbd667 = 13:  8a6b555b58 builtin/config: move `respect_includes_opt` into location options\n> 14:  2b40b784fe ! 14:  0dd22bf51a builtin/config: convert `do_not_match` to a local variable\n>     @@ Commit message\n>          builtin/config: convert `do_not_match` to a local variable\n>\n>          The `do_not_match` variable is used by the `format_config()` callback as\n>     -    an indicator whteher or not the passed regular expression is negated. It\n>     +    an indicator whether or not the passed regular expression is negated. It\n>          is only ever set up by its only caller, `collect_config()` and can thus\n>          easily be moved into the `collect_config_data` structure.\n>\n> 15:  71d1b7a51b = 15:  b656951f0c builtin/config: convert `value_pattern` to a local variable\n> 16:  c3b340f119 = 16:  b56a07bda0 builtin/config: convert `regexp` to a local variable\n> 17:  835cc0acfb = 17:  323cb05120 builtin/config: convert `key_regexp` to a local variable\n> 18:  2aee7ec5d8 ! 18:  e972e63be8 builtin/config: convert `key` to a local variable\n>     @@ Commit message\n>          The `key` variable is used by the `get_value()` function for two\n>          purposes:\n>\n>     -      - It is used to store the result of `git_config_parse_key()`, which si\n>     +      - It is used to store the result of `git_config_parse_key()`, which is\n>              then passed on to `collect_config()`.\n>\n>            - It is used as a store to convert the provided key to an\n>              all-lowercase key when `use_key_regexp` is set.\n>\n>     -    Both of these cases don't warrant a global variable at all. In the\n>     -    former case we can pass the key via `struct collect_config_data`. And in\n>     -    the latter case we really only want to have it as a temporary local\n>     -    variable such that we can free associated memory.\n>     +    Neither of these cases warrant a global variable at all. In the former\n>     +    case we can pass the key via `struct collect_config_data`. And in the\n>     +    latter case we really only want to have it as a temporary local variable\n>     +    such that we can free associated memory.\n>\n>          Refactor the code accordingly to reduce our reliance on global state.\n>\n> 19:  625216a774 ! 19:  d83c3d085e builtin/config: track \"fixed value\" option via flags only\n>     @@ Commit message\n>          is not aware that this is tracked via two separate mechanisms.\n>\n>          Refactor the code to use the flag exclusively. We already pass it to all\n>     -    the require callsites anyway, except for `collect_config()`.\n>     +    the required callsites anyway, except for `collect_config()`.\n>\n>          Signed-off-by: Patrick Steinhardt <ps@pks.im>\n>\n> 20:  05254d512b = 20:  294bcd96a4 builtin/config: convert flags to a local variable\n> 21:  3a5f059789 = 21:  0496b958e2 builtin/config: pass data between callbacks via local variables\n> --\n> 2.45.GIT\n>\n"},{"id":"494717","messageId":"xmqqzfss78kw.fsf@gitster.g","threadId":"61452","inReplyTo":"cover.1715595550.git.ps@pks.im","subject":"Re: [PATCH v2 00/21] builtin/config: remove global state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-14T14:48:47Z","receivedAt":"2024-05-14T14:48:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> this is the second version of my patch series that removes global state\n> from \"builtin/config.c\". Changes compared to v1:\n>\n>   - Reinstated a comment in patch 5.\n>\n>   - Fixed a memory leak in patch 9.\n>\n>   - A couple of commit message fixes.\n>\n> The series continues to build on top of ps/config-subcommands.\n\nI do not offhand know if this iteration has already been seen by me,\nbut a few recent CI runs of 'seen' did break with *-leaks jobs in\nt13XX series around \"config\".  Hopefully with the fix in \"patch 9\"\nlisted above the problem has gone away?  We'll know soon enough when\nI push out the integration result.\n\nThanks.\n"},{"id":"494718","messageId":"ZkN6s1OYfJOpy7x3@tanuki","threadId":"61452","inReplyTo":"xmqqzfss78kw.fsf@gitster.g","subject":"Re: [PATCH v2 00/21] builtin/config: remove global state","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-14T14:52:35Z","receivedAt":"2024-05-14T14:52:41Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 14, 2024 at 07:48:47AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > this is the second version of my patch series that removes global state\n> > from \"builtin/config.c\". Changes compared to v1:\n> >\n> >   - Reinstated a comment in patch 5.\n> >\n> >   - Fixed a memory leak in patch 9.\n> >\n> >   - A couple of commit message fixes.\n> >\n> > The series continues to build on top of ps/config-subcommands.\n> \n> I do not offhand know if this iteration has already been seen by me,\n> but a few recent CI runs of 'seen' did break with *-leaks jobs in\n> t13XX series around \"config\".  Hopefully with the fix in \"patch 9\"\n> listed above the problem has gone away?  We'll know soon enough when\n> I push out the integration result.\n\nI think so, yes. v1 also broke pipelines at GitLab -- I didn't notice\nthough because pipelines had already been broken due to the Python 2\ndeprecation in Ubuntu 24.04, so I missed that there were in fact\nmultiple issues. The GitLab pipeline now passes with v2, so I assume\nthat it would also pass in GitHub now.\n\nPatrick\n"},{"id":"494750","messageId":"ZkPbZGPq6xFrhngN@nand.local","threadId":"61452","inReplyTo":"7ab99a27c16718ad4dff1f7862e80c52b48c3812.1715595550.git.ps@pks.im","subject":"Re: [PATCH v2 06/21] builtin/config: check for writeability after source is set up","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2024-05-14T21:45:08Z","receivedAt":"2024-05-14T21:45:10Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Mon, May 13, 2024 at 12:22:28PM +0200, Patrick Steinhardt wrote:\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 0842e4f198..9866d1a055 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n>\n>  \targc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n>  \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n> -\tcheck_write();\n>  \tcheck_argc(argc, 2, 2);\n>\n>  \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n> @@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n>  \tcomment = git_config_prepare_comment_string(comment_arg);\n>\n>  \thandle_config_location(prefix);\n> +\tcheck_write();\n>\n>  \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n\nNice catch.\n\nI thought about suggesting that check_write() could be inlined into\nhandle_config_location(). But that is not a good idea, since we only\ncare about calling check_write() when we are actually going to write\nsomething.\n\nIn the spots outside of cmd_config_actions() where we explicitly call\ncheck_write(), we do so because we know we're about to write something\n(e.g., from cmd_config_set(), cmd_config_unset(), etc.).\n\nBut in the classic mode we only want to call check_write() when the\naction selected tells us that we're going to write something.\n\nI do wonder if we could set some \"initialized\" bit on the\ngiven_config_source variable so that it is a BUG() to call check_write()\nbefore it is set.\n\nThanks,\nTaylor\n"},{"id":"494758","messageId":"ZkRN8nNWiPdpflsB@tanuki","threadId":"61452","inReplyTo":"ZkN6s1OYfJOpy7x3@tanuki","subject":"Re: [PATCH v2 00/21] builtin/config: remove global state","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T05:53:54Z","receivedAt":"2024-05-15T05:54:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 14, 2024 at 04:52:35PM +0200, Patrick Steinhardt wrote:\n> On Tue, May 14, 2024 at 07:48:47AM -0700, Junio C Hamano wrote:\n> > Patrick Steinhardt <ps@pks.im> writes:\n> > \n> > > this is the second version of my patch series that removes global state\n> > > from \"builtin/config.c\". Changes compared to v1:\n> > >\n> > >   - Reinstated a comment in patch 5.\n> > >\n> > >   - Fixed a memory leak in patch 9.\n> > >\n> > >   - A couple of commit message fixes.\n> > >\n> > > The series continues to build on top of ps/config-subcommands.\n> > \n> > I do not offhand know if this iteration has already been seen by me,\n> > but a few recent CI runs of 'seen' did break with *-leaks jobs in\n> > t13XX series around \"config\".  Hopefully with the fix in \"patch 9\"\n> > listed above the problem has gone away?  We'll know soon enough when\n> > I push out the integration result.\n> \n> I think so, yes. v1 also broke pipelines at GitLab -- I didn't notice\n> though because pipelines had already been broken due to the Python 2\n> deprecation in Ubuntu 24.04, so I missed that there were in fact\n> multiple issues. The GitLab pipeline now passes with v2, so I assume\n> that it would also pass in GitHub now.\n> \n> Patrick\n\nOh, there is a second memory leak that I missed. *sigh* I'll do a better\njob at that next time and send a v3 that fixes it.\n\nPatrick\n"},{"id":"494759","messageId":"ZkRPFv4xSr26QkWa@tanuki","threadId":"61452","inReplyTo":"ZkPbZGPq6xFrhngN@nand.local","subject":"Re: [PATCH v2 06/21] builtin/config: check for writeability after source is set up","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T05:58:46Z","receivedAt":"2024-05-15T05:58:50Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 14, 2024 at 05:45:08PM -0400, Taylor Blau wrote:\n> On Mon, May 13, 2024 at 12:22:28PM +0200, Patrick Steinhardt wrote:\n> > diff --git a/builtin/config.c b/builtin/config.c\n> > index 0842e4f198..9866d1a055 100644\n> > --- a/builtin/config.c\n> > +++ b/builtin/config.c\n> > @@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n> >\n> >  \targc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n> >  \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n> > -\tcheck_write();\n> >  \tcheck_argc(argc, 2, 2);\n> >\n> >  \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n> > @@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n> >  \tcomment = git_config_prepare_comment_string(comment_arg);\n> >\n> >  \thandle_config_location(prefix);\n> > +\tcheck_write();\n> >\n> >  \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n> \n> Nice catch.\n> \n> I thought about suggesting that check_write() could be inlined into\n> handle_config_location(). But that is not a good idea, since we only\n> care about calling check_write() when we are actually going to write\n> something.\n> \n> In the spots outside of cmd_config_actions() where we explicitly call\n> check_write(), we do so because we know we're about to write something\n> (e.g., from cmd_config_set(), cmd_config_unset(), etc.).\n> \n> But in the classic mode we only want to call check_write() when the\n> action selected tells us that we're going to write something.\n\nYeah, I was also wondering whether we want to refactor this, e.g. by\npassing in an additional parameter to `handle_config_location()` that\ntells it whether we want to read or write. But as you noted, this would\nbe trivial for the new subcommand modes, but harder for the acton mode.\nSo I refrained from doing that.\n\n> I do wonder if we could set some \"initialized\" bit on the\n> given_config_source variable so that it is a BUG() to call check_write()\n> before it is set.\n\nWe could do that, but with the subsequent patch I think it's not as\nimportant anymore. The main problem here is that it was not obvious at\nall that `check_write()` and `handle_config_location()` have anything to\ndo with each other because they both accessed global state. With the\nnext patch we make that dependency explicit by accepting it as a param,\nand with that it becomes clearer that `check_write()` depends on a\nproperly initialized variable.\n\nDoes that work for you?\n\nPatrick\n"},{"id":"494765","messageId":"cover.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715339393.git.ps@pks.im","subject":"[PATCH v3 00/21] builtin/config: remove global state","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:33Z","receivedAt":"2024-05-15T06:41:39Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis is the third version of my patch series that removes global state\nfrom \"builtin/config.c\".\n\nThere is only a single change compared to v2, namely another set of\nmemory leak fixes for patch 9. I have double and triple checked now that\nthis does address all the leaks, and all the CI leak jobs (well, all\njobs in fact [1]) do pass now. Sorry for the noise and not doing this\nproperly in v2!\n\n[1]: https://gitlab.com/gitlab-org/git/-/pipelines/1291267388\n\nPatrick\n\nPatrick Steinhardt (21):\n  builtin/config: stop printing full usage on misuse\n  builtin/config: move legacy mode into its own function\n  builtin/config: move subcommand options into `cmd_config()`\n  builtin/config: move legacy options into `cmd_config()`\n  builtin/config: move actions into `cmd_config_actions()`\n  builtin/config: check for writeability after source is set up\n  config: make the config source const\n  builtin/config: refactor functions to have common exit paths\n  builtin/config: move location options into local variables\n  builtin/config: move display options into local variables\n  builtin/config: move type options into display options\n  builtin/config: move default value into display options\n  builtin/config: move `respect_includes_opt` into location options\n  builtin/config: convert `do_not_match` to a local variable\n  builtin/config: convert `value_pattern` to a local variable\n  builtin/config: convert `regexp` to a local variable\n  builtin/config: convert `key_regexp` to a local variable\n  builtin/config: convert `key` to a local variable\n  builtin/config: track \"fixed value\" option via flags only\n  builtin/config: convert flags to a local variable\n  builtin/config: pass data between callbacks via local variables\n\n builtin/config.c  | 970 ++++++++++++++++++++++++++--------------------\n config.c          |   4 +-\n config.h          |   2 +-\n t/t1300-config.sh |   9 +-\n 4 files changed, 552 insertions(+), 433 deletions(-)\n\nRange-diff against v2:\n 1:  0ba7628126 =  1:  32380a12fd builtin/config: stop printing full usage on misuse\n 2:  663e1f74f8 =  2:  8b07b280a9 builtin/config: move legacy mode into its own function\n 3:  1239c151d0 =  3:  b1de0ff74d builtin/config: move subcommand options into `cmd_config()`\n 4:  82964510c5 =  4:  1a0c6a8384 builtin/config: move legacy options into `cmd_config()`\n 5:  0a6ecae2cc =  5:  4950ddcecd builtin/config: move actions into `cmd_config_actions()`\n 6:  7ab99a27c1 =  6:  6de9097fb2 builtin/config: check for writeability after source is set up\n 7:  1460d3a36c =  7:  24053f8f4f config: make the config source const\n 8:  018ed0226b =  8:  9dc1d00f71 builtin/config: refactor functions to have common exit paths\n 9:  b5d43b6f85 !  9:  2ede4a204b builtin/config: move location options into local variables\n    @@ builtin/config.c: static const char *const builtin_config_edit_usage[] = {\n     +struct config_location_options {\n     +\tstruct git_config_source source;\n     +\tstruct config_options options;\n    ++\tchar *file_to_free;\n     +\tint use_global_config;\n     +\tint use_system_config;\n     +\tint use_local_config;\n    @@ builtin/config.c: static char *default_user_config(void)\n     -\t    use_worktree_config +\n     -\t    !!given_config_source.file + !!given_config_source.blob > 1) {\n     +\tif (!opts->source.file)\n    -+\t\topts->source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n    -+\telse\n    -+\t\topts->source.file = xstrdup(opts->source.file);\n    ++\t\topts->source.file = opts->file_to_free =\n    ++\t\t\txstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n     +\n     +\tif (opts->use_global_config + opts->use_system_config +\n     +\t    opts->use_local_config + opts->use_worktree_config +\n    @@ builtin/config.c: static char *default_user_config(void)\n     -\t\tgiven_config_source.file = git_global_config();\n     -\t\tif (!given_config_source.file)\n     +\tif (opts->use_global_config) {\n    -+\t\topts->source.file = git_global_config();\n    ++\t\topts->source.file = opts->file_to_free = git_global_config();\n     +\t\tif (!opts->source.file)\n      \t\t\t/*\n      \t\t\t * It is unknown if HOME/.gitconfig exists, so\n    @@ builtin/config.c: static void handle_config_location(const char *prefix)\n     -\t} else if (use_worktree_config) {\n     +\t\topts->source.scope = CONFIG_SCOPE_GLOBAL;\n     +\t} else if (opts->use_system_config) {\n    -+\t\topts->source.file = xstrdup_or_null(git_system_config());\n    ++\t\topts->source.file = opts->file_to_free = git_system_config();\n     +\t\topts->source.scope = CONFIG_SCOPE_SYSTEM;\n     +\t} else if (opts->use_local_config) {\n    -+\t\topts->source.file = xstrdup_or_null(git_pathdup(\"config\"));\n    ++\t\topts->source.file = opts->file_to_free = git_pathdup(\"config\");\n     +\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n     +\t} else if (opts->use_worktree_config) {\n      \t\tstruct worktree **worktrees = get_worktrees();\n      \t\tif (the_repository->repository_format_worktree_config)\n     -\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n    -+\t\t\topts->source.file = git_pathdup(\"config.worktree\");\n    ++\t\t\topts->source.file = opts->file_to_free =\n    ++\t\t\t\tgit_pathdup(\"config.worktree\");\n      \t\telse if (worktrees[0] && worktrees[1])\n      \t\t\tdie(_(\"--worktree cannot be used with multiple \"\n      \t\t\t      \"working trees unless the config\\n\"\n    @@ builtin/config.c: static void handle_config_location(const char *prefix)\n      \t\telse\n     -\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n     -\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n    -+\t\t\topts->source.file = git_pathdup(\"config\");\n    ++\t\t\topts->source.file = opts->file_to_free =\n    ++\t\t\t\tgit_pathdup(\"config\");\n     +\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n      \t\tfree_worktrees(worktrees);\n     -\t} else if (given_config_source.file) {\n    @@ builtin/config.c: static void handle_config_location(const char *prefix)\n     -\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n     +\t} else if (opts->source.file) {\n     +\t\tif (!is_absolute_path(opts->source.file) && prefix)\n    -+\t\t\topts->source.file =\n    ++\t\t\topts->source.file = opts->file_to_free =\n     +\t\t\t\tprefix_filename(prefix, opts->source.file);\n     +\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n     +\t} else if (opts->source.blob) {\n    @@ builtin/config.c: static void handle_config_location(const char *prefix)\n      \n     +static void location_options_release(struct config_location_options *opts)\n     +{\n    -+\tfree((char *) opts->source.file);\n    ++\tfree(opts->file_to_free);\n     +}\n     +\n      static void handle_nul(void) {\n10:  d66e14af30 ! 10:  4d157942e6 builtin/config: move display options into local variables\n    @@ builtin/config.c: static int get_urlmatch(const struct config_location_options *\n      \t\t\t      &matched->kvi);\n      \t\tfwrite(buf.buf, 1, buf.len, stdout);\n     @@ builtin/config.c: static void location_options_release(struct config_location_options *opts)\n    - \tfree((char *) opts->source.file);\n    + \tfree(opts->file_to_free);\n      }\n      \n     -static void handle_nul(void) {\n11:  63436c3416 = 11:  5579371ad1 builtin/config: move type options into display options\n12:  106b8ac8a2 = 12:  05a072d5d1 builtin/config: move default value into display options\n13:  8a6b555b58 = 13:  15d45ef7d4 builtin/config: move `respect_includes_opt` into location options\n14:  0dd22bf51a = 14:  a729286cc5 builtin/config: convert `do_not_match` to a local variable\n15:  b656951f0c = 15:  821bc68212 builtin/config: convert `value_pattern` to a local variable\n16:  b56a07bda0 = 16:  bac242caf0 builtin/config: convert `regexp` to a local variable\n17:  323cb05120 = 17:  746bdf8733 builtin/config: convert `key_regexp` to a local variable\n18:  e972e63be8 = 18:  f1f390f499 builtin/config: convert `key` to a local variable\n19:  d83c3d085e = 19:  e4dbb4707e builtin/config: track \"fixed value\" option via flags only\n20:  294bcd96a4 = 20:  abe33015b7 builtin/config: convert flags to a local variable\n21:  0496b958e2 = 21:  a5cb075fcd builtin/config: pass data between callbacks via local variables\n-- \n2.45.GIT\n\n"},{"id":"494766","messageId":"32380a12fd8dff2b700d3076e5f5a5441d573405.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 01/21] builtin/config: stop printing full usage on misuse","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:38Z","receivedAt":"2024-05-15T06:41:42Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When invoking git-config(1) with a wrong set of arguments we end up\ncalling `usage_builtin_config()` after printing an error message that\nsays what was wrong. As that function ends up printing the full list of\noptions, which is quite long, the actual error message will be buried by\na wall of text. This makes it really hard to figure out what exactly\ncaused the error.\n\nFurthermore, now that we have recently introduced subcommands, the usage\ninformation may actually be misleading as we unconditionally print\noptions of the subcommand-less mode.\n\nFix both of these issues by just not printing the options at all\nanymore. Instead, we call `usage()` that makes us report in a single\nline what has gone wrong. This should be way more discoverable for our\nusers and addresses the inconsistency.\n\nFurthermore, this change allow us to inline the options into the\nrespective functions that use them to parse the command line.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 28 +++++++++++-----------------\n t/t1300-config.sh |  3 ++-\n 2 files changed, 13 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 80aa9d8a66..3a71d3253f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,8 +125,6 @@ static const char *comment_arg;\n \t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n \tPARSE_OPT_NONEG, option_parse_type, (i) }\n \n-static NORETURN void usage_builtin_config(void);\n-\n static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n@@ -171,7 +169,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t * --type=int'.\n \t\t */\n \t\terror(_(\"only one type at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \t*to_type = new_type;\n \n@@ -187,7 +185,7 @@ static void check_argc(int argc, int min, int max)\n \telse\n \t\terror(_(\"wrong number of arguments, should be from %d to %d\"),\n \t\t      min, max);\n-\tusage_builtin_config();\n+\texit(129);\n }\n \n static void show_config_origin(const struct key_value_info *kvi,\n@@ -672,7 +670,7 @@ static void handle_config_location(const char *prefix)\n \t    use_worktree_config +\n \t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n@@ -802,11 +800,6 @@ static struct option builtin_config_options[] = {\n \tOPT_END(),\n };\n \n-static NORETURN void usage_builtin_config(void)\n-{\n-\tusage_with_options(builtin_config_usage, builtin_config_options);\n-}\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1110,7 +1103,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (actions == 0)\n@@ -1119,30 +1112,31 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcase 2: actions = ACTION_SET; break;\n \t\tcase 3: actions = ACTION_SET_ALL; break;\n \t\tdefault:\n-\t\t\tusage_builtin_config();\n+\t\t\terror(_(\"no action specified\"));\n+\t\t\texit(129);\n \t\t}\n \tif (omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \tif (comment_arg &&\n \t    !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {\n \t\terror(_(\"--comment is only applicable to add/set/replace operations\"));\n-\t\tusage_builtin_config();\n+\t\texit(129);\n \t}\n \n \t/* check usage of --fixed-value */\n@@ -1175,7 +1169,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\tif (!allowed_usage) {\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\t\t\tusage_builtin_config();\n+\t\t\texit(129);\n \t\t}\n \n \t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f3c4d28e06..d90a69b29f 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -596,7 +596,8 @@ test_expect_success 'get bool variable with empty value' '\n \n test_expect_success 'no arguments, but no crash' '\n \ttest_must_fail git config >output 2>&1 &&\n-\ttest_grep usage output\n+\techo \"error: no action specified\" >expect &&\n+\ttest_cmp expect output\n '\n \n cat > .git/config << EOF\n-- \n2.45.GIT\n\n"},{"id":"494767","messageId":"8b07b280a9a8cdadbffb649d4cadc8750ed797ba.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 02/21] builtin/config: move legacy mode into its own function","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:43Z","receivedAt":"2024-05-15T06:41:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In `cmd_config()` we first try to parse the provided arguments as\nsubcommands and, if this is successful, call the respective functions\nof that subcommand. Otherwise we continue with the \"legacy\" mode that\nuses implicit actions and/or flags.\n\nDisentangle this by moving the legacy mode into its own function. This\nallows us to move the options into the respective functions and clearly\nseparates concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 43 ++++++++++++++++++++++++-------------------\n 1 file changed, 24 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3a71d3253f..f6c7e7a082 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -1069,31 +1069,13 @@ static struct option builtin_subcommand_options[] = {\n \tOPT_END(),\n };\n \n-int cmd_config(int argc, const char **argv, const char *prefix)\n+static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n-\t/*\n-\t * This is somewhat hacky: we first parse the command line while\n-\t * keeping all args intact in order to determine whether a subcommand\n-\t * has been specified. If so, we re-parse it a second time, but this\n-\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n-\t * line in case no subcommand was given, which would otherwise confuse\n-\t * us when parsing the legacy-style modes that don't use subcommands.\n-\t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n-\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\t\treturn subcommand(argc, argv, prefix);\n-\t}\n-\n \targc = parse_options(argc, argv, prefix, builtin_config_options,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -1306,3 +1288,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tfree(value);\n \treturn ret;\n }\n+\n+int cmd_config(int argc, const char **argv, const char *prefix)\n+{\n+\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\n+\t/*\n+\t * This is somewhat hacky: we first parse the command line while\n+\t * keeping all args intact in order to determine whether a subcommand\n+\t * has been specified. If so, we re-parse it a second time, but this\n+\t * time we drop KEEP_ARGV0. This is so that we don't munge the command\n+\t * line in case no subcommand was given, which would otherwise confuse\n+\t * us when parsing the legacy-style modes that don't use subcommands.\n+\t */\n+\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\tif (subcommand) {\n+\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\t\treturn subcommand(argc, argv, prefix);\n+\t}\n+\n+\treturn cmd_config_actions(argc, argv, prefix);\n+}\n-- \n2.45.GIT\n\n"},{"id":"494768","messageId":"b1de0ff74d02bb01409bb97068b34fc1c11ccbea.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 03/21] builtin/config: move subcommand options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:47Z","receivedAt":"2024-05-15T06:41:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the subcommand options as well as the `subcommand` variable into\n`cmd_config()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f6c7e7a082..58c8b0abda 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -75,7 +75,6 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static parse_opt_subcommand_fn *subcommand;\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n@@ -1058,17 +1057,6 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \treturn show_editor();\n }\n \n-static struct option builtin_subcommand_options[] = {\n-\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n-\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n-\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n-\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n-\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n-\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n-\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n \tchar *value = NULL, *comment = NULL;\n@@ -1291,6 +1279,18 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n int cmd_config(int argc, const char **argv, const char *prefix)\n {\n+\tparse_opt_subcommand_fn *subcommand = NULL;\n+\tstruct option subcommand_opts[] = {\n+\t\tOPT_SUBCOMMAND(\"list\", &subcommand, cmd_config_list),\n+\t\tOPT_SUBCOMMAND(\"get\", &subcommand, cmd_config_get),\n+\t\tOPT_SUBCOMMAND(\"set\", &subcommand, cmd_config_set),\n+\t\tOPT_SUBCOMMAND(\"unset\", &subcommand, cmd_config_unset),\n+\t\tOPT_SUBCOMMAND(\"rename-section\", &subcommand, cmd_config_rename_section),\n+\t\tOPT_SUBCOMMAND(\"remove-section\", &subcommand, cmd_config_remove_section),\n+\t\tOPT_SUBCOMMAND(\"edit\", &subcommand, cmd_config_edit),\n+\t\tOPT_END(),\n+\t};\n+\n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n \t/*\n@@ -1301,10 +1301,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t * line in case no subcommand was given, which would otherwise confuse\n \t * us when parsing the legacy-style modes that don't use subcommands.\n \t */\n-\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t\t     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \tif (subcommand) {\n-\t\targc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,\n+\t\targc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,\n \t\t       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);\n \t\treturn subcommand(argc, argv, prefix);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494769","messageId":"1a0c6a83844bf455e57db3435793ef6ccbe6a7b0.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 04/21] builtin/config: move legacy options into `cmd_config()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:52Z","receivedAt":"2024-05-15T06:41:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Move the legacy options as well some of the variables it references into\n`cmd_config_action()`. This reduces our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 ++++++++++++++++++++++++------------------------\n 1 file changed, 30 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 58c8b0abda..e9956574fe 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -78,7 +78,7 @@ static char term = '\\n';\n static int use_global_config, use_system_config, use_local_config;\n static int use_worktree_config;\n static struct git_config_source given_config_source;\n-static int actions, type;\n+static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n@@ -86,7 +86,6 @@ static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n-static const char *comment_arg;\n \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -772,33 +771,6 @@ static void handle_nul(void) {\n \tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n \n-static struct option builtin_config_options[] = {\n-\tCONFIG_LOCATION_OPTIONS,\n-\tOPT_GROUP(N_(\"Action\")),\n-\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n-\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n-\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n-\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n-\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n-\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n-\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n-\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n-\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n-\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n-\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n-\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n-\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n-\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\tCONFIG_TYPE_OPTIONS,\n-\tCONFIG_DISPLAY_OPTIONS,\n-\tOPT_GROUP(N_(\"Other\")),\n-\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n-\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\tOPT_END(),\n-};\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct option opts[] = {\n@@ -1059,12 +1031,40 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tconst char *comment_arg = NULL;\n+\tint actions = 0;\n+\tstruct option opts[] = {\n+\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Action\")),\n+\t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n+\t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n+\t\tOPT_CMDMODE(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [<value-pattern>]\"), ACTION_GET_REGEXP),\n+\t\tOPT_CMDMODE(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n+\t\tOPT_CMDMODE(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [<value-pattern>]\"), ACTION_REPLACE_ALL),\n+\t\tOPT_CMDMODE(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n+\t\tOPT_CMDMODE(0, \"unset\", &actions, N_(\"remove a variable: name [<value-pattern>]\"), ACTION_UNSET),\n+\t\tOPT_CMDMODE(0, \"unset-all\", &actions, N_(\"remove all matches: name [<value-pattern>]\"), ACTION_UNSET_ALL),\n+\t\tOPT_CMDMODE(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n+\t\tOPT_CMDMODE(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n+\t\tOPT_CMDMODE('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n+\t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n+\t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n+\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tOPT_GROUP(N_(\"Other\")),\n+\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n+\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_END(),\n+\t};\n \tchar *value = NULL, *comment = NULL;\n \tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n-\targc = parse_options(argc, argv, prefix, builtin_config_options,\n+\targc = parse_options(argc, argv, prefix, opts,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-- \n2.45.GIT\n\n"},{"id":"494770","messageId":"4950ddcecd30257469acc27dd6f5fb4ea7b3a050.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 05/21] builtin/config: move actions into `cmd_config_actions()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:41:57Z","receivedAt":"2024-05-15T06:42:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We only use actions in the legacy mode. Convert them to an enum and move\nthem into `cmd_config_actions()` to clearly demonstrate that they are\nnot used anywhere else.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 48 +++++++++++++++++++++++-------------------------\n 1 file changed, 23 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e9956574fe..0842e4f198 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -87,30 +87,6 @@ static int show_origin;\n static int show_scope;\n static int fixed_value;\n \n-#define ACTION_GET (1<<0)\n-#define ACTION_GET_ALL (1<<1)\n-#define ACTION_GET_REGEXP (1<<2)\n-#define ACTION_REPLACE_ALL (1<<3)\n-#define ACTION_ADD (1<<4)\n-#define ACTION_UNSET (1<<5)\n-#define ACTION_UNSET_ALL (1<<6)\n-#define ACTION_RENAME_SECTION (1<<7)\n-#define ACTION_REMOVE_SECTION (1<<8)\n-#define ACTION_LIST (1<<9)\n-#define ACTION_EDIT (1<<10)\n-#define ACTION_SET (1<<11)\n-#define ACTION_SET_ALL (1<<12)\n-#define ACTION_GET_COLOR (1<<13)\n-#define ACTION_GET_COLORBOOL (1<<14)\n-#define ACTION_GET_URLMATCH (1<<15)\n-\n-/*\n- * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n- * one line of output and which should therefore be paged.\n- */\n-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n-\t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -1031,6 +1007,24 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n {\n+\tenum {\n+\t\tACTION_GET = (1<<0),\n+\t\tACTION_GET_ALL = (1<<1),\n+\t\tACTION_GET_REGEXP = (1<<2),\n+\t\tACTION_REPLACE_ALL = (1<<3),\n+\t\tACTION_ADD = (1<<4),\n+\t\tACTION_UNSET = (1<<5),\n+\t\tACTION_UNSET_ALL = (1<<6),\n+\t\tACTION_RENAME_SECTION = (1<<7),\n+\t\tACTION_REMOVE_SECTION = (1<<8),\n+\t\tACTION_LIST = (1<<9),\n+\t\tACTION_EDIT = (1<<10),\n+\t\tACTION_SET = (1<<11),\n+\t\tACTION_SET_ALL = (1<<12),\n+\t\tACTION_GET_COLOR = (1<<13),\n+\t\tACTION_GET_COLORBOOL = (1<<14),\n+\t\tACTION_GET_URLMATCH = (1<<15),\n+\t};\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1147,7 +1141,11 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\tif (actions & PAGING_ACTIONS)\n+\t/*\n+\t * The following actions may produce more than one line of output and\n+\t * should therefore be paged.\n+\t */\n+\tif (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n \t\tsetup_auto_pager(\"config\", 1);\n \n \tif (actions == ACTION_LIST) {\n-- \n2.45.GIT\n\n"},{"id":"494771","messageId":"6de9097fb2c339be72aab3810abbccbfc39bd480.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 06/21] builtin/config: check for writeability after source is set up","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:02Z","receivedAt":"2024-05-15T06:42:06Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `check_write()` function verifies that we do not try to write to a\nconfig source that cannot be written to, like for example stdin. But\nwhile the new subcommands do call this function, they do so before\ncalling `handle_config_location()`. Consequently, we only end up\nchecking the default config location for writeability, not the location\nthat was actually specified by the caller of git-config(1).\n\nFix this by calling `check_write()` after `handle_config_location()`. We\nwill further clarify the relationship between those two functions in a\nsubsequent commit where we remove the global state that both implicitly\nrely on.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 10 +++++-----\n t/t1300-config.sh |  6 ++++++\n 2 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 0842e4f198..9866d1a055 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -843,7 +843,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n@@ -856,6 +855,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n@@ -891,13 +891,13 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n@@ -918,10 +918,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 2, 2);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n@@ -943,10 +943,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n-\tcheck_write();\n \tcheck_argc(argc, 1, 1);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n@@ -997,10 +997,10 @@ static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n-\tcheck_write();\n \tcheck_argc(argc, 0, 0);\n \n \thandle_config_location(prefix);\n+\tcheck_write();\n \n \treturn show_editor();\n }\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex d90a69b29f..9de2d95f06 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2835,6 +2835,12 @@ test_expect_success 'specifying multiple modes causes failure' '\n \ttest_cmp expect err\n '\n \n+test_expect_success 'writing to stdin is rejected' '\n+\techo \"fatal: writing to stdin is not supported\" >expect &&\n+\ttest_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&\n+\ttest_cmp expect err\n+'\n+\n done\n \n test_done\n-- \n2.45.GIT\n\n"},{"id":"494772","messageId":"24053f8f4fcd89406cefa5fd289097b9cea29405.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 07/21] config: make the config source const","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:06Z","receivedAt":"2024-05-15T06:42:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `struct git_config_source` passed to `config_with_options()` is\nnever modified. Let's mark it as `const` to clarify.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n config.c | 4 ++--\n config.h | 2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex a871c7729e..ca6dac6e23 100644\n--- a/config.c\n+++ b/config.c\n@@ -125,7 +125,7 @@ struct config_include_data {\n \tconfig_fn_t fn;\n \tvoid *data;\n \tconst struct config_options *opts;\n-\tstruct git_config_source *config_source;\n+\tconst struct git_config_source *config_source;\n \tstruct repository *repo;\n \n \t/*\n@@ -2116,7 +2116,7 @@ static int do_git_config_sequence(const struct config_options *opts,\n }\n \n int config_with_options(config_fn_t fn, void *data,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts)\n {\ndiff --git a/config.h b/config.h\nindex db8b608064..e66c84520b 100644\n--- a/config.h\n+++ b/config.h\n@@ -232,7 +232,7 @@ void git_config(config_fn_t fn, void *);\n  * sets `opts.respect_includes` to `1` by default.\n  */\n int config_with_options(config_fn_t fn, void *,\n-\t\t\tstruct git_config_source *config_source,\n+\t\t\tconst struct git_config_source *config_source,\n \t\t\tstruct repository *repo,\n \t\t\tconst struct config_options *opts);\n \n-- \n2.45.GIT\n\n"},{"id":"494773","messageId":"9dc1d00f715a6ded4d87ec3ffbd2a4546d6a59ac.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 08/21] builtin/config: refactor functions to have common exit paths","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:11Z","receivedAt":"2024-05-15T06:42:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Refactor functions to have a single exit path. This will make it easier\nin subsequent commits to add common cleanup code.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 38 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9866d1a055..155564b832 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -798,6 +798,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -816,8 +817,11 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\treturn get_urlmatch(argv[0], url);\n-\treturn get_value(argv[0], value_pattern, flags);\n+\t\tret = get_urlmatch(argv[0], url);\n+\telse\n+\t\tret = get_value(argv[0], value_pattern, flags);\n+\n+\treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n@@ -888,6 +892,7 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n@@ -900,12 +905,14 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tcheck_write();\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, value_pattern,\n-\t\t\t\t\t\t\t      NULL, flags);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n+\t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\treturn git_config_set_in_file_gently(given_config_source.file, argv[0],\n-\t\t\t\t\t\t     NULL, NULL);\n+\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\t\t\t\t\t    NULL, NULL);\n+\n+\treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n@@ -926,11 +933,13 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n@@ -951,11 +960,13 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n-\t\treturn ret;\n+\t\tgoto out;\n \telse if (!ret)\n \t\tdie(_(\"no such section: %s\"), argv[0]);\n+\tret = 0;\n \n-\treturn 0;\n+out:\n+\treturn ret;\n }\n \n static int show_editor(void)\n@@ -1199,41 +1210,41 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\treturn get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t\t      NULL, flags);\n+\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\treturn git_config_set_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t     argv[0], NULL, NULL);\n+\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n-\t\t\t\t\t\t\t      NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n+\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tcheck_write();\n@@ -1241,7 +1252,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1253,7 +1264,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out;\n \t\telse if (!ret)\n \t\t\tdie(_(\"no such section: %s\"), argv[0]);\n \t\telse\n@@ -1267,9 +1278,10 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\treturn get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(argv[0], argc == 2);\n \t}\n \n+out:\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494774","messageId":"2ede4a204b3715b77b0dcb7d310b9e94fad60ec7.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 09/21] builtin/config: move location options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:16Z","receivedAt":"2024-05-15T06:42:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The location options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 313 ++++++++++++++++++++++++++---------------------\n 1 file changed, 176 insertions(+), 137 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 155564b832..ec36d7289a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -62,6 +62,26 @@ static const char *const builtin_config_edit_usage[] = {\n \tNULL\n };\n \n+#define CONFIG_LOCATION_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Config file location\")), \\\n+\tOPT_BOOL(0, \"global\", &opts.use_global_config, N_(\"use global config file\")), \\\n+\tOPT_BOOL(0, \"system\", &opts.use_system_config, N_(\"use system config file\")), \\\n+\tOPT_BOOL(0, \"local\", &opts.use_local_config, N_(\"use repository config file\")), \\\n+\tOPT_BOOL(0, \"worktree\", &opts.use_worktree_config, N_(\"use per-worktree config file\")), \\\n+\tOPT_STRING('f', \"file\", &opts.source.file, N_(\"file\"), N_(\"use given config file\")), \\\n+\tOPT_STRING(0, \"blob\", &opts.source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n+\n+struct config_location_options {\n+\tstruct git_config_source source;\n+\tstruct config_options options;\n+\tchar *file_to_free;\n+\tint use_global_config;\n+\tint use_system_config;\n+\tint use_local_config;\n+\tint use_worktree_config;\n+};\n+#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n@@ -75,14 +95,10 @@ static char delim = '=';\n static char key_delim = ' ';\n static char term = '\\n';\n \n-static int use_global_config, use_system_config, use_local_config;\n-static int use_worktree_config;\n-static struct git_config_source given_config_source;\n static int type;\n static char *default_value;\n static int end_nul;\n static int respect_includes_opt = -1;\n-static struct config_options config_options;\n static int show_origin;\n static int show_scope;\n static int fixed_value;\n@@ -298,7 +314,8 @@ static int collect_config(const char *key_, const char *value_,\n \treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n }\n \n-static int get_value(const char *key_, const char *regex_, unsigned flags)\n+static int get_value(const struct config_location_options *opts,\n+\t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -353,8 +370,8 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)\n \t}\n \n \tconfig_with_options(collect_config, &values,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!values.nr && default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n@@ -464,14 +481,15 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const char *var, const char *def_color)\n+static void get_color(const struct config_location_options *opts,\n+\t\t      const char *var, const char *def_color)\n {\n \tget_color_slot = var;\n \tget_color_found = 0;\n \tparsed_color[0] = '\\0';\n \tconfig_with_options(git_get_color_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (!get_color_found && def_color) {\n \t\tif (color_parse(def_color, parsed_color) < 0)\n@@ -497,15 +515,16 @@ static int git_get_colorbool_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_colorbool(const char *var, int print)\n+static int get_colorbool(const struct config_location_options *opts,\n+\t\t\t const char *var, int print)\n {\n \tget_colorbool_slot = var;\n \tget_colorbool_found = -1;\n \tget_diff_color_found = -1;\n \tget_color_ui_found = -1;\n \tconfig_with_options(git_get_colorbool_config, NULL,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tif (get_colorbool_found < 0) {\n \t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n@@ -527,15 +546,15 @@ static int get_colorbool(const char *var, int print)\n \t\treturn get_colorbool_found ? 0 : 1;\n }\n \n-static void check_write(void)\n+static void check_write(const struct git_config_source *source)\n {\n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!source->file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n \n-\tif (given_config_source.use_stdin)\n+\tif (source->use_stdin)\n \t\tdie(_(\"writing to stdin is not supported\"));\n \n-\tif (given_config_source.blob)\n+\tif (source->blob)\n \t\tdie(_(\"writing config blobs is not supported\"));\n }\n \n@@ -572,7 +591,8 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n \treturn 0;\n }\n \n-static int get_urlmatch(const char *var, const char *url)\n+static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n@@ -599,8 +619,8 @@ static int get_urlmatch(const char *var, const char *url)\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n-\t\t\t    &given_config_source, the_repository,\n-\t\t\t    &config_options);\n+\t\t\t    &opts->source, the_repository,\n+\t\t\t    &opts->options);\n \n \tret = !values.nr;\n \n@@ -638,34 +658,39 @@ static char *default_user_config(void)\n \treturn strbuf_detach(&buf, NULL);\n }\n \n-static void handle_config_location(const char *prefix)\n+static void location_options_init(struct config_location_options *opts,\n+\t\t\t\t  const char *prefix)\n {\n-\tif (use_global_config + use_system_config + use_local_config +\n-\t    use_worktree_config +\n-\t    !!given_config_source.file + !!given_config_source.blob > 1) {\n+\tif (!opts->source.file)\n+\t\topts->source.file = opts->file_to_free =\n+\t\t\txstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n+\n+\tif (opts->use_global_config + opts->use_system_config +\n+\t    opts->use_local_config + opts->use_worktree_config +\n+\t    !!opts->source.file + !!opts->source.blob > 1) {\n \t\terror(_(\"only one config file at a time\"));\n \t\texit(129);\n \t}\n \n \tif (!startup_info->have_repository) {\n-\t\tif (use_local_config)\n+\t\tif (opts->use_local_config)\n \t\t\tdie(_(\"--local can only be used inside a git repository\"));\n-\t\tif (given_config_source.blob)\n+\t\tif (opts->source.blob)\n \t\t\tdie(_(\"--blob can only be used inside a git repository\"));\n-\t\tif (use_worktree_config)\n+\t\tif (opts->use_worktree_config)\n \t\t\tdie(_(\"--worktree can only be used inside a git repository\"));\n \t}\n \n-\tif (given_config_source.file &&\n-\t\t\t!strcmp(given_config_source.file, \"-\")) {\n-\t\tgiven_config_source.file = NULL;\n-\t\tgiven_config_source.use_stdin = 1;\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\tif (opts->source.file &&\n+\t\t\t!strcmp(opts->source.file, \"-\")) {\n+\t\topts->source.file = NULL;\n+\t\topts->source.use_stdin = 1;\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (use_global_config) {\n-\t\tgiven_config_source.file = git_global_config();\n-\t\tif (!given_config_source.file)\n+\tif (opts->use_global_config) {\n+\t\topts->source.file = opts->file_to_free = git_global_config();\n+\t\tif (!opts->source.file)\n \t\t\t/*\n \t\t\t * It is unknown if HOME/.gitconfig exists, so\n \t\t\t * we do not know if we should write to XDG\n@@ -673,17 +698,18 @@ static void handle_config_location(const char *prefix)\n \t\t\t * is set and points at a sane location.\n \t\t\t */\n \t\t\tdie(_(\"$HOME not set\"));\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_GLOBAL;\n-\t} else if (use_system_config) {\n-\t\tgiven_config_source.file = git_system_config();\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_SYSTEM;\n-\t} else if (use_local_config) {\n-\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n-\t} else if (use_worktree_config) {\n+\t\topts->source.scope = CONFIG_SCOPE_GLOBAL;\n+\t} else if (opts->use_system_config) {\n+\t\topts->source.file = opts->file_to_free = git_system_config();\n+\t\topts->source.scope = CONFIG_SCOPE_SYSTEM;\n+\t} else if (opts->use_local_config) {\n+\t\topts->source.file = opts->file_to_free = git_pathdup(\"config\");\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n+\t} else if (opts->use_worktree_config) {\n \t\tstruct worktree **worktrees = get_worktrees();\n \t\tif (the_repository->repository_format_worktree_config)\n-\t\t\tgiven_config_source.file = git_pathdup(\"config.worktree\");\n+\t\t\topts->source.file = opts->file_to_free =\n+\t\t\t\tgit_pathdup(\"config.worktree\");\n \t\telse if (worktrees[0] && worktrees[1])\n \t\t\tdie(_(\"--worktree cannot be used with multiple \"\n \t\t\t      \"working trees unless the config\\n\"\n@@ -691,28 +717,34 @@ static void handle_config_location(const char *prefix)\n \t\t\t      \"Please read \\\"CONFIGURATION FILE\\\"\\n\"\n \t\t\t      \"section in \\\"git help worktree\\\" for details\"));\n \t\telse\n-\t\t\tgiven_config_source.file = git_pathdup(\"config\");\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_LOCAL;\n+\t\t\topts->source.file = opts->file_to_free =\n+\t\t\t\tgit_pathdup(\"config\");\n+\t\topts->source.scope = CONFIG_SCOPE_LOCAL;\n \t\tfree_worktrees(worktrees);\n-\t} else if (given_config_source.file) {\n-\t\tif (!is_absolute_path(given_config_source.file) && prefix)\n-\t\t\tgiven_config_source.file =\n-\t\t\t\tprefix_filename(prefix, given_config_source.file);\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n-\t} else if (given_config_source.blob) {\n-\t\tgiven_config_source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.file) {\n+\t\tif (!is_absolute_path(opts->source.file) && prefix)\n+\t\t\topts->source.file = opts->file_to_free =\n+\t\t\t\tprefix_filename(prefix, opts->source.file);\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n+\t} else if (opts->source.blob) {\n+\t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n \tif (respect_includes_opt == -1)\n-\t\tconfig_options.respect_includes = !given_config_source.file;\n+\t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\tconfig_options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = respect_includes_opt;\n \tif (startup_info->have_repository) {\n-\t\tconfig_options.commondir = get_git_common_dir();\n-\t\tconfig_options.git_dir = get_git_dir();\n+\t\topts->options.commondir = get_git_common_dir();\n+\t\topts->options.git_dir = get_git_dir();\n \t}\n }\n \n+static void location_options_release(struct config_location_options *opts)\n+{\n+\tfree(opts->file_to_free);\n+}\n+\n static void handle_nul(void) {\n \tif (end_nul) {\n \t\tterm = '\\0';\n@@ -721,15 +753,6 @@ static void handle_nul(void) {\n \t}\n }\n \n-#define CONFIG_LOCATION_OPTIONS \\\n-\tOPT_GROUP(N_(\"Config file location\")), \\\n-\tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")), \\\n-\tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")), \\\n-\tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")), \\\n-\tOPT_BOOL(0, \"worktree\", &use_worktree_config, N_(\"use per-worktree config file\")), \\\n-\tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")), \\\n-\tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\"))\n-\n #define CONFIG_TYPE_OPTIONS \\\n \tOPT_GROUP(N_(\"Type\")), \\\n \tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n@@ -749,8 +772,9 @@ static void handle_nul(void) {\n \n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS,\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -760,30 +784,32 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t&config_options) < 0) {\n-\t\tif (given_config_source.file)\n+\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t&location_opts.options) < 0) {\n+\t\tif (location_opts.source.file)\n \t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t  given_config_source.file);\n+\t\t\t\t  location_opts.source.file);\n \t\telse\n \t\t\tdie(_(\"error processing config file(s)\"));\n \t}\n \n+\tlocation_options_release(&location_opts);\n \treturn 0;\n }\n \n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n@@ -811,26 +837,28 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, argv[0], url);\n \telse\n-\t\tret = get_value(argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_set(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n \tint flags = 0, append = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n@@ -858,23 +886,24 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, value_pattern,\n \t\t\t\t\t\t\t     comment, flags);\n \t} else {\n-\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t    argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -882,10 +911,11 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \n static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -901,24 +931,26 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix)\n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, value_pattern,\n \t\t\t\t\t\t\t     NULL, flags);\n \telse\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0],\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0],\n \t\t\t\t\t\t    NULL, NULL);\n \n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -927,10 +959,10 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 2, 2);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], argv[1]);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -939,13 +971,15 @@ static int cmd_config_rename_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -954,10 +988,10 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \tcheck_argc(argc, 1, 1);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\tret = git_config_rename_section_in_file(given_config_source.file,\n+\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\targv[0], NULL);\n \tif (ret < 0)\n \t\tgoto out;\n@@ -966,24 +1000,25 @@ static int cmd_config_remove_section(int argc, const char **argv, const char *pr\n \tret = 0;\n \n out:\n+\tlocation_options_release(&location_opts);\n \treturn ret;\n }\n \n-static int show_editor(void)\n+static int show_editor(struct config_location_options *opts)\n {\n \tchar *config_file;\n \n-\tif (!given_config_source.file && !startup_info->have_repository)\n+\tif (!opts->source.file && !startup_info->have_repository)\n \t\tdie(_(\"not in a git directory\"));\n-\tif (given_config_source.use_stdin)\n+\tif (opts->source.use_stdin)\n \t\tdie(_(\"editing stdin is not supported\"));\n-\tif (given_config_source.blob)\n+\tif (opts->source.blob)\n \t\tdie(_(\"editing blobs is not supported\"));\n \tgit_config(git_default_config, NULL);\n-\tconfig_file = given_config_source.file ?\n-\t\t\txstrdup(given_config_source.file) :\n+\tconfig_file = opts->source.file ?\n+\t\t\txstrdup(opts->source.file) :\n \t\t\tgit_pathdup(\"config\");\n-\tif (use_global_config) {\n+\tif (opts->use_global_config) {\n \t\tint fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);\n \t\tif (fd >= 0) {\n \t\t\tchar *content = default_user_config();\n@@ -1002,18 +1037,22 @@ static int show_editor(void)\n \n static int cmd_config_edit(int argc, const char **argv, const char *prefix)\n {\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_END(),\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);\n \tcheck_argc(argc, 0, 0);\n \n-\thandle_config_location(prefix);\n-\tcheck_write();\n+\tlocation_options_init(&location_opts, prefix);\n+\tcheck_write(&location_opts.source);\n \n-\treturn show_editor();\n+\tret = show_editor(&location_opts);\n+\tlocation_options_release(&location_opts);\n+\treturn ret;\n }\n \n static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n@@ -1036,10 +1075,11 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_COLORBOOL = (1<<14),\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n+\tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n-\t\tCONFIG_LOCATION_OPTIONS,\n+\t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n \t\tOPT_CMDMODE(0, \"get\", &actions, N_(\"get value: name [<value-pattern>]\"), ACTION_GET),\n \t\tOPT_CMDMODE(0, \"get-all\", &actions, N_(\"get all values: key [<value-pattern>]\"), ACTION_GET_ALL),\n@@ -1073,7 +1113,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n-\thandle_config_location(prefix);\n+\tlocation_options_init(&location_opts, prefix);\n \thandle_nul();\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n@@ -1162,94 +1202,94 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (config_with_options(show_all_config, NULL,\n-\t\t\t\t\t&given_config_source, the_repository,\n-\t\t\t\t\t&config_options) < 0) {\n-\t\t\tif (given_config_source.file)\n+\t\t\t\t\t&location_opts.source, the_repository,\n+\t\t\t\t\t&location_opts.options) < 0) {\n+\t\t\tif (location_opts.source.file)\n \t\t\t\tdie_errno(_(\"unable to read config file '%s'\"),\n-\t\t\t\t\t  given_config_source.file);\n+\t\t\t\t\t  location_opts.source.file);\n \t\t\telse\n \t\t\t\tdie(_(\"error processing config file(s)\"));\n \t\t}\n \t}\n \telse if (actions == ACTION_EDIT) {\n-\t\tret = show_editor();\n+\t\tret = show_editor(&location_opts);\n \t}\n \telse if (actions == ACTION_SET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);\n+\t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t}\n \telse if (actions == ACTION_SET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n \t\t\t\t\t\t\t     comment, flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t\t     NULL, flags);\n \t\telse\n-\t\t\tret = git_config_set_in_file_gently(given_config_source.file,\n+\t\t\tret = git_config_set_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t    argv[0], NULL, NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = git_config_set_multivar_in_file_gently(given_config_source.file,\n+\t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], NULL, argv[1],\n \t\t\t\t\t\t\t     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1259,9 +1299,9 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\tret = 0;\n \t}\n \telse if (actions == ACTION_REMOVE_SECTION) {\n-\t\tcheck_write();\n+\t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 1, 1);\n-\t\tret = git_config_rename_section_in_file(given_config_source.file,\n+\t\tret = git_config_rename_section_in_file(location_opts.source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n \t\t\tgoto out;\n@@ -1272,16 +1312,17 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(argv[0], argv[1]);\n+\t\tget_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-\t\tret = get_colorbool(argv[0], argc == 2);\n+\t\tret = get_colorbool(&location_opts, argv[0], argc == 2);\n \t}\n \n out:\n+\tlocation_options_release(&location_opts);\n \tfree(comment);\n \tfree(value);\n \treturn ret;\n@@ -1301,8 +1342,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \n-\tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n-\n \t/*\n \t * This is somewhat hacky: we first parse the command line while\n \t * keeping all args intact in order to determine whether a subcommand\n-- \n2.45.GIT\n\n"},{"id":"494775","messageId":"4d157942e66f9b864e8f3cfe58c945269877f43b.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 10/21] builtin/config: move display options into local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:21Z","receivedAt":"2024-05-15T06:42:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The display options are tracked via a set of global variables. Move\nthem into a self-contained structure so that we can easily parse all\nrelevant options and hand them over to the various functions that\nrequire them.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 171 ++++++++++++++++++++++++++++-------------------\n 1 file changed, 101 insertions(+), 70 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex ec36d7289a..1a67a6caef 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -82,25 +82,42 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_DISPLAY_OPTIONS(opts) \\\n+\tOPT_GROUP(N_(\"Display options\")), \\\n+\tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n+\tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n+\tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n+\tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\n+struct config_display_options {\n+\tint end_nul;\n+\tint omit_values;\n+\tint show_origin;\n+\tint show_scope;\n+\tint show_keys;\n+\t/* Populated via `display_options_init()`. */\n+\tint term;\n+\tint delim;\n+\tint key_delim;\n+};\n+#define CONFIG_DISPLAY_OPTIONS_INIT { \\\n+\t.term = '\\n', \\\n+\t.delim = '=', \\\n+\t.key_delim = ' ', \\\n+}\n+\n static char *key;\n static regex_t *key_regexp;\n static const char *value_pattern;\n static regex_t *regexp;\n-static int show_keys;\n-static int omit_values;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char delim = '=';\n-static char key_delim = ' ';\n-static char term = '\\n';\n \n static int type;\n static char *default_value;\n-static int end_nul;\n static int respect_includes_opt = -1;\n-static int show_origin;\n-static int show_scope;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -178,24 +195,26 @@ static void check_argc(int argc, int min, int max)\n \texit(129);\n }\n \n-static void show_config_origin(const struct key_value_info *kvi,\n+static void show_config_origin(const struct config_display_options *opts,\n+\t\t\t       const struct key_value_info *kvi,\n \t\t\t       struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \n \tstrbuf_addstr(buf, config_origin_type_name(kvi->origin_type));\n \tstrbuf_addch(buf, ':');\n-\tif (end_nul)\n+\tif (opts->end_nul)\n \t\tstrbuf_addstr(buf, kvi->filename ? kvi->filename : \"\");\n \telse\n \t\tquote_c_style(kvi->filename ? kvi->filename : \"\", buf, NULL, 0);\n \tstrbuf_addch(buf, term);\n }\n \n-static void show_config_scope(const struct key_value_info *kvi,\n+static void show_config_scope(const struct config_display_options *opts,\n+\t\t\t      const struct key_value_info *kvi,\n \t\t\t      struct strbuf *buf)\n {\n-\tconst char term = end_nul ? '\\0' : '\\t';\n+\tconst char term = opts->end_nul ? '\\0' : '\\t';\n \tconst char *scope = config_scope_name(kvi->scope);\n \n \tstrbuf_addstr(buf, N_(scope));\n@@ -204,24 +223,25 @@ static void show_config_scope(const struct key_value_info *kvi,\n \n static int show_all_config(const char *key_, const char *value_,\n \t\t\t   const struct config_context *ctx,\n-\t\t\t   void *cb UNUSED)\n+\t\t\t   void *cb)\n {\n+\tconst struct config_display_options *opts = cb;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (show_origin || show_scope) {\n+\tif (opts->show_origin || opts->show_scope) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tif (show_scope)\n-\t\t\tshow_config_scope(kvi, &buf);\n-\t\tif (show_origin)\n-\t\t\tshow_config_origin(kvi, &buf);\n+\t\tif (opts->show_scope)\n+\t\t\tshow_config_scope(opts, kvi, &buf);\n+\t\tif (opts->show_origin)\n+\t\t\tshow_config_origin(opts, kvi, &buf);\n \t\t/* Use fwrite as \"buf\" can contain \\0's if \"end_null\" is set. */\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n \t\tstrbuf_release(&buf);\n \t}\n-\tif (!omit_values && value_)\n-\t\tprintf(\"%s%c%s%c\", key_, delim, value_, term);\n+\tif (!opts->omit_values && value_)\n+\t\tprintf(\"%s%c%s%c\", key_, opts->delim, value_, opts->term);\n \telse\n-\t\tprintf(\"%s%c\", key_, term);\n+\t\tprintf(\"%s%c\", key_, opts->term);\n \treturn 0;\n }\n \n@@ -231,18 +251,19 @@ struct strbuf_list {\n \tint alloc;\n };\n \n-static int format_config(struct strbuf *buf, const char *key_,\n+static int format_config(const struct config_display_options *opts,\n+\t\t\t struct strbuf *buf, const char *key_,\n \t\t\t const char *value_, const struct key_value_info *kvi)\n {\n-\tif (show_scope)\n-\t\tshow_config_scope(kvi, buf);\n-\tif (show_origin)\n-\t\tshow_config_origin(kvi, buf);\n-\tif (show_keys)\n+\tif (opts->show_scope)\n+\t\tshow_config_scope(opts, kvi, buf);\n+\tif (opts->show_origin)\n+\t\tshow_config_origin(opts, kvi, buf);\n+\tif (opts->show_keys)\n \t\tstrbuf_addstr(buf, key_);\n-\tif (!omit_values) {\n-\t\tif (show_keys)\n-\t\t\tstrbuf_addch(buf, key_delim);\n+\tif (!opts->omit_values) {\n+\t\tif (opts->show_keys)\n+\t\t\tstrbuf_addch(buf, opts->key_delim);\n \n \t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n@@ -284,18 +305,24 @@ static int format_config(struct strbuf *buf, const char *key_,\n \t\t\tstrbuf_addstr(buf, value_);\n \t\t} else {\n \t\t\t/* Just show the key name; back out delimiter */\n-\t\t\tif (show_keys)\n+\t\t\tif (opts->show_keys)\n \t\t\t\tstrbuf_setlen(buf, buf->len - 1);\n \t\t}\n \t}\n-\tstrbuf_addch(buf, term);\n+\tstrbuf_addch(buf, opts->term);\n \treturn 0;\n }\n \n+struct collect_config_data {\n+\tconst struct config_display_options *display_opts;\n+\tstruct strbuf_list *values;\n+};\n+\n static int collect_config(const char *key_, const char *value_,\n \t\t\t  const struct config_context *ctx, void *cb)\n {\n-\tstruct strbuf_list *values = cb;\n+\tstruct collect_config_data *data = cb;\n+\tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n \tif (!use_key_regexp && strcmp(key_, key))\n@@ -311,14 +338,20 @@ static int collect_config(const char *key_, const char *value_,\n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n \tstrbuf_init(&values->items[values->nr], 0);\n \n-\treturn format_config(&values->items[values->nr++], key_, value_, kvi);\n+\treturn format_config(data->display_opts, &values->items[values->nr++],\n+\t\t\t     key_, value_, kvi);\n }\n \n static int get_value(const struct config_location_options *opts,\n+\t\t     const struct config_display_options *display_opts,\n \t\t     const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n+\tstruct collect_config_data data = {\n+\t\t.display_opts = display_opts,\n+\t\t.values = &values,\n+\t};\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -369,7 +402,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t}\n \t}\n \n-\tconfig_with_options(collect_config, &values,\n+\tconfig_with_options(collect_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n@@ -381,7 +414,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n \t\t\t\tdefault_value);\n \t}\n@@ -592,10 +625,12 @@ static int urlmatch_collect_fn(const char *var, const char *value,\n }\n \n static int get_urlmatch(const struct config_location_options *opts,\n+\t\t\tconst struct config_display_options *_display_opts,\n \t\t\tconst char *var, const char *url)\n {\n \tint ret;\n \tchar *section_tail;\n+\tstruct config_display_options display_opts = *_display_opts;\n \tstruct string_list_item *item;\n \tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n \tstruct string_list values = STRING_LIST_INIT_DUP;\n@@ -612,10 +647,10 @@ static int get_urlmatch(const struct config_location_options *opts,\n \tif (section_tail) {\n \t\t*section_tail = '\\0';\n \t\tconfig.key = section_tail + 1;\n-\t\tshow_keys = 0;\n+\t\tdisplay_opts.show_keys = 0;\n \t} else {\n \t\tconfig.key = NULL;\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t}\n \n \tconfig_with_options(urlmatch_config_entry, &config,\n@@ -628,7 +663,7 @@ static int get_urlmatch(const struct config_location_options *opts,\n \t\tstruct urlmatch_current_candidate_value *matched = item->util;\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tformat_config(&buf, item->string,\n+\t\tformat_config(&display_opts, &buf, item->string,\n \t\t\t      matched->value_is_null ? NULL : matched->value.buf,\n \t\t\t      &matched->kvi);\n \t\tfwrite(buf.buf, 1, buf.len, stdout);\n@@ -745,11 +780,12 @@ static void location_options_release(struct config_location_options *opts)\n \tfree(opts->file_to_free);\n }\n \n-static void handle_nul(void) {\n-\tif (end_nul) {\n-\t\tterm = '\\0';\n-\t\tdelim = '\\n';\n-\t\tkey_delim = '\\n';\n+static void display_options_init(struct config_display_options *opts)\n+{\n+\tif (opts->end_nul) {\n+\t\topts->term = '\\0';\n+\t\topts->delim = '\\n';\n+\t\topts->key_delim = '\\n';\n \t}\n }\n \n@@ -763,19 +799,13 @@ static void handle_nul(void) {\n \tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n \tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n \n-#define CONFIG_DISPLAY_OPTIONS \\\n-\tOPT_GROUP(N_(\"Display options\")), \\\n-\tOPT_BOOL('z', \"null\", &end_nul, N_(\"terminate values with NUL byte\")), \\\n-\tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")), \\\n-\tOPT_BOOL(0, \"show-origin\", &show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n-\tOPT_BOOL(0, \"show-scope\", &show_scope, N_(\"show scope of config (worktree, local, global, system, command)\"))\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n@@ -785,11 +815,11 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \tcheck_argc(argc, 0, 0);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n-\tif (config_with_options(show_all_config, NULL,\n+\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t&location_opts.options) < 0) {\n \t\tif (location_opts.source.file)\n@@ -806,6 +836,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n static int cmd_config_get(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n \tstruct option opts[] = {\n@@ -817,8 +848,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n-\t\tCONFIG_DISPLAY_OPTIONS,\n-\t\tOPT_BOOL(0, \"show-names\", &show_keys, N_(\"show config keys in addition to their values\")),\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n@@ -838,14 +868,14 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n-\t\tret = get_urlmatch(&location_opts, argv[0], url);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1076,6 +1106,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tACTION_GET_URLMATCH = (1<<15),\n \t};\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n+\tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n \tstruct option opts[] = {\n@@ -1096,7 +1127,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_TYPE_OPTIONS,\n-\t\tCONFIG_DISPLAY_OPTIONS,\n+\t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n@@ -1114,7 +1145,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tlocation_options_init(&location_opts, prefix);\n-\thandle_nul();\n+\tdisplay_options_init(&display_opts);\n \n \tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n@@ -1130,13 +1161,13 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"no action specified\"));\n \t\t\texit(129);\n \t\t}\n-\tif (omit_values &&\n+\tif (display_opts.omit_values &&\n \t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n \t\terror(_(\"--name-only is only applicable to --list or --get-regexp\"));\n \t\texit(129);\n \t}\n \n-\tif (show_origin && !(actions &\n+\tif (display_opts.show_origin && !(actions &\n \t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n \t\terror(_(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t\"--get-regexp, and --list\"));\n@@ -1201,7 +1232,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n-\t\tif (config_with_options(show_all_config, NULL,\n+\t\tif (config_with_options(show_all_config, &display_opts,\n \t\t\t\t\t&location_opts.source, the_repository,\n \t\t\t\t\t&location_opts.options) < 0) {\n \t\t\tif (location_opts.source.file)\n@@ -1250,23 +1281,23 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n-\t\tshow_keys = 1;\n+\t\tdisplay_opts.show_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = get_urlmatch(&location_opts, argv[0], argv[1]);\n+\t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n \t\tcheck_write(&location_opts.source);\n-- \n2.45.GIT\n\n"},{"id":"494776","messageId":"5579371ad192ae4650e9598760df3350dca3b9c9.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 11/21] builtin/config: move type options into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:25Z","receivedAt":"2024-05-15T06:42:29Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The type options are tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 60 +++++++++++++++++++++++-------------------------\n 1 file changed, 29 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 1a67a6caef..ff111d83a9 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -82,13 +82,24 @@ struct config_location_options {\n };\n #define CONFIG_LOCATION_OPTIONS_INIT {0}\n \n+#define CONFIG_TYPE_OPTIONS(type) \\\n+\tOPT_GROUP(N_(\"Type\")), \\\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n+\n #define CONFIG_DISPLAY_OPTIONS(opts) \\\n \tOPT_GROUP(N_(\"Display options\")), \\\n \tOPT_BOOL('z', \"null\", &opts.end_nul, N_(\"terminate values with NUL byte\")), \\\n \tOPT_BOOL(0, \"name-only\", &opts.omit_values, N_(\"show variable names only\")), \\\n \tOPT_BOOL(0, \"show-origin\", &opts.show_origin, N_(\"show origin of config (file, standard input, blob, command line)\")), \\\n \tOPT_BOOL(0, \"show-scope\", &opts.show_scope, N_(\"show scope of config (worktree, local, global, system, command)\")), \\\n-\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\"))\n+\tOPT_BOOL(0, \"show-names\", &opts.show_keys, N_(\"show config keys in addition to their values\")), \\\n+\tCONFIG_TYPE_OPTIONS(opts.type)\n \n struct config_display_options {\n \tint end_nul;\n@@ -96,6 +107,7 @@ struct config_display_options {\n \tint show_origin;\n \tint show_scope;\n \tint show_keys;\n+\tint type;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -114,8 +126,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-\n-static int type;\n static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n@@ -265,13 +275,13 @@ static int format_config(const struct config_display_options *opts,\n \t\tif (opts->show_keys)\n \t\t\tstrbuf_addch(buf, opts->key_delim);\n \n-\t\tif (type == TYPE_INT)\n+\t\tif (opts->type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\", kvi));\n-\t\telse if (type == TYPE_BOOL)\n+\t\telse if (opts->type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (type == TYPE_BOOL_OR_INT) {\n+\t\telse if (opts->type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, kvi,\n \t\t\t\t\t\t   &is_bool);\n@@ -279,24 +289,24 @@ static int format_config(const struct config_display_options *opts,\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (type == TYPE_BOOL_OR_STR) {\n+\t\t} else if (opts->type == TYPE_BOOL_OR_STR) {\n \t\t\tint v = git_parse_maybe_bool(value_);\n \t\t\tif (v < 0)\n \t\t\t\tstrbuf_addstr(buf, value_);\n \t\t\telse\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n-\t\t} else if (type == TYPE_PATH) {\n+\t\t} else if (opts->type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (type == TYPE_EXPIRY_DATE) {\n+\t\t} else if (opts->type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addf(buf, \"%\"PRItime, t);\n-\t\t} else if (type == TYPE_COLOR) {\n+\t\t} else if (opts->type == TYPE_COLOR) {\n \t\t\tchar v[COLOR_MAXLEN];\n \t\t\tif (git_config_color(v, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -444,7 +454,7 @@ static int get_value(const struct config_location_options *opts,\n }\n \n static char *normalize_value(const char *key, const char *value,\n-\t\t\t     struct key_value_info *kvi)\n+\t\t\t     int type, struct key_value_info *kvi)\n {\n \tif (!value)\n \t\treturn NULL;\n@@ -789,16 +799,6 @@ static void display_options_init(struct config_display_options *opts)\n \t}\n }\n \n-#define CONFIG_TYPE_OPTIONS \\\n-\tOPT_GROUP(N_(\"Type\")), \\\n-\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL), \\\n-\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT), \\\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-str\", &type, N_(\"value is --bool or string\"), TYPE_BOOL_OR_STR), \\\n-\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH), \\\n-\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE)\n-\n static int cmd_config_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n@@ -841,7 +841,6 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tint flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tOPT_GROUP(N_(\"Filter options\")),\n \t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n \t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n@@ -886,10 +885,10 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tstruct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *comment_arg = NULL;\n \tchar *comment = NULL;\n-\tint flags = 0, append = 0;\n+\tint flags = 0, append = 0, type = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n-\t\tCONFIG_TYPE_OPTIONS,\n+\t\tCONFIG_TYPE_OPTIONS(type),\n \t\tOPT_GROUP(N_(\"Filter\")),\n \t\tOPT_BIT(0, \"all\", &flags, N_(\"replace multi-valued config option with new value\"), CONFIG_FLAGS_MULTI_REPLACE),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n@@ -919,7 +918,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tcheck_write(&location_opts.source);\n \n-\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\tvalue = normalize_value(argv[0], argv[1], type, &default_kvi);\n \n \tif ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n@@ -1126,7 +1125,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \t\tOPT_CMDMODE(0, \"get-color\", &actions, N_(\"find the color configured: slot [<default>]\"), ACTION_GET_COLOR),\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n-\t\tCONFIG_TYPE_OPTIONS,\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n@@ -1147,7 +1145,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {\n \t\terror(_(\"--get-color and variable type are incoherent\"));\n \t\texit(129);\n \t}\n@@ -1248,7 +1246,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(_(\"cannot overwrite multiple values with a single value\\n\"\n@@ -1257,7 +1255,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_SET_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags);\n@@ -1265,7 +1263,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 2);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value,\n \t\t\t\t\t\t\t     CONFIG_REGEX_NONE,\n@@ -1274,7 +1272,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write(&location_opts.source);\n \t\tcheck_argc(argc, 2, 3);\n-\t\tvalue = normalize_value(argv[0], argv[1], &default_kvi);\n+\t\tvalue = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);\n \t\tret = git_config_set_multivar_in_file_gently(location_opts.source.file,\n \t\t\t\t\t\t\t     argv[0], value, argv[2],\n \t\t\t\t\t\t\t     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);\n-- \n2.45.GIT\n\n"},{"id":"494777","messageId":"05a072d5d1b26c660f2467478fa75ba5373ac928.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 12/21] builtin/config: move default value into display options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:30Z","receivedAt":"2024-05-15T06:42:34Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The default value is tracked via a global variable. Move it into the\ndisplay options instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex ff111d83a9..8a327e770d 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -108,6 +108,7 @@ struct config_display_options {\n \tint show_scope;\n \tint show_keys;\n \tint type;\n+\tchar *default_value;\n \t/* Populated via `display_options_init()`. */\n \tint term;\n \tint delim;\n@@ -126,7 +127,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static char *default_value;\n static int respect_includes_opt = -1;\n static int fixed_value;\n \n@@ -416,7 +416,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!values.nr && default_value) {\n+\tif (!values.nr && display_opts->default_value) {\n \t\tstruct key_value_info kvi = KVI_INIT;\n \t\tstruct strbuf *item;\n \n@@ -424,9 +424,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tALLOC_GROW(values.items, values.nr + 1, values.alloc);\n \t\titem = &values.items[values.nr++];\n \t\tstrbuf_init(item, 0);\n-\t\tif (format_config(display_opts, item, key_, default_value, &kvi) < 0)\n+\t\tif (format_config(display_opts, item, key_,\n+\t\t\t\t  display_opts->default_value, &kvi) < 0)\n \t\t\tdie(_(\"failed to format default config value: %s\"),\n-\t\t\t\tdefault_value);\n+\t\t\t    display_opts->default_value);\n \t}\n \n \tret = !values.nr;\n@@ -850,7 +851,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n \t};\n \tint ret;\n@@ -861,7 +863,7 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (default_value && (do_all || url))\n+\tif (display_opts.default_value && (do_all || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n \tif (url && (do_all || use_key_regexp || value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n@@ -1127,7 +1129,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [<stdout-is-tty>]\"), ACTION_GET_COLORBOOL),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_STRING(0, \"default\", &default_value, N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n+\t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n+\t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n \t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n@@ -1172,7 +1175,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\texit(129);\n \t}\n \n-\tif (default_value && !(actions & ACTION_GET)) {\n+\tif (display_opts.default_value && !(actions & ACTION_GET)) {\n \t\terror(_(\"--default is only applicable to --get\"));\n \t\texit(129);\n \t}\n-- \n2.45.GIT\n\n"},{"id":"494778","messageId":"15d45ef7d42d49623577f9b856e3f791c9a65948.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 13/21] builtin/config: move `respect_includes_opt` into location options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:35Z","receivedAt":"2024-05-15T06:42:39Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The variable tracking whether or not we want to honor includes is\ntracked via a global variable. Move it into the location options\ninstead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 19 ++++++++++++-------\n 1 file changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8a327e770d..c54bdcbcdb 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -79,8 +79,11 @@ struct config_location_options {\n \tint use_system_config;\n \tint use_local_config;\n \tint use_worktree_config;\n+\tint respect_includes_opt;\n };\n-#define CONFIG_LOCATION_OPTIONS_INIT {0}\n+#define CONFIG_LOCATION_OPTIONS_INIT { \\\n+\t.respect_includes_opt = -1, \\\n+}\n \n #define CONFIG_TYPE_OPTIONS(type) \\\n \tOPT_GROUP(N_(\"Type\")), \\\n@@ -127,7 +130,6 @@ static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int do_not_match;\n-static int respect_includes_opt = -1;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -776,10 +778,10 @@ static void location_options_init(struct config_location_options *opts,\n \t\topts->source.scope = CONFIG_SCOPE_COMMAND;\n \t}\n \n-\tif (respect_includes_opt == -1)\n+\tif (opts->respect_includes_opt == -1)\n \t\topts->options.respect_includes = !opts->source.file;\n \telse\n-\t\topts->options.respect_includes = respect_includes_opt;\n+\t\topts->options.respect_includes = opts->respect_includes_opt;\n \tif (startup_info->have_repository) {\n \t\topts->options.commondir = get_git_common_dir();\n \t\topts->options.git_dir = get_git_dir();\n@@ -808,7 +810,8 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix)\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \n@@ -850,7 +853,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n \t\tCONFIG_DISPLAY_OPTIONS(display_opts),\n \t\tOPT_GROUP(N_(\"Other\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"use default value when missing entry\")),\n \t\tOPT_END(),\n@@ -1133,7 +1137,8 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n \t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n-\t\tOPT_BOOL(0, \"includes\", &respect_includes_opt, N_(\"respect include directives on lookup\")),\n+\t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n+\t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-- \n2.45.GIT\n\n"},{"id":"494779","messageId":"a729286cc5ba7ae374e5293bde02cdece4f38715.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 14/21] builtin/config: convert `do_not_match` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:39Z","receivedAt":"2024-05-15T06:42:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `do_not_match` variable is used by the `format_config()` callback as\nan indicator whether or not the passed regular expression is negated. It\nis only ever set up by its only caller, `collect_config()` and can thus\neasily be moved into the `collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex c54bdcbcdb..bc80fd293a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -129,7 +129,6 @@ static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n-static int do_not_match;\n static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n@@ -328,6 +327,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tint do_not_match;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -344,7 +344,7 @@ static int collect_config(const char *key_, const char *value_,\n \tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n-\t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -401,7 +401,7 @@ static int get_value(const struct config_location_options *opts,\n \t\tvalue_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n-\t\t\tdo_not_match = 1;\n+\t\t\tdata.do_not_match = 1;\n \t\t\tregex_++;\n \t\t}\n \n-- \n2.45.GIT\n\n"},{"id":"494780","messageId":"821bc68212a8c2a1dea4c72b2d1df5de63471177.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 15/21] builtin/config: convert `value_pattern` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:44Z","receivedAt":"2024-05-15T06:42:49Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `value_pattern` variable is used by the `format_config()` callback\nwhen `CONFIG_FLAGS_FIXED_VALUE` is used. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex bc80fd293a..aac5f7b976 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,7 +125,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static const char *value_pattern;\n static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n@@ -327,6 +326,7 @@ static int format_config(const struct config_display_options *opts,\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n+\tconst char *value_pattern;\n \tint do_not_match;\n };\n \n@@ -341,7 +341,7 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(value_pattern, (value_?value_:\"\")))\n+\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (regexp != NULL &&\n \t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -398,7 +398,7 @@ static int get_value(const struct config_location_options *opts,\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-\t\tvalue_pattern = regex_;\n+\t\tdata.value_pattern = regex_;\n \telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdata.do_not_match = 1;\n-- \n2.45.GIT\n\n"},{"id":"494781","messageId":"bac242caf0ed8167ee84dccc183e825652f65f0c.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 16/21] builtin/config: convert `regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:49Z","receivedAt":"2024-05-15T06:42:53Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `regexp` variable is used by the `format_config()` callback when\n`CONFIG_FLAGS_FIXED_VALUE` is not set. It is only ever set up by its\nonly caller, `collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex aac5f7b976..ae609c9b97 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,7 +125,6 @@ struct config_display_options {\n \n static char *key;\n static regex_t *key_regexp;\n-static regex_t *regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -327,6 +326,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tregex_t *regexp;\n \tint do_not_match;\n };\n \n@@ -343,8 +343,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n-\tif (regexp != NULL &&\n-\t    (data->do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n+\tif (data->regexp &&\n+\t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n \n \tALLOC_GROW(values->items, values->nr + 1, values->alloc);\n@@ -405,10 +405,10 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tregex_++;\n \t\t}\n \n-\t\tregexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(regexp, regex_, REG_EXTENDED)) {\n+\t\tdata.regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.regexp, regex_, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid pattern: %s\"), regex_);\n-\t\t\tFREE_AND_NULL(regexp);\n+\t\t\tFREE_AND_NULL(data.regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -448,9 +448,9 @@ static int get_value(const struct config_location_options *opts,\n \t\tregfree(key_regexp);\n \t\tfree(key_regexp);\n \t}\n-\tif (regexp) {\n-\t\tregfree(regexp);\n-\t\tfree(regexp);\n+\tif (data.regexp) {\n+\t\tregfree(data.regexp);\n+\t\tfree(data.regexp);\n \t}\n \n \treturn ret;\n-- \n2.45.GIT\n\n"},{"id":"494782","messageId":"746bdf873387090c80451cfc5e1950f737aea4f9.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 17/21] builtin/config: convert `key_regexp` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:53Z","receivedAt":"2024-05-15T06:42:58Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key_regexp` variable is used by the `format_config()` callback when\n`use_key_regexp` is set. It is only ever set up by its only caller,\n`collect_config()` and can thus easily be moved into the\n`collect_config_data` structure.\n\nDo so to remove our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex ae609c9b97..08a11b7999 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -124,7 +124,6 @@ struct config_display_options {\n }\n \n static char *key;\n-static regex_t *key_regexp;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -327,6 +326,7 @@ struct collect_config_data {\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n \tregex_t *regexp;\n+\tregex_t *key_regexp;\n \tint do_not_match;\n };\n \n@@ -339,7 +339,7 @@ static int collect_config(const char *key_, const char *value_,\n \n \tif (!use_key_regexp && strcmp(key_, key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n+\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n@@ -383,10 +383,10 @@ static int get_value(const struct config_location_options *opts,\n \t\tfor (tl = key; *tl && *tl != '.'; tl++)\n \t\t\t*tl = tolower(*tl);\n \n-\t\tkey_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\tif (regcomp(key_regexp, key, REG_EXTENDED)) {\n+\t\tdata.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\tif (regcomp(data.key_regexp, key, REG_EXTENDED)) {\n \t\t\terror(_(\"invalid key pattern: %s\"), key_);\n-\t\t\tFREE_AND_NULL(key_regexp);\n+\t\t\tFREE_AND_NULL(data.key_regexp);\n \t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\tgoto free_strings;\n \t\t}\n@@ -444,9 +444,9 @@ static int get_value(const struct config_location_options *opts,\n \n free_strings:\n \tfree(key);\n-\tif (key_regexp) {\n-\t\tregfree(key_regexp);\n-\t\tfree(key_regexp);\n+\tif (data.key_regexp) {\n+\t\tregfree(data.key_regexp);\n+\t\tfree(data.key_regexp);\n \t}\n \tif (data.regexp) {\n \t\tregfree(data.regexp);\n-- \n2.45.GIT\n\n"},{"id":"494783","messageId":"f1f390f4998a4c87e6233373fb333f2437febe0e.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 18/21] builtin/config: convert `key` to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:42:58Z","receivedAt":"2024-05-15T06:43:02Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `key` variable is used by the `get_value()` function for two\npurposes:\n\n  - It is used to store the result of `git_config_parse_key()`, which is\n    then passed on to `collect_config()`.\n\n  - It is used as a store to convert the provided key to an\n    all-lowercase key when `use_key_regexp` is set.\n\nNeither of these cases warrant a global variable at all. In the former\ncase we can pass the key via `struct collect_config_data`. And in the\nlatter case we really only want to have it as a temporary local variable\nsuch that we can free associated memory.\n\nRefactor the code accordingly to reduce our reliance on global state.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 08a11b7999..5a03dbb452 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -123,7 +123,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static char *key;\n static int use_key_regexp;\n static int do_all;\n static int fixed_value;\n@@ -325,6 +324,7 @@ struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n \tconst char *value_pattern;\n+\tconst char *key;\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n@@ -337,7 +337,7 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, key))\n+\tif (!use_key_regexp && strcmp(key_, data->key))\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n@@ -364,6 +364,7 @@ static int get_value(const struct config_location_options *opts,\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n \t};\n+\tchar *key = NULL;\n \tint i;\n \n \tif (use_key_regexp) {\n@@ -395,6 +396,8 @@ static int get_value(const struct config_location_options *opts,\n \t\t\tret = CONFIG_INVALID_KEY;\n \t\t\tgoto free_strings;\n \t\t}\n+\n+\t\tdata.key = key;\n \t}\n \n \tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n-- \n2.45.GIT\n\n"},{"id":"494784","messageId":"e4dbb4707ec50b7b275cba7b7bd820ecdd05e6f3.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 19/21] builtin/config: track \"fixed value\" option via flags only","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:43:02Z","receivedAt":"2024-05-15T06:43:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We track the \"fixed value\" option via two separate bits: once via the\nglobal variable `fixed_value`, and once via the CONFIG_FLAGS_FIXED_VALUE\nbit in `flags`. This is confusing and may easily lead to issues when one\nis not aware that this is tracked via two separate mechanisms.\n\nRefactor the code to use the flag exclusively. We already pass it to all\nthe required callsites anyway, except for `collect_config()`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5a03dbb452..1f673ebee7 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -125,7 +125,6 @@ struct config_display_options {\n \n static int use_key_regexp;\n static int do_all;\n-static int fixed_value;\n \n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n@@ -328,6 +327,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned flags;\n };\n \n static int collect_config(const char *key_, const char *value_,\n@@ -341,7 +341,8 @@ static int collect_config(const char *key_, const char *value_,\n \t\treturn 0;\n \tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n-\tif (fixed_value && strcmp(data->value_pattern, (value_?value_:\"\")))\n+\tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n+\t    strcmp(data->value_pattern, (value_?value_:\"\")))\n \t\treturn 0;\n \tif (data->regexp &&\n \t    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:\"\"), 0, NULL, 0)))\n@@ -363,6 +364,7 @@ static int get_value(const struct config_location_options *opts,\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n@@ -1117,6 +1119,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *comment_arg = NULL;\n \tint actions = 0;\n+\tunsigned flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Action\")),\n@@ -1139,13 +1142,12 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING(0, \"default\", &display_opts.default_value,\n \t\t\t   N_(\"value\"), N_(\"with --get, use default value when missing entry\")),\n \t\tOPT_STRING(0, \"comment\", &comment_arg, N_(\"value\"), N_(\"human-readable comment string (# will be prepended as needed)\")),\n-\t\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n+\t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_BOOL(0, \"includes\", &location_opts.respect_includes_opt,\n \t\t\t N_(\"respect include directives on lookup\")),\n \t\tOPT_END(),\n \t};\n \tchar *value = NULL, *comment = NULL;\n-\tint flags = 0;\n \tint ret = 0;\n \tstruct key_value_info default_kvi = KVI_INIT;\n \n@@ -1195,7 +1197,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \n \t/* check usage of --fixed-value */\n-\tif (fixed_value) {\n+\tif (flags & CONFIG_FLAGS_FIXED_VALUE) {\n \t\tint allowed_usage = 0;\n \n \t\tswitch (actions) {\n@@ -1226,8 +1228,6 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n \t\t\texit(129);\n \t\t}\n-\n-\t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\n \t}\n \n \tcomment = git_config_prepare_comment_string(comment_arg);\n-- \n2.45.GIT\n\n"},{"id":"494785","messageId":"abe33015b73fdfebe3958574071b1081af22fb90.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 20/21] builtin/config: convert flags to a local variable","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:43:07Z","receivedAt":"2024-05-15T06:43:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Both the `do_all` and `use_key_regexp` bits essentially act like flags\nto `get_value()`. Let's convert them to actual flags so that we can get\nrid of the last two remaining global variables that track options.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 48 +++++++++++++++++++++++++++++-------------------\n 1 file changed, 29 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 1f673ebee7..3b588df8cf 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -123,9 +123,6 @@ struct config_display_options {\n \t.key_delim = ' ', \\\n }\n \n-static int use_key_regexp;\n-static int do_all;\n-\n #define TYPE_BOOL\t\t1\n #define TYPE_INT\t\t2\n #define TYPE_BOOL_OR_INT\t3\n@@ -319,6 +316,9 @@ static int format_config(const struct config_display_options *opts,\n \treturn 0;\n }\n \n+#define GET_VALUE_ALL        (1 << 0)\n+#define GET_VALUE_KEY_REGEXP (1 << 1)\n+\n struct collect_config_data {\n \tconst struct config_display_options *display_opts;\n \tstruct strbuf_list *values;\n@@ -327,6 +327,7 @@ struct collect_config_data {\n \tregex_t *regexp;\n \tregex_t *key_regexp;\n \tint do_not_match;\n+\tunsigned get_value_flags;\n \tunsigned flags;\n };\n \n@@ -337,9 +338,11 @@ static int collect_config(const char *key_, const char *value_,\n \tstruct strbuf_list *values = data->values;\n \tconst struct key_value_info *kvi = ctx->kvi;\n \n-\tif (!use_key_regexp && strcmp(key_, data->key))\n+\tif (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    strcmp(key_, data->key))\n \t\treturn 0;\n-\tif (use_key_regexp && regexec(data->key_regexp, key_, 0, NULL, 0))\n+\tif ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&\n+\t    regexec(data->key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n \tif ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&\n \t    strcmp(data->value_pattern, (value_?value_:\"\")))\n@@ -357,19 +360,21 @@ static int collect_config(const char *key_, const char *value_,\n \n static int get_value(const struct config_location_options *opts,\n \t\t     const struct config_display_options *display_opts,\n-\t\t     const char *key_, const char *regex_, unsigned flags)\n+\t\t     const char *key_, const char *regex_,\n+\t\t     unsigned get_value_flags, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n \tstruct collect_config_data data = {\n \t\t.display_opts = display_opts,\n \t\t.values = &values,\n+\t\t.get_value_flags = get_value_flags,\n \t\t.flags = flags,\n \t};\n \tchar *key = NULL;\n \tint i;\n \n-\tif (use_key_regexp) {\n+\tif (get_value_flags & GET_VALUE_KEY_REGEXP) {\n \t\tchar *tl;\n \n \t\t/*\n@@ -441,7 +446,7 @@ static int get_value(const struct config_location_options *opts,\n \n \tfor (i = 0; i < values.nr; i++) {\n \t\tstruct strbuf *buf = values.items + i;\n-\t\tif (do_all || i == values.nr - 1)\n+\t\tif ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)\n \t\t\tfwrite(buf->buf, 1, buf->len, stdout);\n \t\tstrbuf_release(buf);\n \t}\n@@ -848,11 +853,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tstruct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;\n \tconst char *value_pattern = NULL, *url = NULL;\n \tint flags = 0;\n+\tunsigned get_value_flags = 0;\n \tstruct option opts[] = {\n \t\tCONFIG_LOCATION_OPTIONS(location_opts),\n \t\tOPT_GROUP(N_(\"Filter options\")),\n-\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"return all values for multi-valued config options\")),\n-\t\tOPT_BOOL(0, \"regexp\", &use_key_regexp, N_(\"interpret the name as a regular expression\")),\n+\t\tOPT_BIT(0, \"all\", &get_value_flags, N_(\"return all values for multi-valued config options\"), GET_VALUE_ALL),\n+\t\tOPT_BIT(0, \"regexp\", &get_value_flags, N_(\"interpret the name as a regular expression\"), GET_VALUE_KEY_REGEXP),\n \t\tOPT_STRING(0, \"value\", &value_pattern, N_(\"pattern\"), N_(\"show config with values matching the pattern\")),\n \t\tOPT_BIT(0, \"fixed-value\", &flags, N_(\"use string equality when comparing values to value pattern\"), CONFIG_FLAGS_FIXED_VALUE),\n \t\tOPT_STRING(0, \"url\", &url, N_(\"URL\"), N_(\"show config matching the given URL\")),\n@@ -872,9 +878,12 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \n \tif ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)\n \t\tdie(_(\"--fixed-value only applies with 'value-pattern'\"));\n-\tif (display_opts.default_value && (do_all || url))\n+\tif (display_opts.default_value &&\n+\t    ((get_value_flags & GET_VALUE_ALL) || url))\n \t\tdie(_(\"--default= cannot be used with --all or --url=\"));\n-\tif (url && (do_all || use_key_regexp || value_pattern))\n+\tif (url && ((get_value_flags & GET_VALUE_ALL) ||\n+\t\t    (get_value_flags & GET_VALUE_KEY_REGEXP) ||\n+\t\t    value_pattern))\n \t\tdie(_(\"--url= cannot be used with --all, --regexp or --value\"));\n \n \tlocation_options_init(&location_opts, prefix);\n@@ -885,7 +894,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)\n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n \telse\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern, flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n+\t\t\t\tget_value_flags, flags);\n \n \tlocation_options_release(&location_opts);\n \treturn ret;\n@@ -1290,19 +1300,19 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\t0, flags);\n \t}\n \telse if (actions == ACTION_GET_ALL) {\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL, flags);\n \t}\n \telse if (actions == ACTION_GET_REGEXP) {\n \t\tdisplay_opts.show_keys = 1;\n-\t\tuse_key_regexp = 1;\n-\t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n-\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1], flags);\n+\t\tret = get_value(&location_opts, &display_opts, argv[0], argv[1],\n+\t\t\t\tGET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n-- \n2.45.GIT\n\n"},{"id":"494786","messageId":"a5cb075fcd5ff3ec3da1cc299caae7c3c24dfedb.1715755055.git.ps@pks.im","threadId":"61452","inReplyTo":"cover.1715755055.git.ps@pks.im","subject":"[PATCH v3 21/21] builtin/config: pass data between callbacks via local variables","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-05-15T06:43:12Z","receivedAt":"2024-05-15T06:43:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We use several global variables to pass data between callers and\ncallbacks in `get_color()` and `get_colorbool()`. Convert those to use\ncallback data structures instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 90 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 52 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3b588df8cf..c38264c999 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -518,21 +518,24 @@ static char *normalize_value(const char *key, const char *value,\n \tBUG(\"cannot normalize type %d\", type);\n }\n \n-static int get_color_found;\n-static const char *get_color_slot;\n-static const char *get_colorbool_slot;\n-static char parsed_color[COLOR_MAXLEN];\n+struct get_color_config_data {\n+\tint get_color_found;\n+\tconst char *get_color_slot;\n+\tchar parsed_color[COLOR_MAXLEN];\n+};\n \n static int git_get_color_config(const char *var, const char *value,\n \t\t\t\tconst struct config_context *ctx UNUSED,\n-\t\t\t\tvoid *cb UNUSED)\n+\t\t\t\tvoid *cb)\n {\n-\tif (!strcmp(var, get_color_slot)) {\n+\tstruct get_color_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_color_slot)) {\n \t\tif (!value)\n \t\t\tconfig_error_nonbool(var);\n-\t\tif (color_parse(value, parsed_color) < 0)\n+\t\tif (color_parse(value, data->parsed_color) < 0)\n \t\t\treturn -1;\n-\t\tget_color_found = 1;\n+\t\tdata->get_color_found = 1;\n \t}\n \treturn 0;\n }\n@@ -540,66 +543,77 @@ static int git_get_color_config(const char *var, const char *value,\n static void get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n-\tget_color_slot = var;\n-\tget_color_found = 0;\n-\tparsed_color[0] = '\\0';\n-\tconfig_with_options(git_get_color_config, NULL,\n+\tstruct get_color_config_data data = {\n+\t\t.get_color_slot = var,\n+\t\t.parsed_color[0] = '\\0',\n+\t};\n+\n+\tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (!get_color_found && def_color) {\n-\t\tif (color_parse(def_color, parsed_color) < 0)\n+\tif (!data.get_color_found && def_color) {\n+\t\tif (color_parse(def_color, data.parsed_color) < 0)\n \t\t\tdie(_(\"unable to parse default color value\"));\n \t}\n \n-\tfputs(parsed_color, stdout);\n+\tfputs(data.parsed_color, stdout);\n }\n \n-static int get_colorbool_found;\n-static int get_diff_color_found;\n-static int get_color_ui_found;\n+struct get_colorbool_config_data {\n+\tint get_colorbool_found;\n+\tint get_diff_color_found;\n+\tint get_color_ui_found;\n+\tconst char *get_colorbool_slot;\n+};\n+\n static int git_get_colorbool_config(const char *var, const char *value,\n \t\t\t\t    const struct config_context *ctx UNUSED,\n-\t\t\t\t    void *data UNUSED)\n+\t\t\t\t    void *cb)\n {\n-\tif (!strcmp(var, get_colorbool_slot))\n-\t\tget_colorbool_found = git_config_colorbool(var, value);\n+\tstruct get_colorbool_config_data *data = cb;\n+\n+\tif (!strcmp(var, data->get_colorbool_slot))\n+\t\tdata->get_colorbool_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"diff.color\"))\n-\t\tget_diff_color_found = git_config_colorbool(var, value);\n+\t\tdata->get_diff_color_found = git_config_colorbool(var, value);\n \telse if (!strcmp(var, \"color.ui\"))\n-\t\tget_color_ui_found = git_config_colorbool(var, value);\n+\t\tdata->get_color_ui_found = git_config_colorbool(var, value);\n \treturn 0;\n }\n \n static int get_colorbool(const struct config_location_options *opts,\n \t\t\t const char *var, int print)\n {\n-\tget_colorbool_slot = var;\n-\tget_colorbool_found = -1;\n-\tget_diff_color_found = -1;\n-\tget_color_ui_found = -1;\n-\tconfig_with_options(git_get_colorbool_config, NULL,\n+\tstruct get_colorbool_config_data data = {\n+\t\t.get_colorbool_slot = var,\n+\t\t.get_colorbool_found = -1,\n+\t\t.get_diff_color_found = -1,\n+\t\t.get_color_ui_found = -1,\n+\t};\n+\n+\tconfig_with_options(git_get_colorbool_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n-\tif (get_colorbool_found < 0) {\n-\t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n-\t\t\tget_colorbool_found = get_diff_color_found;\n-\t\tif (get_colorbool_found < 0)\n-\t\t\tget_colorbool_found = get_color_ui_found;\n+\tif (data.get_colorbool_found < 0) {\n+\t\tif (!strcmp(data.get_colorbool_slot, \"color.diff\"))\n+\t\t\tdata.get_colorbool_found = data.get_diff_color_found;\n+\t\tif (data.get_colorbool_found < 0)\n+\t\t\tdata.get_colorbool_found = data.get_color_ui_found;\n \t}\n \n-\tif (get_colorbool_found < 0)\n+\tif (data.get_colorbool_found < 0)\n \t\t/* default value if none found in config */\n-\t\tget_colorbool_found = GIT_COLOR_AUTO;\n+\t\tdata.get_colorbool_found = GIT_COLOR_AUTO;\n \n-\tget_colorbool_found = want_color(get_colorbool_found);\n+\tdata.get_colorbool_found = want_color(data.get_colorbool_found);\n \n \tif (print) {\n-\t\tprintf(\"%s\\n\", get_colorbool_found ? \"true\" : \"false\");\n+\t\tprintf(\"%s\\n\", data.get_colorbool_found ? \"true\" : \"false\");\n \t\treturn 0;\n \t} else\n-\t\treturn get_colorbool_found ? 0 : 1;\n+\t\treturn data.get_colorbool_found ? 0 : 1;\n }\n \n static void check_write(const struct git_config_source *source)\n-- \n2.45.GIT\n\n"}]}