Skip to content

Commit 00b347d

Browse files
committed
git-config: do not complain about duplicate entries
If git-config is asked for a single value, it will complain and exit with an error if it finds multiple instances of that value. This is unlike the usual internal config parsing, however, which will generally overwrite previous values, leaving only the final one. For example: [set a multivar] $ git config user.email [email protected] $ git config --add user.email [email protected] [use the internal parser to fetch it] $ git var GIT_AUTHOR_IDENT Your Name <[email protected]> ... [use git-config to fetch it] $ git config user.email [email protected] error: More than one value for the key user.email: [email protected] This overwriting behavior is critical for the regular parser, which starts with the lowest-priority file (e.g., /etc/gitconfig) and proceeds to the highest-priority file ($GIT_DIR/config). Overwriting yields the highest priority value at the end. Git-config solves this problem by implementing its own parsing. It goes from highest to lowest priorty, but does not proceed to the next file if it has seen a value. So in practice, this distinction never mattered much, because it only triggered for values in the same file. And there was not much point in doing that; the real value is in overwriting values from lower-priority files. However, this changed with the implementation of config include files. Now we might see an include overriding a value from the parent file, which is a sensible thing to do, but git-config will flag as a duplication. This patch drops the duplicate detection for git-config and switches to a pure-overwrite model (for the single case; --get-all can still be used if callers want to do something more fancy). As is shown by the modifications to the test suite, this is a user-visible change in behavior. An alternative would be to just change the include case, but this is much cleaner for a few reasons: 1. If you change the include case, then to what? If you just stop parsing includes after getting a value, then you will get a _different_ answer than the regular config parser (you'll get the first value instead of the last value). So you'd want to implement overwrite semantics anyway. 2. Even though it is a change in behavior for git-config, it is bringing us in line with what the internal parsers already do. 3. The file-order reimplementation is the only thing keeping us from sharing more code with the internal config parser, which will help keep differences to a minimum. Going under the assumption that the primary purpose of git-config is to behave identically to how git's internal parsing works, this change can be seen as a bug-fix. Signed-off-by: Jeff King <[email protected]>
1 parent 7acdd6f commit 00b347d

File tree

3 files changed

+14
-22
lines changed

3 files changed

+14
-22
lines changed

builtin/config.c

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ static int collect_config(const char *key_, const char *value_, void *cb)
107107
char value[256];
108108
const char *vptr = value;
109109
int must_free_vptr = 0;
110-
int dup_error = 0;
111110
int must_print_delim = 0;
112111

113112
if (!use_key_regexp && strcmp(key_, key))
@@ -126,8 +125,6 @@ static int collect_config(const char *key_, const char *value_, void *cb)
126125
strbuf_addstr(buf, key_);
127126
must_print_delim = 1;
128127
}
129-
if (values->nr > 1 && !do_all)
130-
dup_error = 1;
131128
if (types == TYPE_INT)
132129
sprintf(value, "%d", git_config_int(key_, value_?value_:""));
133130
else if (types == TYPE_BOOL)
@@ -149,16 +146,12 @@ static int collect_config(const char *key_, const char *value_, void *cb)
149146
vptr = "";
150147
must_print_delim = 0;
151148
}
152-
if (dup_error) {
153-
error("More than one value for the key %s: %s",
154-
key_, vptr);
155-
}
156-
else {
157-
if (must_print_delim)
158-
strbuf_addch(buf, key_delim);
159-
strbuf_addstr(buf, vptr);
160-
strbuf_addch(buf, term);
161-
}
149+
150+
if (must_print_delim)
151+
strbuf_addch(buf, key_delim);
152+
strbuf_addstr(buf, vptr);
153+
strbuf_addch(buf, term);
154+
162155
if (must_free_vptr)
163156
/* If vptr must be freed, it's a pointer to a
164157
* dynamically allocated buffer, it's safe to cast to
@@ -263,14 +256,12 @@ static int get_value(const char *key_, const char *regex_)
263256
if (!do_all && !values.nr && system_wide)
264257
git_config_from_file(fn, system_wide, data);
265258

266-
if (do_all)
267-
ret = !values.nr;
268-
else
269-
ret = (values.nr == 1) ? 0 : values.nr > 1 ? 2 : 1;
259+
ret = !values.nr;
270260

271261
for (i = 0; i < values.nr; i++) {
272262
struct strbuf *buf = values.items + i;
273-
fwrite(buf->buf, 1, buf->len, stdout);
263+
if (do_all || i == values.nr - 1)
264+
fwrite(buf->buf, 1, buf->len, stdout);
274265
strbuf_release(buf);
275266
}
276267
free(values.items);

t/t1300-repo-config.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,10 @@ test_expect_success 'non-match value' '
254254
test_cmp expect actual
255255
'
256256

257-
test_expect_success 'ambiguous get' '
258-
test_must_fail git config --get nextsection.nonewline
257+
test_expect_success 'multi-valued get returns final one' '
258+
echo "wow2 for me" >expect &&
259+
git config --get nextsection.nonewline >actual &&
260+
test_cmp expect actual
259261
'
260262

261263
test_expect_success 'multi-valued get-all returns all' '

t/t9700/test.pl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ BEGIN
4646
# Save and restore STDERR; we will probably extract this into a
4747
# "dies_ok" method and possibly move the STDERR handling to Git.pm.
4848
open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
49-
eval { $r->config("test.dupstring") };
50-
ok($@, "config: duplicate entry in scalar context fails");
49+
is($r->config("test.dupstring"), "value2", "config: multivar");
5150
eval { $r->config_bool("test.boolother") };
5251
ok($@, "config_bool: non-boolean values fail");
5352
open STDERR, ">&", $tmpstderr or die "cannot restore STDERR";

0 commit comments

Comments
 (0)