From 504ee1290e38fb1ff0d76f940b124e21ab57a99f Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Wed, 25 Nov 2020 22:12:49 +0000 Subject: config: convert multi_replace to flags We will extend the flexibility of the config API. Before doing so, let's take an existing 'int multi_replace' parameter and replace it with a new 'unsigned flags' parameter that can take multiple options as a bit field. Update all callers that specified multi_replace to now specify the CONFIG_FLAGS_MULTI_REPLACE flag. To add more clarity, extend the documentation of git_config_set_multivar_in_file() including a clear labeling of its arguments. Other config API methods in config.h require only a change of the final parameter from 'int' to 'unsigned'. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- config.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 2bdff4457b..b5e8ab13b3 100644 --- a/config.c +++ b/config.c @@ -2729,9 +2729,9 @@ void git_config_set(const char *key, const char *value) * if value_regex!=NULL, disregard key/value pairs where value does not match. * if value_regex==CONFIG_REGEX_NONE, do not match any existing values * (only add a new one) - * if multi_replace==0, nothing, or only one matching key/value is replaced, - * else all matching key/values (regardless how many) are removed, - * before the new pair is written. + * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching + * key/values are removed before a single new pair is written. If the + * flag is not present, then replace only the first match. * * Returns 0 on success. * @@ -2752,7 +2752,7 @@ void git_config_set(const char *key, const char *value) int git_config_set_multivar_in_file_gently(const char *config_filename, const char *key, const char *value, const char *value_regex, - int multi_replace) + unsigned flags) { int fd = -1, in_fd = -1; int ret; @@ -2769,7 +2769,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, if (ret) goto out_free; - store.multi_replace = multi_replace; + store.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0; if (!config_filename) config_filename = filename_buf = git_pathdup("config"); @@ -2858,7 +2858,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, /* if nothing to unset, or too many matches, error out */ if ((store.seen_nr == 0 && value == NULL) || - (store.seen_nr > 1 && multi_replace == 0)) { + (store.seen_nr > 1 && !store.multi_replace)) { ret = CONFIG_NOTHING_SET; goto out_free; } @@ -2997,10 +2997,10 @@ write_err_out: void git_config_set_multivar_in_file(const char *config_filename, const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_regex, unsigned flags) { if (!git_config_set_multivar_in_file_gently(config_filename, key, value, - value_regex, multi_replace)) + value_regex, flags)) return; if (value) die(_("could not set '%s' to '%s'"), key, value); @@ -3009,17 +3009,17 @@ void git_config_set_multivar_in_file(const char *config_filename, } int git_config_set_multivar_gently(const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_regex, unsigned flags) { return git_config_set_multivar_in_file_gently(NULL, key, value, value_regex, - multi_replace); + flags); } void git_config_set_multivar(const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_regex, unsigned flags) { git_config_set_multivar_in_file(NULL, key, value, value_regex, - multi_replace); + flags); } static int section_name_match (const char *buf, const char *name) -- cgit v1.3-5-g9baa From 247e2f822e6ccdccaa19ca1a54d4082ce5d819e7 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Wed, 25 Nov 2020 22:12:50 +0000 Subject: config: replace 'value_regex' with 'value_pattern' The 'value_regex' argument in the 'git config' builtin is poorly named, especially related to an upcoming change that allows exact string matches instead of ERE pattern matches. Perform a mostly mechanical change of every instance of 'value_regex' to 'value_pattern' in the codebase. This is only critical for documentation and error messages, but it is best to be consistent inside the codebase, too. For documentation, use 'value-pattern' which is better punctuation. This affects Documentation/git-config.txt and the usage in builtin/config.c, which was already mixed between 'value_regex' and 'value-regex'. I gave some thought to leaving the value_regex variables inside config.c that are regex_t pointers. However, it is probably best to keep the name consistent with the rest of the variables. This does not update the translations inside the po/ directory, as that creates conflicts with ongoing work. The input strings should automatically update through automation, and a few of the output strings currently use "[value_regex]" directly. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 20 ++++++++-------- builtin/config.c | 12 +++++----- config.c | 54 ++++++++++++++++++++++---------------------- config.h | 2 +- 4 files changed, 44 insertions(+), 44 deletions(-) (limited to 'config.c') diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7573160f21..0be5499952 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,15 +9,15 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]] 'git config' [] [--type=] --add name value -'git config' [] [--type=] --replace-all name value [value_regex] -'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex] -'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex] -'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] +'git config' [] [--type=] --replace-all name value [value-pattern] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get name [value-pattern] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] --get-all name [value-pattern] +'git config' [] [--type=] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value-pattern] 'git config' [] [--type=] [-z|--null] --get-urlmatch name URL -'git config' [] --unset name [value_regex] -'git config' [] --unset-all name [value_regex] +'git config' [] --unset name [value-pattern] +'git config' [] --unset-all name [value-pattern] 'git config' [] --rename-section old_name new_name 'git config' [] --remove-section name 'git config' [] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list @@ -33,7 +33,7 @@ escaped. Multiple lines can be added to an option by using the `--add` option. If you want to update or unset an option which can occur on multiple -lines, a POSIX regexp `value_regex` needs to be given. Only the +lines, a POSIX regexp `value-pattern` needs to be given. Only the existing values that match the regexp are updated or unset. If you want to handle the lines that do *not* match the regex, just prepend a single exclamation mark in front (see also <>). @@ -73,11 +73,11 @@ OPTIONS --replace-all:: Default behavior is to replace at most one line. This replaces - all lines matching the key (and optionally the value_regex). + all lines matching the key (and optionally the `value-pattern`). --add:: Adds a new line to the option without altering any existing - values. This is the same as providing '^$' as the value_regex + values. This is the same as providing '^$' as the `value-pattern` in `--replace-all`. --get:: diff --git a/builtin/config.c b/builtin/config.c index 7c15c5d743..590392c811 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -133,14 +133,14 @@ static struct option builtin_config_options[] = { OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), - OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), - OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), - OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP), + OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET), + OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL), + OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP), OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), - OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL), + OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL), OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), - OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET), - OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL), + OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET), + OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL), OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST), diff --git a/config.c b/config.c index b5e8ab13b3..5b2f00f073 100644 --- a/config.c +++ b/config.c @@ -2415,7 +2415,7 @@ struct config_store_data { size_t baselen; char *key; int do_not_match; - regex_t *value_regex; + regex_t *value_pattern; int multi_replace; struct { size_t begin, end; @@ -2429,10 +2429,10 @@ struct config_store_data { static void config_store_data_clear(struct config_store_data *store) { free(store->key); - if (store->value_regex != NULL && - store->value_regex != CONFIG_REGEX_NONE) { - regfree(store->value_regex); - free(store->value_regex); + if (store->value_pattern != NULL && + store->value_pattern != CONFIG_REGEX_NONE) { + regfree(store->value_pattern); + free(store->value_pattern); } free(store->parsed); free(store->seen); @@ -2444,13 +2444,13 @@ static int matches(const char *key, const char *value, { if (strcmp(key, store->key)) return 0; /* not ours */ - if (!store->value_regex) + if (!store->value_pattern) return 1; /* always matches */ - if (store->value_regex == CONFIG_REGEX_NONE) + if (store->value_pattern == CONFIG_REGEX_NONE) return 0; /* never matches */ return store->do_not_match ^ - (value && !regexec(store->value_regex, value, 0, NULL, 0)); + (value && !regexec(store->value_pattern, value, 0, NULL, 0)); } static int store_aux_event(enum config_event_t type, @@ -2726,8 +2726,8 @@ void git_config_set(const char *key, const char *value) /* * If value==NULL, unset in (remove from) config, - * if value_regex!=NULL, disregard key/value pairs where value does not match. - * if value_regex==CONFIG_REGEX_NONE, do not match any existing values + * if value_pattern!=NULL, disregard key/value pairs where value does not match. + * if value_pattern==CONFIG_REGEX_NONE, do not match any existing values * (only add a new one) * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching * key/values are removed before a single new pair is written. If the @@ -2751,7 +2751,7 @@ void git_config_set(const char *key, const char *value) */ int git_config_set_multivar_in_file_gently(const char *config_filename, const char *key, const char *value, - const char *value_regex, + const char *value_pattern, unsigned flags) { int fd = -1, in_fd = -1; @@ -2812,22 +2812,22 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, int i, new_line = 0; struct config_options opts; - if (value_regex == NULL) - store.value_regex = NULL; - else if (value_regex == CONFIG_REGEX_NONE) - store.value_regex = CONFIG_REGEX_NONE; + if (value_pattern == NULL) + store.value_pattern = NULL; + else if (value_pattern == CONFIG_REGEX_NONE) + store.value_pattern = CONFIG_REGEX_NONE; else { - if (value_regex[0] == '!') { + if (value_pattern[0] == '!') { store.do_not_match = 1; - value_regex++; + value_pattern++; } else store.do_not_match = 0; - store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(store.value_regex, value_regex, + store.value_pattern = (regex_t*)xmalloc(sizeof(regex_t)); + if (regcomp(store.value_pattern, value_pattern, REG_EXTENDED)) { - error(_("invalid pattern: %s"), value_regex); - FREE_AND_NULL(store.value_regex); + error(_("invalid pattern: %s"), value_pattern); + FREE_AND_NULL(store.value_pattern); ret = CONFIG_INVALID_PATTERN; goto out_free; } @@ -2997,10 +2997,10 @@ write_err_out: void git_config_set_multivar_in_file(const char *config_filename, const char *key, const char *value, - const char *value_regex, unsigned flags) + const char *value_pattern, unsigned flags) { if (!git_config_set_multivar_in_file_gently(config_filename, key, value, - value_regex, flags)) + value_pattern, flags)) return; if (value) die(_("could not set '%s' to '%s'"), key, value); @@ -3009,16 +3009,16 @@ void git_config_set_multivar_in_file(const char *config_filename, } int git_config_set_multivar_gently(const char *key, const char *value, - const char *value_regex, unsigned flags) + const char *value_pattern, unsigned flags) { - return git_config_set_multivar_in_file_gently(NULL, key, value, value_regex, + return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern, flags); } void git_config_set_multivar(const char *key, const char *value, - const char *value_regex, unsigned flags) + const char *value_pattern, unsigned flags) { - git_config_set_multivar_in_file(NULL, key, value, value_regex, + git_config_set_multivar_in_file(NULL, key, value, value_pattern, flags); } diff --git a/config.h b/config.h index 84fdf223c8..7535b1f856 100644 --- a/config.h +++ b/config.h @@ -296,7 +296,7 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha void git_config_set_multivar_in_file(const char *config_filename, const char *key, const char *value, - const char *value_regex, + const char *value_pattern, unsigned flags); /** -- cgit v1.3-5-g9baa From c90702a1f6f7473a959994a2dff0f0dd450b8029 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Wed, 25 Nov 2020 22:12:54 +0000 Subject: config: plumb --fixed-value into config API The git_config_set_multivar_in_file_gently() and related methods now take a 'flags' bitfield, so add a new bit representing the --fixed-value option from 'git config'. This alters the purpose of the value_pattern parameter to be an exact string match. This requires some initialization changes in git_config_set_multivar_in_file_gently() and a new strcmp() call in the matches() method. The new CONFIG_FLAGS_FIXED_VALUE flag is initialized in builtin/config.c based on the --fixed-value option, and that needs to be updated in several callers. This patch only affects some of the modes of 'git config', and the rest will be completed in the next change. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- builtin/config.c | 16 +++++++++++----- config.c | 5 +++++ config.h | 7 +++++++ t/t1300-config.sh | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) (limited to 'config.c') diff --git a/builtin/config.c b/builtin/config.c index f1b73ac8b5..21892a784c 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -633,6 +633,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = !startup_info->have_repository; char *value; + int flags = 0; given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); @@ -800,6 +801,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) error(_("--fixed-value only applies with 'value-pattern'")); usage_builtin_config(); } + + flags |= CONFIG_FLAGS_FIXED_VALUE; } if (actions & PAGING_ACTIONS) @@ -863,7 +866,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) value = normalize_value(argv[0], argv[1]); UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], 0); + argv[0], value, argv[2], + flags); } else if (actions == ACTION_ADD) { check_write(); @@ -872,7 +876,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, - CONFIG_REGEX_NONE, 0); + CONFIG_REGEX_NONE, + flags); } else if (actions == ACTION_REPLACE_ALL) { check_write(); @@ -881,7 +886,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, argv[2], - CONFIG_FLAGS_MULTI_REPLACE); + flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); @@ -908,7 +913,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 1, 2); if (argc == 2) return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], 0); + argv[0], NULL, argv[1], + flags); else return git_config_set_in_file_gently(given_config_source.file, argv[0], NULL); @@ -918,7 +924,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 1, 2); return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], NULL, argv[1], - CONFIG_FLAGS_MULTI_REPLACE); + flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { int ret; diff --git a/config.c b/config.c index 5b2f00f073..3b2edc0faf 100644 --- a/config.c +++ b/config.c @@ -2415,6 +2415,7 @@ struct config_store_data { size_t baselen; char *key; int do_not_match; + const char *fixed_value; regex_t *value_pattern; int multi_replace; struct { @@ -2444,6 +2445,8 @@ static int matches(const char *key, const char *value, { if (strcmp(key, store->key)) return 0; /* not ours */ + if (store->fixed_value) + return !strcmp(store->fixed_value, value); if (!store->value_pattern) return 1; /* always matches */ if (store->value_pattern == CONFIG_REGEX_NONE) @@ -2816,6 +2819,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, store.value_pattern = NULL; else if (value_pattern == CONFIG_REGEX_NONE) store.value_pattern = CONFIG_REGEX_NONE; + else if (flags & CONFIG_FLAGS_FIXED_VALUE) + store.fixed_value = value_pattern; else { if (value_pattern[0] == '!') { store.do_not_match = 1; diff --git a/config.h b/config.h index 7535b1f856..c1449bb790 100644 --- a/config.h +++ b/config.h @@ -269,6 +269,13 @@ int git_config_key_is_valid(const char *key); */ #define CONFIG_FLAGS_MULTI_REPLACE (1 << 0) +/* + * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs + * by string comparison (not regex match) to the provided value_pattern + * parameter. + */ +#define CONFIG_FLAGS_FIXED_VALUE (1 << 1) + int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned); void git_config_set_multivar(const char *, const char *, const char *, unsigned); int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned); diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 841ed204d6..4293ba22af 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1994,4 +1994,54 @@ test_expect_success 'refuse --fixed-value for incompatible actions' ' test_must_fail git config --file=config --fixed-value --unset-all dev.null ' +test_expect_success '--fixed-value uses exact string matching' ' + test_when_finished rm -f config initial && + META="a+b*c?d[e]f.g" && + git config --file=initial fixed.test "$META" && + + cp initial config && + git config --file=config fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=$META + fixed.test=bogus + EOF + test_cmp expect actual && + + cp initial config && + git config --file=config --fixed-value fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-\EOF && + fixed.test=bogus + EOF + test_cmp expect actual && + + cp initial config && + test_must_fail git config --file=config --unset fixed.test "$META" && + git config --file=config --fixed-value --unset fixed.test "$META" && + test_must_fail git config --file=config fixed.test && + + cp initial config && + test_must_fail git config --file=config --unset-all fixed.test "$META" && + git config --file=config --fixed-value --unset-all fixed.test "$META" && + test_must_fail git config --file=config fixed.test && + + cp initial config && + git config --file=config --replace-all fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=$META + fixed.test=bogus + EOF + test_cmp expect actual && + + git config --file=config --fixed-value --replace-all fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=bogus + fixed.test=bogus + EOF + test_cmp expect actual +' + test_done -- cgit v1.3-5-g9baa