git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v5 6/8] config: parse more robust format in GIT_CONFIG_PARAMETERS

From
PWPhillip Wood <phillip.wood123@gmail.com>
Date
Dec 16, 2020, 20:01 UTC
Message-ID
<ccb476b8-9835-3810-c272-b74822fe74eb@gmail.com>
In-Reply-To
<d832f3dedf5bde4cd9389ddab734703ff2dbd5a1.1608104755.git.ps@pks.im>
Hi Patrick/Peff
On 16/12/2020 07:57, Patrick Steinhardt wrote:
Show 27 quoted lines
> From: Jeff King <peff@peff.net>
> 
> When we stuff config options into GIT_CONFIG_PARAMETERS, we shell-quote
> each one as a single unit, like:
> 
>    'section.one=value1' 'section.two=value2'
> 
> On the reading side, we de-quote to get the individual strings, and then
> parse them by splitting on the first "=" we find. This format is
> ambiguous, because an "=" may appear in a subsection. So the config
> represented in a file by both:
> 
>    [section "subsection=with=equals"]
>    key = value
> 
> and:
> 
>    [section]
>    subsection = with=equals.key=value
> 
> ends up in this flattened format like:
> 
>    'section.subsection=with=equals.key=value'
> 
> and we can't tell which was desired. We have traditionally resolved this
> by taking the first "=" we see starting from the left, meaning that we
> allowed arbitrary content in the value, but not in the subsection.

I was just wondering what happens if a subsection name contains a single quote - can we handle that now and how is it affected by this change?

