Skip to content

Commit 83786fa

Browse files
trastgitster
authored andcommitted
config: arbitrary number of matches for --unset and --replace-all
git-config used a static match array to hold the matches we want to unset/replace when using --unset or --replace-all. Use a variable-sized array instead. This in particular fixes the symptoms git-svn had when storing large numbers of svn-remote.*.added-placeholder entries in the config file. While the tests are rather more paranoid than just --unset and --replace-all, the other operations already worked. Indeed git-svn's usage only breaks the first time *after* creating so many entries, when it wants to unset and re-add them all. Reported-by: Jess Hottenstein <[email protected]> Signed-off-by: Thomas Rast <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d7d2c87 commit 83786fa

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

config.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,15 +1160,14 @@ int git_config(config_fn_t fn, void *data)
11601160
* Find all the stuff for git_config_set() below.
11611161
*/
11621162

1163-
#define MAX_MATCHES 512
1164-
11651163
static struct {
11661164
int baselen;
11671165
char *key;
11681166
int do_not_match;
11691167
regex_t *value_regex;
11701168
int multi_replace;
1171-
size_t offset[MAX_MATCHES];
1169+
size_t *offset;
1170+
unsigned int offset_alloc;
11721171
enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
11731172
int seen;
11741173
} store;
@@ -1191,11 +1190,11 @@ static int store_aux(const char *key, const char *value, void *cb)
11911190
if (matches(key, value)) {
11921191
if (store.seen == 1 && store.multi_replace == 0) {
11931192
warning("%s has multiple values", key);
1194-
} else if (store.seen >= MAX_MATCHES) {
1195-
error("too many matches for %s", key);
1196-
return 1;
11971193
}
11981194

1195+
ALLOC_GROW(store.offset, store.seen + 1,
1196+
store.offset_alloc);
1197+
11991198
store.offset[store.seen] = cf->do_ftell(cf);
12001199
store.seen++;
12011200
}
@@ -1223,18 +1222,25 @@ static int store_aux(const char *key, const char *value, void *cb)
12231222
* Do not increment matches: this is no match, but we
12241223
* just made sure we are in the desired section.
12251224
*/
1225+
ALLOC_GROW(store.offset, store.seen + 1,
1226+
store.offset_alloc);
12261227
store.offset[store.seen] = cf->do_ftell(cf);
12271228
/* fallthru */
12281229
case SECTION_END_SEEN:
12291230
case START:
12301231
if (matches(key, value)) {
1232+
ALLOC_GROW(store.offset, store.seen + 1,
1233+
store.offset_alloc);
12311234
store.offset[store.seen] = cf->do_ftell(cf);
12321235
store.state = KEY_SEEN;
12331236
store.seen++;
12341237
} else {
12351238
if (strrchr(key, '.') - key == store.baselen &&
12361239
!strncmp(key, store.key, store.baselen)) {
12371240
store.state = SECTION_SEEN;
1241+
ALLOC_GROW(store.offset,
1242+
store.seen + 1,
1243+
store.offset_alloc);
12381244
store.offset[store.seen] = cf->do_ftell(cf);
12391245
}
12401246
}
@@ -1533,6 +1539,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
15331539
}
15341540
}
15351541

1542+
ALLOC_GROW(store.offset, 1, store.offset_alloc);
15361543
store.offset[0] = 0;
15371544
store.state = START;
15381545
store.seen = 0;

t/t1303-wacky-config.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,28 @@
33
test_description='Test wacky input to git config'
44
. ./test-lib.sh
55

6+
# Leaving off the newline is intentional!
67
setup() {
78
(printf "[section]\n" &&
89
printf " key = foo") >.git/config
910
}
1011

12+
# 'check section.key value' verifies that the entry for section.key is
13+
# 'value'
1114
check() {
1215
echo "$2" >expected
1316
git config --get "$1" >actual 2>&1
1417
test_cmp actual expected
1518
}
1619

20+
# 'check section.key regex value' verifies that the entry for
21+
# section.key *that matches 'regex'* is 'value'
22+
check_regex() {
23+
echo "$3" >expected
24+
git config --get "$1" "$2" >actual 2>&1
25+
test_cmp actual expected
26+
}
27+
1728
test_expect_success 'modify same key' '
1829
setup &&
1930
git config section.key bar &&
@@ -47,4 +58,57 @@ test_expect_success 'do not crash on special long config line' '
4758
check section.key "$LONG_VALUE"
4859
'
4960

61+
setup_many() {
62+
setup &&
63+
# This time we want the newline so that we can tack on more
64+
# entries.
65+
echo >>.git/config &&
66+
# Semi-efficient way of concatenating 5^5 = 3125 lines. Note
67+
# that because 'setup' already put one line, this means 3126
68+
# entries for section.key in the config file.
69+
cat >5to1 <<-\EOF &&
70+
key = foo
71+
key = foo
72+
key = foo
73+
key = foo
74+
key = foo
75+
EOF
76+
cat 5to1 5to1 5to1 5to1 5to1 >5to2 && # 25
77+
cat 5to2 5to2 5to2 5to2 5to2 >5to3 && # 125
78+
cat 5to3 5to3 5to3 5to3 5to3 >5to4 && # 635
79+
cat 5to4 5to4 5to4 5to4 5to4 >>.git/config # 3125
80+
}
81+
82+
test_expect_success 'get many entries' '
83+
setup_many &&
84+
git config --get-all section.key >actual &&
85+
test_line_count = 3126 actual
86+
'
87+
88+
test_expect_success 'get many entries by regex' '
89+
setup_many &&
90+
git config --get-regexp "sec.*ke." >actual &&
91+
test_line_count = 3126 actual
92+
'
93+
94+
test_expect_success 'add and replace one of many entries' '
95+
setup_many &&
96+
git config --add section.key bar &&
97+
check_regex section.key "b.*r" bar &&
98+
git config section.key beer "b.*r" &&
99+
check_regex section.key "b.*r" beer
100+
'
101+
102+
test_expect_success 'replace many entries' '
103+
setup_many &&
104+
git config --replace-all section.key bar &&
105+
check section.key bar
106+
'
107+
108+
test_expect_success 'unset many entries' '
109+
setup_many &&
110+
git config --unset-all section.key &&
111+
test_must_fail git config section.key
112+
'
113+
50114
test_done

0 commit comments

Comments
 (0)