diff mbox series

[v2,6/7] config: implement --fixed-value with --get*

Message ID 8e0111c7b4b2c766c61df30c4ae93bd2d724de06.1606147507.git.gitgitgadget@gmail.com (mailing list archive)
State Superseded
Headers show
Series config: add --fixed-value option | expand

Commit Message

Derrick Stolee Nov. 23, 2020, 4:05 p.m. UTC
From: Derrick Stolee <dstolee@microsoft.com>

The config builtin does its own regex matching of values for the --get,
--get-all, and --get-regexp modes. Plumb the existing 'flags' parameter
to the get_value() method so we can initialize the value_regex argument
as a fixed string instead of a regex pattern.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
---
 builtin/config.c  | 15 ++++++++++-----
 t/t1300-config.sh | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+), 5 deletions(-)

Comments

Junio C Hamano Nov. 23, 2020, 7:53 p.m. UTC | #1
"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> -	if (regex_) {
> +	if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
> +		value_regex = regex_;
> +	else if (regex_) {
>  		if (regex_[0] == '!') {
>  			do_not_match = 1;
>  			regex_++;

When the --fixed-value option is in effect, there is no way to say
"replace values that do not match this string", because unlike the
regex case, we do not special case '!' at the beginning.

I think it is a good design decision.  The special case has an
escape hatch to start your value_regex with "[!]" when ERE is in
use, but there is no such escape hatch possible with --fixed-value.

Depending on how this '!' negation is documented to the end-users
for existing value_regex that is ERE, the documentation for the new
option may want to talk about the lack of negation explicitly.

	... goes and looks ...

It is worse than I thought.  Here is what "git config" description
has (and some of the badness is not the fault of this series):

    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
    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 <<EXAMPLES>>).

The end-users these days no longer see "lines" because they do not
hand edit .git/config, so we may need a replacement phrase to refer
to a single "vari.able=value" setting, but we should leave it out of
the scope of this series to clean that up---"git config --help" is
full of references to "line".

It was not "a POSIX regexp" (which does not say if it is BRE or
ERE), and it no longer is a regexp with the new option.

    ... which can occur on multiple lines, a pattern `value_regex`
    needs to be given.  Only the existing values that match the
    pattern are updated or unset.  The pattern is by default matched
    as an extended regular expression, but with the `--fixed-value`
    option, taken as a literal string and values must be identical
    to it to be considered a match.  You can prepend an exclamation
    point '!' to affect lines that do *not* match the pattern, but
    this is applicable only when not using the `--fixed-value`
    option.

Ideally we probably should do s/value_regex/value_pattern/ in the
documentation and error messages eventually, but I do not know if
it is warranted in the scope of this series.
Emily Shaffer Nov. 23, 2020, 10:43 p.m. UTC | #2
On Mon, Nov 23, 2020 at 04:05:06PM +0000, Derrick Stolee via GitGitGadget wrote:
> 

Allowing myself a sensible chuckle at the commit subject using glob
syntax on a series about regex matching. ;)

> 
> The config builtin does its own regex matching of values for the --get,
> --get-all, and --get-regexp modes. Plumb the existing 'flags' parameter
> to the get_value() method so we can initialize the value_regex argument
> as a fixed string instead of a regex pattern.
> 
> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
> ---
>  builtin/config.c  | 15 ++++++++++-----
>  t/t1300-config.sh | 22 ++++++++++++++++++++++
>  2 files changed, 32 insertions(+), 5 deletions(-)
> 
> diff --git a/builtin/config.c b/builtin/config.c
> index 3e49e04411..d3772b5efe 100644
> --- a/builtin/config.c
> +++ b/builtin/config.c
>  	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
>  		return 0;
> +	if (fixed_value && strcmp(value_regex, (value_?value_:"")))
> +		return 0;
Ooh, I can see you're matching style, but the combination of the
spaceless ternary and the trailing underscore is making me so itchy ;)

>  	if (regexp != NULL &&
>  	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
>  		return 0;
> @@ -298,7 +301,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)
>  	return format_config(&values->items[values->nr++], key_, value_);
>  }
>  
> -static int get_value(const char *key_, const char *regex_)
> +static int get_value(const char *key_, const char *regex_, int flags)
Very reasonable, and I appreciate passing 'flags' instead of passing
'fixed' here. Room for growth :)

[snip] a bunch of straightforward plumbing-through.

