config: store "git -c" variables using more robust format

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@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 12, 2021 at 13:27 UTC 1ff21c05ba99ed2d0ade8318e3cb0c1a3f8d4b80
2 files changed +53 -7
config.c
+45 -7
@@ -332,7 +332,7 @@ int git_config_include(const char *var, const char *value, void *data)
332 return ret;
333 }
334
335 -void git_config_push_parameter(const char *text)
335 +static void git_config_push_split_parameter(const char *key, const char *value)
336 {
337 struct strbuf env = STRBUF_INIT;
338 const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
@@ -340,20 +340,60 @@ void git_config_push_parameter(const char *text)
340 strbuf_addstr(&env, old);
341 strbuf_addch(&env, ' ');
342 }
343 - sq_quote_buf(&env, text);
343 + sq_quote_buf(&env, key);
344 + strbuf_addch(&env, '=');
345 + if (value)
346 + sq_quote_buf(&env, value);
347 setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
348 strbuf_release(&env);
349 }
350
351 +void git_config_push_parameter(const char *text)
352 +{
353 + const char *value;
354 +
355 + /*
356 + * When we see:
357 + *
358 + * section.subsection=with=equals.key=value
359 + *
360 + * we cannot tell if it means:
361 + *
362 + * [section "subsection=with=equals"]
363 + * key = value
364 + *
365 + * or:
366 + *
367 + * [section]
368 + * subsection = with=equals.key=value
369 + *
370 + * We parse left-to-right for the first "=", meaning we'll prefer to
371 + * keep the value intact over the subsection. This is historical, but
372 + * also sensible since values are more likely to contain odd or
373 + * untrusted input than a section name.
374 + *
375 + * A missing equals is explicitly allowed (as a bool-only entry).
376 + */
377 + value = strchr(text, '=');
378 + if (value) {
379 + char *key = xmemdupz(text, value - text);
380 + git_config_push_split_parameter(key, value + 1);
381 + free(key);
382 + } else {
383 + git_config_push_split_parameter(text, NULL);
384 + }
385 +}
386 +
387 void git_config_push_env(const char *spec)
388 {
350 - struct strbuf buf = STRBUF_INIT;
389 + char *key;
390 const char *env_name;
391 const char *env_value;
392
393 env_name = strrchr(spec, '=');
394 if (!env_name)
395 die(_("invalid config format: %s"), spec);
396 + key = xmemdupz(spec, env_name - spec);
397 env_name++;
398 if (!*env_name)
399 die(_("missing environment variable name for configuration '%.*s'"),
@@ -364,10 +404,8 @@ void git_config_push_env(const char *spec)
404 die(_("missing environment variable '%s' for configuration '%.*s'"),
405 env_name, (int)(env_name - spec - 1), spec);
406
367 - strbuf_add(&buf, spec, env_name - spec);
368 - strbuf_addstr(&buf, env_value);
369 - git_config_push_parameter(buf.buf);
370 - strbuf_release(&buf);
407 + git_config_push_split_parameter(key, env_value);
408 + free(key);
409 }
410
411 static inline int iskeychar(int c)
t/t1300-config.sh
+8
@@ -1416,6 +1416,14 @@ test_expect_success 'git -c and --config-env override each other' '
1416 test_cmp expect actual
1417 '
1418
1419 +test_expect_success '--config-env handles keys with equals' '
1420 + echo value=with=equals >expect &&
1421 + ENVVAR=value=with=equals git \
1422 + --config-env=section.subsection=with=equals.key=ENVVAR \
1423 + config section.subsection=with=equals.key >actual &&
1424 + test_cmp expect actual
1425 +'
1426 +
1427 test_expect_success 'git config --edit works' '
1428 git config -f tmp test.value no &&
1429 echo test.value=yes >expect &&