On Wed, Dec 09 2020, Jeff King wrote: > The previous commit added a new format for $GIT_CONFIG_PARAMETERS which > is able to robustly handle subsections with "=" in them. Let's start > writing the new format. Unfortunately, this does much less than you'd > hope, because "git -c" itself has the same ambiguity problem! But it's > still worth doing: > > - we've now pushed the problem from the inter-process communication > into the "-c" command-line parser. This would free us up to later > add an unambiguous format there (e.g., separate arguments like "git > --config key value", etc). > > - for --config-env, the parser already disallows "=" in the > environment variable name. So: > > git --config-env section.with=equals.key=ENVVAR > > will robustly set section.with=equals.key to the contents of > $ENVVAR. > > The new test shows the improvement for --config-env. > > Signed-off-by: Jeff King <peff@xxxxxxxx> > --- > One other side effect I just noticed is that we're very aggressive about > trimming leading and trailing whitespace in the old-style format, but > the new one will store values verbatim. IMHO that's better overall, but > we might consider a preparatory patch to remove that trimming > explicitly. > > config.c | 52 ++++++++++++++++++++++++++++++++++++++++------- > t/t1300-config.sh | 8 ++++++++ > 2 files changed, 53 insertions(+), 7 deletions(-) > > diff --git a/config.c b/config.c > index fb160c33d2..04029e45dc 100644 > --- a/config.c > +++ b/config.c > @@ -333,38 +333,76 @@ int git_config_include(const char *var, const char *value, void *data) > return ret; > } > > -void git_config_push_parameter(const char *text) > +static void git_config_push_split_parameter(const char *key, const char *value) > { > struct strbuf env = STRBUF_INIT; > const char *old = getenv(CONFIG_DATA_ENVIRONMENT); > if (old && *old) { > strbuf_addstr(&env, old); > strbuf_addch(&env, ' '); > } > - sq_quote_buf(&env, text); > + sq_quote_buf(&env, key); > + strbuf_addch(&env, '='); > + if (value) > + sq_quote_buf(&env, value); > setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1); > strbuf_release(&env); > } > > +void git_config_push_parameter(const char *text) > +{ > + const char *value; > + > + /* > + * When we see: > + * > + * section.subsection=with=equals.key=value > + * > + * we cannot tell if it means: > + * > + * [section "subsection=with=equals"] > + * key = value > + * > + * or: > + * > + * [section] > + * subsection = with=equals.key=value > + * > + * We parse left-to-right for the first "=", meaning we'll prefer to > + * keep the value intact over the subsection. This is historical, but > + * also sensible since values are more likely to contain odd or > + * untrusted input than a section name. > + * > + * A missing equals is explicitly allowed (as a bool-only entry). > + */ > + value = strchr(text, '='); > + if (value) { > + char *key = xmemdupz(text, value - text); > + git_config_push_split_parameter(key, value + 1); > + free(key); > + } else { > + git_config_push_split_parameter(text, NULL); > + } > +} > + > void git_config_push_env(const char *spec) > { > - struct strbuf buf = STRBUF_INIT; > + char *key; > const char *env_name; > const char *env_value; > > env_name = strrchr(spec, '='); > if (!env_name) > die("invalid config format: %s", spec); > + key = xmemdupz(spec, env_name - spec); > env_name++; > > env_value = getenv(env_name); > if (!env_value) > die("config variable missing for '%s'", env_name); > > - strbuf_add(&buf, spec, env_name - spec); > - strbuf_addstr(&buf, env_value); > - git_config_push_parameter(buf.buf); > - strbuf_release(&buf); > + git_config_push_split_parameter(key, env_value); > + free(key); > } > > static inline int iskeychar(int c) > diff --git a/t/t1300-config.sh b/t/t1300-config.sh > index bd602e7720..e06961767f 100755 > --- a/t/t1300-config.sh > +++ b/t/t1300-config.sh > @@ -1413,6 +1413,14 @@ test_expect_success 'git -c and --config-env override each other' ' > test_cmp expect actual > ' > > +test_expect_success '--config-env handles keys with equals' ' > + echo value=with=equals >expect && > + ENVVAR=value=with=equals git \ > + --config-env=section.subsection=with=equals.key=ENVVAR \ > + config section.subsection=with=equals.key >actual && > + test_cmp expect actual > +' > + Maybe worth adding a test for the strrchr() semantics here with: perl -we '$ENV{"Y=Z"}="why and zed"; system "Z=zed git --config-env=X=Y=Z ..."' Which would show that we can't look up "Y=Z", but will always get "Z". I think that's fine b.t.w., 1/2 of the minor objection I had to --config-env in https://lore.kernel.org/git/87y2i7vvz4.fsf@xxxxxxxxxxxxxxxxxxx/ was mainly about being unable to e.g. support odd token usernames with the "insteadOf" feature. But aside from having a feature meant to improve security being able to be combined with a config variable we have in a way that leaks the password in ps(1) I think these improved semantics make sense. I.e. I can't imagine someone wants an env var with "=" in it, even though POSIX makes such a thing possible (you just can't do it in a shellscript).