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 <jess.hottenstein@gmail.com> Signed-off-by: Thomas Rast <tr@thomasrast.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Thomas Rast committed
Nov 13, 2013 at 11:19 UTC
83786fa412662f6d7cdade9e4043882c5ad21c1a
2 files changed
+77
-6
config.c
+13
-6
@@ -1160,15 +1160,14 @@ int git_config(config_fn_t fn, void *data)
1160
* Find all the stuff for git_config_set() below.
1161
*/
1162
1163
-#define MAX_MATCHES 512
1164
-
1163
static struct {
1164
int baselen;
1165
char *key;
1166
int do_not_match;
1167
regex_t *value_regex;
1168
int multi_replace;
1171
- size_t offset[MAX_MATCHES];
1169
+ size_t *offset;
1170
+ unsigned int offset_alloc;
1171
enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
1172
int seen;
1173
} store;
@@ -1191,11 +1190,11 @@ static int store_aux(const char *key, const char *value, void *cb)
1190
if (matches(key, value)) {
1191
if (store.seen == 1 && store.multi_replace == 0) {
1192
warning("%s has multiple values", key);
1194
- } else if (store.seen >= MAX_MATCHES) {
1195
- error("too many matches for %s", key);
1196
- return 1;
1193
}
1194
1195
+ ALLOC_GROW(store.offset, store.seen + 1,
1196
+ store.offset_alloc);
1197
+
1198
store.offset[store.seen] = cf->do_ftell(cf);
1199
store.seen++;
1200
}
@@ -1223,11 +1222,15 @@ static int store_aux(const char *key, const char *value, void *cb)
1222
* Do not increment matches: this is no match, but we
1223
* just made sure we are in the desired section.
1224
*/
1225
+ ALLOC_GROW(store.offset, store.seen + 1,
1226
+ store.offset_alloc);
1227
store.offset[store.seen] = cf->do_ftell(cf);
1228
/* fallthru */
1229
case SECTION_END_SEEN:
1230
case START:
1231
if (matches(key, value)) {
1232
+ ALLOC_GROW(store.offset, store.seen + 1,
1233
+ store.offset_alloc);
1234
store.offset[store.seen] = cf->do_ftell(cf);
1235
store.state = KEY_SEEN;
1236
store.seen++;
@@ -1235,6 +1238,9 @@ static int store_aux(const char *key, const char *value, void *cb)
1238
if (strrchr(key, '.') - key == store.baselen &&
1239
!strncmp(key, store.key, store.baselen)) {
1240
store.state = SECTION_SEEN;
1241
+ ALLOC_GROW(store.offset,
1242
+ store.seen + 1,
1243
+ store.offset_alloc);
1244
store.offset[store.seen] = cf->do_ftell(cf);
1245
}
1246
}
@@ -1533,6 +1539,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
1539
}
1540
}
1541
1542
+ ALLOC_GROW(store.offset, 1, store.offset_alloc);
1543
store.offset[0] = 0;
1544
store.state = START;
1545
store.seen = 0;
t/t1303-wacky-config.sh
+64
@@ -3,17 +3,28 @@
3
test_description='Test wacky input to git config'
4
. ./test-lib.sh
5
6
+# Leaving off the newline is intentional!
7
setup() {
8
(printf "[section]\n" &&
9
printf " key = foo") >.git/config
10
}
11
12
+# 'check section.key value' verifies that the entry for section.key is
13
+# 'value'
14
check() {
15
echo "$2" >expected
16
git config --get "$1" >actual 2>&1
17
test_cmp actual expected
18
}
19
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
+
28
test_expect_success 'modify same key' '
29
setup &&
30
git config section.key bar &&
@@ -47,4 +58,57 @@ test_expect_success 'do not crash on special long config line' '
58
check section.key "$LONG_VALUE"
59
'
60
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
+
114
test_done