rewrite git_config() to use the config-set API

Of all the functions in `git_config*()` family, `git_config()` has the most invocations in the whole code base. Each `git_config()` invocation causes config file rereads which can be avoided using the config-set API. Use the config-set API to rewrite `git_config()` to use the config caching layer to avoid config file rereads on each invocation during a git process lifetime. First invocation constructs the cache, and after that for each successive invocation, `git_config()` feeds values from the config cache instead of rereading the configuration files. Signed-off-by: Tanay Abhra <tanayabh@gmail.com> Reviewed-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Tanay Abhra committed Aug 7, 2014 at 04:59 UTC 155ef25f12329258c06b8875b5e372abec2d348d
3 files changed +68 -9
cache.h
+24
@@ -8,6 +8,7 @@
8 #include "gettext.h"
9 #include "convert.h"
10 #include "trace.h"
11 +#include "string-list.h"
12
13 #include SHA1_HEADER
14 #ifndef git_SHA_CTX
@@ -1351,9 +1352,32 @@ extern int parse_config_key(const char *var,
1352 const char **subsection, int *subsection_len,
1353 const char **key);
1354
1355 +struct config_set_element {
1356 + struct hashmap_entry ent;
1357 + char *key;
1358 + struct string_list value_list;
1359 +};
1360 +
1361 +struct configset_list_item {
1362 + struct config_set_element *e;
1363 + int value_index;
1364 +};
1365 +
1366 +/*
1367 + * the contents of the list are ordered according to their
1368 + * position in the config files and order of parsing the files.
1369 + * (i.e. key-value pair at the last position of .git/config will
1370 + * be at the last item of the list)
1371 + */
1372 +struct configset_list {
1373 + struct configset_list_item *items;
1374 + unsigned int nr, alloc;
1375 +};
1376 +
1377 struct config_set {
1378 struct hashmap config_hash;
1379 int hash_initialized;
1380 + struct configset_list list;
1381 };
1382
1383 extern void git_configset_init(struct config_set *cs);
config.c
+43 -8
@@ -35,12 +35,6 @@ struct config_source {
35 long (*do_ftell)(struct config_source *c);
36 };
37
38 -struct config_set_element {
39 - struct hashmap_entry ent;
40 - char *key;
41 - struct string_list value_list;
42 -};
43 -
38 static struct config_source *cf;
39
40 static int zlib_compression_seen;
@@ -1232,7 +1226,7 @@ int git_config_with_options(config_fn_t fn, void *data,
1226 return ret;
1227 }
1228
1235 -void git_config(config_fn_t fn, void *data)
1229 +static void git_config_raw(config_fn_t fn, void *data)
1230 {
1231 if (git_config_with_options(fn, data, NULL, 1) < 0)
1232 /*
@@ -1249,6 +1243,33 @@ void git_config(config_fn_t fn, void *data)
1243 die(_("unknown error occured while reading the configuration files"));
1244 }
1245
1246 +static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
1247 +{
1248 + int i, value_index;
1249 + struct string_list *values;
1250 + struct config_set_element *entry;
1251 + struct configset_list *list = &cs->list;
1252 + struct key_value_info *kv_info;
1253 +
1254 + for (i = 0; i < list->nr; i++) {
1255 + entry = list->items[i].e;
1256 + value_index = list->items[i].value_index;
1257 + values = &entry->value_list;
1258 + if (fn(entry->key, values->items[value_index].string, data) < 0) {
1259 + kv_info = values->items[value_index].util;
1260 + git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr);
1261 + }
1262 + }
1263 +}
1264 +
1265 +static void git_config_check_init(void);
1266 +
1267 +void git_config(config_fn_t fn, void *data)
1268 +{
1269 + git_config_check_init();
1270 + configset_iter(&the_config_set, fn, data);
1271 +}
1272 +
1273 static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
1274 {
1275 struct config_set_element k;
@@ -1275,6 +1296,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
1296 {
1297 struct config_set_element *e;
1298 struct string_list_item *si;
1299 + struct configset_list_item *l_item;
1300 struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
1301
1302 e = configset_find_element(cs, key);
@@ -1290,6 +1312,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
1312 hashmap_add(&cs->config_hash, e);
1313 }
1314 si = string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL);
1315 +
1316 + ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
1317 + l_item = &cs->list.items[cs->list.nr++];
1318 + l_item->e = e;
1319 + l_item->value_index = e->value_list.nr - 1;
1320 +
1321 if (cf) {
1322 kv_info->filename = strintern(cf->name);
1323 kv_info->linenr = cf->linenr;
@@ -1313,6 +1341,9 @@ void git_configset_init(struct config_set *cs)
1341 {
1342 hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0);
1343 cs->hash_initialized = 1;
1344 + cs->list.nr = 0;
1345 + cs->list.alloc = 0;
1346 + cs->list.items = NULL;
1347 }
1348
1349 void git_configset_clear(struct config_set *cs)
@@ -1329,6 +1360,10 @@ void git_configset_clear(struct config_set *cs)
1360 }
1361 hashmap_free(&cs->config_hash, 1);
1362 cs->hash_initialized = 0;
1363 + free(cs->list.items);
1364 + cs->list.nr = 0;
1365 + cs->list.alloc = 0;
1366 + cs->list.items = NULL;
1367 }
1368
1369 static int config_set_callback(const char *key, const char *value, void *cb)
@@ -1447,7 +1482,7 @@ static void git_config_check_init(void)
1482 if (the_config_set.hash_initialized)
1483 return;
1484 git_configset_init(&the_config_set);
1450 - git_config(config_set_callback, &the_config_set);
1485 + git_config_raw(config_set_callback, &the_config_set);
1486 }
1487
1488 void git_config_clear(void)
t/t4055-diff-context.sh
+1 -1
@@ -79,7 +79,7 @@ test_expect_success 'non-integer config parsing' '
79 test_expect_success 'negative integer config parsing' '
80 git config diff.context -1 &&
81 test_must_fail git diff 2>output &&
82 - test_i18ngrep "bad config file" output
82 + test_i18ngrep "bad config variable" output
83 '
84
85 test_expect_success '-U0 is valid, so is diff.context=0' '