Best Wishes
Phillip
Show 240 quoted lines
> Let's make our environment format a bit more robust by separately
> quoting the key and value. That turns those examples into:
> 
>    'section.subsection=with=equals.key'='value'
> 
> and:
> 
>    'section.subsection'='with=equals.key=value'
> 
> respectively, and we can tell the difference between them. We can detect
> which format is in use for any given element of the list based on the
> presence of the unquoted "=". That means we can continue to allow the
> old format to work to support any callers which manually used the old
> format, and we can even intermingle the two formats. The old format
> wasn't documented, and nobody was supposed to be using it. But it's
> likely that such callers exist in the wild, so it's nice if we can avoid
> breaking them. Likewise, it may be possible to trigger an older version
> of "git -c" that runs a script that calls into a newer version of "git
> -c"; that new version would see the intermingled format.
> 
> This does create one complication, which is that the obvious format in
> the new scheme for
> 
>    [section]
>    some-bool
> 
> is:
> 
>    'section.some-bool'
> 
> with no equals. We'd mistake that for an old-style variable. And it even
> has the same meaning in the old style, but:
> 
>    [section "with=equals"]
>    some-bool
> 
> does not. It would be:
> 
>    'section.with=equals=some-bool'
> 
> which we'd take to mean:
> 
>    [section]
>    with = equals=some-bool
> 
> in the old, ambiguous style. Likewise, we can't use:
> 
>    'section.some-bool'=''
> 
> because that's ambiguous with an actual empty string. Instead, we'll
> again use the shell-quoting to give us a hint, and use:
> 
>    'section.some-bool'=
> 
> to show that we have no value.
> 
> Note that this commit just expands the reading side. We'll start writing
> the new format via "git -c" in a future patch. In the meantime, the
> existing "git -c" tests will make sure we didn't break reading the old
> format. But we'll also add some explicit coverage of the two formats to
> make sure we continue to handle the old one after we move the writing
> side over.
> 
> And one final note: since we're now using the shell-quoting as a
> semantically meaningful hint, this closes the door to us ever allowing
> arbitrary shell quoting, like:
> 
>    'a'shell'would'be'ok'with'this'.key=value
> 
> But we have never supported that (only what sq_quote() would produce),
> and we are probably better off keeping things simple, robust, and
> backwards-compatible, than trying to make it easier for humans. We'll
> continue not to advertise the format of the variable to users, and
> instead keep "git -c" as the recommended mechanism for setting config
> (even if we are trying to be kind not to break users who may be relying
> on the current undocumented format).
> 
> Signed-off-by: Jeff King <peff@peff.net>
> ---
>   config.c          | 69 +++++++++++++++++++++++++++++++++++------------
>   t/t1300-config.sh | 52 +++++++++++++++++++++++++++++++++++
>   2 files changed, 104 insertions(+), 17 deletions(-)
> 
> diff --git a/config.c b/config.c
> index 53ed048689..60a7261807 100644
> --- a/config.c
> +++ b/config.c
> @@ -541,14 +541,62 @@ int git_config_parse_parameter(const char *text,
>   	return ret;
>   }
>   
> +static int parse_config_env_list(char *env, config_fn_t fn, void *data)
> +{
> +	char *cur = env;
> +	while (cur && *cur) {
> +		const char *key = sq_dequote_step(cur, &cur);
> +		if (!key)
> +			return error(_("bogus format in %s"),
> +				     CONFIG_DATA_ENVIRONMENT);
> +
> +		if (!cur || isspace(*cur)) {
> +			/* old-style 'key=value' */
> +			if (git_config_parse_parameter(key, fn, data) < 0)
> +				return -1;
> +		}
> +		else if (*cur == '=') {
> +			/* new-style 'key'='value' */
> +			const char *value;
> +
> +			cur++;
> +			if (*cur == '\'') {
> +				/* quoted value */
> +				value = sq_dequote_step(cur, &cur);
> +				if (!value || (cur && !isspace(*cur))) {
> +					return error(_("bogus format in %s"),
> +						     CONFIG_DATA_ENVIRONMENT);
> +				}
> +			} else if (!*cur || isspace(*cur)) {
> +				/* implicit bool: 'key'= */
> +				value = NULL;
> +			} else {
> +				return error(_("bogus format in %s"),
> +					     CONFIG_DATA_ENVIRONMENT);
> +			}
> +
> +			if (config_parse_pair(key, value, fn, data) < 0)
> +				return -1;
> +		}
> +		else {
> +			/* unknown format */
> +			return error(_("bogus format in %s"),
> +				     CONFIG_DATA_ENVIRONMENT);
> +		}
> +
> +		if (cur) {
> +			while (isspace(*cur))
> +				cur++;
> +		}
> +	}
> +	return 0;
> +}
> +
>   int git_config_from_parameters(config_fn_t fn, void *data)
>   {
>   	const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
>   	int ret = 0;
>   	char *envw;
> -	const char **argv = NULL;
> -	int nr = 0, alloc = 0;
> -	int i;
>   	struct config_source source;
>   
>   	if (!env)
> @@ -561,21 +609,8 @@ int git_config_from_parameters(config_fn_t fn, void *data)
>   
>   	/* sq_dequote will write over it */
>   	envw = xstrdup(env);
> +	ret = parse_config_env_list(envw, fn, data);
>   
> -	if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
> -		ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT);
> -		goto out;
> -	}
> -
> -	for (i = 0; i < nr; i++) {
> -		if (git_config_parse_parameter(argv[i], fn, data) < 0) {
> -			ret = -1;
> -			goto out;
> -		}
> -	}
> -
> -out:
> -	free(argv);
>   	free(envw);
>   	cf = source.prev;
>   	return ret;
> diff --git a/t/t1300-config.sh b/t/t1300-config.sh
> index 36a60879f6..35a1a6e8b1 100755
> --- a/t/t1300-config.sh
> +++ b/t/t1300-config.sh
> @@ -1294,6 +1294,58 @@ test_expect_success 'git -c is not confused by empty environment' '
>   	GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
>   '
>   
> +test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
> +	v="${SQ}key.one=foo${SQ}" &&
> +	v="$v  ${SQ}key.two=bar${SQ}" &&
> +	v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
> +	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
> +	cat >expect <<-EOF &&
> +	key.one foo
> +	key.two bar
> +	key.ambiguous section.whatever=value
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
> +	v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
> +	v="$v  ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
> +	v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
> +	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
> +	cat >expect <<-EOF &&
> +	key.one foo
> +	key.two bar
> +	key.ambiguous=section.whatever value
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'old and new-style entries can mix' '
> +	v="${SQ}key.oldone=oldfoo${SQ}" &&
> +	v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
> +	v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
> +	v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
> +	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
> +	cat >expect <<-EOF &&
> +	key.oldone oldfoo
> +	key.newone newfoo
> +	key.oldtwo oldbar
> +	key.newtwo newbar
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'old and new bools with ambiguous subsection' '
> +	v="${SQ}key.with=equals.oldbool${SQ}" &&
> +	v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
> +	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
> +	cat >expect <<-EOF &&
> +	key.with equals.oldbool
> +	key.with=equals.newbool
> +	EOF
> +	test_cmp expect actual
> +'
> +
>   test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
>   	cat >expect <<-\EOF &&
>   	env.one one
> 
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 71 of 116 in “config: allow specifying config entries via envvar pairs”
  1. 0/2 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Nov 24, 2020
  2. 1/2 config: extract function to parse config pairsPatrick Steinhardt, Nov 24, 2020
  3. 2/2 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Nov 24, 2020
  4. Junio C HamanoNov 25, 2020
  5. Patrick SteinhardtNov 25, 2020
  6. Junio C HamanoNov 25, 2020
  7. Patrick SteinhardtNov 25, 2020
  8. Ævar Arnfjörð BjarmasonNov 25, 2020
  9. Ævar Arnfjörð BjarmasonNov 25, 2020
  10. Junio C HamanoNov 25, 2020
  11. Jeff KingNov 25, 2020
  12. Patrick SteinhardtNov 25, 2020
  13. Jeff KingNov 26, 2020
  14. Junio C HamanoNov 25, 2020
  15. brian m. carlsonNov 25, 2020
  16. Patrick SteinhardtNov 26, 2020
  17. Patrick SteinhardtDec 1, 2020
  18. Jeff KingDec 1, 2020
  19. 0/4 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Dec 1, 2020
  20. 1/4 environment: make `getenv_safe()` non-staticPatrick Steinhardt, Dec 1, 2020
  21. 3/4 config: refactor parsing of GIT_CONFIG_PARAMETERSPatrick Steinhardt, Dec 1, 2020
  22. 2/4 config: extract function to parse config pairsPatrick Steinhardt, Dec 1, 2020
  23. 4/4 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Dec 1, 2020
  24. 0/6 config: allow specifying config entries via envPatrick Steinhardt, Dec 9, 2020
  25. 1/6 git: add `--super-prefix` to usage stringPatrick Steinhardt, Dec 9, 2020
  26. 3/6 environment: make `getenv_safe()` non-staticPatrick Steinhardt, Dec 9, 2020
  27. 2/6 config: add new way to pass config via `--config-env`Patrick Steinhardt, Dec 9, 2020
  28. Ævar Arnfjörð BjarmasonDec 9, 2020
  29. Jeff KingDec 9, 2020
  30. Patrick SteinhardtDec 11, 2020
  31. Jeff KingDec 11, 2020
  32. Patrick SteinhardtDec 11, 2020
  33. Jeff KingDec 11, 2020
  34. Jeff KingDec 9, 2020
  35. 1/3 quote: make sq_dequote_step() a public functionJeff King, Dec 9, 2020
  36. 2/3 config: parse more robust format in GIT_CONFIG_PARAMETERSJeff King, Dec 9, 2020
  37. 3/3 config: store "git -c" variables using more robust formatJeff King, Dec 9, 2020
  38. Jeff KingDec 9, 2020
  39. Ævar Arnfjörð BjarmasonDec 10, 2020
  40. Junio C HamanoDec 10, 2020
  41. Jeff KingDec 11, 2020
  42. Junio C HamanoDec 10, 2020
  43. Jeff KingDec 10, 2020
  44. Junio C HamanoDec 10, 2020
  45. Patrick SteinhardtDec 11, 2020
  46. Jeff KingDec 11, 2020
  47. 4/6 config: extract function to parse config pairsPatrick Steinhardt, Dec 9, 2020
  48. Ævar Arnfjörð BjarmasonDec 9, 2020
  49. 6/6 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Dec 9, 2020
  50. 5/6 config: refactor parsing of GIT_CONFIG_PARAMETERSPatrick Steinhardt, Dec 9, 2020
  51. Ævar Arnfjörð BjarmasonDec 9, 2020
  52. Patrick SteinhardtDec 11, 2020
  53. Jeff KingDec 11, 2020
  54. Jeff KingDec 11, 2020
  55. Patrick SteinhardtDec 11, 2020
  56. Patrick SteinhardtDec 11, 2020
  57. Ævar Arnfjörð BjarmasonDec 11, 2020
  58. Jeff KingDec 11, 2020
  59. 0/8 config: allow specifying config entries via envPatrick Steinhardt, Dec 16, 2020
  60. 1/8 git: add `--super-prefix` to usage stringPatrick Steinhardt, Dec 16, 2020
  61. 2/8 config: add new way to pass config via `--config-env`Patrick Steinhardt, Dec 16, 2020
  62. Junio C HamanoDec 23, 2020
  63. 4/8 config: extract function to parse config pairsPatrick Steinhardt, Dec 16, 2020
  64. 8/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Dec 16, 2020
  65. Junio C HamanoDec 23, 2020
  66. Junio C HamanoDec 23, 2020
  67. Patrick SteinhardtJan 6, 2021
  68. Junio C HamanoJan 6, 2021
  69. 7/8 environment: make `getenv_safe()` a public functionPatrick Steinhardt, Dec 16, 2020
  70. 6/8 config: parse more robust format in GIT_CONFIG_PARAMETERSPatrick Steinhardt, Dec 16, 2020
  71. Phillip WoodDec 16, 2020
  72. 3/8 quote: make sq_dequote_step() a public functionPatrick Steinhardt, Dec 16, 2020
  73. 5/8 config: store "git -c" variables using more robust formatPatrick Steinhardt, Dec 16, 2020
  74. 0/8 config: allow specifying config entries via envPatrick Steinhardt, Jan 7, 2021
  75. 4/8 config: extract function to parse config pairsPatrick Steinhardt, Jan 7, 2021
  76. 1/8 git: add `--super-prefix` to usage stringPatrick Steinhardt, Jan 7, 2021
  77. 2/8 config: add new way to pass config via `--config-env`Patrick Steinhardt, Jan 7, 2021
  78. Simon RuderichJan 10, 2021
  79. Junio C HamanoJan 11, 2021
  80. Patrick SteinhardtJan 11, 2021
  81. 3/8 quote: make sq_dequote_step() a public functionPatrick Steinhardt, Jan 7, 2021
  82. 5/8 config: store "git -c" variables using more robust formatPatrick Steinhardt, Jan 7, 2021
  83. 6/8 config: parse more robust format in GIT_CONFIG_PARAMETERSPatrick Steinhardt, Jan 7, 2021
  84. 7/8 environment: make `getenv_safe()` a public functionPatrick Steinhardt, Jan 7, 2021
  85. 8/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Jan 7, 2021
  86. 0/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Jan 11, 2021
  87. 2/8 config: add new way to pass config via `--config-env`Patrick Steinhardt, Jan 11, 2021
  88. Junio C HamanoJan 11, 2021
  89. 3/8 quote: make sq_dequote_step() a public functionPatrick Steinhardt, Jan 11, 2021
  90. 1/8 git: add `--super-prefix` to usage stringPatrick Steinhardt, Jan 11, 2021
  91. 4/8 config: extract function to parse config pairsPatrick Steinhardt, Jan 11, 2021
  92. 5/8 config: store "git -c" variables using more robust formatPatrick Steinhardt, Jan 11, 2021
  93. 7/8 environment: make `getenv_safe()` a public functionPatrick Steinhardt, Jan 11, 2021
  94. 6/8 config: parse more robust format in GIT_CONFIG_PARAMETERSPatrick Steinhardt, Jan 11, 2021
  95. 8/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Jan 11, 2021
  96. 0/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Jan 12, 2021
  97. 2/8 config: add new way to pass config via `--config-env`Patrick Steinhardt, Jan 12, 2021
  98. Ævar Arnfjörð BjarmasonApr 16, 2021
  99. Jeff KingApr 17, 2021
  100. Patrick SteinhardtApr 19, 2021
  101. Ævar Arnfjörð BjarmasonApr 20, 2021
  102. Ævar Arnfjörð BjarmasonApr 20, 2021
  103. Jeff KingApr 23, 2021
  104. Ævar Arnfjörð BjarmasonMay 19, 2021
  105. 4/8 config: extract function to parse config pairsPatrick Steinhardt, Jan 12, 2021
  106. 1/8 git: add `--super-prefix` to usage stringPatrick Steinhardt, Jan 12, 2021
  107. 3/8 quote: make sq_dequote_step() a public functionPatrick Steinhardt, Jan 12, 2021
  108. 6/8 config: parse more robust format in GIT_CONFIG_PARAMETERSPatrick Steinhardt, Jan 12, 2021
  109. 8/8 config: allow specifying config entries via envvar pairsPatrick Steinhardt, Jan 12, 2021
  110. 5/8 config: store "git -c" variables using more robust formatPatrick Steinhardt, Jan 12, 2021
  111. Jeff KingJan 15, 2021
  112. Patrick SteinhardtJan 20, 2021
  113. Junio C HamanoJan 20, 2021
  114. Patrick SteinhardtJan 20, 2021
  115. Junio C HamanoJan 20, 2021
  116. 7/8 environment: make `getenv_safe()` a public functionPatrick Steinhardt, Jan 12, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.