Skip to content

Commit f9dbb64

Browse files
peffgitster
authored andcommitted
config: parse more robust format in GIT_CONFIG_PARAMETERS
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. 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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b342ae6 commit f9dbb64

File tree

2 files changed

+104
-17
lines changed

2 files changed

+104
-17
lines changed

config.c

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -507,14 +507,62 @@ int git_config_parse_parameter(const char *text,
507507
return ret;
508508
}
509509

510+
static int parse_config_env_list(char *env, config_fn_t fn, void *data)
511+
{
512+
char *cur = env;
513+
while (cur && *cur) {
514+
const char *key = sq_dequote_step(cur, &cur);
515+
if (!key)
516+
return error(_("bogus format in %s"),
517+
CONFIG_DATA_ENVIRONMENT);
518+
519+
if (!cur || isspace(*cur)) {
520+
/* old-style 'key=value' */
521+
if (git_config_parse_parameter(key, fn, data) < 0)
522+
return -1;
523+
}
524+
else if (*cur == '=') {
525+
/* new-style 'key'='value' */
526+
const char *value;
527+
528+
cur++;
529+
if (*cur == '\'') {
530+
/* quoted value */
531+
value = sq_dequote_step(cur, &cur);
532+
if (!value || (cur && !isspace(*cur))) {
533+
return error(_("bogus format in %s"),
534+
CONFIG_DATA_ENVIRONMENT);
535+
}
536+
} else if (!*cur || isspace(*cur)) {
537+
/* implicit bool: 'key'= */
538+
value = NULL;
539+
} else {
540+
return error(_("bogus format in %s"),
541+
CONFIG_DATA_ENVIRONMENT);
542+
}
543+
544+
if (config_parse_pair(key, value, fn, data) < 0)
545+
return -1;
546+
}
547+
else {
548+
/* unknown format */
549+
return error(_("bogus format in %s"),
550+
CONFIG_DATA_ENVIRONMENT);
551+
}
552+
553+
if (cur) {
554+
while (isspace(*cur))
555+
cur++;
556+
}
557+
}
558+
return 0;
559+
}
560+
510561
int git_config_from_parameters(config_fn_t fn, void *data)
511562
{
512563
const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
513564
int ret = 0;
514565
char *envw;
515-
const char **argv = NULL;
516-
int nr = 0, alloc = 0;
517-
int i;
518566
struct config_source source;
519567

520568
if (!env)
@@ -527,21 +575,8 @@ int git_config_from_parameters(config_fn_t fn, void *data)
527575

528576
/* sq_dequote will write over it */
529577
envw = xstrdup(env);
578+
ret = parse_config_env_list(envw, fn, data);
530579

531-
if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
532-
ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT);
533-
goto out;
534-
}
535-
536-
for (i = 0; i < nr; i++) {
537-
if (git_config_parse_parameter(argv[i], fn, data) < 0) {
538-
ret = -1;
539-
goto out;
540-
}
541-
}
542-
543-
out:
544-
free(argv);
545580
free(envw);
546581
cf = source.prev;
547582
return ret;

t/t1300-config.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,58 @@ test_expect_success 'git -c is not confused by empty environment' '
12941294
GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
12951295
'
12961296

1297+
test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
1298+
v="${SQ}key.one=foo${SQ}" &&
1299+
v="$v ${SQ}key.two=bar${SQ}" &&
1300+
v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
1301+
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
1302+
cat >expect <<-EOF &&
1303+
key.one foo
1304+
key.two bar
1305+
key.ambiguous section.whatever=value
1306+
EOF
1307+
test_cmp expect actual
1308+
'
1309+
1310+
test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
1311+
v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
1312+
v="$v ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
1313+
v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
1314+
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
1315+
cat >expect <<-EOF &&
1316+
key.one foo
1317+
key.two bar
1318+
key.ambiguous=section.whatever value
1319+
EOF
1320+
test_cmp expect actual
1321+
'
1322+
1323+
test_expect_success 'old and new-style entries can mix' '
1324+
v="${SQ}key.oldone=oldfoo${SQ}" &&
1325+
v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
1326+
v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
1327+
v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
1328+
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
1329+
cat >expect <<-EOF &&
1330+
key.oldone oldfoo
1331+
key.newone newfoo
1332+
key.oldtwo oldbar
1333+
key.newtwo newbar
1334+
EOF
1335+
test_cmp expect actual
1336+
'
1337+
1338+
test_expect_success 'old and new bools with ambiguous subsection' '
1339+
v="${SQ}key.with=equals.oldbool${SQ}" &&
1340+
v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
1341+
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
1342+
cat >expect <<-EOF &&
1343+
key.with equals.oldbool
1344+
key.with=equals.newbool
1345+
EOF
1346+
test_cmp expect actual
1347+
'
1348+
12971349
test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
12981350
cat >expect <<-\EOF &&
12991351
env.one one

0 commit comments

Comments
 (0)