{"thread":{"id":"54676","subject":"[PATCH 0/7] config: add --literal-value option","startedAt":"2020-11-19T15:52:48Z","lastAt":"2020-12-01T04:47:03Z","messageCount":72,"participants":["Derrick Stolee via GitGitGadget","Junio C Hamano","brian m. carlson","Martin Ågren","Ævar Arnfjörð Bjarmason","Derrick Stolee","Jeff King","Emily Shaffer","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"410359","messageId":"pull.796.git.1605801143.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":null,"subject":"[PATCH 0/7] config: add --literal-value option","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:16Z","receivedAt":"2020-11-19T15:52:48Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"As reported [1], 'git maintenance unregister' fails when a repository is\nlocated in a directory with regex glob characters.\n\n[1] \nhttps://lore.kernel.org/git/2c2db228-069a-947d-8446-89f4d3f6181a@gmail.com/T/#mb96fa4187a0d6aeda097cd95804a8aafc0273022\n\nThe discussed solution was to modify 'git config' to specify that the\n'value_regex' argument should be treated as an exact string match. This is\nthe primary change in this series, with an additional patch at the end to\nmake 'git maintenance [un]register' use this option, when necessary.\n\nThanks, -Stolee\n\nDerrick Stolee (7):\n  t1300: test \"set all\" mode with value_regex\n  t1300: add test for --replace-all with value_regex\n  config: convert multi_replace to flags\n  config: add --literal-value option, un-implemented\n  config: plumb --literal-value into config API\n  config: implement --literal-value with --get*\n  maintenance: use 'git config --literal-value'\n\n Documentation/git-config.txt |  20 ++++--\n builtin/branch.c             |   4 +-\n builtin/config.c             |  50 ++++++++++---\n builtin/gc.c                 |   5 +-\n builtin/remote.c             |   8 ++-\n config.c                     |  29 ++++----\n config.h                     |  24 +++++--\n t/t1300-config.sh            | 136 +++++++++++++++++++++++++++++++++++\n t/t7900-maintenance.sh       |  12 ++++\n 9 files changed, 248 insertions(+), 40 deletions(-)\n\n\nbase-commit: 0016b618182f642771dc589cf0090289f9fe1b4f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-796%2Fderrickstolee%2Fmaintenance%2Fconfig-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-796/derrickstolee/maintenance/config-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/796\n-- \ngitgitgadget\n"},{"id":"410360","messageId":"2da2131114eb47e70ccaf8fb9c51bf7fb5b173b0.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:17Z","receivedAt":"2020-11-19T15:52:48Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWithout additional modifiers, 'git config' attempts to set a single\nvalue in the .git/config file. When the value_regex parameter is\nsupplied, this command behaves in a non-trivial manner.\n\nConsider 'git config key value value_regex'. The expected behavior\nis as follows:\n\n1. If there are multiple existing values that match 'value_regex',\n   then the command fails. Users should use --replace-all instead.\n\n2. If there is one existing value that matches 'value_regex', then\n   the new config has one entry where 'key=value'.\n\n3. If there is no existing values match 'value_regex', then the\n   'key=value' pair is appended, making this 'key' a multi-valued\n   config setting.\n\nAdd a test that demonstrates these options.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 97ebfe1f9d..ef56b08070 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1914,4 +1914,39 @@ test_expect_success '--replace-all does not invent newlines' '\n \ttest_cmp expect .git/config\n '\n \n+test_expect_success 'set all config with value_regex' '\n+\tq_to_tab >initial <<-\\EOF &&\n+\t[abc]\n+\tQkey = one\n+\tEOF\n+\n+\tcp initial .git/config &&\n+\tgit config abc.key two a+ &&\n+\tq_to_tab >expect <<-\\EOF &&\n+\t[abc]\n+\tQkey = one\n+\tQkey = two\n+\tEOF\n+\ttest_cmp expect .git/config &&\n+\n+\ttest_must_fail git config abc.key three o+ 2>err &&\n+\ttest_i18ngrep \"has multiple values\" err &&\n+\tgit config abc.key three a+ &&\n+\tq_to_tab >expect <<-\\EOF &&\n+\t[abc]\n+\tQkey = one\n+\tQkey = two\n+\tQkey = three\n+\tEOF\n+\ttest_cmp expect .git/config &&\n+\n+\tcp initial .git/config &&\n+\tgit config abc.key three o+ &&\n+\tq_to_tab >expect <<-\\EOF &&\n+\t[abc]\n+\tQkey = three\n+\tEOF\n+\ttest_cmp expect .git/config\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410361","messageId":"1237289706d3786b332c8f5930402088a6382258.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 2/7] t1300: add test for --replace-all with value_regex","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:18Z","receivedAt":"2020-11-19T15:52:48Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe --replace-all option was added in 4ddba79d (git-config-set: add more\noptions) but was not tested along with the 'value_regex' parameter.\nSince we will be updating this option to optionally treat 'value_regex'\nas a fixed string, let's add a test here that documents the current\nbehavior.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex ef56b08070..74e0f84c0a 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1949,4 +1949,20 @@ test_expect_success 'set all config with value_regex' '\n \ttest_cmp expect .git/config\n '\n \n+test_expect_success '--replace-all and value_regex' '\n+\tq_to_tab >.git/config <<-\\EOF &&\n+\t[abc]\n+\tQkey = one\n+\tQkey = two\n+\tQkey = three\n+\tEOF\n+\tq_to_tab >expect <<-\\EOF &&\n+\t[abc]\n+\tQkey = four\n+\tQkey = three\n+\tEOF\n+\tgit config --replace-all abc.key four \"o+\" &&\n+\ttest_cmp expect .git/config\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410362","messageId":"5881b2d987e3e8575d054a64fe21fbe7e7967aba.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 5/7] config: plumb --literal-value into config API","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:21Z","receivedAt":"2020-11-19T15:52:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe git_config_set_multivar_in_file_gently() and related methods now\ntake a 'flags' bitfield, so add a new bit representing the\n--literal-value option from 'git config'. This alters the purpose of the\nvalue_regex parameter to be an exact string match. This requires some\ninitialization changes in git_config_set_multivar_in_file_gently() and a\nnew strcmp() call in the matches() method.\n\nThe new CONFIG_FLAGS_LITERAL_VALUE flag is initialized in\nbuiltin/config.c based on the --literal-value option, and that needs to\nbe updated in several callers.\n\nThis patch only affects some of the modes of 'git config', and the rest\nwill be completed in the next change.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 16 +++++++++++-----\n config.c          |  5 +++++\n config.h          |  7 +++++++\n t/t1300-config.sh | 48 +++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 71 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex ad6c695737..6ab5a0d68f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -626,6 +626,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n {\n \tint nongit = !startup_info->have_repository;\n \tchar *value;\n+\tint flags = 0;\n \n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n@@ -761,6 +762,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\terror(_(\"--literal only applies with 'value_regex'\"));\n \t\tusage_builtin_config();\n \t}\n+\tif (literal)\n+\t\tflags = CONFIG_FLAGS_LITERAL_VALUE;\n \n \tif (actions & PAGING_ACTIONS)\n \t\tsetup_auto_pager(\"config\", 1);\n@@ -823,7 +826,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 0);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write();\n@@ -832,7 +836,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value,\n-\t\t\t\t\t\t\t      CONFIG_REGEX_NONE, 0);\n+\t\t\t\t\t\t\t      CONFIG_REGEX_NONE,\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write();\n@@ -841,7 +846,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value, argv[2],\n-\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -868,7 +873,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 0);\n+\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t      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);\n@@ -878,7 +884,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/config.c b/config.c\nindex 4841c68a91..5028c1e736 100644\n--- a/config.c\n+++ b/config.c\n@@ -2402,6 +2402,7 @@ struct config_store_data {\n \tsize_t baselen;\n \tchar *key;\n \tint do_not_match;\n+\tconst char *literal_value;\n \tregex_t *value_regex;\n \tint multi_replace;\n \tstruct {\n@@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,\n {\n \tif (strcmp(key, store->key))\n \t\treturn 0; /* not ours */\n+\tif (store->literal_value)\n+\t\treturn !strcmp(store->literal_value, value);\n \tif (!store->value_regex)\n \t\treturn 1; /* always matches */\n \tif (store->value_regex == CONFIG_REGEX_NONE)\n@@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\tstore.value_regex = NULL;\n \t\telse if (value_regex == CONFIG_REGEX_NONE)\n \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n+\t\telse if (flags & CONFIG_FLAGS_LITERAL_VALUE)\n+\t\t\tstore.literal_value = value_regex;\n \t\telse {\n \t\t\tif (value_regex[0] == '!') {\n \t\t\t\tstore.do_not_match = 1;\ndiff --git a/config.h b/config.h\nindex 266eb22e46..ddb81e1e32 100644\n--- a/config.h\n+++ b/config.h\n@@ -77,6 +77,13 @@ enum config_event_t {\n  */\n #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n \n+/*\n+ * When CONFIG_FLAGS_LITERAL_VALUE is specified, match key/value pairs\n+ * by string comparison (not regex match) to the provided value_regex\n+ * parameter.\n+ */\n+#define CONFIG_FLAGS_LITERAL_VALUE (1 << 1)\n+\n /*\n  * The parser event function (if not NULL) is called with the event type and\n  * the begin/end offsets of the parsed elements.\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 73f5ca4361..ed12b4f7a0 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1978,4 +1978,52 @@ test_expect_success 'refuse --literal-value for incompatible actions' '\n \ttest_must_fail git config --literal-value --edit\n '\n \n+test_expect_success '--literal-value uses exact string matching' '\n+\tGLOB=\"a+b*c?d[e]f.g\" &&\n+\tq_to_tab >initial <<-EOF &&\n+\t[literal]\n+\tQtest = $GLOB\n+\tEOF\n+\n+\tcp initial .git/config &&\n+\tgit config literal.test bogus \"$GLOB\" &&\n+\tq_to_tab >expect <<-EOF &&\n+\t[literal]\n+\tQtest = $GLOB\n+\tQtest = bogus\n+\tEOF\n+\ttest_cmp expect .git/config &&\n+\tcp initial .git/config &&\n+\tgit config --literal-value literal.test bogus \"$GLOB\" &&\n+\tq_to_tab >expect <<-EOF &&\n+\t[literal]\n+\tQtest = bogus\n+\tEOF\n+\ttest_cmp expect .git/config &&\n+\n+\tcp initial .git/config &&\n+\ttest_must_fail git config --unset literal.test \"$GLOB\" &&\n+\tgit config --literal-value --unset literal.test \"$GLOB\" &&\n+\ttest_must_fail git config literal.test &&\n+\n+\tcp initial .git/config &&\n+\ttest_must_fail git config --unset-all literal.test \"$GLOB\" &&\n+\tgit config --literal-value --unset-all literal.test \"$GLOB\" &&\n+\ttest_must_fail git config literal.test &&\n+\n+\tcp initial .git/config &&\n+\tgit config --replace-all literal.test bogus \"$GLOB\" &&\n+\tq_to_tab >expect <<-EOF &&\n+\t[literal]\n+\tQtest = $GLOB\n+\tQtest = bogus\n+\tEOF\n+\ttest_cmp expect .git/config &&\n+\n+\tcp initial .git/config &&\n+\tgit config --replace-all literal.test bogus \"$GLOB\" &&\n+\tgit config --literal-value --replace-all literal.test bogus \"$GLOB\" &&\n+\ttest_cmp_config bogus literal.test\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410363","messageId":"28493184b60d3fa46cde346eaae6128bce87c114.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 4/7] config: add --literal-value option, un-implemented","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:20Z","receivedAt":"2020-11-19T15:52:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe 'git config' builtin takes a 'value_regex' parameter for several\nactions. This can cause confusion when expecting exact value matches\ninstead of regex matches, especially when the input string contains glob\ncharacters. While callers can escape the patterns themselves, it would\nbe more friendly to allow an argument to disable the pattern matching in\nfavor of an exact string match.\n\nAdd a new '--literal-value' option that does not currently change the\nbehavior. The implementation will follow for each appropriate action.\nFor now, check and test that --literal-value will abort the command when\nincluded with an incompatible action.\n\nThe name '--literal-value' was chosen over something simpler like\n'--literal' because some commands allow regular expressions on the\nkey in addition to the value.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-config.txt | 20 +++++++++++++-------\n builtin/config.c             | 17 +++++++++++++++++\n t/t1300-config.sh            | 13 +++++++++++++\n 3 files changed, 43 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7573160f21..e244dd73f0 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,15 +9,15 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] [--literal-value] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n 'git config' [<file-option>] [--type=<type>] --add name value\n-'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--literal-value] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] [--name-only] --get-regexp name_regex [value_regex]\n 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n-'git config' [<file-option>] --unset name [value_regex]\n-'git config' [<file-option>] --unset-all name [value_regex]\n+'git config' [<file-option>] [--literal-value] --unset name [value_regex]\n+'git config' [<file-option>] [--literal-value] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n 'git config' [<file-option>] --remove-section name\n 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n@@ -165,6 +165,12 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n+--literal-value::\n+\tWhen used with the `value_regex` argument, treat `value_regex` as\n+\tan exact string instead of a regular expression. This will restrict\n+\tthe name/value pairs that are matched to only those where the value\n+\tis exactly equal to the `value_regex`.\n+\n --type <type>::\n   'git config' will ensure that any input or output is valid under the given\n   type constraint(s), and will canonicalize outgoing values in `<type>`'s\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e7c7f3d455..ad6c695737 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -34,6 +34,7 @@ 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 literal;\n \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -52,6 +53,16 @@ static int show_scope;\n #define ACTION_GET_COLORBOOL (1<<14)\n #define ACTION_GET_URLMATCH (1<<15)\n \n+#define ACTION_LITERAL_ALLOWED (\\\n+\tACTION_GET |\\\n+\tACTION_GET_ALL |\\\n+\tACTION_GET_REGEXP |\\\n+\tACTION_REPLACE_ALL |\\\n+\tACTION_UNSET |\\\n+\tACTION_UNSET_ALL |\\\n+\tACTION_SET_ALL\\\n+)\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@@ -141,6 +152,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\tOPT_BOOL(0, \"literal-value\", &literal, N_(\"use literal equality when matching values\")),\n \tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n@@ -745,6 +757,11 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_builtin_config();\n \t}\n \n+\tif (literal && !(actions & ACTION_LITERAL_ALLOWED)) {\n+\t\terror(_(\"--literal only applies with 'value_regex'\"));\n+\t\tusage_builtin_config();\n+\t}\n+\n \tif (actions & PAGING_ACTIONS)\n \t\tsetup_auto_pager(\"config\", 1);\n \ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 74e0f84c0a..73f5ca4361 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1965,4 +1965,17 @@ test_expect_success '--replace-all and value_regex' '\n \ttest_cmp expect .git/config\n '\n \n+test_expect_success 'refuse --literal-value for incompatible actions' '\n+\tgit config dev.null bogus &&\n+\ttest_must_fail git config --literal-value --add dev.null bogus &&\n+\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --literal-value --rename-section dev null &&\n+\ttest_must_fail git config --literal-value --remove-section dev &&\n+\ttest_must_fail git config --literal-value --list &&\n+\ttest_must_fail git config --literal-value --get-color dev.null &&\n+\ttest_must_fail git config --literal-value --get-colorbool dev.null &&\n+\ttest_must_fail git config --literal-value --edit\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410364","messageId":"c9caed3854cfe694f105b19657cf57f73412ad4a.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 3/7] config: convert multi_replace to flags","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:19Z","receivedAt":"2020-11-19T15:52:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWe will extend the flexibility of the config API. Before doing so, let's\ntake an existing 'int multi_replace' parameter and replace it with a new\n'int flags' parameter that can take multiple options as a bit field.\n\nUpdate all callers that specified multi_replace to now specify the\nCONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the\ndocumentation of git_config_set_multivar_in_file() including a clear\nlabeling of its arguments. Other config API methods in config.h do not\nrequire a change to their prototypes, but they have an equivalent update\nto their implementations.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/branch.c |  4 ++--\n builtin/config.c |  6 ++++--\n builtin/remote.c |  8 +++++---\n config.c         | 24 ++++++++++++------------\n config.h         | 17 +++++++++++++----\n 5 files changed, 36 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex e82301fb1b..5ce3844d22 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"Branch '%s' has no upstream information\"), branch->name);\n \n \t\tstrbuf_addf(&buf, \"branch.%s.remote\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"branch.%s.merge\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_release(&buf);\n \t} else if (argc > 0 && argc <= 2) {\n \t\tif (filter.kind != FILTER_REFS_BRANCHES)\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5e39f61885..e7c7f3d455 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -823,7 +823,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 1);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -859,7 +860,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 1);\n+\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex c8240e9fcd..29b1652975 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -708,7 +708,7 @@ static int mv(int argc, const char **argv)\n \n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new_name);\n-\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \tstrbuf_addf(&old_remote_context, \":refs/remotes/%s/\", rename.old_name);\n \tfor (i = 0; i < oldremote->fetch.raw_nr; i++) {\n \t\tchar *ptr;\n@@ -1485,7 +1485,8 @@ static int update(int argc, const char **argv)\n \n static int remove_all_fetch_refspecs(const char *key)\n {\n-\treturn git_config_set_multivar_gently(key, NULL, NULL, 1);\n+\treturn git_config_set_multivar_gently(key, NULL, NULL,\n+\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n }\n \n static void add_branches(struct remote *remote, const char **branches,\n@@ -1674,7 +1675,8 @@ static int set_url(int argc, const char **argv)\n \tif (!delete_mode)\n \t\tgit_config_set_multivar(name_buf.buf, newurl, oldurl, 0);\n \telse\n-\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl, 1);\n+\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl,\n+\t\t\t\t\tCONFIG_FLAGS_MULTI_REPLACE);\n out:\n \tstrbuf_release(&name_buf);\n \treturn 0;\ndiff --git a/config.c b/config.c\nindex 2b79fe76ad..4841c68a91 100644\n--- a/config.c\n+++ b/config.c\n@@ -2716,9 +2716,9 @@ void git_config_set(const char *key, const char *value)\n  * if value_regex!=NULL, disregard key/value pairs where value does not match.\n  * if value_regex==CONFIG_REGEX_NONE, do not match any existing values\n  *     (only add a new one)\n- * if multi_replace==0, nothing, or only one matching key/value is replaced,\n- *     else all matching key/values (regardless how many) are removed,\n- *     before the new pair is written.\n+ * if (flags & CONFIG_FLAGS_MULTI_REPLACE) == 0, at most one matching\n+ *     key/value is replaced, else all matching key/values (regardless\n+ *     how many) are removed, before the new pair is written.\n  *\n  * Returns 0 on success.\n  *\n@@ -2739,7 +2739,7 @@ void git_config_set(const char *key, const char *value)\n int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\t\t\t   const char *key, const char *value,\n \t\t\t\t\t   const char *value_regex,\n-\t\t\t\t\t   int multi_replace)\n+\t\t\t\t\t   int flags)\n {\n \tint fd = -1, in_fd = -1;\n \tint ret;\n@@ -2756,7 +2756,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \tif (ret)\n \t\tgoto out_free;\n \n-\tstore.multi_replace = multi_replace;\n+\tstore.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0;\n \n \tif (!config_filename)\n \t\tconfig_filename = filename_buf = git_pathdup(\"config\");\n@@ -2845,7 +2845,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n \t\t/* if nothing to unset, or too many matches, error out */\n \t\tif ((store.seen_nr == 0 && value == NULL) ||\n-\t\t    (store.seen_nr > 1 && multi_replace == 0)) {\n+\t\t    (store.seen_nr > 1 && !store.multi_replace)) {\n \t\t\tret = CONFIG_NOTHING_SET;\n \t\t\tgoto out_free;\n \t\t}\n@@ -2984,10 +2984,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n void git_config_set_multivar_in_file(const char *config_filename,\n \t\t\t\t     const char *key, const char *value,\n-\t\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t\t     const char *value_regex, int flags)\n {\n \tif (!git_config_set_multivar_in_file_gently(config_filename, key, value,\n-\t\t\t\t\t\t    value_regex, multi_replace))\n+\t\t\t\t\t\t    value_regex, flags))\n \t\treturn;\n \tif (value)\n \t\tdie(_(\"could not set '%s' to '%s'\"), key, value);\n@@ -2996,17 +2996,17 @@ void git_config_set_multivar_in_file(const char *config_filename,\n }\n \n int git_config_set_multivar_gently(const char *key, const char *value,\n-\t\t\t\t   const char *value_regex, int multi_replace)\n+\t\t\t\t   const char *value_regex, int flags)\n {\n \treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,\n-\t\t\t\t\t\t      multi_replace);\n+\t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n-\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t     const char *value_regex, int flags)\n {\n \tgit_config_set_multivar_in_file(NULL, key, value, value_regex,\n-\t\t\t\t\tmulti_replace);\n+\t\t\t\t\tflags);\n }\n \n static int section_name_match (const char *buf, const char *name)\ndiff --git a/config.h b/config.h\nindex 060874488f..266eb22e46 100644\n--- a/config.h\n+++ b/config.h\n@@ -70,6 +70,13 @@ enum config_event_t {\n \tCONFIG_EVENT_ERROR\n };\n \n+/*\n+ * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n+ * are removed before a new pair is written. If the flag is not present,\n+ * then set operations replace only the first match.\n+ */\n+#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n+\n /*\n  * The parser event function (if not NULL) is called with the event type and\n  * the begin/end offsets of the parsed elements.\n@@ -276,13 +283,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n  * - the value regex, as a string. It will disregard key/value pairs where value\n  *   does not match.\n  *\n- * - a multi_replace value, as an int. If value is equal to zero, nothing or only\n- *   one matching key/value is replaced, else all matching key/values (regardless\n- *   how many) are removed, before the new pair is written.\n+ * - a flags value with bits corresponding to the CONFIG_FLAG_* macros.\n  *\n  * It returns 0 on success.\n  */\n-void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n+void git_config_set_multivar_in_file(const char *config_filename,\n+\t\t\t\t     const char *key,\n+\t\t\t\t     const char *value,\n+\t\t\t\t     const char *value_regex,\n+\t\t\t\t     int flags);\n \n /**\n  * rename or remove sections in the config file\n-- \ngitgitgadget\n\n"},{"id":"410366","messageId":"1d1c2fd23e805e0f25edd324a614715b1d11a2bb.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 7/7] maintenance: use 'git config --literal-value'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:23Z","receivedAt":"2020-11-19T15:52:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen a repository's leading directories contain regex glob characters,\nthe config calls for 'git maintenance register' and 'git maintenance\nunregister' are not careful enough. Use the new --literal-value option\nto direct the config machinery to use exact string matches. This is a\nmore robust option than excaping these arguments in a piecemeal fashion.\n\nReported-by: Emily Shaffer <emilyshaffer@google.com>\nReported-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/gc.c           |  5 +++--\n t/t7900-maintenance.sh | 12 ++++++++++++\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex e3098ef6a1..0784bbdc6a 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n \n \tconfig_get.git_cmd = 1;\n-\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n+\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n+\t\t     \"--literal-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t\t NULL);\n@@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n \n \tconfig_unset.git_cmd = 1;\n \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n-\t\t     \"maintenance.repo\",\n+\t\t     \"--literal-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t     NULL);\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex 20184e96e1..2ee5512158 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n \ttest_cmp before actual\n '\n \n+test_expect_success 'register and unregister with glob characters' '\n+\tGLOB=\"a+b*c\" &&\n+\tgit init \"$GLOB\" &&\n+\tgit -C \"$GLOB\" maintenance register &&\n+\tgit config --get-all --show-origin maintenance.repo &&\n+\tgit config --get-all --global --literal-value \\\n+\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n+\tgit -C \"$GLOB\" maintenance unregister &&\n+\ttest_must_fail git config --get-all --global --literal-value \\\n+\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n+'\n+\n test_expect_success 'start from empty cron table' '\n \tGIT_TEST_CRONTAB=\"test-tool crontab cron.txt\" git maintenance start &&\n \n-- \ngitgitgadget\n"},{"id":"410365","messageId":"7c8e3769c723405a421de3b44b4c90ce48b26e4d.1605801143.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH 6/7] config: implement --literal-value with --get*","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-19T15:52:22Z","receivedAt":"2020-11-19T15:52:50Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe config builtin does its own regex matching of values for the --get,\n--get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\nto the get_value() method so we can initialize the value_regex argument\nas a fixed string instead of a regex pattern.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 15 ++++++++++-----\n t/t1300-config.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 34 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 6ab5a0d68f..1d5cfab8f1 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -14,6 +14,7 @@ static const char *const builtin_config_usage[] = {\n \n static char *key;\n static regex_t *key_regexp;\n+static const char *value_regex;\n static regex_t *regexp;\n static int show_keys;\n static int omit_values;\n@@ -298,6 +299,8 @@ static int collect_config(const char *key_, const char *value_, void *cb)\n \t\treturn 0;\n \tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n \t\treturn 0;\n+\tif (literal && strcmp(value_regex, (value_?value_:\"\")))\n+\t\treturn 0;\n \tif (regexp != NULL &&\n \t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n@@ -308,7 +311,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)\n \treturn format_config(&values->items[values->nr++], key_, value_);\n }\n \n-static int get_value(const char *key_, const char *regex_)\n+static int get_value(const char *key_, const char *regex_, int flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -345,7 +348,9 @@ static int get_value(const char *key_, const char *regex_)\n \t\t}\n \t}\n \n-\tif (regex_) {\n+\tif (regex_ && (flags & CONFIG_FLAGS_LITERAL_VALUE))\n+\t\tvalue_regex = regex_;\n+\telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdo_not_match = 1;\n \t\t\tregex_++;\n@@ -850,19 +855,19 @@ int cmd_config(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]);\n+\t\treturn 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]);\n+\t\treturn 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]);\n+\t\treturn get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex ed12b4f7a0..8531d01c7c 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2026,4 +2026,28 @@ test_expect_success '--literal-value uses exact string matching' '\n \ttest_cmp_config bogus literal.test\n '\n \n+test_expect_success '--get and --get-all with --literal-value' '\n+\tGLOB=\"a+b*c?d[e]f.g\" &&\n+\tq_to_tab >.git/config <<-EOF &&\n+\t[literal]\n+\tQtest = bogus\n+\tQtest = $GLOB\n+\tEOF\n+\n+\tgit config --get literal.test bogus &&\n+\ttest_must_fail git config --get literal.test \"$GLOB\" &&\n+\tgit config --get --literal-value literal.test \"$GLOB\" &&\n+\ttest_must_fail git config --get --literal-value literal.test non-existent &&\n+\n+\tgit config --get-all literal.test bogus &&\n+\ttest_must_fail git config --get-all literal.test \"$GLOB\" &&\n+\tgit config --get-all --literal-value literal.test \"$GLOB\" &&\n+\ttest_must_fail git config --get-all --literal-value literal.test non-existent &&\n+\n+\tgit config --get-regexp literal+ bogus &&\n+\ttest_must_fail git config  --get-regexp literal+ \"$GLOB\" &&\n+\tgit config --get-regexp --literal-value literal+ \"$GLOB\" &&\n+\ttest_must_fail git config --get-regexp --literal-value literal+ non-existent\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410388","messageId":"xmqqo8jtvvby.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"2da2131114eb47e70ccaf8fb9c51bf7fb5b173b0.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-19T22:24:33Z","receivedAt":"2020-11-19T22:25:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +test_expect_success 'set all config with value_regex' '\n> +\tq_to_tab >initial <<-\\EOF &&\n> +\t[abc]\n> +\tQkey = one\n> +\tEOF\n> +\n> +\tcp initial .git/config &&\n\nNot a new problem with this patch, but does the above pattern\nintroduce potential problems?  I am wondering if overwriting the\nconfig file with a little piece that has only the stuff the test is\ninterested in, while wiping the parts that may be essential for\nrepository integrity (e.g. \"extensions.objectFormat\"), is OK in the\nlong run (brian cc'ed for his sha256 work).  There also are\nautodetected crlf settings etc. that are in the .git/config when a\ntest repository is created, and we probably would want to keep them\nintact.\n"},{"id":"410389","messageId":"xmqqk0uhvuy9.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"c9caed3854cfe694f105b19657cf57f73412ad4a.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/7] config: convert multi_replace to flags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-19T22:32:46Z","receivedAt":"2020-11-19T22:32:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> We will extend the flexibility of the config API. Before doing so, let's\n> take an existing 'int multi_replace' parameter and replace it with a new\n> 'int flags' parameter that can take multiple options as a bit field.\n\nNice.  I was afraid we may have to _add_ a new parameter, but here,\nwe can turn an existing bool into a flags word, which is great.  But\nplease use \"unsigned\" for such flags, just out of inertia/convention.\n\n> @@ -70,6 +70,13 @@ enum config_event_t {\n>  \tCONFIG_EVENT_ERROR\n>  };\n>  \n> +/*\n> + * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n> + * are removed before a new pair is written. If the flag is not present,\n> + * then set operations replace only the first match.\n> + */\n> +#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n> +\n\nThe description is clear, but how far is this comment from the\nactual parameter definition where \"flag\" is used?  What I am trying\nto get at is if it is clear enough to the readers, when we say \"when\n... is specified\", where they can specify it.  If it is not clear,\nperhaps we want to start the above comment like so:\n\n\t/*\n\t * These are bits in the flags word to be given to functions\n\t * like git_config_set_multivar_in_file(), etc.\n\t *\n\t * When CONFIG_FLAGS_MULTI_REPLACE is specified, ...\n\n\n> @@ -276,13 +283,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n>   * - the value regex, as a string. It will disregard key/value pairs where value\n>   *   does not match.\n>   *\n> - * - a multi_replace value, as an int. If value is equal to zero, nothing or only\n> - *   one matching key/value is replaced, else all matching key/values (regardless\n> - *   how many) are removed, before the new pair is written.\n> + * - a flags value with bits corresponding to the CONFIG_FLAG_* macros.\n\nThis side is good---it tells readers what the bits in the flags word\nare called (i.e. they can look for CONFIG_FLAGS_ in the file).\n\n>   *\n>   * It returns 0 on success.\n>   */\n> -void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n> +void git_config_set_multivar_in_file(const char *config_filename,\n> +\t\t\t\t     const char *key,\n> +\t\t\t\t     const char *value,\n> +\t\t\t\t     const char *value_regex,\n> +\t\t\t\t     int flags);\n>  \n>  /**\n>   * rename or remove sections in the config file\n"},{"id":"410390","messageId":"xmqqft55vuhv.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"28493184b60d3fa46cde346eaae6128bce87c114.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/7] config: add --literal-value option, un-implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-19T22:42:36Z","receivedAt":"2020-11-19T22:43:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +--literal-value::\n> +\tWhen used with the `value_regex` argument, treat `value_regex` as\n> +\tan exact string instead of a regular expression. This will restrict\n> +\tthe name/value pairs that are matched to only those where the value\n> +\tis exactly equal to the `value_regex`.\n\nMakes sense.\n\n>  --type <type>::\n>    'git config' will ensure that any input or output is valid under the given\n>    type constraint(s), and will canonicalize outgoing values in `<type>`'s\n> diff --git a/builtin/config.c b/builtin/config.c\n> index e7c7f3d455..ad6c695737 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -34,6 +34,7 @@ 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 literal;\n>  \n>  #define ACTION_GET (1<<0)\n>  #define ACTION_GET_ALL (1<<1)\n> @@ -52,6 +53,16 @@ static int show_scope;\n>  #define ACTION_GET_COLORBOOL (1<<14)\n>  #define ACTION_GET_URLMATCH (1<<15)\n>  \n> +#define ACTION_LITERAL_ALLOWED (\\\n> +\tACTION_GET |\\\n> +\tACTION_GET_ALL |\\\n> +\tACTION_GET_REGEXP |\\\n> +\tACTION_REPLACE_ALL |\\\n> +\tACTION_UNSET |\\\n> +\tACTION_UNSET_ALL |\\\n> +\tACTION_SET_ALL\\\n> +)\n\nI am not sure about this, though.\n\nEven if an action can potentially take value_regex, e.g.\n\n    git config --get name [value_regex]\n\nshould we allow litral when value_regex is not given?  IOW\n\n    $ git config --get --literal-value vari.able v2.26.0+next\n\nmay make sense, but shouldn't\n\n    $ git config --get --literal-value vari.able\n\nbe an error?\n\nTo put it differently, I think the macro being defined is not\nACTION_LITERAL_ALLOWED, but ACTION_VALUE_REGEX_ALLOWED. i.e. list of\nactions that can potentially take value_regex.  A command line that\ngives value_regex to an action that is not listed there is an error.\n\n> +\tif (literal && !(actions & ACTION_LITERAL_ALLOWED)) {\n> +\t\terror(_(\"--literal only applies with 'value_regex'\"));\n> +\t\tusage_builtin_config();\n> +\t}\n\nThis check is not incorrect per-se, because an action that never\ntakes value_regex is by definition incompatible with the literal\nvalue option.  But it does not flag it as an error to ask a\nvalue_regex to be treated as a literal string when there is no\nvalue_regex given.\n\nHaving said that, I think it is OK, at least for now, to leave such\nan error undetected---it falls into the \"if it hurts, don't do it\"\ncategory.\n\nThanks.\n\n>  \tif (actions & PAGING_ACTIONS)\n>  \t\tsetup_auto_pager(\"config\", 1);\n>  \n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n> index 74e0f84c0a..73f5ca4361 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> @@ -1965,4 +1965,17 @@ test_expect_success '--replace-all and value_regex' '\n>  \ttest_cmp expect .git/config\n>  '\n>  \n> +test_expect_success 'refuse --literal-value for incompatible actions' '\n> +\tgit config dev.null bogus &&\n> +\ttest_must_fail git config --literal-value --add dev.null bogus &&\n> +\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n> +\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n> +\ttest_must_fail git config --literal-value --rename-section dev null &&\n> +\ttest_must_fail git config --literal-value --remove-section dev &&\n> +\ttest_must_fail git config --literal-value --list &&\n> +\ttest_must_fail git config --literal-value --get-color dev.null &&\n> +\ttest_must_fail git config --literal-value --get-colorbool dev.null &&\n> +\ttest_must_fail git config --literal-value --edit\n> +'\n> +\n>  test_done\n"},{"id":"410391","messageId":"xmqqblftvudk.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"5881b2d987e3e8575d054a64fe21fbe7e7967aba.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 5/7] config: plumb --literal-value into config API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-19T22:45:11Z","receivedAt":"2020-11-19T22:45:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/config.c b/config.c\n> index 4841c68a91..5028c1e736 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -2402,6 +2402,7 @@ struct config_store_data {\n>  \tsize_t baselen;\n>  \tchar *key;\n>  \tint do_not_match;\n> +\tconst char *literal_value;\n>  \tregex_t *value_regex;\n>  \tint multi_replace;\n>  \tstruct {\n> @@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,\n>  {\n>  \tif (strcmp(key, store->key))\n>  \t\treturn 0; /* not ours */\n> +\tif (store->literal_value)\n> +\t\treturn !strcmp(store->literal_value, value);\n>  \tif (!store->value_regex)\n>  \t\treturn 1; /* always matches */\n>  \tif (store->value_regex == CONFIG_REGEX_NONE)\n> @@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n>  \t\t\tstore.value_regex = NULL;\n>  \t\telse if (value_regex == CONFIG_REGEX_NONE)\n>  \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n> +\t\telse if (flags & CONFIG_FLAGS_LITERAL_VALUE)\n> +\t\t\tstore.literal_value = value_regex;\n>  \t\telse {\n>  \t\t\tif (value_regex[0] == '!') {\n>  \t\t\t\tstore.do_not_match = 1;\n\nThis is nice.  It is a surprisingly small change.\n\nThanks.\n"},{"id":"410392","messageId":"xmqq7dqhvsvs.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"1d1c2fd23e805e0f25edd324a614715b1d11a2bb.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 7/7] maintenance: use 'git config --literal-value'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-19T23:17:27Z","receivedAt":"2020-11-19T23:17:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> When a repository's leading directories contain regex glob characters,\n> the config calls for 'git maintenance register' and 'git maintenance\n> unregister' are not careful enough. Use the new --literal-value option\n> to direct the config machinery to use exact string matches. This is a\n> more robust option than excaping these arguments in a piecemeal fashion.\n>\n> Reported-by: Emily Shaffer <emilyshaffer@google.com>\n> Reported-by: Jonathan Nieder <jrnieder@gmail.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/gc.c           |  5 +++--\n>  t/t7900-maintenance.sh | 12 ++++++++++++\n>  2 files changed, 15 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/gc.c b/builtin/gc.c\n> index e3098ef6a1..0784bbdc6a 100644\n> --- a/builtin/gc.c\n> +++ b/builtin/gc.c\n> @@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n>  \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n>  \n>  \tconfig_get.git_cmd = 1;\n> -\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n> +\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n> +\t\t     \"--literal-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t\t NULL);\n> @@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n>  \n>  \tconfig_unset.git_cmd = 1;\n>  \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n> -\t\t     \"maintenance.repo\",\n> +\t\t     \"--literal-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t     NULL);\n> diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\n> index 20184e96e1..2ee5512158 100755\n> --- a/t/t7900-maintenance.sh\n> +++ b/t/t7900-maintenance.sh\n> @@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n>  \ttest_cmp before actual\n>  '\n>  \n> +test_expect_success 'register and unregister with glob characters' '\n> +\tGLOB=\"a+b*c\" &&\n> +\tgit init \"$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance register &&\n> +\tgit config --get-all --show-origin maintenance.repo &&\n> +\tgit config --get-all --global --literal-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance unregister &&\n> +\ttest_must_fail git config --get-all --global --literal-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n> +'\n> +\n\nMakes sense.  Thanks.\n\nAs to the topic-branch organization, my plan is for the final shape\n(I haven't really reviewed the early 6 yet so I do not know if they\nrequire rerolling at this point) would be to:\n\n - create ds/config-literal-value topic on somewhere old enough and\n   queue [1/7] thru [6/7] on it.\n\n - merge the above into ds/maintenance-part-3\n\n - apply [7/7] on top of ds/maintenance-part-3\n\n"},{"id":"410403","messageId":"20201120020924.GF389879@camp.crustytoothpaste.net","threadId":"54676","inReplyTo":"xmqqo8jtvvby.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2020-11-20T02:09:24Z","receivedAt":"2020-11-20T02:09:35Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2020-11-19 at 22:24:33, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > +test_expect_success 'set all config with value_regex' '\n> > +\tq_to_tab >initial <<-\\EOF &&\n> > +\t[abc]\n> > +\tQkey = one\n> > +\tEOF\n> > +\n> > +\tcp initial .git/config &&\n> \n> Not a new problem with this patch, but does the above pattern\n> introduce potential problems?  I am wondering if overwriting the\n> config file with a little piece that has only the stuff the test is\n> interested in, while wiping the parts that may be essential for\n> repository integrity (e.g. \"extensions.objectFormat\"), is OK in the\n> long run (brian cc'ed for his sha256 work).  There also are\n> autodetected crlf settings etc. that are in the .git/config when a\n> test repository is created, and we probably would want to keep them\n> intact.\n\nI haven't looked at the code, but if you're just using git config in a\ntest, then overwriting the config file shouldn't be a problem with\nSHA-256.  If you're trying to read or write objects or the index, then\nthat's definitely a problem, and you'll definitely notice exciting\nfailures if you do that.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"410404","messageId":"xmqq8sawvjsp.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"20201120020924.GF389879@camp.crustytoothpaste.net","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-20T02:33:42Z","receivedAt":"2020-11-20T02:33:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> On 2020-11-19 at 22:24:33, Junio C Hamano wrote:\n>> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>> \n>> > +test_expect_success 'set all config with value_regex' '\n>> > +\tq_to_tab >initial <<-\\EOF &&\n>> > +\t[abc]\n>> > +\tQkey = one\n>> > +\tEOF\n>> > +\n>> > +\tcp initial .git/config &&\n>> \n>> Not a new problem with this patch, but does the above pattern\n>> introduce potential problems?  I am wondering if overwriting the\n>> config file with a little piece that has only the stuff the test is\n>> interested in, while wiping the parts that may be essential for\n>> repository integrity (e.g. \"extensions.objectFormat\"), is OK in the\n>> long run (brian cc'ed for his sha256 work).  There also are\n>> autodetected crlf settings etc. that are in the .git/config when a\n>> test repository is created, and we probably would want to keep them\n>> intact.\n>\n> I haven't looked at the code, but if you're just using git config in a\n> test, then overwriting the config file shouldn't be a problem with\n> SHA-256.  If you're trying to read or write objects or the index, then\n> that's definitely a problem, and you'll definitely notice exciting\n> failures if you do that.\n\nYes, that is exactly what worries me.\n\nAnd I was trolling for ideas from those on cc: list to come up with\na better convention to test the behaviour of \"git config\".  I think\ntaking \"git config --list [--file FILE]\" before and after the ops of\ninterest and basing the good/bad decision on the difference would be\na saner approach.  It obviously relies on \"git config --list\" to be\nworking correctly, which the current approach that uses hardcoded\n\"initial\" state and expects result that is byte-for-byte identical\nto a hardcoded file contents after the ops does not have to rely on,\nbut at the same time, the current approach assumes too much on the\nactual file format (e.g. no user or script would care how \"key =\nval\" line is indented, but the current approach insists that they\nare indented exactly with one tab), so overall it may even be an\nimprovement.\n\nThanks.\n\n\n\n"},{"id":"410407","messageId":"CAN0heSrx6VgV4siQG3V3Bo9d_HKW9HikoTFF34_iZqQxeJkggA@mail.gmail.com","threadId":"54676","inReplyTo":"28493184b60d3fa46cde346eaae6128bce87c114.1605801143.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/7] config: add --literal-value option, un-implemented","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2020-11-20T06:35:34Z","receivedAt":"2020-11-20T06:35:59Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On Thu, 19 Nov 2020 at 16:55, Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> +       OPT_BOOL(0, \"literal-value\", &literal, N_(\"use literal equality when matching values\")),\n\n> +       if (literal && !(actions & ACTION_LITERAL_ALLOWED)) {\n> +               error(_(\"--literal only applies with 'value_regex'\"));\n\ns/literal/&-value/\n\n> +               usage_builtin_config();\n> +       }\n\n\nMartin\n"},{"id":"410419","messageId":"87k0ugp3mg.fsf@evledraar.gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-20T13:19:35Z","receivedAt":"2020-11-20T13:19:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 19 2020, Derrick Stolee via GitGitGadget wrote:\n\n> As reported [1], 'git maintenance unregister' fails when a repository is\n> located in a directory with regex glob characters.\n\nJust as bikeshedding on the name: Did you consider something\nthematically similar to the corresponding git-grep option,\ni.e. --fixed-string[s]. I see -F is also free in git-config(1).\n"},{"id":"410420","messageId":"e7973fe1-eb64-2f5a-ecb3-fadf2ba9764d@gmail.com","threadId":"54676","inReplyTo":"87k0ugp3mg.fsf@evledraar.gmail.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-20T13:23:07Z","receivedAt":"2020-11-20T13:23:33Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/20/2020 8:19 AM, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Thu, Nov 19 2020, Derrick Stolee via GitGitGadget wrote:\n> \n>> As reported [1], 'git maintenance unregister' fails when a repository is\n>> located in a directory with regex glob characters.\n> \n> Just as bikeshedding on the name: Did you consider something\n> thematically similar to the corresponding git-grep option,\n> i.e. --fixed-string[s]. I see -F is also free in git-config(1).\n\nI definitely wanted to be specific about \"value\" in the name,\nsince some options include regexes on the key as well. I'm open\nto new ideas, and combining your idea with mine would introduce\n\"--fixed-value\". Thoughts?\n\nThanks,\n-Stolee\n"},{"id":"410434","messageId":"xmqqk0ufubha.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"e7973fe1-eb64-2f5a-ecb3-fadf2ba9764d@gmail.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-20T18:30:57Z","receivedAt":"2020-11-20T18:31:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 11/20/2020 8:19 AM, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Thu, Nov 19 2020, Derrick Stolee via GitGitGadget wrote:\n>> \n>>> As reported [1], 'git maintenance unregister' fails when a repository is\n>>> located in a directory with regex glob characters.\n>> \n>> Just as bikeshedding on the name: Did you consider something\n>> thematically similar to the corresponding git-grep option,\n>> i.e. --fixed-string[s]. I see -F is also free in git-config(1).\n>\n> I definitely wanted to be specific about \"value\" in the name,\n> since some options include regexes on the key as well. I'm open\n> to new ideas, and combining your idea with mine would introduce\n> \"--fixed-value\". Thoughts?\n\nI very much appreciate \"value\" is in the name, with the current\nsemantics that this only controls how the pattern matching is done\non the value side and not on the key side.  When making an obvious\naddition of a separate option to control how the pattern matching is\ndone on keys in the future, we would regret if we called this option\n\"--fixed-strings\" today.  And no, I do not think it is an acceptable\noption to introduce \"--fixed-strings\" that only affects value side\nand then later change its behaviour to affect also on the key side.\n\n\tSide note.  It _is_ possible to ship such a \"--fixed-strings\"\n\toption that does not work on the key side and document it as\n\ta known bug, later to be fixed.  I am not sure if I like it.\n\nBut stepping back a bit, is the extra flexibility that allows us to\ncontrol the matching on keys and values separately with such a\nscheme really worth the complexity (at the end-user facing interface\nlevel, not the implementation level)?\n\nSo an alternative may be to use a single option, whose name would\nprobably be one of \"--(literal|fixed)-(match|strings)\", but extend\nthe implementation in this series to make the single option affect\nboth the value and key matching the same way.\n\nThat would however be more work in the shorter term.  Offhand, I am\nnot sure if I like it (i.e. spending time and effort that is more\nthan the absolute minimum necessary to fix a breakage.  And the end\nresult of doing so is less powerful/flexible, even though it may be\neasier to explain to users simply because the feature is less\npowerful than it could be).  It would be easier, if I can convince\nmyself that the extra flexibility is not worth it, to just declare\nthat simpler is better here, but I am not quite ready to do so yet.\n\nAs to \"-F\", I do not think it is a good idea; in some context \"-F\"\nmeans work on a <file> given via that option, i.e. \"-F <file>\".\n\nThanks.\n"},{"id":"410436","messageId":"20201120183903.GA320614@coredump.intra.peff.net","threadId":"54676","inReplyTo":"xmqqo8jtvvby.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-20T18:39:03Z","receivedAt":"2020-11-20T18:39:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 19, 2020 at 02:24:33PM -0800, Junio C Hamano wrote:\n\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > +test_expect_success 'set all config with value_regex' '\n> > +\tq_to_tab >initial <<-\\EOF &&\n> > +\t[abc]\n> > +\tQkey = one\n> > +\tEOF\n> > +\n> > +\tcp initial .git/config &&\n> \n> Not a new problem with this patch, but does the above pattern\n> introduce potential problems?  I am wondering if overwriting the\n> config file with a little piece that has only the stuff the test is\n> interested in, while wiping the parts that may be essential for\n> repository integrity (e.g. \"extensions.objectFormat\"), is OK in the\n> long run (brian cc'ed for his sha256 work).  There also are\n> autodetected crlf settings etc. that are in the .git/config when a\n> test repository is created, and we probably would want to keep them\n> intact.\n\nt1300 is full of this kind of junk. Several years ago, while working on\nsome of the repositoryformatversion code, I noticed that we will accept\na repository that does not have core.repositoryformatversion set at all,\nnor even has a .git/config present!\n\nIt's easy to fix in the code, but it causes failures all over t1300. So\nthen I started converting t1300 to use \"config --file\" (which\nalmost certainly didn't exist back when most of those tests were\noriginally written).  I don't remember how or why it got hairy, but it\nwas enough that I eventually dropped it (unlike many of my other stale\ntopics, I don't think I've even kept rebasing it forward as a WIP).\n\nPossibly I was concerned that people in the wild were relying on a blank\nor missing config being the same as repositoryformatversion=0. That will\ndefinitely stop working in a sha256 world anyway, though, because\nthey'll need the objectFormat extension.\n\nSo that got a bit off-track, but I think:\n\n  - t1300 already is very much like this, so it's not a new thing\n\n  - but I would be happy not to see it go further in that direction,\n    even if it means inconsistency with the rest of the script\n\n-Peff\n"},{"id":"410437","messageId":"3a347d16-8549-5244-c7db-82b97d7311de@gmail.com","threadId":"54676","inReplyTo":"xmqqk0ufubha.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-20T18:51:16Z","receivedAt":"2020-11-20T18:51:21Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/20/2020 1:30 PM, Junio C Hamano wrote:\n> Derrick Stolee <stolee@gmail.com> writes:\n> \n>> On 11/20/2020 8:19 AM, Ævar Arnfjörð Bjarmason wrote:\n>>>\n>>> On Thu, Nov 19 2020, Derrick Stolee via GitGitGadget wrote:\n>>>\n>>>> As reported [1], 'git maintenance unregister' fails when a repository is\n>>>> located in a directory with regex glob characters.\n>>>\n>>> Just as bikeshedding on the name: Did you consider something\n>>> thematically similar to the corresponding git-grep option,\n>>> i.e. --fixed-string[s]. I see -F is also free in git-config(1).\n>>\n>> I definitely wanted to be specific about \"value\" in the name,\n>> since some options include regexes on the key as well. I'm open\n>> to new ideas, and combining your idea with mine would introduce\n>> \"--fixed-value\". Thoughts?\n> \n> I very much appreciate \"value\" is in the name, with the current\n> semantics that this only controls how the pattern matching is done\n> on the value side and not on the key side.  When making an obvious\n> addition of a separate option to control how the pattern matching is\n> done on keys in the future, we would regret if we called this option\n> \"--fixed-strings\" today.  And no, I do not think it is an acceptable\n> option to introduce \"--fixed-strings\" that only affects value side\n> and then later change its behaviour to affect also on the key side.\n> \n> \tSide note.  It _is_ possible to ship such a \"--fixed-strings\"\n> \toption that does not work on the key side and document it as\n> \ta known bug, later to be fixed.  I am not sure if I like it.\n> \n> But stepping back a bit, is the extra flexibility that allows us to\n> control the matching on keys and values separately with such a\n> scheme really worth the complexity (at the end-user facing interface\n> level, not the implementation level)?\n>\n> So an alternative may be to use a single option, whose name would\n> probably be one of \"--(literal|fixed)-(match|strings)\", but extend\n> the implementation in this series to make the single option affect\n> both the value and key matching the same way.\n> \n> That would however be more work in the shorter term.  Offhand, I am\n> not sure if I like it (i.e. spending time and effort that is more\n> than the absolute minimum necessary to fix a breakage.  And the end\n> result of doing so is less powerful/flexible, even though it may be\n> easier to explain to users simply because the feature is less\n> powerful than it could be).  It would be easier, if I can convince\n> myself that the extra flexibility is not worth it, to just declare\n> that simpler is better here, but I am not quite ready to do so yet.\nI had not thought about making this option be related to the key\nnames at all. In particular, we already have the --get-regexp option:\n\n--get-regexp::\n\tLike --get-all, but interprets the name as a regular expression and\n\twrites out the key names.  Regular expression matching is currently\n\tcase-sensitive and done against a canonicalized version of the key\n\tin which section and variable names are lowercased, but subsection\n\tnames are not.\n\nI suppose that there could be reason to create a similar --unset-regexp\nthat is the same equivalent of --unset-all. Perhaps the --replace-all\nmode could benefit, too? These are also more dangerous to use with regex\nmatches since they also _change_ the config, not just query it.\n\nSince there is already asymmetry between the key and value (only one\nmode, --get-regexp, has a \"name_regex\"), I would prefer to treat the\nvalue_regex in isolation here.\n\nThanks,\n-Stolee\n"},{"id":"410442","messageId":"xmqqft53u252.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"3a347d16-8549-5244-c7db-82b97d7311de@gmail.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-20T21:52:41Z","receivedAt":"2020-11-20T21:53:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> Since there is already asymmetry between the key and value (only one\n> mode, --get-regexp, has a \"name_regex\"), I would prefer to treat the\n> value_regex in isolation here.\n\nAh, I forgot about --get-regexp vs --get; so the ship has already\nsailed---I agree with you that --(fixed|literal)-value is all we\nneed.\n\nThanks.\n"},{"id":"410445","messageId":"xmqq7dqfu05f.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"20201120183903.GA320614@coredump.intra.peff.net","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-20T22:35:40Z","receivedAt":"2020-11-20T22:35:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> So that got a bit off-track, but I think:\n>\n>   - t1300 already is very much like this, so it's not a new thing\n>\n>   - but I would be happy not to see it go further in that direction,\n>     even if it means inconsistency with the rest of the script\n\nYeah, I especially like the latter half ;-)\n"},{"id":"410475","messageId":"20201121222734.GG389879@camp.crustytoothpaste.net","threadId":"54676","inReplyTo":"20201120183903.GA320614@coredump.intra.peff.net","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2020-11-21T22:27:34Z","receivedAt":"2020-11-21T22:34:22Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2020-11-20 at 18:39:03, Jeff King wrote:\n> t1300 is full of this kind of junk. Several years ago, while working on\n> some of the repositoryformatversion code, I noticed that we will accept\n> a repository that does not have core.repositoryformatversion set at all,\n> nor even has a .git/config present!\n\nYup.  We test this in t3200 as well.  I don't love it, but it exists.\n\n> It's easy to fix in the code, but it causes failures all over t1300. So\n> then I started converting t1300 to use \"config --file\" (which\n> almost certainly didn't exist back when most of those tests were\n> originally written).  I don't remember how or why it got hairy, but it\n> was enough that I eventually dropped it (unlike many of my other stale\n> topics, I don't think I've even kept rebasing it forward as a WIP).\n> \n> Possibly I was concerned that people in the wild were relying on a blank\n> or missing config being the same as repositoryformatversion=0. That will\n> definitely stop working in a sha256 world anyway, though, because\n> they'll need the objectFormat extension.\n\nWhich is exactly why that test in t3200 has a SHA1 prerequisite.  I'm\nsure we'll hear someone complain about the fact that SHA-256\nrepositories have to have a config file, but I'm fine with us not\nsupporting it.\n\nI should point out that lacking a config file also only works on Unix\nsystems with a POSIX file system (including case-sensitive macOS), since\notherwise core.ignorecase (and core.symlinks, if appropriate) aren't set\ncorrectly.  It also doesn't work for bare repositories on any OS.  So\nhopefully the number of people doing this is quite small.\n\n> So that got a bit off-track, but I think:\n> \n>   - t1300 already is very much like this, so it's not a new thing\n> \n>   - but I would be happy not to see it go further in that direction,\n>     even if it means inconsistency with the rest of the script\n\nI agree we shouldn't make things worse.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"410482","messageId":"xmqqlfeuqd8d.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"20201121222734.GG389879@camp.crustytoothpaste.net","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-22T03:31:14Z","receivedAt":"2020-11-22T03:31:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n>> So that got a bit off-track, but I think:\n>> \n>>   - t1300 already is very much like this, so it's not a new thing\n>> \n>>   - but I would be happy not to see it go further in that direction,\n>>     even if it means inconsistency with the rest of the script\n>\n> I agree we shouldn't make things worse.\n\nI started looking at early parts of t1300 and here is how far I\nmanaged to get before I can no longer keep staring the existing\ntests without vomitting.\n\nI am reasonably happy with the \"let's keep the vanilla untouched one\nin .git/config-initial, refrain from using [core] and other sections\nthat MUST be in the initial configuration for testing, and use a\nwrapper that reads expected addition to the initial one from the\nstandard input for validation\" approach I came up with, but I am not\nhappy with the name 'compare_expect'; 'validate_config_result' might\nbe a better name.\n\nIn any case, the reason I am sending this out early is if people\nfind this approach to clean things up a sensible one.  If we can\nfind concensus, perhaps I (or somebody else---hint, hint) can find\ntime to do the #leftoverbits following the approach after the\nds/config-literal-value and ds/maintenance-part-3 topics graduate\nto 'master'.\n\n\n\n t/t1300-config.sh | 139 ++++++++++++++++++++++++++++--------------------------\n 1 file changed, 71 insertions(+), 68 deletions(-)\n\ndiff --git c/t/t1300-config.sh w/t/t1300-config.sh\nindex df13afaffd..c33520d7fa 100755\n--- c/t/t1300-config.sh\n+++ w/t/t1300-config.sh\n@@ -7,80 +7,84 @@ test_description='Test git config in different settings'\n \n . ./test-lib.sh\n \n-test_expect_success 'clear default config' '\n-\trm -f .git/config\n+test_expect_success 'save away default config' '\n+\tcp .git/config .git/config-initial\n '\n \n-cat > expect << EOF\n-[core]\n-\tpenguin = little blue\n-EOF\n-test_expect_success 'initial' '\n-\tgit config core.penguin \"little blue\" &&\n+compare_expect () {\n+\t{\n+\t\tcat .git/config-initial &&\n+\t\tsed -e 's/^[|]//'\n+\t} >expect &&\n \ttest_cmp expect .git/config\n+}\n+\n+test_expect_success 'initial' '\n+\tgit config configtest.penguin \"little blue\" &&\n+\tcompare_expect <<-\\EOF\n+\t[configtest]\n+\t|\tpenguin = little blue\n+\tEOF\n '\n \n-cat > expect << EOF\n-[core]\n-\tpenguin = little blue\n-\tMovie = BadPhysics\n-EOF\n test_expect_success 'mixed case' '\n-\tgit config Core.Movie BadPhysics &&\n-\ttest_cmp expect .git/config\n+\tgit config ConfigTest.Movie BadPhysics &&\n+\tcompare_expect <<-\\EOF\n+\t[configtest]\n+\t|\tpenguin = little blue\n+\t|\tMovie = BadPhysics\n+\tEOF\n '\n \n-cat > expect << EOF\n-[core]\n-\tpenguin = little blue\n-\tMovie = BadPhysics\n-[Cores]\n-\tWhatEver = Second\n-EOF\n test_expect_success 'similar section' '\n-\tgit config Cores.WhatEver Second &&\n-\ttest_cmp expect .git/config\n+\tgit config ConfigTests.WhatEver Second &&\n+\tcompare_expect <<-\\EOF\n+\t[configtest]\n+\t|\tpenguin = little blue\n+\t|\tMovie = BadPhysics\n+\t[ConfigTests]\n+\t|\tWhatEver = Second\n+\tEOF\n '\n \n-cat > expect << EOF\n-[core]\n-\tpenguin = little blue\n-\tMovie = BadPhysics\n-\tUPPERCASE = true\n-[Cores]\n-\tWhatEver = Second\n-EOF\n test_expect_success 'uppercase section' '\n-\tgit config CORE.UPPERCASE true &&\n-\ttest_cmp expect .git/config\n+\tgit config CONFIGTEST.UPPERCASE true &&\n+\tcompare_expect <<-\\EOF\n+\t[configtest]\n+\t|\tpenguin = little blue\n+\t|\tMovie = BadPhysics\n+\t|\tUPPERCASE = true\n+\t[ConfigTests]\n+\t|\tWhatEver = Second\n+\tEOF\n '\n \n test_expect_success 'replace with non-match' '\n-\tgit config core.penguin kingpin !blue\n+\tgit config configtest.penguin kingpin !blue\n '\n \n test_expect_success 'replace with non-match (actually matching)' '\n-\tgit config core.penguin \"very blue\" !kingpin\n+\tgit config configtest.penguin \"very blue\" !kingpin\n '\n \n-cat > expect << EOF\n-[core]\n-\tpenguin = very blue\n-\tMovie = BadPhysics\n-\tUPPERCASE = true\n-\tpenguin = kingpin\n-[Cores]\n-\tWhatEver = Second\n-EOF\n-\n-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n+test_expect_success 'non-match result' '\n+\tcompare_expect <<-\\EOF\n+\t[configtest]\n+\t|\tpenguin = very blue\n+\t|\tMovie = BadPhysics\n+\t|\tUPPERCASE = true\n+\t|\tpenguin = kingpin\n+\t[ConfigTests]\n+\t|\tWhatEver = Second\n+\tEOF\n+'\n \n test_expect_success 'find mixed-case key by canonical name' '\n-\ttest_cmp_config Second cores.whatever\n+\ttest_cmp_config Second configtests.whatever\n '\n \n test_expect_success 'find mixed-case key by non-canonical name' '\n-\ttest_cmp_config Second CoReS.WhAtEvEr\n+\ttest_cmp_config Second CoNfIgTeSts.WhAtEvEr\n '\n \n test_expect_success 'subsections are not canonicalized by git-config' '\n@@ -94,28 +98,27 @@ test_expect_success 'subsections are not canonicalized by git-config' '\n \ttest_cmp_config two section.SubSection.key\n '\n \n-cat > .git/config <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-baz = multiple \\\n-lines\n-foo = bar\n-EOF\n-\n test_expect_success 'unset with cont. lines' '\n-\tgit config --unset beta.baz\n+\t{\n+\t\tcat .git/config-initial &&\n+\t\tcat <<-\\EOF\n+\t\t[alpha]\n+\t\tbar = foo\n+\t\t[beta]\n+\t\tbaz = multiple \\\n+\t\tlines\n+\t\tfoo = bar\n+\t\tEOF\n+\t} >.git/config &&\n+\tgit config --unset beta.baz &&\n+\tcompare_expect <<-\\EOF\n+\t[alpha]\n+\tbar = foo\n+\t[beta]\n+\tfoo = bar\n+\tEOF\n '\n \n-cat > expect <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-foo = bar\n-EOF\n-\n-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'\n-\n cat > .git/config << EOF\n [beta] ; silly comment # another comment\n noIndent= sillyValue ; 'nother silly comment\n"},{"id":"410532","messageId":"ea3099719c867a5a475468ddb6741bbcd713dccc.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:01Z","receivedAt":"2020-11-23T16:05:34Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWithout additional modifiers, 'git config' attempts to set a single\nvalue in the .git/config file. When the value_regex parameter is\nsupplied, this command behaves in a non-trivial manner.\n\nConsider 'git config key value value_regex'. The expected behavior\nis as follows:\n\n1. If there are multiple existing values that match 'value_regex',\n   then the command fails. Users should use --replace-all instead.\n\n2. If there is one existing value that matches 'value_regex', then\n   the new config has one entry where 'key=value'.\n\n3. If there is no existing values match 'value_regex', then the\n   'key=value' pair is appended, making this 'key' a multi-valued\n   config setting.\n\nAdd a test that demonstrates these options. Break from the existing\npattern in t1300-config.sh to use 'git config --file=<file>' instead of\nmodifying .git/config directly. Also use 'git config --file=<file>\n--list' for config state comparison instead of the config file format.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 97ebfe1f9d..19836ec83b 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1914,4 +1914,36 @@ test_expect_success '--replace-all does not invent newlines' '\n \ttest_cmp expect .git/config\n '\n \n+test_expect_success 'set all config with value_regex' '\n+\tgit config --file=initial abc.key one &&\n+\n+\tcp initial config &&\n+\tgit config --file=config abc.key two a+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=one\n+\tabc.key=two\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\ttest_must_fail git config --file=config abc.key three o+ 2>err &&\n+\ttest_i18ngrep \"has multiple values\" err &&\n+\tgit config --file=config abc.key three a+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=one\n+\tabc.key=two\n+\tabc.key=three\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\tgit config --file=config abc.key three o+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=three\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410535","messageId":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.git.1605801143.gitgitgadget@gmail.com","subject":"[PATCH v2 0/7] config: add --fixed-value option","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:00Z","receivedAt":"2020-11-23T16:05:34Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"As reported [1], 'git maintenance unregister' fails when a repository is\nlocated in a directory with regex glob characters.\n\n[1] \nhttps://lore.kernel.org/git/2c2db228-069a-947d-8446-89f4d3f6181a@gmail.com/T/#mb96fa4187a0d6aeda097cd95804a8aafc0273022\n\nThe discussed solution was to modify 'git config' to specify that the\n'value_regex' argument should be treated as an exact string match. This is\nthe primary change in this series, with an additional patch at the end to\nmake 'git maintenance [un]register' use this option, when necessary.\n\nUpdates in V2\n=============\n\n * Updated the test structure for the new tests in t1300-config.sh\n   \n   \n * Renamed option from '--literal-value' to '--fixed-value'\n   \n   \n * Use 'unsigned flags' instead of 'int flags'. This changes more prototypes\n   than before.\n   \n   \n * Move flag definitions closer to method declarations.\n   \n   \n * Test the uses of --fixed-value to reject when value_regex is missing.\n   \n   \n\nThanks, -Stolee\n\nDerrick Stolee (7):\n  t1300: test \"set all\" mode with value_regex\n  t1300: add test for --replace-all with value_regex\n  config: convert multi_replace to flags\n  config: add --fixed-value option, un-implemented\n  config: plumb --fixed-value into config API\n  config: implement --fixed-value with --get*\n  maintenance: use 'git config --fixed-value'\n\n Documentation/git-config.txt |  20 +++--\n builtin/branch.c             |   4 +-\n builtin/config.c             |  59 +++++++++++---\n builtin/gc.c                 |   5 +-\n builtin/remote.c             |   8 +-\n config.c                     |  29 ++++---\n config.h                     |  36 +++++++--\n t/t1300-config.sh            | 148 +++++++++++++++++++++++++++++++++++\n t/t7900-maintenance.sh       |  12 +++\n 9 files changed, 278 insertions(+), 43 deletions(-)\n\n\nbase-commit: 0016b618182f642771dc589cf0090289f9fe1b4f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-796%2Fderrickstolee%2Fmaintenance%2Fconfig-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-796/derrickstolee/maintenance/config-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/796\n\nRange-diff vs v1:\n\n 1:  2da2131114 ! 1:  ea3099719c t1300: test \"set all\" mode with value_regex\n     @@ Commit message\n             'key=value' pair is appended, making this 'key' a multi-valued\n             config setting.\n      \n     -    Add a test that demonstrates these options.\n     +    Add a test that demonstrates these options. Break from the existing\n     +    pattern in t1300-config.sh to use 'git config --file=<file>' instead of\n     +    modifying .git/config directly. Also use 'git config --file=<file>\n     +    --list' for config state comparison instead of the config file format.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ t/t1300-config.sh: test_expect_success '--replace-all does not invent newlines'\n       '\n       \n      +test_expect_success 'set all config with value_regex' '\n     -+\tq_to_tab >initial <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = one\n     -+\tEOF\n     ++\tgit config --file=initial abc.key one &&\n      +\n     -+\tcp initial .git/config &&\n     -+\tgit config abc.key two a+ &&\n     -+\tq_to_tab >expect <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = one\n     -+\tQkey = two\n     ++\tcp initial config &&\n     ++\tgit config --file=config abc.key two a+ &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-\\EOF &&\n     ++\tabc.key=one\n     ++\tabc.key=two\n      +\tEOF\n     -+\ttest_cmp expect .git/config &&\n     ++\ttest_cmp expect actual &&\n      +\n     -+\ttest_must_fail git config abc.key three o+ 2>err &&\n     ++\ttest_must_fail git config --file=config abc.key three o+ 2>err &&\n      +\ttest_i18ngrep \"has multiple values\" err &&\n     -+\tgit config abc.key three a+ &&\n     -+\tq_to_tab >expect <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = one\n     -+\tQkey = two\n     -+\tQkey = three\n     ++\tgit config --file=config abc.key three a+ &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-\\EOF &&\n     ++\tabc.key=one\n     ++\tabc.key=two\n     ++\tabc.key=three\n      +\tEOF\n     -+\ttest_cmp expect .git/config &&\n     ++\ttest_cmp expect actual &&\n      +\n     -+\tcp initial .git/config &&\n     -+\tgit config abc.key three o+ &&\n     -+\tq_to_tab >expect <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = three\n     ++\tcp initial config &&\n     ++\tgit config --file=config abc.key three o+ &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-\\EOF &&\n     ++\tabc.key=three\n      +\tEOF\n     -+\ttest_cmp expect .git/config\n     ++\ttest_cmp expect actual\n      +'\n      +\n       test_done\n 2:  1237289706 ! 2:  829d0ccd8c t1300: add test for --replace-all with value_regex\n     @@ Commit message\n      \n       ## t/t1300-config.sh ##\n      @@ t/t1300-config.sh: test_expect_success 'set all config with value_regex' '\n     - \ttest_cmp expect .git/config\n     + \ttest_cmp expect actual\n       '\n       \n      +test_expect_success '--replace-all and value_regex' '\n     -+\tq_to_tab >.git/config <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = one\n     -+\tQkey = two\n     -+\tQkey = three\n     ++\trm -f config &&\n     ++\tgit config --file=config --add abc.key one &&\n     ++\tgit config --file=config --add abc.key two &&\n     ++\tgit config --file=config --add abc.key three &&\n     ++\tgit config --file=config --replace-all abc.key four \"o+\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-\\EOF &&\n     ++\tabc.key=four\n     ++\tabc.key=three\n      +\tEOF\n     -+\tq_to_tab >expect <<-\\EOF &&\n     -+\t[abc]\n     -+\tQkey = four\n     -+\tQkey = three\n     -+\tEOF\n     -+\tgit config --replace-all abc.key four \"o+\" &&\n     -+\ttest_cmp expect .git/config\n     ++\ttest_cmp expect actual\n      +'\n      +\n       test_done\n 3:  c9caed3854 ! 3:  0c152faa00 config: convert multi_replace to flags\n     @@ Commit message\n      \n          We will extend the flexibility of the config API. Before doing so, let's\n          take an existing 'int multi_replace' parameter and replace it with a new\n     -    'int flags' parameter that can take multiple options as a bit field.\n     +    'unsigned flags' parameter that can take multiple options as a bit field.\n      \n          Update all callers that specified multi_replace to now specify the\n          CONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the\n          documentation of git_config_set_multivar_in_file() including a clear\n     -    labeling of its arguments. Other config API methods in config.h do not\n     -    require a change to their prototypes, but they have an equivalent update\n     -    to their implementations.\n     +    labeling of its arguments. Other config API methods in config.h require\n     +    only a change of the final parameter from 'int' to 'unsigned'.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ config.c: void git_config_set(const char *key, const char *value)\n       \t\t\t\t\t   const char *key, const char *value,\n       \t\t\t\t\t   const char *value_regex,\n      -\t\t\t\t\t   int multi_replace)\n     -+\t\t\t\t\t   int flags)\n     ++\t\t\t\t\t   unsigned flags)\n       {\n       \tint fd = -1, in_fd = -1;\n       \tint ret;\n     @@ config.c: int git_config_set_multivar_in_file_gently(const char *config_filename\n       void git_config_set_multivar_in_file(const char *config_filename,\n       \t\t\t\t     const char *key, const char *value,\n      -\t\t\t\t     const char *value_regex, int multi_replace)\n     -+\t\t\t\t     const char *value_regex, int flags)\n     ++\t\t\t\t     const char *value_regex, unsigned flags)\n       {\n       \tif (!git_config_set_multivar_in_file_gently(config_filename, key, value,\n      -\t\t\t\t\t\t    value_regex, multi_replace))\n     @@ config.c: void git_config_set_multivar_in_file(const char *config_filename,\n       \n       int git_config_set_multivar_gently(const char *key, const char *value,\n      -\t\t\t\t   const char *value_regex, int multi_replace)\n     -+\t\t\t\t   const char *value_regex, int flags)\n     ++\t\t\t\t   const char *value_regex, unsigned flags)\n       {\n       \treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,\n      -\t\t\t\t\t\t      multi_replace);\n     @@ config.c: void git_config_set_multivar_in_file(const char *config_filename,\n       \n       void git_config_set_multivar(const char *key, const char *value,\n      -\t\t\t     const char *value_regex, int multi_replace)\n     -+\t\t\t     const char *value_regex, int flags)\n     ++\t\t\t     const char *value_regex, unsigned flags)\n       {\n       \tgit_config_set_multivar_in_file(NULL, key, value, value_regex,\n      -\t\t\t\t\tmulti_replace);\n     @@ config.c: void git_config_set_multivar_in_file(const char *config_filename,\n       static int section_name_match (const char *buf, const char *name)\n      \n       ## config.h ##\n     -@@ config.h: enum config_event_t {\n     - \tCONFIG_EVENT_ERROR\n     - };\n     +@@ config.h: void git_config_set(const char *, const char *);\n       \n     + int git_config_parse_key(const char *, char **, size_t *);\n     + int git_config_key_is_valid(const char *key);\n     +-int git_config_set_multivar_gently(const char *, const char *, const char *, int);\n     +-void git_config_set_multivar(const char *, const char *, const char *, int);\n     +-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);\n     ++\n     ++/*\n     ++ * The following macros specify flag bits that alter the behavior\n     ++ * of the git_config_set_multivar*() methods.\n     ++ */\n     ++\n      +/*\n      + * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n      + * are removed before a new pair is written. If the flag is not present,\n     @@ config.h: enum config_event_t {\n      + */\n      +#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n      +\n     - /*\n     -  * The parser event function (if not NULL) is called with the event type and\n     -  * the begin/end offsets of the parsed elements.\n     ++int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n     ++void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n     ++int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n     + \n     + /**\n     +  * takes four parameters:\n      @@ config.h: int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n        * - the value regex, as a string. It will disregard key/value pairs where value\n        *   does not match.\n     @@ config.h: int git_config_set_multivar_in_file_gently(const char *, const char *,\n      +\t\t\t\t     const char *key,\n      +\t\t\t\t     const char *value,\n      +\t\t\t\t     const char *value_regex,\n     -+\t\t\t\t     int flags);\n     ++\t\t\t\t     unsigned flags);\n       \n       /**\n        * rename or remove sections in the config file\n 4:  28493184b6 ! 4:  0e6a7371ed config: add --literal-value option, un-implemented\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    config: add --literal-value option, un-implemented\n     +    config: add --fixed-value option, un-implemented\n      \n          The 'git config' builtin takes a 'value_regex' parameter for several\n          actions. This can cause confusion when expecting exact value matches\n     @@ Commit message\n          be more friendly to allow an argument to disable the pattern matching in\n          favor of an exact string match.\n      \n     -    Add a new '--literal-value' option that does not currently change the\n     +    Add a new '--fixed-value' option that does not currently change the\n          behavior. The implementation will follow for each appropriate action.\n     -    For now, check and test that --literal-value will abort the command when\n     -    included with an incompatible action.\n     +    For now, check and test that --fixed-value will abort the command when\n     +    included with an incompatible action or without a 'value_regex'\n     +    argument.\n      \n     -    The name '--literal-value' was chosen over something simpler like\n     -    '--literal' because some commands allow regular expressions on the\n     +    The name '--fixed-value' was chosen over something simpler like\n     +    '--fixed' because some commands allow regular expressions on the\n          key in addition to the value.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n     @@ Documentation/git-config.txt: git-config - Get and set repository or global opti\n       --------\n       [verse]\n      -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n     -+'git config' [<file-option>] [--type=<type>] [--literal-value] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n     ++'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n       'git config' [<file-option>] [--type=<type>] --add name value\n      -'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n      -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]\n      -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]\n      -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--literal-value] --replace-all name value [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] --get name [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] --get-all name [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--literal-value] [--name-only] --get-regexp name_regex [value_regex]\n     ++'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value_regex]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value_regex]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value_regex]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value_regex]\n       'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n      -'git config' [<file-option>] --unset name [value_regex]\n      -'git config' [<file-option>] --unset-all name [value_regex]\n     -+'git config' [<file-option>] [--literal-value] --unset name [value_regex]\n     -+'git config' [<file-option>] [--literal-value] --unset-all name [value_regex]\n     ++'git config' [<file-option>] [--fixed-value] --unset name [value_regex]\n     ++'git config' [<file-option>] [--fixed-value] --unset-all name [value_regex]\n       'git config' [<file-option>] --rename-section old_name new_name\n       'git config' [<file-option>] --remove-section name\n       'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n     @@ Documentation/git-config.txt: See also <<FILES>>.\n       --list::\n       \tList all variables set in config file, along with their values.\n       \n     -+--literal-value::\n     ++--fixed-value::\n      +\tWhen used with the `value_regex` argument, treat `value_regex` as\n      +\tan exact string instead of a regular expression. This will restrict\n      +\tthe name/value pairs that are matched to only those where the value\n     @@ builtin/config.c: 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 literal;\n     ++static int fixed_value;\n       \n       #define ACTION_GET (1<<0)\n       #define ACTION_GET_ALL (1<<1)\n     -@@ builtin/config.c: static int show_scope;\n     - #define ACTION_GET_COLORBOOL (1<<14)\n     - #define ACTION_GET_URLMATCH (1<<15)\n     - \n     -+#define ACTION_LITERAL_ALLOWED (\\\n     -+\tACTION_GET |\\\n     -+\tACTION_GET_ALL |\\\n     -+\tACTION_GET_REGEXP |\\\n     -+\tACTION_REPLACE_ALL |\\\n     -+\tACTION_UNSET |\\\n     -+\tACTION_UNSET_ALL |\\\n     -+\tACTION_SET_ALL\\\n     -+)\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      @@ builtin/config.c: static struct option builtin_config_options[] = {\n       \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n       \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n       \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n     -+\tOPT_BOOL(0, \"literal-value\", &literal, N_(\"use literal equality when matching values\")),\n     ++\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n       \tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n       \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n       \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \t\tusage_builtin_config();\n       \t}\n       \n     -+\tif (literal && !(actions & ACTION_LITERAL_ALLOWED)) {\n     -+\t\terror(_(\"--literal only applies with 'value_regex'\"));\n     -+\t\tusage_builtin_config();\n     ++\tif (fixed_value) {\n     ++\t\tint allowed_usage = 0;\n     ++\n     ++\t\tswitch (actions) {\n     ++\t\tcase ACTION_GET:\n     ++\t\tcase ACTION_GET_ALL:\n     ++\t\tcase ACTION_GET_REGEXP:\n     ++\t\tcase ACTION_UNSET:\n     ++\t\tcase ACTION_UNSET_ALL:\n     ++\t\t\tallowed_usage = argc > 1 && !!argv[1];\n     ++\t\t\tbreak;\n     ++\n     ++\t\tcase ACTION_SET_ALL:\n     ++\t\tcase ACTION_REPLACE_ALL:\n     ++\t\t\tallowed_usage = argc > 2 && !!argv[2];\n     ++\t\t\tbreak;\n     ++\t\t}\n     ++\n     ++\t\tif (!allowed_usage) {\n     ++\t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n     ++\t\t\tusage_builtin_config();\n     ++\t\t}\n      +\t}\n      +\n       \tif (actions & PAGING_ACTIONS)\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n      \n       ## t/t1300-config.sh ##\n      @@ t/t1300-config.sh: test_expect_success '--replace-all and value_regex' '\n     - \ttest_cmp expect .git/config\n     + \ttest_cmp expect actual\n       '\n       \n     -+test_expect_success 'refuse --literal-value for incompatible actions' '\n     -+\tgit config dev.null bogus &&\n     -+\ttest_must_fail git config --literal-value --add dev.null bogus &&\n     -+\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n     -+\ttest_must_fail git config --literal-value --get-urlmatch dev.null bogus &&\n     -+\ttest_must_fail git config --literal-value --rename-section dev null &&\n     -+\ttest_must_fail git config --literal-value --remove-section dev &&\n     -+\ttest_must_fail git config --literal-value --list &&\n     -+\ttest_must_fail git config --literal-value --get-color dev.null &&\n     -+\ttest_must_fail git config --literal-value --get-colorbool dev.null &&\n     -+\ttest_must_fail git config --literal-value --edit\n     ++test_expect_success 'refuse --fixed-value for incompatible actions' '\n     ++\tgit config --file=config dev.null bogus &&\n     ++\n     ++\t# These modes do not allow --fixed-value at all\n     ++\ttest_must_fail git config --file=config --fixed-value --add dev.null bogus &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n     ++\ttest_must_fail git config --file=config --fixed-value --rename-section dev null &&\n     ++\ttest_must_fail git config --file=config --fixed-value --remove-section dev &&\n     ++\ttest_must_fail git config --file=config --fixed-value --list &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-color dev.null &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&\n     ++\n     ++\t# These modes complain when --fixed-value has no value_regex\n     ++\ttest_must_fail git config --file=config --fixed-value dev.null bogus &&\n     ++\ttest_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get dev.null &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-all dev.null &&\n     ++\ttest_must_fail git config --file=config --fixed-value --get-regexp \"dev.*\" &&\n     ++\ttest_must_fail git config --file=config --fixed-value --unset dev.null &&\n     ++\ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n      +'\n      +\n       test_done\n 5:  5881b2d987 ! 5:  39718048cd config: plumb --literal-value into config API\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    config: plumb --literal-value into config API\n     +    config: plumb --fixed-value into config API\n      \n          The git_config_set_multivar_in_file_gently() and related methods now\n     -    take a 'flags' bitfield, so add a new bit representing the\n     -    --literal-value option from 'git config'. This alters the purpose of the\n     -    value_regex parameter to be an exact string match. This requires some\n     -    initialization changes in git_config_set_multivar_in_file_gently() and a\n     -    new strcmp() call in the matches() method.\n     +    take a 'flags' bitfield, so add a new bit representing the --fixed-value\n     +    option from 'git config'. This alters the purpose of the value_regex\n     +    parameter to be an exact string match. This requires some initialization\n     +    changes in git_config_set_multivar_in_file_gently() and a new strcmp()\n     +    call in the matches() method.\n      \n     -    The new CONFIG_FLAGS_LITERAL_VALUE flag is initialized in\n     -    builtin/config.c based on the --literal-value option, and that needs to\n     -    be updated in several callers.\n     +    The new CONFIG_FLAGS_FIXED_VALUE flag is initialized in builtin/config.c\n     +    based on the --fixed-value option, and that needs to be updated in\n     +    several callers.\n      \n          This patch only affects some of the modes of 'git config', and the rest\n          will be completed in the next change.\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n       \n      @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix)\n     - \t\terror(_(\"--literal only applies with 'value_regex'\"));\n     - \t\tusage_builtin_config();\n     + \t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n     + \t\t\tusage_builtin_config();\n     + \t\t}\n     ++\n     ++\t\tflags = CONFIG_FLAGS_FIXED_VALUE;\n       \t}\n     -+\tif (literal)\n     -+\t\tflags = CONFIG_FLAGS_LITERAL_VALUE;\n       \n       \tif (actions & PAGING_ACTIONS)\n     - \t\tsetup_auto_pager(\"config\", 1);\n      @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix)\n       \t\tvalue = normalize_value(argv[0], argv[1]);\n       \t\tUNLEAK(value);\n     @@ config.c: int git_config_set_multivar_in_file_gently(const char *config_filename\n       \t\t\tstore.value_regex = NULL;\n       \t\telse if (value_regex == CONFIG_REGEX_NONE)\n       \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n     -+\t\telse if (flags & CONFIG_FLAGS_LITERAL_VALUE)\n     ++\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n      +\t\t\tstore.literal_value = value_regex;\n       \t\telse {\n       \t\t\tif (value_regex[0] == '!') {\n       \t\t\t\tstore.do_not_match = 1;\n      \n       ## config.h ##\n     -@@ config.h: enum config_event_t {\n     +@@ config.h: int git_config_key_is_valid(const char *key);\n        */\n       #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n       \n      +/*\n     -+ * When CONFIG_FLAGS_LITERAL_VALUE is specified, match key/value pairs\n     ++ * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs\n      + * by string comparison (not regex match) to the provided value_regex\n      + * parameter.\n      + */\n     -+#define CONFIG_FLAGS_LITERAL_VALUE (1 << 1)\n     ++#define CONFIG_FLAGS_FIXED_VALUE (1 << 1)\n      +\n     - /*\n     -  * The parser event function (if not NULL) is called with the event type and\n     -  * the begin/end offsets of the parsed elements.\n     + int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n     + void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n     + int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n      \n       ## t/t1300-config.sh ##\n     -@@ t/t1300-config.sh: test_expect_success 'refuse --literal-value for incompatible actions' '\n     - \ttest_must_fail git config --literal-value --edit\n     +@@ t/t1300-config.sh: test_expect_success 'refuse --fixed-value for incompatible actions' '\n     + \ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n       '\n       \n     -+test_expect_success '--literal-value uses exact string matching' '\n     ++test_expect_success '--fixed-value uses exact string matching' '\n      +\tGLOB=\"a+b*c?d[e]f.g\" &&\n     -+\tq_to_tab >initial <<-EOF &&\n     -+\t[literal]\n     -+\tQtest = $GLOB\n     -+\tEOF\n     ++\trm -f initial &&\n     ++\tgit config --file=initial fixed.test \"$GLOB\" &&\n      +\n     -+\tcp initial .git/config &&\n     -+\tgit config literal.test bogus \"$GLOB\" &&\n     -+\tq_to_tab >expect <<-EOF &&\n     -+\t[literal]\n     -+\tQtest = $GLOB\n     -+\tQtest = bogus\n     ++\tcp initial config &&\n     ++\tgit config --file=config fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-EOF &&\n     ++\tfixed.test=$GLOB\n     ++\tfixed.test=bogus\n      +\tEOF\n     -+\ttest_cmp expect .git/config &&\n     -+\tcp initial .git/config &&\n     -+\tgit config --literal-value literal.test bogus \"$GLOB\" &&\n     -+\tq_to_tab >expect <<-EOF &&\n     -+\t[literal]\n     -+\tQtest = bogus\n     -+\tEOF\n     -+\ttest_cmp expect .git/config &&\n     ++\ttest_cmp expect actual &&\n     ++\n     ++\tcp initial config &&\n     ++\tgit config --file=config --fixed-value fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tprintf \"fixed.test=bogus\\n\" >expect &&\n     ++\ttest_cmp expect actual &&\n      +\n     -+\tcp initial .git/config &&\n     -+\ttest_must_fail git config --unset literal.test \"$GLOB\" &&\n     -+\tgit config --literal-value --unset literal.test \"$GLOB\" &&\n     -+\ttest_must_fail git config literal.test &&\n     ++\tcp initial config &&\n     ++\ttest_must_fail git config --file=config --unset fixed.test \"$GLOB\" &&\n     ++\tgit config --file=config --fixed-value --unset fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config fixed.test &&\n      +\n     -+\tcp initial .git/config &&\n     -+\ttest_must_fail git config --unset-all literal.test \"$GLOB\" &&\n     -+\tgit config --literal-value --unset-all literal.test \"$GLOB\" &&\n     -+\ttest_must_fail git config literal.test &&\n     ++\tcp initial config &&\n     ++\ttest_must_fail git config --file=config --unset-all fixed.test \"$GLOB\" &&\n     ++\tgit config --file=config --fixed-value --unset-all fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config fixed.test &&\n      +\n     -+\tcp initial .git/config &&\n     -+\tgit config --replace-all literal.test bogus \"$GLOB\" &&\n     -+\tq_to_tab >expect <<-EOF &&\n     -+\t[literal]\n     -+\tQtest = $GLOB\n     -+\tQtest = bogus\n     ++\tcp initial config &&\n     ++\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-EOF &&\n     ++\tfixed.test=$GLOB\n     ++\tfixed.test=bogus\n      +\tEOF\n     -+\ttest_cmp expect .git/config &&\n     ++\ttest_cmp expect actual &&\n      +\n     -+\tcp initial .git/config &&\n     -+\tgit config --replace-all literal.test bogus \"$GLOB\" &&\n     -+\tgit config --literal-value --replace-all literal.test bogus \"$GLOB\" &&\n     -+\ttest_cmp_config bogus literal.test\n     ++\tcp initial config &&\n     ++\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-EOF &&\n     ++\tfixed.test=$GLOB\n     ++\tfixed.test=bogus\n     ++\tEOF\n     ++\ttest_cmp expect actual &&\n     ++\n     ++\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --list >actual &&\n     ++\tcat >expect <<-EOF &&\n     ++\tfixed.test=bogus\n     ++\tfixed.test=bogus\n     ++\tEOF\n     ++\ttest_cmp expect actual\n      +'\n      +\n       test_done\n 6:  7c8e3769c7 ! 6:  8e0111c7b4 config: implement --literal-value with --get*\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    config: implement --literal-value with --get*\n     +    config: implement --fixed-value with --get*\n      \n          The config builtin does its own regex matching of values for the --get,\n          --get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\n     @@ builtin/config.c: 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 (literal && strcmp(value_regex, (value_?value_:\"\")))\n     ++\tif (fixed_value && strcmp(value_regex, (value_?value_:\"\")))\n      +\t\treturn 0;\n       \tif (regexp != NULL &&\n       \t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n     @@ builtin/config.c: static int get_value(const char *key_, const char *regex_)\n       \t}\n       \n      -\tif (regex_) {\n     -+\tif (regex_ && (flags & CONFIG_FLAGS_LITERAL_VALUE))\n     ++\tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n      +\t\tvalue_regex = regex_;\n      +\telse if (regex_) {\n       \t\tif (regex_[0] == '!') {\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \t\tcheck_argc(argc, 2, 2);\n      \n       ## t/t1300-config.sh ##\n     -@@ t/t1300-config.sh: test_expect_success '--literal-value uses exact string matching' '\n     - \ttest_cmp_config bogus literal.test\n     +@@ t/t1300-config.sh: test_expect_success '--fixed-value uses exact string matching' '\n     + \ttest_cmp expect actual\n       '\n       \n     -+test_expect_success '--get and --get-all with --literal-value' '\n     ++test_expect_success '--get and --get-all with --fixed-value' '\n      +\tGLOB=\"a+b*c?d[e]f.g\" &&\n     -+\tq_to_tab >.git/config <<-EOF &&\n     -+\t[literal]\n     -+\tQtest = bogus\n     -+\tQtest = $GLOB\n     -+\tEOF\n     ++\trm -f config &&\n     ++\tgit config --file=config fixed.test bogus &&\n     ++\tgit config --file=config --add fixed.test \"$GLOB\" &&\n      +\n     -+\tgit config --get literal.test bogus &&\n     -+\ttest_must_fail git config --get literal.test \"$GLOB\" &&\n     -+\tgit config --get --literal-value literal.test \"$GLOB\" &&\n     -+\ttest_must_fail git config --get --literal-value literal.test non-existent &&\n     ++\tgit config --file=config --get fixed.test bogus &&\n     ++\ttest_must_fail git config --file=config --get fixed.test \"$GLOB\" &&\n     ++\tgit config --file=config --get --fixed-value fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&\n      +\n     -+\tgit config --get-all literal.test bogus &&\n     -+\ttest_must_fail git config --get-all literal.test \"$GLOB\" &&\n     -+\tgit config --get-all --literal-value literal.test \"$GLOB\" &&\n     -+\ttest_must_fail git config --get-all --literal-value literal.test non-existent &&\n     ++\tgit config --file=config --get-all fixed.test bogus &&\n     ++\ttest_must_fail git config --file=config --get-all fixed.test \"$GLOB\" &&\n     ++\tgit config --file=config --get-all --fixed-value fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&\n      +\n     -+\tgit config --get-regexp literal+ bogus &&\n     -+\ttest_must_fail git config  --get-regexp literal+ \"$GLOB\" &&\n     -+\tgit config --get-regexp --literal-value literal+ \"$GLOB\" &&\n     -+\ttest_must_fail git config --get-regexp --literal-value literal+ non-existent\n     ++\tgit config --file=config --get-regexp fixed+ bogus &&\n     ++\ttest_must_fail git config --file=config --get-regexp fixed+ \"$GLOB\" &&\n     ++\tgit config --file=config --get-regexp --fixed-value fixed+ \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent\n      +'\n      +\n       test_done\n 7:  1d1c2fd23e ! 7:  5a3acf8119 maintenance: use 'git config --literal-value'\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    maintenance: use 'git config --literal-value'\n     +    maintenance: use 'git config --fixed-value'\n      \n          When a repository's leading directories contain regex glob characters,\n          the config calls for 'git maintenance register' and 'git maintenance\n     -    unregister' are not careful enough. Use the new --literal-value option\n     +    unregister' are not careful enough. Use the new --fixed-value option\n          to direct the config machinery to use exact string matches. This is a\n          more robust option than excaping these arguments in a piecemeal fashion.\n      \n     +    For the test, require that we are not running on Windows since the '+'\n     +    character is not allowed on that filesystem.\n     +\n          Reported-by: Emily Shaffer <emilyshaffer@google.com>\n          Reported-by: Jonathan Nieder <jrnieder@gmail.com>\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n     @@ builtin/gc.c: static int maintenance_register(void)\n       \tconfig_get.git_cmd = 1;\n      -\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n      +\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n     -+\t\t     \"--literal-value\", \"maintenance.repo\",\n     ++\t\t     \"--fixed-value\", \"maintenance.repo\",\n       \t\t     the_repository->worktree ? the_repository->worktree\n       \t\t\t\t\t      : the_repository->gitdir,\n       \t\t\t NULL);\n     @@ builtin/gc.c: static int maintenance_unregister(void)\n       \tconfig_unset.git_cmd = 1;\n       \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n      -\t\t     \"maintenance.repo\",\n     -+\t\t     \"--literal-value\", \"maintenance.repo\",\n     ++\t\t     \"--fixed-value\", \"maintenance.repo\",\n       \t\t     the_repository->worktree ? the_repository->worktree\n       \t\t\t\t\t      : the_repository->gitdir,\n       \t\t     NULL);\n     @@ t/t7900-maintenance.sh: test_expect_success 'register and unregister' '\n       \ttest_cmp before actual\n       '\n       \n     -+test_expect_success 'register and unregister with glob characters' '\n     ++test_expect_success !MINGW 'register and unregister with glob characters' '\n      +\tGLOB=\"a+b*c\" &&\n      +\tgit init \"$GLOB\" &&\n      +\tgit -C \"$GLOB\" maintenance register &&\n      +\tgit config --get-all --show-origin maintenance.repo &&\n     -+\tgit config --get-all --global --literal-value \\\n     ++\tgit config --get-all --global --fixed-value \\\n      +\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n      +\tgit -C \"$GLOB\" maintenance unregister &&\n     -+\ttest_must_fail git config --get-all --global --literal-value \\\n     ++\ttest_must_fail git config --get-all --global --fixed-value \\\n      +\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n      +'\n      +\n\n-- \ngitgitgadget\n"},{"id":"410533","messageId":"829d0ccd8c6ae52e78eda52b15283867a0be673e.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 2/7] t1300: add test for --replace-all with value_regex","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:02Z","receivedAt":"2020-11-23T16:05:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe --replace-all option was added in 4ddba79d (git-config-set: add more\noptions) but was not tested along with the 'value_regex' parameter.\nSince we will be updating this option to optionally treat 'value_regex'\nas a fixed string, let's add a test here that documents the current\nbehavior.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 19836ec83b..8783767d4f 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1946,4 +1946,18 @@ test_expect_success 'set all config with value_regex' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--replace-all and value_regex' '\n+\trm -f config &&\n+\tgit config --file=config --add abc.key one &&\n+\tgit config --file=config --add abc.key two &&\n+\tgit config --file=config --add abc.key three &&\n+\tgit config --file=config --replace-all abc.key four \"o+\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=four\n+\tabc.key=three\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410534","messageId":"0e6a7371ed4696f6cc85df07466fb6c20d58d62e.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:04Z","receivedAt":"2020-11-23T16:05:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe 'git config' builtin takes a 'value_regex' parameter for several\nactions. This can cause confusion when expecting exact value matches\ninstead of regex matches, especially when the input string contains glob\ncharacters. While callers can escape the patterns themselves, it would\nbe more friendly to allow an argument to disable the pattern matching in\nfavor of an exact string match.\n\nAdd a new '--fixed-value' option that does not currently change the\nbehavior. The implementation will follow for each appropriate action.\nFor now, check and test that --fixed-value will abort the command when\nincluded with an incompatible action or without a 'value_regex'\nargument.\n\nThe name '--fixed-value' was chosen over something simpler like\n'--fixed' because some commands allow regular expressions on the\nkey in addition to the value.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-config.txt | 20 +++++++++++++-------\n builtin/config.c             | 26 ++++++++++++++++++++++++++\n t/t1300-config.sh            | 23 +++++++++++++++++++++++\n 3 files changed, 62 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7573160f21..d4bb928ea7 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,15 +9,15 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n 'git config' [<file-option>] [--type=<type>] --add name value\n-'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value_regex]\n 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n-'git config' [<file-option>] --unset name [value_regex]\n-'git config' [<file-option>] --unset-all name [value_regex]\n+'git config' [<file-option>] [--fixed-value] --unset name [value_regex]\n+'git config' [<file-option>] [--fixed-value] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n 'git config' [<file-option>] --remove-section name\n 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n@@ -165,6 +165,12 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n+--fixed-value::\n+\tWhen used with the `value_regex` argument, treat `value_regex` as\n+\tan exact string instead of a regular expression. This will restrict\n+\tthe name/value pairs that are matched to only those where the value\n+\tis exactly equal to the `value_regex`.\n+\n --type <type>::\n   'git config' will ensure that any input or output is valid under the given\n   type constraint(s), and will canonicalize outgoing values in `<type>`'s\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e7c7f3d455..bfb55a96df 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -34,6 +34,7 @@ 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 \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -141,6 +142,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n \tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n@@ -745,6 +747,30 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_builtin_config();\n \t}\n \n+\tif (fixed_value) {\n+\t\tint allowed_usage = 0;\n+\n+\t\tswitch (actions) {\n+\t\tcase ACTION_GET:\n+\t\tcase ACTION_GET_ALL:\n+\t\tcase ACTION_GET_REGEXP:\n+\t\tcase ACTION_UNSET:\n+\t\tcase ACTION_UNSET_ALL:\n+\t\t\tallowed_usage = argc > 1 && !!argv[1];\n+\t\t\tbreak;\n+\n+\t\tcase ACTION_SET_ALL:\n+\t\tcase ACTION_REPLACE_ALL:\n+\t\t\tallowed_usage = argc > 2 && !!argv[2];\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (!allowed_usage) {\n+\t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n+\t\t\tusage_builtin_config();\n+\t\t}\n+\t}\n+\n \tif (actions & PAGING_ACTIONS)\n \t\tsetup_auto_pager(\"config\", 1);\n \ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 8783767d4f..6dc8117241 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1960,4 +1960,27 @@ test_expect_success '--replace-all and value_regex' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'refuse --fixed-value for incompatible actions' '\n+\tgit config --file=config dev.null bogus &&\n+\n+\t# These modes do not allow --fixed-value at all\n+\ttest_must_fail git config --file=config --fixed-value --add dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --rename-section dev null &&\n+\ttest_must_fail git config --file=config --fixed-value --remove-section dev &&\n+\ttest_must_fail git config --file=config --fixed-value --list &&\n+\ttest_must_fail git config --file=config --fixed-value --get-color dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&\n+\n+\t# These modes complain when --fixed-value has no value_regex\n+\ttest_must_fail git config --file=config --fixed-value dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-all dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-regexp \"dev.*\" &&\n+\ttest_must_fail git config --file=config --fixed-value --unset dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410536","messageId":"0c152faa00881483db0a59f4c5bc7204ebed8966.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 3/7] config: convert multi_replace to flags","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:03Z","receivedAt":"2020-11-23T16:05:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWe will extend the flexibility of the config API. Before doing so, let's\ntake an existing 'int multi_replace' parameter and replace it with a new\n'unsigned flags' parameter that can take multiple options as a bit field.\n\nUpdate all callers that specified multi_replace to now specify the\nCONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the\ndocumentation of git_config_set_multivar_in_file() including a clear\nlabeling of its arguments. Other config API methods in config.h require\nonly a change of the final parameter from 'int' to 'unsigned'.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/branch.c |  4 ++--\n builtin/config.c |  6 ++++--\n builtin/remote.c |  8 +++++---\n config.c         | 24 ++++++++++++------------\n config.h         | 29 ++++++++++++++++++++++-------\n 5 files changed, 45 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex e82301fb1b..5ce3844d22 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"Branch '%s' has no upstream information\"), branch->name);\n \n \t\tstrbuf_addf(&buf, \"branch.%s.remote\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"branch.%s.merge\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_release(&buf);\n \t} else if (argc > 0 && argc <= 2) {\n \t\tif (filter.kind != FILTER_REFS_BRANCHES)\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5e39f61885..e7c7f3d455 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -823,7 +823,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 1);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -859,7 +860,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 1);\n+\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex c8240e9fcd..29b1652975 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -708,7 +708,7 @@ static int mv(int argc, const char **argv)\n \n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new_name);\n-\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \tstrbuf_addf(&old_remote_context, \":refs/remotes/%s/\", rename.old_name);\n \tfor (i = 0; i < oldremote->fetch.raw_nr; i++) {\n \t\tchar *ptr;\n@@ -1485,7 +1485,8 @@ static int update(int argc, const char **argv)\n \n static int remove_all_fetch_refspecs(const char *key)\n {\n-\treturn git_config_set_multivar_gently(key, NULL, NULL, 1);\n+\treturn git_config_set_multivar_gently(key, NULL, NULL,\n+\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n }\n \n static void add_branches(struct remote *remote, const char **branches,\n@@ -1674,7 +1675,8 @@ static int set_url(int argc, const char **argv)\n \tif (!delete_mode)\n \t\tgit_config_set_multivar(name_buf.buf, newurl, oldurl, 0);\n \telse\n-\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl, 1);\n+\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl,\n+\t\t\t\t\tCONFIG_FLAGS_MULTI_REPLACE);\n out:\n \tstrbuf_release(&name_buf);\n \treturn 0;\ndiff --git a/config.c b/config.c\nindex 2b79fe76ad..096f2eae0d 100644\n--- a/config.c\n+++ b/config.c\n@@ -2716,9 +2716,9 @@ void git_config_set(const char *key, const char *value)\n  * if value_regex!=NULL, disregard key/value pairs where value does not match.\n  * if value_regex==CONFIG_REGEX_NONE, do not match any existing values\n  *     (only add a new one)\n- * if multi_replace==0, nothing, or only one matching key/value is replaced,\n- *     else all matching key/values (regardless how many) are removed,\n- *     before the new pair is written.\n+ * if (flags & CONFIG_FLAGS_MULTI_REPLACE) == 0, at most one matching\n+ *     key/value is replaced, else all matching key/values (regardless\n+ *     how many) are removed, before the new pair is written.\n  *\n  * Returns 0 on success.\n  *\n@@ -2739,7 +2739,7 @@ void git_config_set(const char *key, const char *value)\n int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\t\t\t   const char *key, const char *value,\n \t\t\t\t\t   const char *value_regex,\n-\t\t\t\t\t   int multi_replace)\n+\t\t\t\t\t   unsigned flags)\n {\n \tint fd = -1, in_fd = -1;\n \tint ret;\n@@ -2756,7 +2756,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \tif (ret)\n \t\tgoto out_free;\n \n-\tstore.multi_replace = multi_replace;\n+\tstore.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0;\n \n \tif (!config_filename)\n \t\tconfig_filename = filename_buf = git_pathdup(\"config\");\n@@ -2845,7 +2845,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n \t\t/* if nothing to unset, or too many matches, error out */\n \t\tif ((store.seen_nr == 0 && value == NULL) ||\n-\t\t    (store.seen_nr > 1 && multi_replace == 0)) {\n+\t\t    (store.seen_nr > 1 && !store.multi_replace)) {\n \t\t\tret = CONFIG_NOTHING_SET;\n \t\t\tgoto out_free;\n \t\t}\n@@ -2984,10 +2984,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n void git_config_set_multivar_in_file(const char *config_filename,\n \t\t\t\t     const char *key, const char *value,\n-\t\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t\t     const char *value_regex, unsigned flags)\n {\n \tif (!git_config_set_multivar_in_file_gently(config_filename, key, value,\n-\t\t\t\t\t\t    value_regex, multi_replace))\n+\t\t\t\t\t\t    value_regex, flags))\n \t\treturn;\n \tif (value)\n \t\tdie(_(\"could not set '%s' to '%s'\"), key, value);\n@@ -2996,17 +2996,17 @@ void git_config_set_multivar_in_file(const char *config_filename,\n }\n \n int git_config_set_multivar_gently(const char *key, const char *value,\n-\t\t\t\t   const char *value_regex, int multi_replace)\n+\t\t\t\t   const char *value_regex, unsigned flags)\n {\n \treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,\n-\t\t\t\t\t\t      multi_replace);\n+\t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n-\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t     const char *value_regex, unsigned flags)\n {\n \tgit_config_set_multivar_in_file(NULL, key, value, value_regex,\n-\t\t\t\t\tmulti_replace);\n+\t\t\t\t\tflags);\n }\n \n static int section_name_match (const char *buf, const char *name)\ndiff --git a/config.h b/config.h\nindex 060874488f..80844604ab 100644\n--- a/config.h\n+++ b/config.h\n@@ -256,9 +256,22 @@ void git_config_set(const char *, const char *);\n \n int git_config_parse_key(const char *, char **, size_t *);\n int git_config_key_is_valid(const char *key);\n-int git_config_set_multivar_gently(const char *, const char *, const char *, int);\n-void git_config_set_multivar(const char *, const char *, const char *, int);\n-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);\n+\n+/*\n+ * The following macros specify flag bits that alter the behavior\n+ * of the git_config_set_multivar*() methods.\n+ */\n+\n+/*\n+ * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n+ * are removed before a new pair is written. If the flag is not present,\n+ * then set operations replace only the first match.\n+ */\n+#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n+\n+int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n+void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n  * takes four parameters:\n@@ -276,13 +289,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n  * - the value regex, as a string. It will disregard key/value pairs where value\n  *   does not match.\n  *\n- * - a multi_replace value, as an int. If value is equal to zero, nothing or only\n- *   one matching key/value is replaced, else all matching key/values (regardless\n- *   how many) are removed, before the new pair is written.\n+ * - a flags value with bits corresponding to the CONFIG_FLAG_* macros.\n  *\n  * It returns 0 on success.\n  */\n-void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n+void git_config_set_multivar_in_file(const char *config_filename,\n+\t\t\t\t     const char *key,\n+\t\t\t\t     const char *value,\n+\t\t\t\t     const char *value_regex,\n+\t\t\t\t     unsigned flags);\n \n /**\n  * rename or remove sections in the config file\n-- \ngitgitgadget\n\n"},{"id":"410538","messageId":"39718048cd8f5be053a13ff73f531e3400b80b25.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 5/7] config: plumb --fixed-value into config API","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:05Z","receivedAt":"2020-11-23T16:05:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe git_config_set_multivar_in_file_gently() and related methods now\ntake a 'flags' bitfield, so add a new bit representing the --fixed-value\noption from 'git config'. This alters the purpose of the value_regex\nparameter to be an exact string match. This requires some initialization\nchanges in git_config_set_multivar_in_file_gently() and a new strcmp()\ncall in the matches() method.\n\nThe new CONFIG_FLAGS_FIXED_VALUE flag is initialized in builtin/config.c\nbased on the --fixed-value option, and that needs to be updated in\nseveral callers.\n\nThis patch only affects some of the modes of 'git config', and the rest\nwill be completed in the next change.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 16 ++++++++-----\n config.c          |  5 +++++\n config.h          |  7 ++++++\n t/t1300-config.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex bfb55a96df..3e49e04411 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -616,6 +616,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n {\n \tint nongit = !startup_info->have_repository;\n \tchar *value;\n+\tint flags = 0;\n \n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n@@ -769,6 +770,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n \t\t\tusage_builtin_config();\n \t\t}\n+\n+\t\tflags = CONFIG_FLAGS_FIXED_VALUE;\n \t}\n \n \tif (actions & PAGING_ACTIONS)\n@@ -832,7 +835,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 0);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write();\n@@ -841,7 +845,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value,\n-\t\t\t\t\t\t\t      CONFIG_REGEX_NONE, 0);\n+\t\t\t\t\t\t\t      CONFIG_REGEX_NONE,\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write();\n@@ -850,7 +855,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value, argv[2],\n-\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -877,7 +882,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 0);\n+\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t      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);\n@@ -887,7 +893,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/config.c b/config.c\nindex 096f2eae0d..64bab2b08e 100644\n--- a/config.c\n+++ b/config.c\n@@ -2402,6 +2402,7 @@ struct config_store_data {\n \tsize_t baselen;\n \tchar *key;\n \tint do_not_match;\n+\tconst char *literal_value;\n \tregex_t *value_regex;\n \tint multi_replace;\n \tstruct {\n@@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,\n {\n \tif (strcmp(key, store->key))\n \t\treturn 0; /* not ours */\n+\tif (store->literal_value)\n+\t\treturn !strcmp(store->literal_value, value);\n \tif (!store->value_regex)\n \t\treturn 1; /* always matches */\n \tif (store->value_regex == CONFIG_REGEX_NONE)\n@@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\tstore.value_regex = NULL;\n \t\telse if (value_regex == CONFIG_REGEX_NONE)\n \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n+\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n+\t\t\tstore.literal_value = value_regex;\n \t\telse {\n \t\t\tif (value_regex[0] == '!') {\n \t\t\t\tstore.do_not_match = 1;\ndiff --git a/config.h b/config.h\nindex 80844604ab..977e690be8 100644\n--- a/config.h\n+++ b/config.h\n@@ -269,6 +269,13 @@ int git_config_key_is_valid(const char *key);\n  */\n #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n \n+/*\n+ * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs\n+ * by string comparison (not regex match) to the provided value_regex\n+ * parameter.\n+ */\n+#define CONFIG_FLAGS_FIXED_VALUE (1 << 1)\n+\n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 6dc8117241..30e80ae9cb 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1983,4 +1983,61 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '\n \ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n '\n \n+test_expect_success '--fixed-value uses exact string matching' '\n+\tGLOB=\"a+b*c?d[e]f.g\" &&\n+\trm -f initial &&\n+\tgit config --file=initial fixed.test \"$GLOB\" &&\n+\n+\tcp initial config &&\n+\tgit config --file=config fixed.test bogus \"$GLOB\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=$GLOB\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\tgit config --file=config --fixed-value fixed.test bogus \"$GLOB\" &&\n+\tgit config --file=config --list >actual &&\n+\tprintf \"fixed.test=bogus\\n\" >expect &&\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\ttest_must_fail git config --file=config --unset fixed.test \"$GLOB\" &&\n+\tgit config --file=config --fixed-value --unset fixed.test \"$GLOB\" &&\n+\ttest_must_fail git config --file=config fixed.test &&\n+\n+\tcp initial config &&\n+\ttest_must_fail git config --file=config --unset-all fixed.test \"$GLOB\" &&\n+\tgit config --file=config --fixed-value --unset-all fixed.test \"$GLOB\" &&\n+\ttest_must_fail git config --file=config fixed.test &&\n+\n+\tcp initial config &&\n+\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=$GLOB\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=$GLOB\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$GLOB\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=bogus\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410539","messageId":"8e0111c7b4b2c766c61df30c4ae93bd2d724de06.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 6/7] config: implement --fixed-value with --get*","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:06Z","receivedAt":"2020-11-23T16:05:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe config builtin does its own regex matching of values for the --get,\n--get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\nto the get_value() method so we can initialize the value_regex argument\nas a fixed string instead of a regex pattern.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 15 ++++++++++-----\n t/t1300-config.sh | 22 ++++++++++++++++++++++\n 2 files changed, 32 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 3e49e04411..d3772b5efe 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -14,6 +14,7 @@ static const char *const builtin_config_usage[] = {\n \n static char *key;\n static regex_t *key_regexp;\n+static const char *value_regex;\n static regex_t *regexp;\n static int show_keys;\n static int omit_values;\n@@ -288,6 +289,8 @@ static int collect_config(const char *key_, const char *value_, void *cb)\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_regex, (value_?value_:\"\")))\n+\t\treturn 0;\n \tif (regexp != NULL &&\n \t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n@@ -298,7 +301,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)\n \treturn format_config(&values->items[values->nr++], key_, value_);\n }\n \n-static int get_value(const char *key_, const char *regex_)\n+static int get_value(const char *key_, const char *regex_, int flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -335,7 +338,9 @@ static int get_value(const char *key_, const char *regex_)\n \t\t}\n \t}\n \n-\tif (regex_) {\n+\tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n+\t\tvalue_regex = regex_;\n+\telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdo_not_match = 1;\n \t\t\tregex_++;\n@@ -859,19 +864,19 @@ int cmd_config(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]);\n+\t\treturn 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]);\n+\t\treturn 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]);\n+\t\treturn get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 30e80ae9cb..13211b6bf4 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2040,4 +2040,26 @@ test_expect_success '--fixed-value uses exact string matching' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--get and --get-all with --fixed-value' '\n+\tGLOB=\"a+b*c?d[e]f.g\" &&\n+\trm -f config &&\n+\tgit config --file=config fixed.test bogus &&\n+\tgit config --file=config --add fixed.test \"$GLOB\" &&\n+\n+\tgit config --file=config --get fixed.test bogus &&\n+\ttest_must_fail git config --file=config --get fixed.test \"$GLOB\" &&\n+\tgit config --file=config --get --fixed-value fixed.test \"$GLOB\" &&\n+\ttest_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&\n+\n+\tgit config --file=config --get-all fixed.test bogus &&\n+\ttest_must_fail git config --file=config --get-all fixed.test \"$GLOB\" &&\n+\tgit config --file=config --get-all --fixed-value fixed.test \"$GLOB\" &&\n+\ttest_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&\n+\n+\tgit config --file=config --get-regexp fixed+ bogus &&\n+\ttest_must_fail git config --file=config --get-regexp fixed+ \"$GLOB\" &&\n+\tgit config --file=config --get-regexp --fixed-value fixed+ \"$GLOB\" &&\n+\ttest_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410537","messageId":"5a3acf811998bb728ce94c69611c237861775142.1606147507.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v2 7/7] maintenance: use 'git config --fixed-value'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-23T16:05:07Z","receivedAt":"2020-11-23T16:05:36Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen a repository's leading directories contain regex glob characters,\nthe config calls for 'git maintenance register' and 'git maintenance\nunregister' are not careful enough. Use the new --fixed-value option\nto direct the config machinery to use exact string matches. This is a\nmore robust option than excaping these arguments in a piecemeal fashion.\n\nFor the test, require that we are not running on Windows since the '+'\ncharacter is not allowed on that filesystem.\n\nReported-by: Emily Shaffer <emilyshaffer@google.com>\nReported-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/gc.c           |  5 +++--\n t/t7900-maintenance.sh | 12 ++++++++++++\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex e3098ef6a1..6dde3ce1bb 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n \n \tconfig_get.git_cmd = 1;\n-\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n+\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n+\t\t     \"--fixed-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t\t NULL);\n@@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n \n \tconfig_unset.git_cmd = 1;\n \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n-\t\t     \"maintenance.repo\",\n+\t\t     \"--fixed-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t     NULL);\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex 20184e96e1..c4e5564c31 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n \ttest_cmp before actual\n '\n \n+test_expect_success !MINGW 'register and unregister with glob characters' '\n+\tGLOB=\"a+b*c\" &&\n+\tgit init \"$GLOB\" &&\n+\tgit -C \"$GLOB\" maintenance register &&\n+\tgit config --get-all --show-origin maintenance.repo &&\n+\tgit config --get-all --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n+\tgit -C \"$GLOB\" maintenance unregister &&\n+\ttest_must_fail git config --get-all --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n+'\n+\n test_expect_success 'start from empty cron table' '\n \tGIT_TEST_CRONTAB=\"test-tool crontab cron.txt\" git maintenance start &&\n \n-- \ngitgitgadget\n"},{"id":"410553","messageId":"xmqq8sarool3.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/7] config: add --fixed-value option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T19:33:28Z","receivedAt":"2020-11-23T19:33:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>  * Updated the test structure for the new tests in t1300-config.sh\n\nPatches [1/7] and [2/7] got much less scary with this change.\n\nNice.\n\nThanks, will replace (but with other topics depend on this series,\nit may take a bit more time than usual).\n"},{"id":"410554","messageId":"20201123193714.GA499823@google.com","threadId":"54676","inReplyTo":"ea3099719c867a5a475468ddb6741bbcd713dccc.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T19:37:14Z","receivedAt":"2020-11-23T19:37:38Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:01PM +0000, Derrick Stolee via GitGitGadget wrote:\n> Add a test that demonstrates these options. Break from the existing\n> pattern in t1300-config.sh to use 'git config --file=<file>' instead of\n> modifying .git/config directly. Also use 'git config --file=<file>\n> --list' for config state comparison instead of the config file format.\n\nI'd normally say \"but *why* do you want to do that?\" but I think it's\nactually pretty clear in the diff based on the naming of the config\nfiles (and clever - I like it). I do think pointing out that you're\nbreaking from existing style is useful since the existing style isn't\ncontained in the diff alone.\n\n> +test_expect_success 'set all config with value_regex' '\n> +\tgit config --file=initial abc.key one &&\n> +\n> +\tcp initial config &&\n> +\tgit config --file=config abc.key two a+ &&\nMake sure that if the value_regex misses existing keys, it can still add\nnormally.\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-\\EOF &&\n> +\tabc.key=one\n> +\tabc.key=two\n> +\tEOF\n> +\ttest_cmp expect actual &&\n> +\n> +\ttest_must_fail git config --file=config abc.key three o+ 2>err &&\n> +\ttest_i18ngrep \"has multiple values\" err &&\nTry to change the value of the config, but it's a multiconfig already.\nOk.\n\n> +\tgit config --file=config abc.key three a+ &&\nBut /a+/ doesn't match any configs that already exist, so this one gets\nadded fine.\n\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-\\EOF &&\n> +\tabc.key=one\n> +\tabc.key=two\n> +\tabc.key=three\n> +\tEOF\n> +\ttest_cmp expect actual &&\n> +\n> +\tcp initial config &&\nGet back the config before the last exercise (with just 'one').\n> +\tgit config --file=config abc.key three o+ &&\nAnd if the value_regex matches exactly one config, then replace that\none. I could see this case being a little bit more compelling if the\nvalue_regex was doing some lifting, e.g.\n\nabc.key=nil\nabc.key=one\n\ngit config abc.key two o+\n\nabc.key=nil\nabc.key=two\n\nThis criteria was kind of implied in #2 in your commit message - does it\nwork?\n\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-\\EOF &&\n> +\tabc.key=three\n> +\tEOF\n> +\ttest_cmp expect actual\n> +'\n\nAnyway, it's a pain, but I'd like to see either comments describing\nwhich case each one is, or individual tests.\n\n - Emily\n"},{"id":"410556","messageId":"xmqq1rgjoodx.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"0e6a7371ed4696f6cc85df07466fb6c20d58d62e.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T19:37:46Z","receivedAt":"2020-11-23T19:38:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +\tif (fixed_value) {\n> +\t\tint allowed_usage = 0;\n> +\n> +\t\tswitch (actions) {\n> +\t\tcase ACTION_GET:\n> +\t\tcase ACTION_GET_ALL:\n> +\t\tcase ACTION_GET_REGEXP:\n> +\t\tcase ACTION_UNSET:\n> +\t\tcase ACTION_UNSET_ALL:\n> +\t\t\tallowed_usage = argc > 1 && !!argv[1];\n> +\t\t\tbreak;\n> +\n> +\t\tcase ACTION_SET_ALL:\n> +\t\tcase ACTION_REPLACE_ALL:\n> +\t\t\tallowed_usage = argc > 2 && !!argv[2];\n> +\t\t\tbreak;\n\nImplicitly all other ops do not want to work with the --fixed-value\noption, which makes sense.\n\n> +\t\t}\n> +\n> +\t\tif (!allowed_usage) {\n> +\t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n> +\t\t\tusage_builtin_config();\n> +\t\t}\n> +\t}\n\nI was afraid that abandoning the bitmask based approach of the\nprevious round may bloat the code but seeing the result here makes\nme quite happy ;-)  It is quite clear what is going on.\n\nNice.\n"},{"id":"410558","messageId":"20201123194048.GB499823@google.com","threadId":"54676","inReplyTo":"829d0ccd8c6ae52e78eda52b15283867a0be673e.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 2/7] t1300: add test for --replace-all with value_regex","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T19:40:48Z","receivedAt":"2020-11-23T19:40:56Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:02PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n> \n> The --replace-all option was added in 4ddba79d (git-config-set: add more\n> options) but was not tested along with the 'value_regex' parameter.\n> Since we will be updating this option to optionally treat 'value_regex'\n> as a fixed string, let's add a test here that documents the current\n> behavior.\n\nI like the commit message and the diff a lot. Concise and complete,\nexplains when the problem came from. I don't think it even needs the\nrationale about adding more options later - there's missing test\ncoverage and you're adding it. But it's useful to reviewers and a handy\nstep on the journey of this topic.\n\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\nReviewed-by: Emily Shaffer <emilyshaffer@google.com>\n"},{"id":"410560","messageId":"xmqqwnybn92q.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"8e0111c7b4b2c766c61df30c4ae93bd2d724de06.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 6/7] config: implement --fixed-value with --get*","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T19:53:49Z","receivedAt":"2020-11-23T19:53:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> -\tif (regex_) {\n> +\tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n> +\t\tvalue_regex = regex_;\n> +\telse if (regex_) {\n>  \t\tif (regex_[0] == '!') {\n>  \t\t\tdo_not_match = 1;\n>  \t\t\tregex_++;\n\nWhen the --fixed-value option is in effect, there is no way to say\n\"replace values that do not match this string\", because unlike the\nregex case, we do not special case '!' at the beginning.\n\nI think it is a good design decision.  The special case has an\nescape hatch to start your value_regex with \"[!]\" when ERE is in\nuse, but there is no such escape hatch possible with --fixed-value.\n\nDepending on how this '!' negation is documented to the end-users\nfor existing value_regex that is ERE, the documentation for the new\noption may want to talk about the lack of negation explicitly.\n\n\t... goes and looks ...\n\nIt is worse than I thought.  Here is what \"git config\" description\nhas (and some of the badness is not the fault of this series):\n\n    Multiple lines can be added to an option by using the `--add` option.\n    If you want to update or unset an option which can occur on multiple\n    lines, a POSIX regexp `value_regex` needs to be given.  Only the\n    existing values that match the regexp are updated or unset.  If\n    you want to handle the lines that do *not* match the regex, just\n    prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n\nThe end-users these days no longer see \"lines\" because they do not\nhand edit .git/config, so we may need a replacement phrase to refer\nto a single \"vari.able=value\" setting, but we should leave it out of\nthe scope of this series to clean that up---\"git config --help\" is\nfull of references to \"line\".\n\nIt was not \"a POSIX regexp\" (which does not say if it is BRE or\nERE), and it no longer is a regexp with the new option.\n\n    ... which can occur on multiple lines, a pattern `value_regex`\n    needs to be given.  Only the existing values that match the\n    pattern are updated or unset.  The pattern is by default matched\n    as an extended regular expression, but with the `--fixed-value`\n    option, taken as a literal string and values must be identical\n    to it to be considered a match.  You can prepend an exclamation\n    point '!' to affect lines that do *not* match the pattern, but\n    this is applicable only when not using the `--fixed-value`\n    option.\n\nIdeally we probably should do s/value_regex/value_pattern/ in the\ndocumentation and error messages eventually, but I do not know if\nit is warranted in the scope of this series.\n"},{"id":"410573","messageId":"xmqqo8jnn46v.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"5a3acf811998bb728ce94c69611c237861775142.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 7/7] maintenance: use 'git config --fixed-value'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T21:39:20Z","receivedAt":"2020-11-23T21:39:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> For the test, require that we are not running on Windows since the '+'\n> character is not allowed on that filesystem.\n\nNot a huge deal, but we do not have to rely on '+', though.  Using\n\"a*b*c\" instead of \"a+b*c\" would perfectly be fine.  It would only\nmatch \"a*b*c\" literally and not as ERE.\n\n\n> Reported-by: Emily Shaffer <emilyshaffer@google.com>\n> Reported-by: Jonathan Nieder <jrnieder@gmail.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/gc.c           |  5 +++--\n>  t/t7900-maintenance.sh | 12 ++++++++++++\n>  2 files changed, 15 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/gc.c b/builtin/gc.c\n> index e3098ef6a1..6dde3ce1bb 100644\n> --- a/builtin/gc.c\n> +++ b/builtin/gc.c\n> @@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n>  \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n>  \n>  \tconfig_get.git_cmd = 1;\n> -\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n> +\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n> +\t\t     \"--fixed-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t\t NULL);\n> @@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n>  \n>  \tconfig_unset.git_cmd = 1;\n>  \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n> -\t\t     \"maintenance.repo\",\n> +\t\t     \"--fixed-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t     NULL);\n> diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\n> index 20184e96e1..c4e5564c31 100755\n> --- a/t/t7900-maintenance.sh\n> +++ b/t/t7900-maintenance.sh\n> @@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n>  \ttest_cmp before actual\n>  '\n>  \n> +test_expect_success !MINGW 'register and unregister with glob characters' '\n> +\tGLOB=\"a+b*c\" &&\n> +\tgit init \"$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance register &&\n> +\tgit config --get-all --show-origin maintenance.repo &&\n> +\tgit config --get-all --global --fixed-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance unregister &&\n> +\ttest_must_fail git config --get-all --global --fixed-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n> +'\n> +\n>  test_expect_success 'start from empty cron table' '\n>  \tGIT_TEST_CRONTAB=\"test-tool crontab cron.txt\" git maintenance start &&\n"},{"id":"410574","messageId":"20201123214322.GC499823@google.com","threadId":"54676","inReplyTo":"0c152faa00881483db0a59f4c5bc7204ebed8966.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/7] config: convert multi_replace to flags","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T21:43:22Z","receivedAt":"2020-11-23T21:43:51Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:03PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n> \n> We will extend the flexibility of the config API. Before doing so, let's\n> take an existing 'int multi_replace' parameter and replace it with a new\n> 'unsigned flags' parameter that can take multiple options as a bit field.\n> \n> Update all callers that specified multi_replace to now specify the\n> CONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the\n> documentation of git_config_set_multivar_in_file() including a clear\n> labeling of its arguments. Other config API methods in config.h require\n> only a change of the final parameter from 'int' to 'unsigned'.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index e82301fb1b..5ce3844d22 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n>  \t\t\tdie(_(\"Branch '%s' has no upstream information\"), branch->name);\n>  \n>  \t\tstrbuf_addf(&buf, \"branch.%s.remote\", branch->name);\n> -\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n> +\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n>  \t\tstrbuf_reset(&buf);\n>  \t\tstrbuf_addf(&buf, \"branch.%s.merge\", branch->name);\n> -\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n> +\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n\nConverting callers. Straightforward.\n\n[snipping more similar work]\n\n> diff --git a/config.c b/config.c\n> index 2b79fe76ad..096f2eae0d 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -2716,9 +2716,9 @@ void git_config_set(const char *key, const char *value)\n>   * if value_regex!=NULL, disregard key/value pairs where value does not match.\n>   * if value_regex==CONFIG_REGEX_NONE, do not match any existing values\n>   *     (only add a new one)\n> - * if multi_replace==0, nothing, or only one matching key/value is replaced,\n> - *     else all matching key/values (regardless how many) are removed,\n> - *     before the new pair is written.\n> + * if (flags & CONFIG_FLAGS_MULTI_REPLACE) == 0, at most one matching\n> + *     key/value is replaced, else all matching key/values (regardless\n> + *     how many) are removed, before the new pair is written.\n\nThis documentation to me sounds like the question you asked on-list the\nother day: \"does replace-all turn many configs into one, or many configs\ninto many with the same value?\" Is it reflected in user-facing\ndocumentation? Looks like no - you might have a good opportunity here to\nmake that more clear.\n\n>   *\n>   * Returns 0 on success.\n>   *\n> @@ -2739,7 +2739,7 @@ void git_config_set(const char *key, const char *value)\n>  int git_config_set_multivar_in_file_gently(const char *config_filename,\n>  \t\t\t\t\t   const char *key, const char *value,\n>  \t\t\t\t\t   const char *value_regex,\n> -\t\t\t\t\t   int multi_replace)\n> +\t\t\t\t\t   unsigned flags)\n\nWell, I wanted to complain about using 'unsigned' instead of 'unsigned\nint', but 'git grep -P \"unsigned(?! int)\"' tells me that it's not a\nthing anybody else seems to mind. So I'll just grumble in my corner\ninstead :)\n\n>  {\n>  \tint fd = -1, in_fd = -1;\n>  \tint ret;\n> @@ -2756,7 +2756,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n>  \tif (ret)\n>  \t\tgoto out_free;\n>  \n> -\tstore.multi_replace = multi_replace;\n> +\tstore.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0;\n>  \n>  \tif (!config_filename)\n>  \t\tconfig_filename = filename_buf = git_pathdup(\"config\");\n> @@ -2845,7 +2845,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n>  \n>  \t\t/* if nothing to unset, or too many matches, error out */\n>  \t\tif ((store.seen_nr == 0 && value == NULL) ||\n> -\t\t    (store.seen_nr > 1 && multi_replace == 0)) {\n> +\t\t    (store.seen_nr > 1 && !store.multi_replace)) {\n\nHuh, I wonder why 'store.multi_replace' wasn't used here before, since\nit was bothered to be set at earlier. Ah well.\n\n>  void git_config_set_multivar_in_file(const char *config_filename,\n>  \t\t\t\t     const char *key, const char *value,\n> -\t\t\t\t     const char *value_regex, int multi_replace)\n> +\t\t\t\t     const char *value_regex, unsigned flags)\n\nAnd some more signature conversions. [snip]\n\n>  /**\n>   * takes four parameters:\n> @@ -276,13 +289,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n>   * - the value regex, as a string. It will disregard key/value pairs where value\n>   *   does not match.\n>   *\n> - * - a multi_replace value, as an int. If value is equal to zero, nothing or only\n> - *   one matching key/value is replaced, else all matching key/values (regardless\n> - *   how many) are removed, before the new pair is written.\n> + * - a flags value with bits corresponding to the CONFIG_FLAG_* macros.\n>   *\n>   * It returns 0 on success.\n>   */\n> -void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n> +void git_config_set_multivar_in_file(const char *config_filename,\n> +\t\t\t\t     const char *key,\n> +\t\t\t\t     const char *value,\n> +\t\t\t\t     const char *value_regex,\n> +\t\t\t\t     unsigned flags);\n\nNice opportunity to make the header a little easier to read here.\nThanks.\n\nWith just one optional comment,\n\nReviewed-by: Emily Shaffer <emilyshaffer@google.com>\n"},{"id":"410576","messageId":"20201123215122.GD499823@google.com","threadId":"54676","inReplyTo":"0e6a7371ed4696f6cc85df07466fb6c20d58d62e.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T21:51:22Z","receivedAt":"2020-11-23T21:51:50Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:04PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n> \n> The 'git config' builtin takes a 'value_regex' parameter for several\n> actions. This can cause confusion when expecting exact value matches\n> instead of regex matches, especially when the input string contains glob\n> characters. While callers can escape the patterns themselves, it would\n> be more friendly to allow an argument to disable the pattern matching in\n> favor of an exact string match.\n> \n> Add a new '--fixed-value' option that does not currently change the\n> behavior. The implementation will follow for each appropriate action.\n> For now, check and test that --fixed-value will abort the command when\n> included with an incompatible action or without a 'value_regex'\n> argument.\n> \n> The name '--fixed-value' was chosen over something simpler like\n> '--fixed' because some commands allow regular expressions on the\n> key in addition to the value.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  Documentation/git-config.txt | 20 +++++++++++++-------\n>  builtin/config.c             | 26 ++++++++++++++++++++++++++\n>  t/t1300-config.sh            | 23 +++++++++++++++++++++++\n>  3 files changed, 62 insertions(+), 7 deletions(-)\n> \n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> index 7573160f21..d4bb928ea7 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -34,6 +34,7 @@ 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>  \n>  #define ACTION_GET (1<<0)\n>  #define ACTION_GET_ALL (1<<1)\n> @@ -141,6 +142,7 @@ static struct option builtin_config_options[] = {\n>  \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n>  \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n>  \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n> +\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\nI'm not sure how to feel about this phrasing. I wonder if it would be\nclearer to say something like 'treat 'value_regex' as a literal string\ninstead'? Hmmm.\n\n> +\tif (fixed_value) {\n> +\t\tint allowed_usage = 0;\n> +\n> +\t\tswitch (actions) {\n> +\t\tcase ACTION_GET:\n> +\t\tcase ACTION_GET_ALL:\n> +\t\tcase ACTION_GET_REGEXP:\n> +\t\tcase ACTION_UNSET:\n> +\t\tcase ACTION_UNSET_ALL:\n> +\t\t\tallowed_usage = argc > 1 && !!argv[1];\n> +\t\t\tbreak;\n> +\n> +\t\tcase ACTION_SET_ALL:\n> +\t\tcase ACTION_REPLACE_ALL:\n> +\t\t\tallowed_usage = argc > 2 && !!argv[2];\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\tif (!allowed_usage) {\n> +\t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n> +\t\t\tusage_builtin_config();\n> +\t\t}\n> +\t}\n> +\n\nTo me this really needs a comment. I think you are checking whether the\nvalue_regex is actually present, and the position of that regex changes\ndepending on the rest of the arg list... What about something like:\n\n  /* If set, ensure 'value_regex' was provided also */\n  if (fixed_value) {\n    ...\n    /* 'git config --unset-all <key> <value_regex>' */\n    case ACTION_UNSET_ALL:\n    ...\n    /* 'git config --replace-all <key> <new value> <value_regex>' */\n    case ACTION_REPLACE_ALL:\n    ...\n\n> +test_expect_success 'refuse --fixed-value for incompatible actions' '\n> +\tgit config --file=config dev.null bogus &&\n> +\n> +\t# These modes do not allow --fixed-value at all\n> +\ttest_must_fail git config --file=config --fixed-value --add dev.null bogus &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n> +\ttest_must_fail git config --file=config --fixed-value --rename-section dev null &&\n> +\ttest_must_fail git config --file=config --fixed-value --remove-section dev &&\n> +\ttest_must_fail git config --file=config --fixed-value --list &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-color dev.null &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&\n> +\n> +\t# These modes complain when --fixed-value has no value_regex\n> +\ttest_must_fail git config --file=config --fixed-value dev.null bogus &&\n> +\ttest_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&\n> +\ttest_must_fail git config --file=config --fixed-value --get dev.null &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-all dev.null &&\n> +\ttest_must_fail git config --file=config --fixed-value --get-regexp \"dev.*\" &&\n> +\ttest_must_fail git config --file=config --fixed-value --unset dev.null &&\n> +\ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n> +'\n> +\n\nI'd expect to see a positive test as well, which you could later evolve\ninto a test to ensure behavior.\n\n - Emily\n"},{"id":"410580","messageId":"20201123222123.GE499823@google.com","threadId":"54676","inReplyTo":"39718048cd8f5be053a13ff73f531e3400b80b25.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 5/7] config: plumb --fixed-value into config API","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T22:21:23Z","receivedAt":"2020-11-23T22:21:31Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:05PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n> \n> The git_config_set_multivar_in_file_gently() and related methods now\n> take a 'flags' bitfield, so add a new bit representing the --fixed-value\n> option from 'git config'. This alters the purpose of the value_regex\n> parameter to be an exact string match. This requires some initialization\n> changes in git_config_set_multivar_in_file_gently() and a new strcmp()\n> call in the matches() method.\n> \n> The new CONFIG_FLAGS_FIXED_VALUE flag is initialized in builtin/config.c\n> based on the --fixed-value option, and that needs to be updated in\n> several callers.\n> \n> This patch only affects some of the modes of 'git config', and the rest\n> will be completed in the next change.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/config.c  | 16 ++++++++-----\n>  config.c          |  5 +++++\n>  config.h          |  7 ++++++\n>  t/t1300-config.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++\n>  4 files changed, 80 insertions(+), 5 deletions(-)\n> \n> diff --git a/builtin/config.c b/builtin/config.c\n> index bfb55a96df..3e49e04411 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -616,6 +616,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint nongit = !startup_info->have_repository;\n>  \tchar *value;\n> +\tint flags = 0;\n>  \n>  \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n>  \n> @@ -769,6 +770,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  \t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n>  \t\t\tusage_builtin_config();\n>  \t\t}\n> +\n> +\t\tflags = CONFIG_FLAGS_FIXED_VALUE;\n\nI wonder whether using |= here will save someone from a headache later,\nwhen they want to add another flag value or move the\nCONFIG_FLAGS_MULTI_REPLACE calculation out of the tail calls below.\n\n[snip]\n>  \t}\n>  \telse if (actions == ACTION_REPLACE_ALL) {\n>  \t\tcheck_write();\n> @@ -850,7 +855,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  \t\tUNLEAK(value);\n>  \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n>  \t\t\t\t\t\t\t      argv[0], value, argv[2],\n> -\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n> +\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n[snip]\n> @@ -887,7 +893,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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      CONFIG_FLAGS_MULTI_REPLACE);\n> +\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n\n> --- a/config.c\n> +++ b/config.c\n> @@ -2402,6 +2402,7 @@ struct config_store_data {\n>  \tsize_t baselen;\n>  \tchar *key;\n>  \tint do_not_match;\n> +\tconst char *literal_value;\n>  \tregex_t *value_regex;\n>  \tint multi_replace;\n>  \tstruct {\n> @@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,\n>  {\n>  \tif (strcmp(key, store->key))\n>  \t\treturn 0; /* not ours */\n> +\tif (store->literal_value)\n> +\t\treturn !strcmp(store->literal_value, value);\n\nNice. Short-circuiting the entire regex process if necessary.\n\n> @@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n>  \t\t\tstore.value_regex = NULL;\n>  \t\telse if (value_regex == CONFIG_REGEX_NONE)\n>  \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n> +\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n> +\t\t\tstore.literal_value = value_regex;\n\nAh, so we use .literal_value instead of pulling the string from\nvalue_regex because value_regex undergoes some special parsing and is\npacked into a regex_t instead.\n\n>  \t\telse {\n>  \t\t\tif (value_regex[0] == '!') {\n>  \t\t\t\tstore.do_not_match = 1;\n> diff --git a/config.h b/config.h\n> index 80844604ab..977e690be8 100644\n> --- a/config.h\n> +++ b/config.h\n> @@ -269,6 +269,13 @@ int git_config_key_is_valid(const char *key);\n>   */\n>  #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n>  \n> +/*\n> + * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs\n> + * by string comparison (not regex match) to the provided value_regex\nIn contrast to my comment elsewhere, I think here it makes sense to talk\nabout strcmp like you did. But notice that here in the dev-facing\ndocumentation you said \"not regex\" - so I think that you thought it was\na useful distinction, but forgot to say so to the user.\n> + * parameter.\n> + */\n> +#define CONFIG_FLAGS_FIXED_VALUE (1 << 1)\n> +\n\n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n> index 6dc8117241..30e80ae9cb 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> +test_expect_success '--fixed-value uses exact string matching' '\n> +\tGLOB=\"a+b*c?d[e]f.g\" &&\n> +\trm -f initial &&\nThis tells me that when you used 'initial' earlier you probably should\nhave done something like 'test_when_finished rm initial' in this test\nand your earlier ones. Whoops.\n\n> +\tgit config --file=initial fixed.test \"$GLOB\" &&\n> +\n> +\tcp initial config &&\n> +\tgit config --file=config fixed.test bogus \"$GLOB\" &&\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-EOF &&\n> +\tfixed.test=$GLOB\n> +\tfixed.test=bogus\n> +\tEOF\n> +\ttest_cmp expect actual &&\n> +\n> +\tcp initial config &&\n> +\tgit config --file=config --fixed-value fixed.test bogus \"$GLOB\" &&\n> +\tgit config --file=config --list >actual &&\n> +\tprintf \"fixed.test=bogus\\n\" >expect &&\nIt is jarring to me to see a printf here when everywhere else we use\nheredocs. 'git grep' tells me it's not unheard of, but it looks like\nthose are cases where the whole file doesn't use heredocs.\n\n> +\ttest_cmp expect actual &&\n> +\n> +\tcp initial config &&\n> +\ttest_must_fail git config --file=config --unset fixed.test \"$GLOB\" &&\n> +\tgit config --file=config --fixed-value --unset fixed.test \"$GLOB\" &&\n> +\ttest_must_fail git config --file=config fixed.test &&\nIs this one supposed to verify that there is a 'fixed.test' value\nalready in 'config'? I'd prefer to see that explicitly checked with 'git\nconfig --get' rather than watching for a symptom, that is, fail to set.\nThis comment applies to the next case too.\n\n> +\n> +\tcp initial config &&\n> +\ttest_must_fail git config --file=config --unset-all fixed.test \"$GLOB\" &&\n> +\tgit config --file=config --fixed-value --unset-all fixed.test \"$GLOB\" &&\n> +\ttest_must_fail git config --file=config fixed.test &&\n> +\n> +\tcp initial config &&\n> +\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-EOF &&\n> +\tfixed.test=$GLOB\n> +\tfixed.test=bogus\n> +\tEOF\n> +\ttest_cmp expect actual &&\nHm, isn't this the same functionality as the tests you added at the\nbeginning of this series? I guess you are setting up for the last case\nwith --replace-all...\n> +\n> +\tcp initial config &&\n> +\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-EOF &&\n> +\tfixed.test=$GLOB\n> +\tfixed.test=bogus\n> +\tEOF\n> +\ttest_cmp expect actual &&\n\nIs this one identical to the previous one? I think it is, but if it\nisn't and I can't tell, all the more reason that each case here should\neither be labeled with a comment or separated into its own test. (Bonus\n- you could extend the individual tests from patch 1 to make sure they\nwork correctly with --fixed-value too ;) )\n\n> +\n> +\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$GLOB\" &&\n> +\tgit config --file=config --list >actual &&\n> +\tcat >expect <<-EOF &&\n> +\tfixed.test=bogus\n> +\tfixed.test=bogus\n\nHum, it does what it says on the box, but is this a valid config, I\nwonder?\n\n  $ git config --file=test foo.test aaa\n  $ git config --file=test --add foo.test bbb\n  $ git config --list --file=test\n  foo.test=aaa\n  foo.test=bbb\n  $ git config --file=test --replace-all foo.test bbb a+\n  $ git config --list --file=test\n  foo.test=bbb\n  foo.test=bbb\n\nSo I guess it's fine :)\n\n - Emily\n"},{"id":"410584","messageId":"xmqqy2irlmq5.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"20201123215122.GD499823@google.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T22:41:54Z","receivedAt":"2020-11-23T22:42:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n>>  \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n>> +\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n> I'm not sure how to feel about this phrasing. I wonder if it would be\n> clearer to say something like 'treat 'value_regex' as a literal string\n> instead'? Hmmm.\n\nUpdate the document and help text with s/value_regex/value_pattern/\nand say \"use value_pattern as a fixed string, not an extended regexp\",\nperhaps?\n\n>   /* If set, ensure 'value_regex' was provided also */\n>   if (fixed_value) {\n>     ...\n>     /* 'git config --unset-all <key> <value_regex>' */\n>     case ACTION_UNSET_ALL:\n\nNice.\n"},{"id":"410585","messageId":"20201123224357.GF499823@google.com","threadId":"54676","inReplyTo":"8e0111c7b4b2c766c61df30c4ae93bd2d724de06.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 6/7] config: implement --fixed-value with --get*","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T22:43:57Z","receivedAt":"2020-11-23T22:44:44Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:06PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n\nAllowing myself a sensible chuckle at the commit subject using glob\nsyntax on a series about regex matching. ;)\n\n> \n> The config builtin does its own regex matching of values for the --get,\n> --get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\n> to the get_value() method so we can initialize the value_regex argument\n> as a fixed string instead of a regex pattern.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/config.c  | 15 ++++++++++-----\n>  t/t1300-config.sh | 22 ++++++++++++++++++++++\n>  2 files changed, 32 insertions(+), 5 deletions(-)\n> \n> diff --git a/builtin/config.c b/builtin/config.c\n> index 3e49e04411..d3772b5efe 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n>  \tif (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))\n>  \t\treturn 0;\n> +\tif (fixed_value && strcmp(value_regex, (value_?value_:\"\")))\n> +\t\treturn 0;\nOoh, I can see you're matching style, but the combination of the\nspaceless ternary and the trailing underscore is making me so itchy ;)\n\n>  \tif (regexp != NULL &&\n>  \t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n>  \t\treturn 0;\n> @@ -298,7 +301,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)\n>  \treturn format_config(&values->items[values->nr++], key_, value_);\n>  }\n>  \n> -static int get_value(const char *key_, const char *regex_)\n> +static int get_value(const char *key_, const char *regex_, int flags)\nVery reasonable, and I appreciate passing 'flags' instead of passing\n'fixed' here. Room for growth :)\n\n[snip] a bunch of straightforward plumbing-through.\n\n> +test_expect_success '--get and --get-all with --fixed-value' '\n> +\tGLOB=\"a+b*c?d[e]f.g\" &&\n> +\trm -f config &&\nAgain this tells me that your other tests want 'test_when_finished'\ninstead.\n\n> +\tgit config --file=config fixed.test bogus &&\n> +\tgit config --file=config --add fixed.test \"$GLOB\" &&\n> +\n> +\tgit config --file=config --get fixed.test bogus &&\n> +\ttest_must_fail git config --file=config --get fixed.test \"$GLOB\" &&\n> +\tgit config --file=config --get --fixed-value fixed.test \"$GLOB\" &&\n> +\ttest_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&\n> +\n> +\tgit config --file=config --get-all fixed.test bogus &&\n> +\ttest_must_fail git config --file=config --get-all fixed.test \"$GLOB\" &&\n> +\tgit config --file=config --get-all --fixed-value fixed.test \"$GLOB\" &&\n> +\ttest_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&\n> +\n> +\tgit config --file=config --get-regexp fixed+ bogus &&\n> +\ttest_must_fail git config --file=config --get-regexp fixed+ \"$GLOB\" &&\n> +\tgit config --file=config --get-regexp --fixed-value fixed+ \"$GLOB\" &&\n> +\ttest_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent\n> +'\n\nOtherwise the test seems fine to me, although I wouldn't yell if it grew\nsome comments :)\n\nWith the exception of the 'test_when_finished', which might not even\nmatch style (I didn't look):\n\nReviewed-by: Emily Shaffer <emilyshaffer@google.com>\n"},{"id":"410588","messageId":"20201123224814.GG499823@google.com","threadId":"54676","inReplyTo":"5a3acf811998bb728ce94c69611c237861775142.1606147507.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 7/7] maintenance: use 'git config --fixed-value'","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2020-11-23T22:48:14Z","receivedAt":"2020-11-23T22:48:55Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Nov 23, 2020 at 04:05:07PM +0000, Derrick Stolee via GitGitGadget wrote:\n> \n> \n> When a repository's leading directories contain regex glob characters,\nMinor \"well, actually\" - I'm not sure 'glob' is the right word to use\nhere.\n\n> the config calls for 'git maintenance register' and 'git maintenance\n> unregister' are not careful enough. Use the new --fixed-value option\n> to direct the config machinery to use exact string matches. This is a\n> more robust option than excaping these arguments in a piecemeal fashion.\nTypo on 'escaping'.\n\n> \n> For the test, require that we are not running on Windows since the '+'\n> character is not allowed on that filesystem.\n> \n> Reported-by: Emily Shaffer <emilyshaffer@google.com>\n> Reported-by: Jonathan Nieder <jrnieder@gmail.com>\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n>  builtin/gc.c           |  5 +++--\n>  t/t7900-maintenance.sh | 12 ++++++++++++\n>  2 files changed, 15 insertions(+), 2 deletions(-)\n> \n> diff --git a/builtin/gc.c b/builtin/gc.c\n> index e3098ef6a1..6dde3ce1bb 100644\n> --- a/builtin/gc.c\n> +++ b/builtin/gc.c\n> @@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n>  \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n>  \n>  \tconfig_get.git_cmd = 1;\n> -\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n> +\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n> +\t\t     \"--fixed-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t\t NULL);\n> @@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n>  \n>  \tconfig_unset.git_cmd = 1;\n>  \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n> -\t\t     \"maintenance.repo\",\n> +\t\t     \"--fixed-value\", \"maintenance.repo\",\n>  \t\t     the_repository->worktree ? the_repository->worktree\n>  \t\t\t\t\t      : the_repository->gitdir,\n>  \t\t     NULL);\n> diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\n> index 20184e96e1..c4e5564c31 100755\n> --- a/t/t7900-maintenance.sh\n> +++ b/t/t7900-maintenance.sh\n> @@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n>  \ttest_cmp before actual\n>  '\n>  \n> +test_expect_success !MINGW 'register and unregister with glob characters' '\n> +\tGLOB=\"a+b*c\" &&\n> +\tgit init \"$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance register &&\n> +\tgit config --get-all --show-origin maintenance.repo &&\nHm, what's the reason for --show-origin when the output isn't captured\nor checked? Is this leftover?\n\n> +\tgit config --get-all --global --fixed-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n> +\tgit -C \"$GLOB\" maintenance unregister &&\n> +\ttest_must_fail git config --get-all --global --fixed-value \\\n> +\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n> +'\n> +\n>  test_expect_success 'start from empty cron table' '\n>  \tGIT_TEST_CRONTAB=\"test-tool crontab cron.txt\" git maintenance start &&\n>  \n\nAnd after all that buildup, this patch is a very straightforward test\nfix: explain the problem, add the arg in question, add a regression\ntest. Thanks.\n\nOther than the one extraneous(?) line,\nReviewed-by: Emily Shaffer <emilyshaffer@google.com>\n"},{"id":"410600","messageId":"xmqqmtz7lklr.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"20201123224814.GG499823@google.com","subject":"Re: [PATCH v2 7/7] maintenance: use 'git config --fixed-value'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T23:27:44Z","receivedAt":"2020-11-23T23:28:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> On Mon, Nov 23, 2020 at 04:05:07PM +0000, Derrick Stolee via GitGitGadget wrote:\n>> \n>> \n>> When a repository's leading directories contain regex glob characters,\n> Minor \"well, actually\" - I'm not sure 'glob' is the right word to use\n> here.\n\nA careful reader ;-)  \"metacharacters\" is probably what we want.\n\n"},{"id":"410611","messageId":"CAPig+cR_FBT+kYPW2pGJmHCVFUB=cC=Z8DfaE+R4Oy5iabnr0g@mail.gmail.com","threadId":"54676","inReplyTo":"20201123222123.GE499823@google.com","subject":"Re: [PATCH v2 5/7] config: plumb --fixed-value into config API","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2020-11-24T00:52:50Z","receivedAt":"2020-11-24T00:53:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Nov 23, 2020 at 5:22 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> On Mon, Nov 23, 2020 at 04:05:05PM +0000, Derrick Stolee via GitGitGadget wrote:\n> > +     printf \"fixed.test=bogus\\n\" >expect &&\n> It is jarring to me to see a printf here when everywhere else we use\n> heredocs. 'git grep' tells me it's not unheard of, but it looks like\n> those are cases where the whole file doesn't use heredocs.\n\nFor creating a one-line file, the verbosity of a here-doc may be\noverkill. At any rate, `echo` would be more idiomatic than `printf`:\n\n    echo \"fixed.test=bogus\" >expect &&\n\n(Though, this alone is likely not worth a re-roll.)\n"},{"id":"410621","messageId":"X7xyHyFMU2E8cBCE@coredump.intra.peff.net","threadId":"54676","inReplyTo":"xmqqlfeuqd8d.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-24T02:38:23Z","receivedAt":"2020-11-24T02:38:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Nov 21, 2020 at 07:31:14PM -0800, Junio C Hamano wrote:\n\n> >> So that got a bit off-track, but I think:\n> >> \n> >>   - t1300 already is very much like this, so it's not a new thing\n> >> \n> >>   - but I would be happy not to see it go further in that direction,\n> >>     even if it means inconsistency with the rest of the script\n> >\n> > I agree we shouldn't make things worse.\n> \n> I started looking at early parts of t1300 and here is how far I\n> managed to get before I can no longer keep staring the existing\n> tests without vomitting.\n\nI think my similar gastric reaction is what caused me to stop looking at\nit long ago. But it may also have been the test brian mentioned that\nexplicitly checks that this case works (and for which he had to set SHA1\nprereq).\n\n> I am reasonably happy with the \"let's keep the vanilla untouched one\n> in .git/config-initial, refrain from using [core] and other sections\n> that MUST be in the initial configuration for testing, and use a\n> wrapper that reads expected addition to the initial one from the\n> standard input for validation\" approach I came up with, but I am not\n> happy with the name 'compare_expect'; 'validate_config_result' might\n> be a better name.\n\nIMHO this is worse than just using \"config --file\" in most of the tests.\nIt's more steps to remember to deal with. And most tests do not care at\nall what the source file is. There are a few that check the order of\nlookup with respect to system and user files, but they could probably be\nrun what non-destructive changes.\n\nThat said, most of the effort is in the tedium of switching each\nindividual test. I am happy for whoever volunteers to do that work to\nhave the final say in the approach.\n\n-Peff\n"},{"id":"410671","messageId":"87blfnoruj.fsf@evledraar.gmail.com","threadId":"54676","inReplyTo":"e7973fe1-eb64-2f5a-ecb3-fadf2ba9764d@gmail.com","subject":"Re: [PATCH 0/7] config: add --literal-value option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-24T12:35:16Z","receivedAt":"2020-11-24T12:36:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Nov 20 2020, Derrick Stolee wrote:\n\n> On 11/20/2020 8:19 AM, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Thu, Nov 19 2020, Derrick Stolee via GitGitGadget wrote:\n>> \n>>> As reported [1], 'git maintenance unregister' fails when a repository is\n>>> located in a directory with regex glob characters.\n>> \n>> Just as bikeshedding on the name: Did you consider something\n>> thematically similar to the corresponding git-grep option,\n>> i.e. --fixed-string[s]. I see -F is also free in git-config(1).\n>\n> I definitely wanted to be specific about \"value\" in the name,\n> since some options include regexes on the key as well. I'm open\n> to new ideas, and combining your idea with mine would introduce\n> \"--fixed-value\". Thoughts?\n\nHi. I see you've already sent a v2 with that. Looks good to me. FWIW I\ndidn't feel strongly one way or the other about it, either would be a\nfine choice by someone who's eyeballing this more than my cursory\nglance.\n\nI just wanted to point out the grep option in case you'd missed it,\nsince we've got a bazillion CLI options all over the place, and\nsometimes there's an existing option that's thematically similar to a\nproposed new one that's easy to miss. So we can re-use (or partially\nre-use) the name, behavior, or not.\n"},{"id":"410697","messageId":"xmqqtutek0ba.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"X7xyHyFMU2E8cBCE@coredump.intra.peff.net","subject":"Re: [PATCH 1/7] t1300: test \"set all\" mode with value_regex","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-24T19:43:37Z","receivedAt":"2020-11-24T19:44:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> I am reasonably happy with the \"let's keep the vanilla untouched one\n>> in .git/config-initial, refrain from using [core] and other sections\n>> that MUST be in the initial configuration for testing, and use a\n>> wrapper that reads expected addition to the initial one from the\n>> standard input for validation\" approach I came up with, but I am not\n>> happy with the name 'compare_expect'; 'validate_config_result' might\n>> be a better name.\n>\n> IMHO this is worse than just using \"config --file\" in most of the tests.\n> It's more steps to remember to deal with. And most tests do not care at\n> all what the source file is.\n\n\"Most tests do not care\" only indicates the lack of test coverage.\n\nKnowing the implementation, it probably is OK to assume that things\nwould work fine as long as \"--file <file>\" works correctly, though\n;-) \n\nNot having to keep the minimum parts of the real configuration file,\nand being able to use a throw-away file for each test, certainly\nmakes things cleaner.\n\n> That said, most of the effort is in the tedium of switching each\n> individual test. I am happy for whoever volunteers to do that work to\n> have the final say in the approach.\n\nYup.\n"},{"id":"410782","messageId":"91af356f-d0a0-1149-15dc-32da803ac6d9@gmail.com","threadId":"54676","inReplyTo":"xmqqy2irlmq5.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-25T14:08:15Z","receivedAt":"2020-11-25T14:08:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/23/2020 5:41 PM, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n>>>  \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n>>> +\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n>> I'm not sure how to feel about this phrasing. I wonder if it would be\n>> clearer to say something like 'treat 'value_regex' as a literal string\n>> instead'? Hmmm.\n> \n> Update the document and help text with s/value_regex/value_pattern/\n> and say \"use value_pattern as a fixed string, not an extended regexp\",\n> perhaps?\n\nIf I go about changing all documentation and error messages to say\n\"value_pattern\" instead of \"value_regex\", should I also update the uses\nin the *.po translation files? Or, should I leave them unmodified to\ntrigger manual intervention by the translators?\n\nThanks,\n-Stolee\n"},{"id":"410783","messageId":"d18a2f7f-67e9-05eb-95d1-ce0b4bd0b187@gmail.com","threadId":"54676","inReplyTo":"20201123222123.GE499823@google.com","subject":"Re: [PATCH v2 5/7] config: plumb --fixed-value into config API","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-25T15:41:59Z","receivedAt":"2020-11-25T15:42:45Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/23/2020 5:21 PM, Emily Shaffer wrote:\n> On Mon, Nov 23, 2020 at 04:05:05PM +0000, Derrick Stolee via GitGitGadget wrote:\n>> +\n>> +\t\tflags = CONFIG_FLAGS_FIXED_VALUE;\n> \n> I wonder whether using |= here will save someone from a headache later,\n> when they want to add another flag value or move the\n> CONFIG_FLAGS_MULTI_REPLACE calculation out of the tail calls below.\n\nGood catch.\n\n>> @@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n>>  \t\t\tstore.value_regex = NULL;\n>>  \t\telse if (value_regex == CONFIG_REGEX_NONE)\n>>  \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n>> +\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n>> +\t\t\tstore.literal_value = value_regex;\n> \n> Ah, so we use .literal_value instead of pulling the string from\n> value_regex because value_regex undergoes some special parsing and is\n> packed into a regex_t instead.\n\nReminds me to call this 'fixed_value'.\n\n>> +test_expect_success '--fixed-value uses exact string matching' '\n>> +\tGLOB=\"a+b*c?d[e]f.g\" &&\n>> +\trm -f initial &&\n> This tells me that when you used 'initial' earlier you probably should\n> have done something like 'test_when_finished rm initial' in this test\n> and your earlier ones. Whoops.\n\nGood idea. Will do.\n\n>> +\tcp initial config &&\n>> +\tgit config --file=config --fixed-value fixed.test bogus \"$GLOB\" &&\n>> +\tgit config --file=config --list >actual &&\n>> +\tprintf \"fixed.test=bogus\\n\" >expect &&\n> It is jarring to me to see a printf here when everywhere else we use\n> heredocs. 'git grep' tells me it's not unheard of, but it looks like\n> those are cases where the whole file doesn't use heredocs.\n\nI can use a heredoc just to be consistent.\n\n(To also respond to Eric's message, I tend to use printf instead\nof echo because echo starts a process while printf does not.)\n\n>> +\ttest_cmp expect actual &&\n>> +\n>> +\tcp initial config &&\n>> +\ttest_must_fail git config --file=config --unset fixed.test \"$GLOB\" &&\n>> +\tgit config --file=config --fixed-value --unset fixed.test \"$GLOB\" &&\n>> +\ttest_must_fail git config --file=config fixed.test &&\n> Is this one supposed to verify that there is a 'fixed.test' value\n> already in 'config'? I'd prefer to see that explicitly checked with 'git\n> config --get' rather than watching for a symptom, that is, fail to set.\n> This comment applies to the next case too.\n\nIf no value is provided, then 'git config <name>' _is_ a query. It's not a\nfailed set. 'git config <name> \"\"' would be the way to try and set the value\nto an empty string.\n\n>> +\n>> +\tcp initial config &&\n>> +\ttest_must_fail git config --file=config --unset-all fixed.test \"$GLOB\" &&\n>> +\tgit config --file=config --fixed-value --unset-all fixed.test \"$GLOB\" &&\n>> +\ttest_must_fail git config --file=config fixed.test &&\n>> +\n>> +\tcp initial config &&\n>> +\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n>> +\tgit config --file=config --list >actual &&\n>> +\tcat >expect <<-EOF &&\n>> +\tfixed.test=$GLOB\n>> +\tfixed.test=bogus\n>> +\tEOF\n>> +\ttest_cmp expect actual &&\n> Hm, isn't this the same functionality as the tests you added at the\n> beginning of this series? I guess you are setting up for the last case\n> with --replace-all...\n\nThis is specifically demonstrating the difference that --fixed-value\nprovides. The tests here show \"the match doesn't work by default, but\nthen works with --fixed-value\". I'll make this clearer in the commit\nmessage.\n\n>> +\n>> +\tcp initial config &&\n>> +\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n>> +\tgit config --file=config --list >actual &&\n>> +\tcat >expect <<-EOF &&\n>> +\tfixed.test=$GLOB\n>> +\tfixed.test=bogus\n>> +\tEOF\n>> +\ttest_cmp expect actual &&\n> \n> Is this one identical to the previous one? I think it is, but if it\n> isn't and I can't tell, all the more reason that each case here should\n> either be labeled with a comment or separated into its own test. (Bonus\n> - you could extend the individual tests from patch 1 to make sure they\n> work correctly with --fixed-value too ;) )\n\nYes, accidentally over-zealous copy/paste.\n\nI'm less in favor of splitting the tests, since they rely on a shared\ninitial config. Any failure in one part of this test is likely to also\nfail the rest of the commands, so grouping them by this toggle makes\nsense to me.\n\nThanks,\n-Stolee\n"},{"id":"410787","messageId":"6b2b5af7-e73e-7584-6515-0e1d65c5e6d1@gmail.com","threadId":"54676","inReplyTo":"91af356f-d0a0-1149-15dc-32da803ac6d9@gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-25T17:22:09Z","receivedAt":"2020-11-25T17:22:28Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/25/2020 9:08 AM, Derrick Stolee wrote:\n> On 11/23/2020 5:41 PM, Junio C Hamano wrote:\n>> Emily Shaffer <emilyshaffer@google.com> writes:\n>>\n>>>>  \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n>>>> +\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n>>> I'm not sure how to feel about this phrasing. I wonder if it would be\n>>> clearer to say something like 'treat 'value_regex' as a literal string\n>>> instead'? Hmmm.\n>>\n>> Update the document and help text with s/value_regex/value_pattern/\n>> and say \"use value_pattern as a fixed string, not an extended regexp\",\n>> perhaps?\n> \n> If I go about changing all documentation and error messages to say\n> \"value_pattern\" instead of \"value_regex\", should I also update the uses\n> in the *.po translation files? Or, should I leave them unmodified to\n> trigger manual intervention by the translators?\n\nI have discovered that changing these causes conflicts with automated\nline number updates. I'll leave these unchanged with a note for\ntranslators.\n\nThanks,\n-Stolee\n\n"},{"id":"410789","messageId":"CAPig+cTQMCho=wFHhg-ACrP2oOEQwBHnfmV-Rk1vARpF2Fafng@mail.gmail.com","threadId":"54676","inReplyTo":"91af356f-d0a0-1149-15dc-32da803ac6d9@gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2020-11-25T17:28:46Z","receivedAt":"2020-11-25T17:29:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Nov 25, 2020 at 9:09 AM Derrick Stolee <stolee@gmail.com> wrote:\n> On 11/23/2020 5:41 PM, Junio C Hamano wrote:\n> > Update the document and help text with s/value_regex/value_pattern/\n> > and say \"use value_pattern as a fixed string, not an extended regexp\",\n> > perhaps?\n>\n> If I go about changing all documentation and error messages to say\n> \"value_pattern\" instead of \"value_regex\", should I also update the uses\n> in the *.po translation files? Or, should I leave them unmodified to\n> trigger manual intervention by the translators?\n\nA minor request: If you are going to put in the work to make that\nsubstitution, perhaps change it to \"value-pattern\" rather than\n\"value_pattern\" since a hyphen is more common in documentation for\nthis sort of thing than underscore.\n"},{"id":"410790","messageId":"CAPig+cQ+GPUoddGXP47bJVMLCdpozoC752M6bmc=jpmhe33J8g@mail.gmail.com","threadId":"54676","inReplyTo":"d18a2f7f-67e9-05eb-95d1-ce0b4bd0b187@gmail.com","subject":"Re: [PATCH v2 5/7] config: plumb --fixed-value into config API","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2020-11-25T17:55:10Z","receivedAt":"2020-11-25T17:55:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Nov 25, 2020 at 10:49 AM Derrick Stolee <stolee@gmail.com> wrote:\n> On 11/23/2020 5:21 PM, Emily Shaffer wrote:\n> > On Mon, Nov 23, 2020 at 04:05:05PM +0000, Derrick Stolee via GitGitGadget wrote:\n> >> +    printf \"fixed.test=bogus\\n\" >expect &&\n> > It is jarring to me to see a printf here when everywhere else we use\n> > heredocs. 'git grep' tells me it's not unheard of, but it looks like\n> > those are cases where the whole file doesn't use heredocs.\n>\n> I can use a heredoc just to be consistent.\n>\n> (To also respond to Eric's message, I tend to use printf instead\n> of echo because echo starts a process while printf does not.)\n\n`echo` is a builtin[1] in many or most shells (just as `printf` also\noften is), thus is unlikely to start a process.\n\n[1] Aside: You can use `type <cmd>` or `command -V <cmd>` to see if\n<cmd> is a builtin in your shell.\n"},{"id":"410795","messageId":"xmqq8sapfd56.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"91af356f-d0a0-1149-15dc-32da803ac6d9@gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-25T19:29:57Z","receivedAt":"2020-11-25T19:30:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 11/23/2020 5:41 PM, Junio C Hamano wrote:\n>> Emily Shaffer <emilyshaffer@google.com> writes:\n>> \n>>>>  \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n>>>> +\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n>>> I'm not sure how to feel about this phrasing. I wonder if it would be\n>>> clearer to say something like 'treat 'value_regex' as a literal string\n>>> instead'? Hmmm.\n>> \n>> Update the document and help text with s/value_regex/value_pattern/\n>> and say \"use value_pattern as a fixed string, not an extended regexp\",\n>> perhaps?\n>\n> If I go about changing all documentation and error messages to say\n> \"value_pattern\" instead of \"value_regex\", should I also update the uses\n> in the *.po translation files? Or, should I leave them unmodified to\n> trigger manual intervention by the translators?\n\nIf you do, you do not have to worry.  The i18n/l10n coordinator will\nupdate the po/git.pot file when we near the code freeze using an\nautomated tool that extracts strings from the sources, and the\npo/xx.po files for languages are updated from the updated po/git.pot\nmechanically with another tool, reusing unmodified entries, adding\nnew ones, and marking near-hit ones to help avoid unnecessary work\nby translators.\n\nI earlier thought that the \"when matching values\" phrase you chose\nin this round, without such a clean-up, would be OK in the context\nof this topic (which is depended on by a bugfix topic), but after\nseeing how we need to clarify the way '!' negation prefix works in\nthe documentation that has deeply ingrained assumption that value is\nmatched using ERE, it may be necessary to bite the bullet and do the\nregex->pattern now.\n\nThanks.\n\n\n\n\n"},{"id":"410796","messageId":"xmqq4kldfd3z.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"CAPig+cTQMCho=wFHhg-ACrP2oOEQwBHnfmV-Rk1vARpF2Fafng@mail.gmail.com","subject":"Re: [PATCH v2 4/7] config: add --fixed-value option, un-implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-25T19:30:40Z","receivedAt":"2020-11-25T19:31:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Wed, Nov 25, 2020 at 9:09 AM Derrick Stolee <stolee@gmail.com> wrote:\n>> On 11/23/2020 5:41 PM, Junio C Hamano wrote:\n>> > Update the document and help text with s/value_regex/value_pattern/\n>> > and say \"use value_pattern as a fixed string, not an extended regexp\",\n>> > perhaps?\n>>\n>> If I go about changing all documentation and error messages to say\n>> \"value_pattern\" instead of \"value_regex\", should I also update the uses\n>> in the *.po translation files? Or, should I leave them unmodified to\n>> trigger manual intervention by the translators?\n>\n> A minor request: If you are going to put in the work to make that\n> substitution, perhaps change it to \"value-pattern\" rather than\n> \"value_pattern\" since a hyphen is more common in documentation for\n> this sort of thing than underscore.\n\nYup, I misspoke.  s/value_regex/value-regex/ unless it is a variable\nname where '-' cannot appear.\n\n"},{"id":"410812","messageId":"3b72082326b692542f29a71737e2459a1ae12900.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 3/8] t1300: test \"set all\" mode with value-pattern","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:51Z","receivedAt":"2020-11-25T22:13:38Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWithout additional modifiers, 'git config <key> <value>' attempts\nto set a single value in the .git/config file. When the\nvalue-pattern parameter is supplied, this command behaves in a\nnon-trivial manner.\n\nConsider 'git config <key> <value> <value-pattern>'. The expected\nbehavior is as follows:\n\n1. If there are multiple existing values that match 'value-pattern',\n   then the command fails. Users should use --replace-all instead.\n\n2. If there is no existing values match 'value-pattern', then the\n   'key=value' pair is appended, making this 'key' a multi-valued\n   config setting.\n\n3. If there is one existing value that matches 'value-pattern', then\n   the new config has one entry where 'key=value'.\n\nAdd a test that demonstrates these options. Break from the existing\npattern in t1300-config.sh to use 'git config --file=<file>' instead of\nmodifying .git/config directly to prevent possibly incompatible repo\nstates. Also use 'git config --file=<file> --list' for config state\ncomparison instead of the config file format. This makes the tests\nmore readable.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 97ebfe1f9d..72f930ada0 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1914,4 +1914,43 @@ test_expect_success '--replace-all does not invent newlines' '\n \ttest_cmp expect .git/config\n '\n \n+test_expect_success 'set all config with value-pattern' '\n+\ttest_when_finished rm -f config initial &&\n+\tgit config --file=initial abc.key one &&\n+\n+\t# no match => add new entry\n+\tcp initial config &&\n+\tgit config --file=config abc.key two a+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=one\n+\tabc.key=two\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\t# multiple matches => failure\n+\ttest_must_fail git config --file=config abc.key three o+ 2>err &&\n+\ttest_i18ngrep \"has multiple values\" err &&\n+\n+\t# multiple values, no match => add\n+\tgit config --file=config abc.key three a+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=one\n+\tabc.key=two\n+\tabc.key=three\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\t# single match => replace\n+\tgit config --file=config abc.key four h+ &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=one\n+\tabc.key=two\n+\tabc.key=four\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410814","messageId":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v2.git.1606147507.gitgitgadget@gmail.com","subject":"[PATCH v3 0/8] config: add --fixed-value option","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:48Z","receivedAt":"2020-11-25T22:13:38Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"As reported [1], 'git maintenance unregister' fails when a repository is\nlocated in a directory with regex glob characters.\n\n[1] \nhttps://lore.kernel.org/git/2c2db228-069a-947d-8446-89f4d3f6181a@gmail.com/T/#mb96fa4187a0d6aeda097cd95804a8aafc0273022\n\nThe discussed solution was to modify 'git config' to specify that the\n'value_regex' argument should be treated as an exact string match. This is\nthe primary change in this series, with an additional patch at the end to\nmake 'git maintenance [un]register' use this option, when necessary.\n\nWhile we're here, let's rename 'value_regex' to 'value_pattern' to make\nthings a bit clearer.\n\nUpdates in V3\n=============\n\n * Renamed 'value_regex' to 'value_pattern' in code and 'value-pattern' in\n   docs (except po/)\n   \n   \n * Reordered commits slightly to help with that rename.\n   \n   \n * Updated tests to use 'test_when_finished rm -f ...'\n   \n   \n * Changed all references to \"glob\" characters to \"meta\" characters.\n   \n   \n * Several other test modifications. Thanks, Emily, for the review!\n   \n   \n\nThanks, -Stolee\n\nP.S. Happy Thanksgiving to those celebrating!\n\nDerrick Stolee (8):\n  config: convert multi_replace to flags\n  config: replace 'value_regex' with 'value_pattern'\n  t1300: test \"set all\" mode with value-pattern\n  t1300: add test for --replace-all with value-pattern\n  config: add --fixed-value option, un-implemented\n  config: plumb --fixed-value into config API\n  config: implement --fixed-value with --get*\n  maintenance: use 'git config --fixed-value'\n\n Documentation/git-config.txt |  26 +++---\n builtin/branch.c             |   4 +-\n builtin/config.c             |  81 +++++++++++++++----\n builtin/gc.c                 |   5 +-\n builtin/remote.c             |   8 +-\n config.c                     |  75 ++++++++++--------\n config.h                     |  36 +++++++--\n t/t1300-config.sh            | 149 +++++++++++++++++++++++++++++++++++\n t/t7900-maintenance.sh       |  12 +++\n 9 files changed, 321 insertions(+), 75 deletions(-)\n\n\nbase-commit: 0016b618182f642771dc589cf0090289f9fe1b4f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-796%2Fderrickstolee%2Fmaintenance%2Fconfig-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-796/derrickstolee/maintenance/config-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/796\n\nRange-diff vs v2:\n\n 3:  0c152faa00 ! 1:  f0ed492096 config: convert multi_replace to flags\n     @@ config.c: void git_config_set(const char *key, const char *value)\n      - * if multi_replace==0, nothing, or only one matching key/value is replaced,\n      - *     else all matching key/values (regardless how many) are removed,\n      - *     before the new pair is written.\n     -+ * if (flags & CONFIG_FLAGS_MULTI_REPLACE) == 0, at most one matching\n     -+ *     key/value is replaced, else all matching key/values (regardless\n     -+ *     how many) are removed, before the new pair is written.\n     ++ * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching\n     ++ *     key/values are removed before a single new pair is written. If the\n     ++ *     flag is not present, then replace only the first match.\n        *\n        * Returns 0 on success.\n        *\n     @@ config.h: void git_config_set(const char *, const char *);\n      +\n      +/*\n      + * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n     -+ * are removed before a new pair is written. If the flag is not present,\n     -+ * then set operations replace only the first match.\n     ++ * are removed before a single new pair is written. If the flag is not\n     ++ * present, then set operations replace only the first match.\n      + */\n      +#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n      +\n -:  ---------- > 2:  f135b001ad config: replace 'value_regex' with 'value_pattern'\n 1:  ea3099719c ! 3:  3b72082326 t1300: test \"set all\" mode with value_regex\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    t1300: test \"set all\" mode with value_regex\n     +    t1300: test \"set all\" mode with value-pattern\n      \n     -    Without additional modifiers, 'git config' attempts to set a single\n     -    value in the .git/config file. When the value_regex parameter is\n     -    supplied, this command behaves in a non-trivial manner.\n     +    Without additional modifiers, 'git config <key> <value>' attempts\n     +    to set a single value in the .git/config file. When the\n     +    value-pattern parameter is supplied, this command behaves in a\n     +    non-trivial manner.\n      \n     -    Consider 'git config key value value_regex'. The expected behavior\n     -    is as follows:\n     +    Consider 'git config <key> <value> <value-pattern>'. The expected\n     +    behavior is as follows:\n      \n     -    1. If there are multiple existing values that match 'value_regex',\n     +    1. If there are multiple existing values that match 'value-pattern',\n             then the command fails. Users should use --replace-all instead.\n      \n     -    2. If there is one existing value that matches 'value_regex', then\n     -       the new config has one entry where 'key=value'.\n     -\n     -    3. If there is no existing values match 'value_regex', then the\n     +    2. If there is no existing values match 'value-pattern', then the\n             'key=value' pair is appended, making this 'key' a multi-valued\n             config setting.\n      \n     +    3. If there is one existing value that matches 'value-pattern', then\n     +       the new config has one entry where 'key=value'.\n     +\n          Add a test that demonstrates these options. Break from the existing\n          pattern in t1300-config.sh to use 'git config --file=<file>' instead of\n     -    modifying .git/config directly. Also use 'git config --file=<file>\n     -    --list' for config state comparison instead of the config file format.\n     +    modifying .git/config directly to prevent possibly incompatible repo\n     +    states. Also use 'git config --file=<file> --list' for config state\n     +    comparison instead of the config file format. This makes the tests\n     +    more readable.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ t/t1300-config.sh: test_expect_success '--replace-all does not invent newlines'\n       \ttest_cmp expect .git/config\n       '\n       \n     -+test_expect_success 'set all config with value_regex' '\n     ++test_expect_success 'set all config with value-pattern' '\n     ++\ttest_when_finished rm -f config initial &&\n      +\tgit config --file=initial abc.key one &&\n      +\n     ++\t# no match => add new entry\n      +\tcp initial config &&\n      +\tgit config --file=config abc.key two a+ &&\n      +\tgit config --file=config --list >actual &&\n     @@ t/t1300-config.sh: test_expect_success '--replace-all does not invent newlines'\n      +\tEOF\n      +\ttest_cmp expect actual &&\n      +\n     ++\t# multiple matches => failure\n      +\ttest_must_fail git config --file=config abc.key three o+ 2>err &&\n      +\ttest_i18ngrep \"has multiple values\" err &&\n     ++\n     ++\t# multiple values, no match => add\n      +\tgit config --file=config abc.key three a+ &&\n      +\tgit config --file=config --list >actual &&\n      +\tcat >expect <<-\\EOF &&\n     @@ t/t1300-config.sh: test_expect_success '--replace-all does not invent newlines'\n      +\tEOF\n      +\ttest_cmp expect actual &&\n      +\n     -+\tcp initial config &&\n     -+\tgit config --file=config abc.key three o+ &&\n     ++\t# single match => replace\n     ++\tgit config --file=config abc.key four h+ &&\n      +\tgit config --file=config --list >actual &&\n      +\tcat >expect <<-\\EOF &&\n     -+\tabc.key=three\n     ++\tabc.key=one\n     ++\tabc.key=two\n     ++\tabc.key=four\n      +\tEOF\n      +\ttest_cmp expect actual\n      +'\n 2:  829d0ccd8c ! 4:  75fb74da83 t1300: add test for --replace-all with value_regex\n     @@ Metadata\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## Commit message ##\n     -    t1300: add test for --replace-all with value_regex\n     +    t1300: add test for --replace-all with value-pattern\n      \n          The --replace-all option was added in 4ddba79d (git-config-set: add more\n     -    options) but was not tested along with the 'value_regex' parameter.\n     -    Since we will be updating this option to optionally treat 'value_regex'\n     +    options) but was not tested along with the 'value-pattern' parameter.\n     +    Since we will be updating this option to optionally treat 'value-pattern'\n          as a fixed string, let's add a test here that documents the current\n          behavior.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n       ## t/t1300-config.sh ##\n     -@@ t/t1300-config.sh: test_expect_success 'set all config with value_regex' '\n     +@@ t/t1300-config.sh: test_expect_success 'set all config with value-pattern' '\n       \ttest_cmp expect actual\n       '\n       \n     -+test_expect_success '--replace-all and value_regex' '\n     -+\trm -f config &&\n     ++test_expect_success '--replace-all and value-pattern' '\n     ++\ttest_when_finished rm -f config &&\n      +\tgit config --file=config --add abc.key one &&\n      +\tgit config --file=config --add abc.key two &&\n      +\tgit config --file=config --add abc.key three &&\n 4:  0e6a7371ed ! 5:  0c276ffcee config: add --fixed-value option, un-implemented\n     @@ Metadata\n       ## Commit message ##\n          config: add --fixed-value option, un-implemented\n      \n     -    The 'git config' builtin takes a 'value_regex' parameter for several\n     +    The 'git config' builtin takes a 'value-pattern' parameter for several\n          actions. This can cause confusion when expecting exact value matches\n     -    instead of regex matches, especially when the input string contains glob\n     -    characters. While callers can escape the patterns themselves, it would\n     -    be more friendly to allow an argument to disable the pattern matching in\n     -    favor of an exact string match.\n     +    instead of regex matches, especially when the input string contains\n     +    metacharacters. While callers can escape the patterns themselves, it\n     +    would be more friendly to allow an argument to disable the pattern\n     +    matching in favor of an exact string match.\n      \n          Add a new '--fixed-value' option that does not currently change the\n     -    behavior. The implementation will follow for each appropriate action.\n     -    For now, check and test that --fixed-value will abort the command when\n     -    included with an incompatible action or without a 'value_regex'\n     -    argument.\n     +    behavior. The implementation will be filled in by later changes for\n     +    each appropriate action. For now, check and test that --fixed-value\n     +    will abort the command when included with an incompatible action or\n     +    without a 'value-pattern' argument.\n      \n          The name '--fixed-value' was chosen over something simpler like\n          '--fixed' because some commands allow regular expressions on the\n     @@ Documentation/git-config.txt: git-config - Get and set repository or global opti\n       SYNOPSIS\n       --------\n       [verse]\n     --'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n     -+'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n     +-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]\n     ++'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]\n       'git config' [<file-option>] [--type=<type>] --add name value\n     --'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n     --'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]\n     --'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]\n     --'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value_regex]\n     -+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value_regex]\n     +-'git config' [<file-option>] [--type=<type>] --replace-all name value [value-pattern]\n     +-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value-pattern]\n     +-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value-pattern]\n     +-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value-pattern]\n     ++'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value-pattern]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern]\n     ++'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern]\n       'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n     --'git config' [<file-option>] --unset name [value_regex]\n     --'git config' [<file-option>] --unset-all name [value_regex]\n     -+'git config' [<file-option>] [--fixed-value] --unset name [value_regex]\n     -+'git config' [<file-option>] [--fixed-value] --unset-all name [value_regex]\n     +-'git config' [<file-option>] --unset name [value-pattern]\n     +-'git config' [<file-option>] --unset-all name [value-pattern]\n     ++'git config' [<file-option>] [--fixed-value] --unset name [value-pattern]\n     ++'git config' [<file-option>] [--fixed-value] --unset-all name [value-pattern]\n       'git config' [<file-option>] --rename-section old_name new_name\n       'git config' [<file-option>] --remove-section name\n       'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n     @@ Documentation/git-config.txt: See also <<FILES>>.\n       \tList all variables set in config file, along with their values.\n       \n      +--fixed-value::\n     -+\tWhen used with the `value_regex` argument, treat `value_regex` as\n     ++\tWhen used with the `value-pattern` argument, treat `value-pattern` as\n      +\tan exact string instead of a regular expression. This will restrict\n      +\tthe name/value pairs that are matched to only those where the value\n     -+\tis exactly equal to the `value_regex`.\n     ++\tis exactly equal to the `value-pattern`.\n      +\n       --type <type>::\n         'git config' will ensure that any input or output is valid under the given\n     @@ builtin/config.c: static struct option builtin_config_options[] = {\n       \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n       \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n       \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n     -+\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when matching values\")),\n     ++\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n       \tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n       \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n       \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \t\tusage_builtin_config();\n       \t}\n       \n     ++\t/* check usage of --fixed-value */\n      +\tif (fixed_value) {\n      +\t\tint allowed_usage = 0;\n      +\n      +\t\tswitch (actions) {\n     ++\t\t/* git config --get <name> <value-pattern> */\n      +\t\tcase ACTION_GET:\n     ++\t\t/* git config --get-all <name> <value-pattern> */\n      +\t\tcase ACTION_GET_ALL:\n     ++\t\t/* git config --get-regexp <name-pattern> <value-pattern> */\n      +\t\tcase ACTION_GET_REGEXP:\n     ++\t\t/* git config --unset <name> <value-pattern> */\n      +\t\tcase ACTION_UNSET:\n     ++\t\t/* git config --unset-all <name> <value-pattern> */\n      +\t\tcase ACTION_UNSET_ALL:\n      +\t\t\tallowed_usage = argc > 1 && !!argv[1];\n      +\t\t\tbreak;\n      +\n     ++\t\t/* git config <name> <value> <value-pattern> */\n      +\t\tcase ACTION_SET_ALL:\n     ++\t\t/* git config --replace-all <name> <value> <value-pattern> */\n      +\t\tcase ACTION_REPLACE_ALL:\n      +\t\t\tallowed_usage = argc > 2 && !!argv[2];\n      +\t\t\tbreak;\n     ++\n     ++\t\t/* other options don't allow --fixed-value */\n      +\t\t}\n      +\n      +\t\tif (!allowed_usage) {\n     -+\t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n     ++\t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n      +\t\t\tusage_builtin_config();\n      +\t\t}\n      +\t}\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \n      \n       ## t/t1300-config.sh ##\n     -@@ t/t1300-config.sh: test_expect_success '--replace-all and value_regex' '\n     +@@ t/t1300-config.sh: test_expect_success '--replace-all and value-pattern' '\n       \ttest_cmp expect actual\n       '\n       \n      +test_expect_success 'refuse --fixed-value for incompatible actions' '\n     ++\ttest_when_finished rm -f config &&\n      +\tgit config --file=config dev.null bogus &&\n      +\n      +\t# These modes do not allow --fixed-value at all\n     @@ t/t1300-config.sh: test_expect_success '--replace-all and value_regex' '\n      +\ttest_must_fail git config --file=config --fixed-value --get-color dev.null &&\n      +\ttest_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&\n      +\n     -+\t# These modes complain when --fixed-value has no value_regex\n     ++\t# These modes complain when --fixed-value has no value-pattern\n      +\ttest_must_fail git config --file=config --fixed-value dev.null bogus &&\n      +\ttest_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&\n      +\ttest_must_fail git config --file=config --fixed-value --get dev.null &&\n 5:  39718048cd ! 6:  763401da97 config: plumb --fixed-value into config API\n     @@ Commit message\n      \n          The git_config_set_multivar_in_file_gently() and related methods now\n          take a 'flags' bitfield, so add a new bit representing the --fixed-value\n     -    option from 'git config'. This alters the purpose of the value_regex\n     +    option from 'git config'. This alters the purpose of the value_pattern\n          parameter to be an exact string match. This requires some initialization\n          changes in git_config_set_multivar_in_file_gently() and a new strcmp()\n          call in the matches() method.\n     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix\n       \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n       \n      @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix)\n     - \t\t\terror(_(\"--fixed-value only applies with 'value_regex'\"));\n     + \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n       \t\t\tusage_builtin_config();\n       \t\t}\n      +\n     -+\t\tflags = CONFIG_FLAGS_FIXED_VALUE;\n     ++\t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\n       \t}\n       \n       \tif (actions & PAGING_ACTIONS)\n     @@ config.c: struct config_store_data {\n       \tsize_t baselen;\n       \tchar *key;\n       \tint do_not_match;\n     -+\tconst char *literal_value;\n     - \tregex_t *value_regex;\n     ++\tconst char *fixed_value;\n     + \tregex_t *value_pattern;\n       \tint multi_replace;\n       \tstruct {\n      @@ config.c: static int matches(const char *key, const char *value,\n       {\n       \tif (strcmp(key, store->key))\n       \t\treturn 0; /* not ours */\n     -+\tif (store->literal_value)\n     -+\t\treturn !strcmp(store->literal_value, value);\n     - \tif (!store->value_regex)\n     ++\tif (store->fixed_value)\n     ++\t\treturn !strcmp(store->fixed_value, value);\n     + \tif (!store->value_pattern)\n       \t\treturn 1; /* always matches */\n     - \tif (store->value_regex == CONFIG_REGEX_NONE)\n     + \tif (store->value_pattern == CONFIG_REGEX_NONE)\n      @@ config.c: int git_config_set_multivar_in_file_gently(const char *config_filename,\n     - \t\t\tstore.value_regex = NULL;\n     - \t\telse if (value_regex == CONFIG_REGEX_NONE)\n     - \t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n     + \t\t\tstore.value_pattern = NULL;\n     + \t\telse if (value_pattern == CONFIG_REGEX_NONE)\n     + \t\t\tstore.value_pattern = CONFIG_REGEX_NONE;\n      +\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n     -+\t\t\tstore.literal_value = value_regex;\n     ++\t\t\tstore.fixed_value = value_pattern;\n       \t\telse {\n     - \t\t\tif (value_regex[0] == '!') {\n     + \t\t\tif (value_pattern[0] == '!') {\n       \t\t\t\tstore.do_not_match = 1;\n      \n       ## config.h ##\n     @@ config.h: int git_config_key_is_valid(const char *key);\n       \n      +/*\n      + * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs\n     -+ * by string comparison (not regex match) to the provided value_regex\n     ++ * by string comparison (not regex match) to the provided value_pattern\n      + * parameter.\n      + */\n      +#define CONFIG_FLAGS_FIXED_VALUE (1 << 1)\n     @@ t/t1300-config.sh: test_expect_success 'refuse --fixed-value for incompatible ac\n       '\n       \n      +test_expect_success '--fixed-value uses exact string matching' '\n     -+\tGLOB=\"a+b*c?d[e]f.g\" &&\n     -+\trm -f initial &&\n     -+\tgit config --file=initial fixed.test \"$GLOB\" &&\n     ++\ttest_when_finished rm -f config initial &&\n     ++\tMETA=\"a+b*c?d[e]f.g\" &&\n     ++\tgit config --file=initial fixed.test \"$META\" &&\n      +\n      +\tcp initial config &&\n     -+\tgit config --file=config fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config fixed.test bogus \"$META\" &&\n      +\tgit config --file=config --list >actual &&\n      +\tcat >expect <<-EOF &&\n     -+\tfixed.test=$GLOB\n     ++\tfixed.test=$META\n      +\tfixed.test=bogus\n      +\tEOF\n      +\ttest_cmp expect actual &&\n      +\n      +\tcp initial config &&\n     -+\tgit config --file=config --fixed-value fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --fixed-value fixed.test bogus \"$META\" &&\n      +\tgit config --file=config --list >actual &&\n     -+\tprintf \"fixed.test=bogus\\n\" >expect &&\n     ++\tcat >expect <<-\\EOF &&\n     ++\tfixed.test=bogus\n     ++\tEOF\n      +\ttest_cmp expect actual &&\n      +\n      +\tcp initial config &&\n     -+\ttest_must_fail git config --file=config --unset fixed.test \"$GLOB\" &&\n     -+\tgit config --file=config --fixed-value --unset fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --unset fixed.test \"$META\" &&\n     ++\tgit config --file=config --fixed-value --unset fixed.test \"$META\" &&\n      +\ttest_must_fail git config --file=config fixed.test &&\n      +\n      +\tcp initial config &&\n     -+\ttest_must_fail git config --file=config --unset-all fixed.test \"$GLOB\" &&\n     -+\tgit config --file=config --fixed-value --unset-all fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --unset-all fixed.test \"$META\" &&\n     ++\tgit config --file=config --fixed-value --unset-all fixed.test \"$META\" &&\n      +\ttest_must_fail git config --file=config fixed.test &&\n      +\n      +\tcp initial config &&\n     -+\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n     -+\tgit config --file=config --list >actual &&\n     -+\tcat >expect <<-EOF &&\n     -+\tfixed.test=$GLOB\n     -+\tfixed.test=bogus\n     -+\tEOF\n     -+\ttest_cmp expect actual &&\n     -+\n     -+\tcp initial config &&\n     -+\tgit config --file=config --replace-all fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --replace-all fixed.test bogus \"$META\" &&\n      +\tgit config --file=config --list >actual &&\n      +\tcat >expect <<-EOF &&\n     -+\tfixed.test=$GLOB\n     ++\tfixed.test=$META\n      +\tfixed.test=bogus\n      +\tEOF\n      +\ttest_cmp expect actual &&\n      +\n     -+\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$GLOB\" &&\n     ++\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$META\" &&\n      +\tgit config --file=config --list >actual &&\n      +\tcat >expect <<-EOF &&\n      +\tfixed.test=bogus\n 6:  8e0111c7b4 ! 7:  d813c84275 config: implement --fixed-value with --get*\n     @@ Commit message\n      \n          The config builtin does its own regex matching of values for the --get,\n          --get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\n     -    to the get_value() method so we can initialize the value_regex argument\n     +    to the get_value() method so we can initialize the value-pattern argument\n          as a fixed string instead of a regex pattern.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n     @@ builtin/config.c: static const char *const builtin_config_usage[] = {\n       \n       static char *key;\n       static regex_t *key_regexp;\n     -+static const char *value_regex;\n     ++static const char *value_pattern;\n       static regex_t *regexp;\n       static int show_keys;\n       static int omit_values;\n     @@ builtin/config.c: 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_regex, (value_?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     @@ builtin/config.c: static int collect_config(const char *key_, const char *value_\n       }\n       \n      -static int get_value(const char *key_, const char *regex_)\n     -+static int get_value(const char *key_, const char *regex_, int flags)\n     ++static int get_value(const char *key_, const char *regex_, unsigned flags)\n       {\n       \tint ret = CONFIG_GENERIC_ERROR;\n       \tstruct strbuf_list values = {NULL};\n     @@ builtin/config.c: static int get_value(const char *key_, const char *regex_)\n       \n      -\tif (regex_) {\n      +\tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n     -+\t\tvalue_regex = regex_;\n     ++\t\tvalue_pattern = regex_;\n      +\telse if (regex_) {\n       \t\tif (regex_[0] == '!') {\n       \t\t\tdo_not_match = 1;\n     @@ t/t1300-config.sh: test_expect_success '--fixed-value uses exact string matching\n       '\n       \n      +test_expect_success '--get and --get-all with --fixed-value' '\n     -+\tGLOB=\"a+b*c?d[e]f.g\" &&\n     -+\trm -f config &&\n     ++\ttest_when_finished rm -f config &&\n     ++\tMETA=\"a+b*c?d[e]f.g\" &&\n      +\tgit config --file=config fixed.test bogus &&\n     -+\tgit config --file=config --add fixed.test \"$GLOB\" &&\n     ++\tgit config --file=config --add fixed.test \"$META\" &&\n      +\n      +\tgit config --file=config --get fixed.test bogus &&\n     -+\ttest_must_fail git config --file=config --get fixed.test \"$GLOB\" &&\n     -+\tgit config --file=config --get --fixed-value fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get fixed.test \"$META\" &&\n     ++\tgit config --file=config --get --fixed-value fixed.test \"$META\" &&\n      +\ttest_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&\n      +\n      +\tgit config --file=config --get-all fixed.test bogus &&\n     -+\ttest_must_fail git config --file=config --get-all fixed.test \"$GLOB\" &&\n     -+\tgit config --file=config --get-all --fixed-value fixed.test \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get-all fixed.test \"$META\" &&\n     ++\tgit config --file=config --get-all --fixed-value fixed.test \"$META\" &&\n      +\ttest_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&\n      +\n      +\tgit config --file=config --get-regexp fixed+ bogus &&\n     -+\ttest_must_fail git config --file=config --get-regexp fixed+ \"$GLOB\" &&\n     -+\tgit config --file=config --get-regexp --fixed-value fixed+ \"$GLOB\" &&\n     ++\ttest_must_fail git config --file=config --get-regexp fixed+ \"$META\" &&\n     ++\tgit config --file=config --get-regexp --fixed-value fixed+ \"$META\" &&\n      +\ttest_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent\n      +'\n      +\n 7:  5a3acf8119 ! 8:  558775f83d maintenance: use 'git config --fixed-value'\n     @@ Metadata\n       ## Commit message ##\n          maintenance: use 'git config --fixed-value'\n      \n     -    When a repository's leading directories contain regex glob characters,\n     +    When a repository's leading directories contain regex metacharacters,\n          the config calls for 'git maintenance register' and 'git maintenance\n          unregister' are not careful enough. Use the new --fixed-value option\n          to direct the config machinery to use exact string matches. This is a\n     -    more robust option than excaping these arguments in a piecemeal fashion.\n     +    more robust option than escaping these arguments in a piecemeal fashion.\n      \n          For the test, require that we are not running on Windows since the '+'\n     -    character is not allowed on that filesystem.\n     +    and '*' characters are not allowed on that filesystem.\n      \n          Reported-by: Emily Shaffer <emilyshaffer@google.com>\n          Reported-by: Jonathan Nieder <jrnieder@gmail.com>\n     @@ t/t7900-maintenance.sh: test_expect_success 'register and unregister' '\n       \ttest_cmp before actual\n       '\n       \n     -+test_expect_success !MINGW 'register and unregister with glob characters' '\n     -+\tGLOB=\"a+b*c\" &&\n     -+\tgit init \"$GLOB\" &&\n     -+\tgit -C \"$GLOB\" maintenance register &&\n     ++test_expect_success !MINGW 'register and unregister with regex metacharacters' '\n     ++\tMETA=\"a+b*c\" &&\n     ++\tgit init \"$META\" &&\n     ++\tgit -C \"$META\" maintenance register &&\n      +\tgit config --get-all --show-origin maintenance.repo &&\n      +\tgit config --get-all --global --fixed-value \\\n     -+\t\tmaintenance.repo \"$(pwd)/$GLOB\" &&\n     -+\tgit -C \"$GLOB\" maintenance unregister &&\n     ++\t\tmaintenance.repo \"$(pwd)/$META\" &&\n     ++\tgit -C \"$META\" maintenance unregister &&\n      +\ttest_must_fail git config --get-all --global --fixed-value \\\n     -+\t\tmaintenance.repo \"$(pwd)/$GLOB\"\n     ++\t\tmaintenance.repo \"$(pwd)/$META\"\n      +'\n      +\n       test_expect_success 'start from empty cron table' '\n\n-- \ngitgitgadget\n"},{"id":"410811","messageId":"75fb74da8324674194cca2b27fc4e6c0fdc6486d.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 4/8] t1300: add test for --replace-all with value-pattern","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:52Z","receivedAt":"2020-11-25T22:13:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe --replace-all option was added in 4ddba79d (git-config-set: add more\noptions) but was not tested along with the 'value-pattern' parameter.\nSince we will be updating this option to optionally treat 'value-pattern'\nas a fixed string, let's add a test here that documents the current\nbehavior.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n t/t1300-config.sh | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 72f930ada0..040b9f7506 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1953,4 +1953,18 @@ test_expect_success 'set all config with value-pattern' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--replace-all and value-pattern' '\n+\ttest_when_finished rm -f config &&\n+\tgit config --file=config --add abc.key one &&\n+\tgit config --file=config --add abc.key two &&\n+\tgit config --file=config --add abc.key three &&\n+\tgit config --file=config --replace-all abc.key four \"o+\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tabc.key=four\n+\tabc.key=three\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410813","messageId":"763401da97289ea00eed372c6ed40ad508781681.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 6/8] config: plumb --fixed-value into config API","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:54Z","receivedAt":"2020-11-25T22:13:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe git_config_set_multivar_in_file_gently() and related methods now\ntake a 'flags' bitfield, so add a new bit representing the --fixed-value\noption from 'git config'. This alters the purpose of the value_pattern\nparameter to be an exact string match. This requires some initialization\nchanges in git_config_set_multivar_in_file_gently() and a new strcmp()\ncall in the matches() method.\n\nThe new CONFIG_FLAGS_FIXED_VALUE flag is initialized in builtin/config.c\nbased on the --fixed-value option, and that needs to be updated in\nseveral callers.\n\nThis patch only affects some of the modes of 'git config', and the rest\nwill be completed in the next change.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 16 ++++++++++-----\n config.c          |  5 +++++\n config.h          |  7 +++++++\n t/t1300-config.sh | 50 +++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 73 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex d0adbed872..8049d8f5fc 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -616,6 +616,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n {\n \tint nongit = !startup_info->have_repository;\n \tchar *value;\n+\tint flags = 0;\n \n \tgiven_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));\n \n@@ -779,6 +780,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\terror(_(\"--fixed-value only applies with 'value-pattern'\"));\n \t\t\tusage_builtin_config();\n \t\t}\n+\n+\t\tflags |= CONFIG_FLAGS_FIXED_VALUE;\n \t}\n \n \tif (actions & PAGING_ACTIONS)\n@@ -842,7 +845,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 0);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write();\n@@ -851,7 +855,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value,\n-\t\t\t\t\t\t\t      CONFIG_REGEX_NONE, 0);\n+\t\t\t\t\t\t\t      CONFIG_REGEX_NONE,\n+\t\t\t\t\t\t\t      flags);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write();\n@@ -860,7 +865,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value, argv[2],\n-\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -887,7 +892,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 0);\n+\t\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t\t      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);\n@@ -897,7 +903,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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      CONFIG_FLAGS_MULTI_REPLACE);\n+\t\t\t\t\t\t\t      flags | CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/config.c b/config.c\nindex ac6aace166..d827167d83 100644\n--- a/config.c\n+++ b/config.c\n@@ -2402,6 +2402,7 @@ struct config_store_data {\n \tsize_t baselen;\n \tchar *key;\n \tint do_not_match;\n+\tconst char *fixed_value;\n \tregex_t *value_pattern;\n \tint multi_replace;\n \tstruct {\n@@ -2431,6 +2432,8 @@ static int matches(const char *key, const char *value,\n {\n \tif (strcmp(key, store->key))\n \t\treturn 0; /* not ours */\n+\tif (store->fixed_value)\n+\t\treturn !strcmp(store->fixed_value, value);\n \tif (!store->value_pattern)\n \t\treturn 1; /* always matches */\n \tif (store->value_pattern == CONFIG_REGEX_NONE)\n@@ -2803,6 +2806,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\tstore.value_pattern = NULL;\n \t\telse if (value_pattern == CONFIG_REGEX_NONE)\n \t\t\tstore.value_pattern = CONFIG_REGEX_NONE;\n+\t\telse if (flags & CONFIG_FLAGS_FIXED_VALUE)\n+\t\t\tstore.fixed_value = value_pattern;\n \t\telse {\n \t\t\tif (value_pattern[0] == '!') {\n \t\t\t\tstore.do_not_match = 1;\ndiff --git a/config.h b/config.h\nindex 8ad1085b74..34901e2b64 100644\n--- a/config.h\n+++ b/config.h\n@@ -269,6 +269,13 @@ int git_config_key_is_valid(const char *key);\n  */\n #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n \n+/*\n+ * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs\n+ * by string comparison (not regex match) to the provided value_pattern\n+ * parameter.\n+ */\n+#define CONFIG_FLAGS_FIXED_VALUE (1 << 1)\n+\n int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f6f071006d..413fcbba03 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1991,4 +1991,54 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '\n \ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n '\n \n+test_expect_success '--fixed-value uses exact string matching' '\n+\ttest_when_finished rm -f config initial &&\n+\tMETA=\"a+b*c?d[e]f.g\" &&\n+\tgit config --file=initial fixed.test \"$META\" &&\n+\n+\tcp initial config &&\n+\tgit config --file=config fixed.test bogus \"$META\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=$META\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\tgit config --file=config --fixed-value fixed.test bogus \"$META\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tcp initial config &&\n+\ttest_must_fail git config --file=config --unset fixed.test \"$META\" &&\n+\tgit config --file=config --fixed-value --unset fixed.test \"$META\" &&\n+\ttest_must_fail git config --file=config fixed.test &&\n+\n+\tcp initial config &&\n+\ttest_must_fail git config --file=config --unset-all fixed.test \"$META\" &&\n+\tgit config --file=config --fixed-value --unset-all fixed.test \"$META\" &&\n+\ttest_must_fail git config --file=config fixed.test &&\n+\n+\tcp initial config &&\n+\tgit config --file=config --replace-all fixed.test bogus \"$META\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=$META\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual &&\n+\n+\tgit config --file=config --fixed-value --replace-all fixed.test bogus \"$META\" &&\n+\tgit config --file=config --list >actual &&\n+\tcat >expect <<-EOF &&\n+\tfixed.test=bogus\n+\tfixed.test=bogus\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410815","messageId":"558775f83d7cbf7ad0e2090ab13be404cdf16b24.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 8/8] maintenance: use 'git config --fixed-value'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:56Z","receivedAt":"2020-11-25T22:13:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen a repository's leading directories contain regex metacharacters,\nthe config calls for 'git maintenance register' and 'git maintenance\nunregister' are not careful enough. Use the new --fixed-value option\nto direct the config machinery to use exact string matches. This is a\nmore robust option than escaping these arguments in a piecemeal fashion.\n\nFor the test, require that we are not running on Windows since the '+'\nand '*' characters are not allowed on that filesystem.\n\nReported-by: Emily Shaffer <emilyshaffer@google.com>\nReported-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/gc.c           |  5 +++--\n t/t7900-maintenance.sh | 12 ++++++++++++\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex e3098ef6a1..6dde3ce1bb 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1452,7 +1452,8 @@ static int maintenance_register(void)\n \t\tgit_config_set(\"maintenance.strategy\", \"incremental\");\n \n \tconfig_get.git_cmd = 1;\n-\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\", \"maintenance.repo\",\n+\tstrvec_pushl(&config_get.args, \"config\", \"--global\", \"--get\",\n+\t\t     \"--fixed-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t\t NULL);\n@@ -1483,7 +1484,7 @@ static int maintenance_unregister(void)\n \n \tconfig_unset.git_cmd = 1;\n \tstrvec_pushl(&config_unset.args, \"config\", \"--global\", \"--unset\",\n-\t\t     \"maintenance.repo\",\n+\t\t     \"--fixed-value\", \"maintenance.repo\",\n \t\t     the_repository->worktree ? the_repository->worktree\n \t\t\t\t\t      : the_repository->gitdir,\n \t\t     NULL);\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex 20184e96e1..bc2207edd6 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -367,6 +367,18 @@ test_expect_success 'register and unregister' '\n \ttest_cmp before actual\n '\n \n+test_expect_success !MINGW 'register and unregister with regex metacharacters' '\n+\tMETA=\"a+b*c\" &&\n+\tgit init \"$META\" &&\n+\tgit -C \"$META\" maintenance register &&\n+\tgit config --get-all --show-origin maintenance.repo &&\n+\tgit config --get-all --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/$META\" &&\n+\tgit -C \"$META\" maintenance unregister &&\n+\ttest_must_fail git config --get-all --global --fixed-value \\\n+\t\tmaintenance.repo \"$(pwd)/$META\"\n+'\n+\n test_expect_success 'start from empty cron table' '\n \tGIT_TEST_CRONTAB=\"test-tool crontab cron.txt\" git maintenance start &&\n \n-- \ngitgitgadget\n"},{"id":"410816","messageId":"0c276ffcee4be302be3db9b43ab8371ef26a0aec.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 5/8] config: add --fixed-value option, un-implemented","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:53Z","receivedAt":"2020-11-25T22:13:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe 'git config' builtin takes a 'value-pattern' parameter for several\nactions. This can cause confusion when expecting exact value matches\ninstead of regex matches, especially when the input string contains\nmetacharacters. While callers can escape the patterns themselves, it\nwould be more friendly to allow an argument to disable the pattern\nmatching in favor of an exact string match.\n\nAdd a new '--fixed-value' option that does not currently change the\nbehavior. The implementation will be filled in by later changes for\neach appropriate action. For now, check and test that --fixed-value\nwill abort the command when included with an incompatible action or\nwithout a 'value-pattern' argument.\n\nThe name '--fixed-value' was chosen over something simpler like\n'--fixed' because some commands allow regular expressions on the\nkey in addition to the value.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-config.txt | 20 +++++++++++++-------\n builtin/config.c             | 36 ++++++++++++++++++++++++++++++++++++\n t/t1300-config.sh            | 24 ++++++++++++++++++++++++\n 3 files changed, 73 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 0be5499952..09a1d273a9 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,15 +9,15 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]\n+'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]\n 'git config' [<file-option>] [--type=<type>] --add name value\n-'git config' [<file-option>] [--type=<type>] --replace-all name value [value-pattern]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value-pattern]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value-pattern]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern]\n 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n-'git config' [<file-option>] --unset name [value-pattern]\n-'git config' [<file-option>] --unset-all name [value-pattern]\n+'git config' [<file-option>] [--fixed-value] --unset name [value-pattern]\n+'git config' [<file-option>] [--fixed-value] --unset-all name [value-pattern]\n 'git config' [<file-option>] --rename-section old_name new_name\n 'git config' [<file-option>] --remove-section name\n 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n@@ -165,6 +165,12 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n+--fixed-value::\n+\tWhen used with the `value-pattern` argument, treat `value-pattern` as\n+\tan exact string instead of a regular expression. This will restrict\n+\tthe name/value pairs that are matched to only those where the value\n+\tis exactly equal to the `value-pattern`.\n+\n --type <type>::\n   'git config' will ensure that any input or output is valid under the given\n   type constraint(s), and will canonicalize outgoing values in `<type>`'s\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 9d97091561..d0adbed872 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -34,6 +34,7 @@ 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 \n #define ACTION_GET (1<<0)\n #define ACTION_GET_ALL (1<<1)\n@@ -141,6 +142,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n+\tOPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n \tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n@@ -745,6 +747,40 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_builtin_config();\n \t}\n \n+\t/* check usage of --fixed-value */\n+\tif (fixed_value) {\n+\t\tint allowed_usage = 0;\n+\n+\t\tswitch (actions) {\n+\t\t/* git config --get <name> <value-pattern> */\n+\t\tcase ACTION_GET:\n+\t\t/* git config --get-all <name> <value-pattern> */\n+\t\tcase ACTION_GET_ALL:\n+\t\t/* git config --get-regexp <name-pattern> <value-pattern> */\n+\t\tcase ACTION_GET_REGEXP:\n+\t\t/* git config --unset <name> <value-pattern> */\n+\t\tcase ACTION_UNSET:\n+\t\t/* git config --unset-all <name> <value-pattern> */\n+\t\tcase ACTION_UNSET_ALL:\n+\t\t\tallowed_usage = argc > 1 && !!argv[1];\n+\t\t\tbreak;\n+\n+\t\t/* git config <name> <value> <value-pattern> */\n+\t\tcase ACTION_SET_ALL:\n+\t\t/* git config --replace-all <name> <value> <value-pattern> */\n+\t\tcase ACTION_REPLACE_ALL:\n+\t\t\tallowed_usage = argc > 2 && !!argv[2];\n+\t\t\tbreak;\n+\n+\t\t/* other options don't allow --fixed-value */\n+\t\t}\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}\n+\t}\n+\n \tif (actions & PAGING_ACTIONS)\n \t\tsetup_auto_pager(\"config\", 1);\n \ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 040b9f7506..f6f071006d 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1967,4 +1967,28 @@ test_expect_success '--replace-all and value-pattern' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'refuse --fixed-value for incompatible actions' '\n+\ttest_when_finished rm -f config &&\n+\tgit config --file=config dev.null bogus &&\n+\n+\t# These modes do not allow --fixed-value at all\n+\ttest_must_fail git config --file=config --fixed-value --add dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --rename-section dev null &&\n+\ttest_must_fail git config --file=config --fixed-value --remove-section dev &&\n+\ttest_must_fail git config --file=config --fixed-value --list &&\n+\ttest_must_fail git config --file=config --fixed-value --get-color dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&\n+\n+\t# These modes complain when --fixed-value has no value-pattern\n+\ttest_must_fail git config --file=config --fixed-value dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&\n+\ttest_must_fail git config --file=config --fixed-value --get dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-all dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --get-regexp \"dev.*\" &&\n+\ttest_must_fail git config --file=config --fixed-value --unset dev.null &&\n+\ttest_must_fail git config --file=config --fixed-value --unset-all dev.null\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410819","messageId":"f135b001ad911c079b0754c61e2179277ce37e0f.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 2/8] config: replace 'value_regex' with 'value_pattern'","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:50Z","receivedAt":"2020-11-25T22:13:39Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe 'value_regex' argument in the 'git config' builtin is poorly named,\nespecially related to an upcoming change that allows exact string\nmatches instead of ERE pattern matches.\n\nPerform a mostly mechanical change of every instance of 'value_regex' to\n'value_pattern' in the codebase. This is only critical for documentation\nand error messages, but it is best to be consistent inside the codebase,\ntoo.\n\nFor documentation, use 'value-pattern' which is better punctuation. This\naffects Documentation/git-config.txt and the usage in builtin/config.c,\nwhich was already mixed between 'value_regex' and 'value-regex'.\n\nI gave some thought to leaving the value_regex variables inside config.c\nthat are regex_t pointers. However, it is probably best to keep the name\nconsistent with the rest of the variables.\n\nThis does not update the translations inside the po/ directory, as that\ncreates conflicts with ongoing work. The input strings should\nautomatically update through automation, and a few of the output strings\ncurrently use \"[value_regex]\" directly.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/git-config.txt | 20 ++++++-------\n builtin/config.c             | 12 ++++----\n config.c                     | 54 ++++++++++++++++++------------------\n config.h                     |  2 +-\n 4 files changed, 44 insertions(+), 44 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7573160f21..0be5499952 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,15 +9,15 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]\n 'git config' [<file-option>] [--type=<type>] --add name value\n-'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] --replace-all name value [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value-pattern]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value-pattern]\n 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n-'git config' [<file-option>] --unset name [value_regex]\n-'git config' [<file-option>] --unset-all name [value_regex]\n+'git config' [<file-option>] --unset name [value-pattern]\n+'git config' [<file-option>] --unset-all name [value-pattern]\n 'git config' [<file-option>] --rename-section old_name new_name\n 'git config' [<file-option>] --remove-section name\n 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list\n@@ -33,7 +33,7 @@ escaped.\n \n Multiple lines can be added to an option by using the `--add` option.\n If you want to update or unset an option which can occur on multiple\n-lines, a POSIX regexp `value_regex` needs to be given.  Only the\n+lines, a POSIX regexp `value-pattern` needs to be given.  Only the\n existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n@@ -73,11 +73,11 @@ OPTIONS\n \n --replace-all::\n \tDefault behavior is to replace at most one line. This replaces\n-\tall lines matching the key (and optionally the value_regex).\n+\tall lines matching the key (and optionally the `value-pattern`).\n \n --add::\n \tAdds a new line to the option without altering any existing\n-\tvalues.  This is the same as providing '^$' as the value_regex\n+\tvalues.  This is the same as providing '^$' as the `value-pattern`\n \tin `--replace-all`.\n \n --get::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e7c7f3d455..9d97091561 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -130,14 +130,14 @@ static struct option builtin_config_options[] = {\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 \tOPT_GROUP(N_(\"Action\")),\n-\tOPT_BIT(0, \"get\", &actions, N_(\"get value: name [value-regex]\"), ACTION_GET),\n-\tOPT_BIT(0, \"get-all\", &actions, N_(\"get all values: key [value-regex]\"), ACTION_GET_ALL),\n-\tOPT_BIT(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [value-regex]\"), ACTION_GET_REGEXP),\n+\tOPT_BIT(0, \"get\", &actions, N_(\"get value: name [value-pattern]\"), ACTION_GET),\n+\tOPT_BIT(0, \"get-all\", &actions, N_(\"get all values: key [value-pattern]\"), ACTION_GET_ALL),\n+\tOPT_BIT(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [value-pattern]\"), ACTION_GET_REGEXP),\n \tOPT_BIT(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n-\tOPT_BIT(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [value_regex]\"), ACTION_REPLACE_ALL),\n+\tOPT_BIT(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [value-pattern]\"), ACTION_REPLACE_ALL),\n \tOPT_BIT(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n-\tOPT_BIT(0, \"unset\", &actions, N_(\"remove a variable: name [value-regex]\"), ACTION_UNSET),\n-\tOPT_BIT(0, \"unset-all\", &actions, N_(\"remove all matches: name [value-regex]\"), ACTION_UNSET_ALL),\n+\tOPT_BIT(0, \"unset\", &actions, N_(\"remove a variable: name [value-pattern]\"), ACTION_UNSET),\n+\tOPT_BIT(0, \"unset-all\", &actions, N_(\"remove all matches: name [value-pattern]\"), ACTION_UNSET_ALL),\n \tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n \tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n \tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\ndiff --git a/config.c b/config.c\nindex f34a11d94b..ac6aace166 100644\n--- a/config.c\n+++ b/config.c\n@@ -2402,7 +2402,7 @@ struct config_store_data {\n \tsize_t baselen;\n \tchar *key;\n \tint do_not_match;\n-\tregex_t *value_regex;\n+\tregex_t *value_pattern;\n \tint multi_replace;\n \tstruct {\n \t\tsize_t begin, end;\n@@ -2416,10 +2416,10 @@ struct config_store_data {\n static void config_store_data_clear(struct config_store_data *store)\n {\n \tfree(store->key);\n-\tif (store->value_regex != NULL &&\n-\t    store->value_regex != CONFIG_REGEX_NONE) {\n-\t\tregfree(store->value_regex);\n-\t\tfree(store->value_regex);\n+\tif (store->value_pattern != NULL &&\n+\t    store->value_pattern != CONFIG_REGEX_NONE) {\n+\t\tregfree(store->value_pattern);\n+\t\tfree(store->value_pattern);\n \t}\n \tfree(store->parsed);\n \tfree(store->seen);\n@@ -2431,13 +2431,13 @@ static int matches(const char *key, const char *value,\n {\n \tif (strcmp(key, store->key))\n \t\treturn 0; /* not ours */\n-\tif (!store->value_regex)\n+\tif (!store->value_pattern)\n \t\treturn 1; /* always matches */\n-\tif (store->value_regex == CONFIG_REGEX_NONE)\n+\tif (store->value_pattern == CONFIG_REGEX_NONE)\n \t\treturn 0; /* never matches */\n \n \treturn store->do_not_match ^\n-\t\t(value && !regexec(store->value_regex, value, 0, NULL, 0));\n+\t\t(value && !regexec(store->value_pattern, value, 0, NULL, 0));\n }\n \n static int store_aux_event(enum config_event_t type,\n@@ -2713,8 +2713,8 @@ void git_config_set(const char *key, const char *value)\n \n /*\n  * If value==NULL, unset in (remove from) config,\n- * if value_regex!=NULL, disregard key/value pairs where value does not match.\n- * if value_regex==CONFIG_REGEX_NONE, do not match any existing values\n+ * if value_pattern!=NULL, disregard key/value pairs where value does not match.\n+ * if value_pattern==CONFIG_REGEX_NONE, do not match any existing values\n  *     (only add a new one)\n  * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching\n  *     key/values are removed before a single new pair is written. If the\n@@ -2738,7 +2738,7 @@ void git_config_set(const char *key, const char *value)\n  */\n int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\t\t\t   const char *key, const char *value,\n-\t\t\t\t\t   const char *value_regex,\n+\t\t\t\t\t   const char *value_pattern,\n \t\t\t\t\t   unsigned flags)\n {\n \tint fd = -1, in_fd = -1;\n@@ -2799,22 +2799,22 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\tint i, new_line = 0;\n \t\tstruct config_options opts;\n \n-\t\tif (value_regex == NULL)\n-\t\t\tstore.value_regex = NULL;\n-\t\telse if (value_regex == CONFIG_REGEX_NONE)\n-\t\t\tstore.value_regex = CONFIG_REGEX_NONE;\n+\t\tif (value_pattern == NULL)\n+\t\t\tstore.value_pattern = NULL;\n+\t\telse if (value_pattern == CONFIG_REGEX_NONE)\n+\t\t\tstore.value_pattern = CONFIG_REGEX_NONE;\n \t\telse {\n-\t\t\tif (value_regex[0] == '!') {\n+\t\t\tif (value_pattern[0] == '!') {\n \t\t\t\tstore.do_not_match = 1;\n-\t\t\t\tvalue_regex++;\n+\t\t\t\tvalue_pattern++;\n \t\t\t} else\n \t\t\t\tstore.do_not_match = 0;\n \n-\t\t\tstore.value_regex = (regex_t*)xmalloc(sizeof(regex_t));\n-\t\t\tif (regcomp(store.value_regex, value_regex,\n+\t\t\tstore.value_pattern = (regex_t*)xmalloc(sizeof(regex_t));\n+\t\t\tif (regcomp(store.value_pattern, value_pattern,\n \t\t\t\t\tREG_EXTENDED)) {\n-\t\t\t\terror(_(\"invalid pattern: %s\"), value_regex);\n-\t\t\t\tFREE_AND_NULL(store.value_regex);\n+\t\t\t\terror(_(\"invalid pattern: %s\"), value_pattern);\n+\t\t\t\tFREE_AND_NULL(store.value_pattern);\n \t\t\t\tret = CONFIG_INVALID_PATTERN;\n \t\t\t\tgoto out_free;\n \t\t\t}\n@@ -2984,10 +2984,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n void git_config_set_multivar_in_file(const char *config_filename,\n \t\t\t\t     const char *key, const char *value,\n-\t\t\t\t     const char *value_regex, unsigned flags)\n+\t\t\t\t     const char *value_pattern, unsigned flags)\n {\n \tif (!git_config_set_multivar_in_file_gently(config_filename, key, value,\n-\t\t\t\t\t\t    value_regex, flags))\n+\t\t\t\t\t\t    value_pattern, flags))\n \t\treturn;\n \tif (value)\n \t\tdie(_(\"could not set '%s' to '%s'\"), key, value);\n@@ -2996,16 +2996,16 @@ void git_config_set_multivar_in_file(const char *config_filename,\n }\n \n int git_config_set_multivar_gently(const char *key, const char *value,\n-\t\t\t\t   const char *value_regex, unsigned flags)\n+\t\t\t\t   const char *value_pattern, unsigned flags)\n {\n-\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,\n+\treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,\n \t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n-\t\t\t     const char *value_regex, unsigned flags)\n+\t\t\t     const char *value_pattern, unsigned flags)\n {\n-\tgit_config_set_multivar_in_file(NULL, key, value, value_regex,\n+\tgit_config_set_multivar_in_file(NULL, key, value, value_pattern,\n \t\t\t\t\tflags);\n }\n \ndiff --git a/config.h b/config.h\nindex 58726c34d6..8ad1085b74 100644\n--- a/config.h\n+++ b/config.h\n@@ -296,7 +296,7 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n void git_config_set_multivar_in_file(const char *config_filename,\n \t\t\t\t     const char *key,\n \t\t\t\t     const char *value,\n-\t\t\t\t     const char *value_regex,\n+\t\t\t\t     const char *value_pattern,\n \t\t\t\t     unsigned flags);\n \n /**\n-- \ngitgitgadget\n\n"},{"id":"410817","messageId":"f0ed492096b302de0ccccfb9fc3b0c6e8bbcb261.1606342376.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 1/8] config: convert multi_replace to flags","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:49Z","receivedAt":"2020-11-25T22:13:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWe will extend the flexibility of the config API. Before doing so, let's\ntake an existing 'int multi_replace' parameter and replace it with a new\n'unsigned flags' parameter that can take multiple options as a bit field.\n\nUpdate all callers that specified multi_replace to now specify the\nCONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the\ndocumentation of git_config_set_multivar_in_file() including a clear\nlabeling of its arguments. Other config API methods in config.h require\nonly a change of the final parameter from 'int' to 'unsigned'.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/branch.c |  4 ++--\n builtin/config.c |  6 ++++--\n builtin/remote.c |  8 +++++---\n config.c         | 24 ++++++++++++------------\n config.h         | 29 ++++++++++++++++++++++-------\n 5 files changed, 45 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex e82301fb1b..5ce3844d22 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"Branch '%s' has no upstream information\"), branch->name);\n \n \t\tstrbuf_addf(&buf, \"branch.%s.remote\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"branch.%s.merge\", branch->name);\n-\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \t\tstrbuf_release(&buf);\n \t} else if (argc > 0 && argc <= 2) {\n \t\tif (filter.kind != FILTER_REFS_BRANCHES)\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5e39f61885..e7c7f3d455 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -823,7 +823,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tUNLEAK(value);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n-\t\t\t\t\t\t\t      argv[0], value, argv[2], 1);\n+\t\t\t\t\t\t\t      argv[0], value, argv[2],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n@@ -859,7 +860,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\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], 1);\n+\t\t\t\t\t\t\t      argv[0], NULL, argv[1],\n+\t\t\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex c8240e9fcd..29b1652975 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -708,7 +708,7 @@ static int mv(int argc, const char **argv)\n \n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new_name);\n-\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\tgit_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);\n \tstrbuf_addf(&old_remote_context, \":refs/remotes/%s/\", rename.old_name);\n \tfor (i = 0; i < oldremote->fetch.raw_nr; i++) {\n \t\tchar *ptr;\n@@ -1485,7 +1485,8 @@ static int update(int argc, const char **argv)\n \n static int remove_all_fetch_refspecs(const char *key)\n {\n-\treturn git_config_set_multivar_gently(key, NULL, NULL, 1);\n+\treturn git_config_set_multivar_gently(key, NULL, NULL,\n+\t\t\t\t\t      CONFIG_FLAGS_MULTI_REPLACE);\n }\n \n static void add_branches(struct remote *remote, const char **branches,\n@@ -1674,7 +1675,8 @@ static int set_url(int argc, const char **argv)\n \tif (!delete_mode)\n \t\tgit_config_set_multivar(name_buf.buf, newurl, oldurl, 0);\n \telse\n-\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl, 1);\n+\t\tgit_config_set_multivar(name_buf.buf, NULL, oldurl,\n+\t\t\t\t\tCONFIG_FLAGS_MULTI_REPLACE);\n out:\n \tstrbuf_release(&name_buf);\n \treturn 0;\ndiff --git a/config.c b/config.c\nindex 2b79fe76ad..f34a11d94b 100644\n--- a/config.c\n+++ b/config.c\n@@ -2716,9 +2716,9 @@ void git_config_set(const char *key, const char *value)\n  * if value_regex!=NULL, disregard key/value pairs where value does not match.\n  * if value_regex==CONFIG_REGEX_NONE, do not match any existing values\n  *     (only add a new one)\n- * if multi_replace==0, nothing, or only one matching key/value is replaced,\n- *     else all matching key/values (regardless how many) are removed,\n- *     before the new pair is written.\n+ * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching\n+ *     key/values are removed before a single new pair is written. If the\n+ *     flag is not present, then replace only the first match.\n  *\n  * Returns 0 on success.\n  *\n@@ -2739,7 +2739,7 @@ void git_config_set(const char *key, const char *value)\n int git_config_set_multivar_in_file_gently(const char *config_filename,\n \t\t\t\t\t   const char *key, const char *value,\n \t\t\t\t\t   const char *value_regex,\n-\t\t\t\t\t   int multi_replace)\n+\t\t\t\t\t   unsigned flags)\n {\n \tint fd = -1, in_fd = -1;\n \tint ret;\n@@ -2756,7 +2756,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \tif (ret)\n \t\tgoto out_free;\n \n-\tstore.multi_replace = multi_replace;\n+\tstore.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0;\n \n \tif (!config_filename)\n \t\tconfig_filename = filename_buf = git_pathdup(\"config\");\n@@ -2845,7 +2845,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n \t\t/* if nothing to unset, or too many matches, error out */\n \t\tif ((store.seen_nr == 0 && value == NULL) ||\n-\t\t    (store.seen_nr > 1 && multi_replace == 0)) {\n+\t\t    (store.seen_nr > 1 && !store.multi_replace)) {\n \t\t\tret = CONFIG_NOTHING_SET;\n \t\t\tgoto out_free;\n \t\t}\n@@ -2984,10 +2984,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,\n \n void git_config_set_multivar_in_file(const char *config_filename,\n \t\t\t\t     const char *key, const char *value,\n-\t\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t\t     const char *value_regex, unsigned flags)\n {\n \tif (!git_config_set_multivar_in_file_gently(config_filename, key, value,\n-\t\t\t\t\t\t    value_regex, multi_replace))\n+\t\t\t\t\t\t    value_regex, flags))\n \t\treturn;\n \tif (value)\n \t\tdie(_(\"could not set '%s' to '%s'\"), key, value);\n@@ -2996,17 +2996,17 @@ void git_config_set_multivar_in_file(const char *config_filename,\n }\n \n int git_config_set_multivar_gently(const char *key, const char *value,\n-\t\t\t\t   const char *value_regex, int multi_replace)\n+\t\t\t\t   const char *value_regex, unsigned flags)\n {\n \treturn git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,\n-\t\t\t\t\t\t      multi_replace);\n+\t\t\t\t\t\t      flags);\n }\n \n void git_config_set_multivar(const char *key, const char *value,\n-\t\t\t     const char *value_regex, int multi_replace)\n+\t\t\t     const char *value_regex, unsigned flags)\n {\n \tgit_config_set_multivar_in_file(NULL, key, value, value_regex,\n-\t\t\t\t\tmulti_replace);\n+\t\t\t\t\tflags);\n }\n \n static int section_name_match (const char *buf, const char *name)\ndiff --git a/config.h b/config.h\nindex 060874488f..58726c34d6 100644\n--- a/config.h\n+++ b/config.h\n@@ -256,9 +256,22 @@ void git_config_set(const char *, const char *);\n \n int git_config_parse_key(const char *, char **, size_t *);\n int git_config_key_is_valid(const char *key);\n-int git_config_set_multivar_gently(const char *, const char *, const char *, int);\n-void git_config_set_multivar(const char *, const char *, const char *, int);\n-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);\n+\n+/*\n+ * The following macros specify flag bits that alter the behavior\n+ * of the git_config_set_multivar*() methods.\n+ */\n+\n+/*\n+ * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values\n+ * are removed before a single new pair is written. If the flag is not\n+ * present, then set operations replace only the first match.\n+ */\n+#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0)\n+\n+int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);\n+void git_config_set_multivar(const char *, const char *, const char *, unsigned);\n+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);\n \n /**\n  * takes four parameters:\n@@ -276,13 +289,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha\n  * - the value regex, as a string. It will disregard key/value pairs where value\n  *   does not match.\n  *\n- * - a multi_replace value, as an int. If value is equal to zero, nothing or only\n- *   one matching key/value is replaced, else all matching key/values (regardless\n- *   how many) are removed, before the new pair is written.\n+ * - a flags value with bits corresponding to the CONFIG_FLAG_* macros.\n  *\n  * It returns 0 on success.\n  */\n-void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n+void git_config_set_multivar_in_file(const char *config_filename,\n+\t\t\t\t     const char *key,\n+\t\t\t\t     const char *value,\n+\t\t\t\t     const char *value_regex,\n+\t\t\t\t     unsigned flags);\n \n /**\n  * rename or remove sections in the config file\n-- \ngitgitgadget\n\n"},{"id":"410818","messageId":"d813c84275cf3c259ec5fa93b5af822937692c81.1606342377.git.gitgitgadget@gmail.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"[PATCH v3 7/8] config: implement --fixed-value with --get*","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-11-25T22:12:55Z","receivedAt":"2020-11-25T22:13:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nThe config builtin does its own regex matching of values for the --get,\n--get-all, and --get-regexp modes. Plumb the existing 'flags' parameter\nto the get_value() method so we can initialize the value-pattern argument\nas a fixed string instead of a regex pattern.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/config.c  | 15 ++++++++++-----\n t/t1300-config.sh | 22 ++++++++++++++++++++++\n 2 files changed, 32 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8049d8f5fc..82e36c595e 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -14,6 +14,7 @@ static const char *const builtin_config_usage[] = {\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@@ -288,6 +289,8 @@ static int collect_config(const char *key_, const char *value_, void *cb)\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+\t\treturn 0;\n \tif (regexp != NULL &&\n \t    (do_not_match ^ !!regexec(regexp, (value_?value_:\"\"), 0, NULL, 0)))\n \t\treturn 0;\n@@ -298,7 +301,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)\n \treturn format_config(&values->items[values->nr++], key_, value_);\n }\n \n-static int get_value(const char *key_, const char *regex_)\n+static int get_value(const char *key_, const char *regex_, unsigned flags)\n {\n \tint ret = CONFIG_GENERIC_ERROR;\n \tstruct strbuf_list values = {NULL};\n@@ -335,7 +338,9 @@ static int get_value(const char *key_, const char *regex_)\n \t\t}\n \t}\n \n-\tif (regex_) {\n+\tif (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))\n+\t\tvalue_pattern = regex_;\n+\telse if (regex_) {\n \t\tif (regex_[0] == '!') {\n \t\t\tdo_not_match = 1;\n \t\t\tregex_++;\n@@ -869,19 +874,19 @@ int cmd_config(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]);\n+\t\treturn 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]);\n+\t\treturn 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]);\n+\t\treturn get_value(argv[0], argv[1], flags);\n \t}\n \telse if (actions == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 413fcbba03..ea38ba9ab1 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2041,4 +2041,26 @@ test_expect_success '--fixed-value uses exact string matching' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--get and --get-all with --fixed-value' '\n+\ttest_when_finished rm -f config &&\n+\tMETA=\"a+b*c?d[e]f.g\" &&\n+\tgit config --file=config fixed.test bogus &&\n+\tgit config --file=config --add fixed.test \"$META\" &&\n+\n+\tgit config --file=config --get fixed.test bogus &&\n+\ttest_must_fail git config --file=config --get fixed.test \"$META\" &&\n+\tgit config --file=config --get --fixed-value fixed.test \"$META\" &&\n+\ttest_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&\n+\n+\tgit config --file=config --get-all fixed.test bogus &&\n+\ttest_must_fail git config --file=config --get-all fixed.test \"$META\" &&\n+\tgit config --file=config --get-all --fixed-value fixed.test \"$META\" &&\n+\ttest_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&\n+\n+\tgit config --file=config --get-regexp fixed+ bogus &&\n+\ttest_must_fail git config --file=config --get-regexp fixed+ \"$META\" &&\n+\tgit config --file=config --get-regexp --fixed-value fixed+ \"$META\" &&\n+\ttest_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"410823","messageId":"CAPig+cRL1FksVbS7jN5s=BDm-Wyx7ABo+h=EC2-3bw7Lkp2WHg@mail.gmail.com","threadId":"54676","inReplyTo":"f135b001ad911c079b0754c61e2179277ce37e0f.1606342377.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 2/8] config: replace 'value_regex' with 'value_pattern'","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2020-11-25T22:50:42Z","receivedAt":"2020-11-25T22:50:56Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Nov 25, 2020 at 5:13 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> For documentation, use 'value-pattern' which is better punctuation. This\n> affects Documentation/git-config.txt and the usage in builtin/config.c,\n> which was already mixed between 'value_regex' and 'value-regex'.\n> [...]\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value-pattern]\n>  'git config' [<file-option>] --rename-section old_name new_name\n\nA mini-project for someone to tackle some day would be to replace the\nremaining underscores with hyphens in the documentation, such as the\nabove \"name_regex\", \"old_name\", \"new_name\". That's outside the scope\nof this series, though, and can easily be done at any time.\n"},{"id":"410825","messageId":"xmqq7dq9ca94.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"pull.796.v3.git.1606342376.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/8] config: add --fixed-value option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-25T23:00:39Z","receivedAt":"2020-11-25T23:01:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> As reported [1], 'git maintenance unregister' fails when a repository is\n> located in a directory with regex glob characters.\n>\n> [1] \n> https://lore.kernel.org/git/2c2db228-069a-947d-8446-89f4d3f6181a@gmail.com/T/#mb96fa4187a0d6aeda097cd95804a8aafc0273022\n>\n> The discussed solution was to modify 'git config' to specify that the\n> 'value_regex' argument should be treated as an exact string match. This is\n> the primary change in this series, with an additional patch at the end to\n> make 'git maintenance [un]register' use this option, when necessary.\n>\n> While we're here, let's rename 'value_regex' to 'value_pattern' to make\n> things a bit clearer.\n>\n> Updates in V3\n> =============\n>\n>  * Renamed 'value_regex' to 'value_pattern' in code and 'value-pattern' in\n>    docs (except po/)\n>    \n>    \n>  * Reordered commits slightly to help with that rename.\n>    \n>    \n>  * Updated tests to use 'test_when_finished rm -f ...'\n>    \n>    \n>  * Changed all references to \"glob\" characters to \"meta\" characters.\n>    \n>    \n>  * Several other test modifications. Thanks, Emily, for the review!\n>    \n>    \n>\n> Thanks, -Stolee\n>\n> P.S. Happy Thanksgiving to those celebrating!\n\nThanks.  The only thing that still stand out, after the first 7\npatches (queued as its own \"add --fixed-value option to config\"\ntopic) are queued, is that the early part of the documentation,\nnamely, a paragraph where it mentions value-pattern, still lives\nin the world where it can only be a regexp.\n\nPerhaps insert the following between step 7 and 8?\n\n--- >8 ------ >8 ------ >8 ------ >8 ------ >8 ------ >8 ---\nSubject: config doc: value-pattern is not necessarily a regexp\n\nThe introductory part of the \"git config --help\" mentions the\noptional value-pattern argument, but give no hint that it can be\nsomething other than a regular expression (worse, it just says\n\"POSIX regexp\", which usually means BRE but the regexp the command\ntakes is ERE).  Also, it needs to be documented that the '!' prefix\nto negate the match, which is only mentioned in this part of the\ndocument, works only with regexp and not with the --fixed-value.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-config.txt | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git c/Documentation/git-config.txt w/Documentation/git-config.txt\nindex 09a1d273a9..0e9351d3cb 100644\n--- c/Documentation/git-config.txt\n+++ w/Documentation/git-config.txt\n@@ -33,10 +33,13 @@ escaped.\n \n Multiple lines can be added to an option by using the `--add` option.\n If you want to update or unset an option which can occur on multiple\n-lines, a POSIX regexp `value-pattern` needs to be given.  Only the\n-existing values that match the regexp are updated or unset.  If\n-you want to handle the lines that do *not* match the regex, just\n-prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n+lines, a `value-pattern` (which is an extended regular expression,\n+unless the `--fixed-value` option is given) needs to be given.  Only the\n+existing values that match the pattern are updated or unset.  If\n+you want to handle the lines that do *not* match the pattern, just\n+prepend a single exclamation mark in front (see also <<EXAMPLES>>),\n+but note that this only works when the `--fixed-value` option is not\n+in use.\n \n The `--type=<type>` option instructs 'git config' to ensure that incoming and\n outgoing values are canonicalize-able under the given <type>.  If no\n"},{"id":"410826","messageId":"CAPig+cTVu4tpvzCyje7_nUbVD0ZdZudzw-SDSm9e8LmOo3w5ng@mail.gmail.com","threadId":"54676","inReplyTo":"0c276ffcee4be302be3db9b43ab8371ef26a0aec.1606342377.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 5/8] config: add --fixed-value option, un-implemented","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2020-11-25T23:04:05Z","receivedAt":"2020-11-25T23:04:43Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Nov 25, 2020 at 5:13 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> [...]\n> Add a new '--fixed-value' option that does not currently change the\n> behavior. The implementation will be filled in by later changes for\n> each appropriate action. For now, check and test that --fixed-value\n> will abort the command when included with an incompatible action or\n> without a 'value-pattern' argument.\n> [...]\n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n> ---\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> @@ -9,15 +9,15 @@ git-config - Get and set repository or global options\n> +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern]\n> +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern]\n> +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern]\n\nDidn't notice this before (since I wasn't paying close attention), but\nportions of the command which are to be filled-in by the user are\nnormally surrounded by angle brackets. Without the angle brackets, the\ninterpretation is that the argument is to be typed literally as shown.\nSo, the above should really be <value-pattern> and <name_regex> (or\neven better <name-regex>), just as other fill-in arguments\n<file-option> and <type> are formatted already.\n\nI'm not suggesting that this series should tackle such a change since\nthat would be straying quite far from the original goal (which, as I\nrecall, was fixing a misbehavior of git-maintenance), but just\npointing out a possible mini-project for someone to tackle at some\npoint.\n\n> +--fixed-value::\n> +       When used with the `value-pattern` argument, treat `value-pattern` as\n> +       an exact string instead of a regular expression. This will restrict\n> +       the name/value pairs that are matched to only those where the value\n> +       is exactly equal to the `value-pattern`.\n\nLikewise, this would also use the angle brackets as `<value-pattern>`\n(or '<value-pattern>' with single quotes rather than backquotes, for\nwhich, I believe, Junio recently stated his preference)...\n\n>  --type <type>::\n>    'git config' will ensure that any input or output is valid under the given\n>    type constraint(s), and will canonicalize outgoing values in `<type>`'s\n\n... just like <type> is shown in angle brackets.\n\n> +       OPT_BOOL(0, \"fixed-value\", &fixed_value, N_(\"use string equality when comparing values to 'value-pattern'\")),\n\nHere, I think omitting angle brackets is common, so this is probably okay.\n"},{"id":"410827","messageId":"xmqq360xc9ur.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"558775f83d7cbf7ad0e2090ab13be404cdf16b24.1606342377.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 8/8] maintenance: use 'git config --fixed-value'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-25T23:09:16Z","receivedAt":"2020-11-25T23:09:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Derrick Stolee <dstolee@microsoft.com>\n>\n> When a repository's leading directories contain regex metacharacters,\n> the config calls for 'git maintenance register' and 'git maintenance\n> unregister' are not careful enough. Use the new --fixed-value option\n> to direct the config machinery to use exact string matches. This is a\n> more robust option than escaping these arguments in a piecemeal fashion.\n>\n> For the test, require that we are not running on Windows since the '+'\n> and '*' characters are not allowed on that filesystem.\n\nThanks.  \n\nI was hoping there would be some byte we could use, but it seems\n\"a[]b\" would not work if we go back beyond VFAT or NTFS.  !MINGW\nit is, then.\n\n"},{"id":"410877","messageId":"94cabd0e-004c-f3fc-315f-f984abce51e6@gmail.com","threadId":"54676","inReplyTo":"xmqq7dq9ca94.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v3 0/8] config: add --fixed-value option","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2020-11-26T11:17:17Z","receivedAt":"2020-11-26T11:17:21Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 11/25/2020 6:00 PM, Junio C Hamano wrote:\n\n> Thanks.  The only thing that still stand out, after the first 7\n> patches (queued as its own \"add --fixed-value option to config\"\n> topic) are queued, is that the early part of the documentation,\n> namely, a paragraph where it mentions value-pattern, still lives\n> in the world where it can only be a regexp.\n> \n> Perhaps insert the following between step 7 and 8?\n\nThis patch LGTM. Thanks!\n\n-Stolee\n \n> --- >8 ------ >8 ------ >8 ------ >8 ------ >8 ------ >8 ---\n> Subject: config doc: value-pattern is not necessarily a regexp\n> \n> The introductory part of the \"git config --help\" mentions the\n> optional value-pattern argument, but give no hint that it can be\n> something other than a regular expression (worse, it just says\n> \"POSIX regexp\", which usually means BRE but the regexp the command\n> takes is ERE).  Also, it needs to be documented that the '!' prefix\n> to negate the match, which is only mentioned in this part of the\n> document, works only with regexp and not with the --fixed-value.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/git-config.txt | 11 +++++++----\n>  1 file changed, 7 insertions(+), 4 deletions(-)\n> \n> diff --git c/Documentation/git-config.txt w/Documentation/git-config.txt\n> index 09a1d273a9..0e9351d3cb 100644\n> --- c/Documentation/git-config.txt\n> +++ w/Documentation/git-config.txt\n> @@ -33,10 +33,13 @@ escaped.\n>  \n>  Multiple lines can be added to an option by using the `--add` option.\n>  If you want to update or unset an option which can occur on multiple\n> -lines, a POSIX regexp `value-pattern` needs to be given.  Only the\n> -existing values that match the regexp are updated or unset.  If\n> -you want to handle the lines that do *not* match the regex, just\n> -prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n> +lines, a `value-pattern` (which is an extended regular expression,\n> +unless the `--fixed-value` option is given) needs to be given.  Only the\n> +existing values that match the pattern are updated or unset.  If\n> +you want to handle the lines that do *not* match the pattern, just\n> +prepend a single exclamation mark in front (see also <<EXAMPLES>>),\n> +but note that this only works when the `--fixed-value` option is not\n> +in use.\n>  \n>  The `--type=<type>` option instructs 'git config' to ensure that incoming and\n>  outgoing values are canonicalize-able under the given <type>.  If no\n> \n\n"},{"id":"411033","messageId":"xmqqy2iirv5p.fsf@gitster.c.googlers.com","threadId":"54676","inReplyTo":"94cabd0e-004c-f3fc-315f-f984abce51e6@gmail.com","subject":"Re: [PATCH v3 0/8] config: add --fixed-value option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T04:45:54Z","receivedAt":"2020-12-01T04:47:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 11/25/2020 6:00 PM, Junio C Hamano wrote:\n>\n>> Thanks.  The only thing that still stand out, after the first 7\n>> patches (queued as its own \"add --fixed-value option to config\"\n>> topic) are queued, is that the early part of the documentation,\n>> namely, a paragraph where it mentions value-pattern, still lives\n>> in the world where it can only be a regexp.\n>> \n>> Perhaps insert the following between step 7 and 8?\n>\n> This patch LGTM. Thanks!\n\nThanks.\n"}]}