> +test_expect_success '--get and --get-all with --fixed-value' '
> +	GLOB="a+b*c?d[e]f.g" &&
> +	rm -f config &&
Again this tells me that your other tests want 'test_when_finished'
instead.

> +	git config --file=config fixed.test bogus &&
> +	git config --file=config --add fixed.test "$GLOB" &&
> +
> +	git config --file=config --get fixed.test bogus &&
> +	test_must_fail git config --file=config --get fixed.test "$GLOB" &&
> +	git config --file=config --get --fixed-value fixed.test "$GLOB" &&
> +	test_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&
> +
> +	git config --file=config --get-all fixed.test bogus &&
> +	test_must_fail git config --file=config --get-all fixed.test "$GLOB" &&
> +	git config --file=config --get-all --fixed-value fixed.test "$GLOB" &&
> +	test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&
> +
> +	git config --file=config --get-regexp fixed+ bogus &&
> +	test_must_fail git config --file=config --get-regexp fixed+ "$GLOB" &&
> +	git config --file=config --get-regexp --fixed-value fixed+ "$GLOB" &&
> +	test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
> +'

Otherwise the test seems fine to me, although I wouldn't yell if it grew
some comments :)

With the exception of the 'test_when_finished', which might not even
match style (I didn't look):

Reviewed-by: Emily Shaffer <emilyshaffer@google.com>
diff mbox series

Patch

diff --git a/builtin/config.c b/builtin/config.c
index 3e49e04411..d3772b5efe 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -14,6 +14,7 @@  static const char *const builtin_config_usage[] = {
 
 static char *key;
 static regex_t *key_regexp;
+static const char *value_regex;
 static regex_t *regexp;
 static int show_keys;
 static int omit_values;
@@ -288,6 +289,8 @@  static int collect_config(const char *key_, const char *value_, void *cb)
 		return 0;
 	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
 		return 0;
+	if (fixed_value && strcmp(value_regex, (value_?value_:"")))
+		return 0;
 	if (regexp != NULL &&
 	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
 		return 0;
@@ -298,7 +301,7 @@  static int collect_config(const char *key_, const char *value_, void *cb)
 	return format_config(&values->items[values->nr++], key_, value_);
 }
 
-static int get_value(const char *key_, const char *regex_)
+static int get_value(const char *key_, const char *regex_, int flags)
 {
 	int ret = CONFIG_GENERIC_ERROR;
 	struct strbuf_list values = {NULL};
@@ -335,7 +338,9 @@  static int get_value(const char *key_, const char *regex_)
 		}
 	}
 
-	if (regex_) {
+	if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
+		value_regex = regex_;
+	else if (regex_) {
 		if (regex_[0] == '!') {
 			do_not_match = 1;
 			regex_++;
@@ -859,19 +864,19 @@  int cmd_config(int argc, const char **argv, const char *prefix)
 	}
 	else if (actions == ACTION_GET) {
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
+		return get_value(argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_ALL) {
 		do_all = 1;
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
+		return get_value(argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_REGEXP) {
 		show_keys = 1;
 		use_key_regexp = 1;
 		do_all = 1;
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1]);
+		return get_value(argv[0], argv[1], flags);
 	}
 	else if (actions == ACTION_GET_URLMATCH) {
 		check_argc(argc, 2, 2);
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 30e80ae9cb..13211b6bf4 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2040,4 +2040,26 @@  test_expect_success '--fixed-value uses exact string matching' '
 	test_cmp expect actual
 '
 
+test_expect_success '--get and --get-all with --fixed-value' '
+	GLOB="a+b*c?d[e]f.g" &&
+	rm -f config &&
+	git config --file=config fixed.test bogus &&
+	git config --file=config --add fixed.test "$GLOB" &&
+
+	git config --file=config --get fixed.test bogus &&
+	test_must_fail git config --file=config --get fixed.test "$GLOB" &&
+	git config --file=config --get --fixed-value fixed.test "$GLOB" &&
+	test_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&
+
+	git config --file=config --get-all fixed.test bogus &&
+	test_must_fail git config --file=config --get-all fixed.test "$GLOB" &&
+	git config --file=config --get-all --fixed-value fixed.test "$GLOB" &&
+	test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&
+
+	git config --file=config --get-regexp fixed+ bogus &&
+	test_must_fail git config --file=config --get-regexp fixed+ "$GLOB" &&
+	git config --file=config --get-regexp --fixed-value fixed+ "$GLOB" &&
+	test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
+'
+
 test_done