sequencer: make the todo_list structure public
[gitweb.git] / config.c
index 27e800c7ce8b343923433e9d1f6f96ff12ad4065..ff521eb27ad243b27c7bd95f3ad7a1b777a4ae00 100644 (file)
--- a/config.c
+++ b/config.c
@@ -6,16 +6,19 @@
  *
  */
 #include "cache.h"
+#include "branch.h"
 #include "config.h"
 #include "repository.h"
 #include "lockfile.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
 #include "strbuf.h"
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
+#include "object-store.h"
 #include "utf8.h"
 #include "dir.h"
+#include "color.h"
 
 struct config_source {
        struct config_source *prev;
@@ -30,7 +33,7 @@ struct config_source {
        enum config_origin_type origin_type;
        const char *name;
        const char *path;
-       int die_on_error;
+       enum config_error_action default_error_action;
        int linenr;
        int eof;
        struct strbuf value;
@@ -103,7 +106,7 @@ static int config_buf_ungetc(int c, struct config_source *conf)
        if (conf->u.buf.pos > 0) {
                conf->u.buf.pos--;
                if (conf->u.buf.buf[conf->u.buf.pos] != c)
-                       die("BUG: config_buf can only ungetc the same character");
+                       BUG("config_buf can only ungetc the same character");
                return c;
        }
 
@@ -116,12 +119,12 @@ static long config_buf_ftell(struct config_source *conf)
 }
 
 #define MAX_INCLUDE_DEPTH 10
-static const char include_depth_advice[] =
+static const char include_depth_advice[] = N_(
 "exceeded maximum include depth (%d) while including\n"
 "      %s\n"
 "from\n"
 "      %s\n"
-"Do you have circular includes?";
+"This might be due to circular includes.");
 static int handle_path_include(const char *path, struct config_include_data *inc)
 {
        int ret = 0;
@@ -133,7 +136,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
 
        expanded = expand_user_path(path, 0);
        if (!expanded)
-               return error("could not expand include path '%s'", path);
+               return error(_("could not expand include path '%s'"), path);
        path = expanded;
 
        /*
@@ -144,7 +147,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                char *slash;
 
                if (!cf || !cf->path)
-                       return error("relative config includes must come from files");
+                       return error(_("relative config includes must come from files"));
 
                slash = find_last_dir_sep(cf->path);
                if (slash)
@@ -155,7 +158,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
 
        if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
-                       die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
+                       die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
                            !cf ? "<unknown>" :
                            cf->name ? cf->name :
                            "the command line");
@@ -190,7 +193,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
                strbuf_realpath(&path, cf->path, 1);
                slash = find_last_dir_sep(path.buf);
                if (!slash)
-                       die("BUG: how is this possible?");
+                       BUG("how is this possible?");
                strbuf_splice(pat, 0, 1, path.buf, slash - path.buf);
                prefix = slash - path.buf + 1 /* slash */;
        } else if (!is_absolute_path(pat->buf))
@@ -342,13 +345,13 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele
 
        if (last_dot == NULL || last_dot == key) {
                if (!quiet)
-                       error("key does not contain a section: %s", key);
+                       error(_("key does not contain a section: %s"), key);
                return -CONFIG_NO_SECTION_OR_NAME;
        }
 
        if (!last_dot[1]) {
                if (!quiet)
-                       error("key does not contain variable name: %s", key);
+                       error(_("key does not contain variable name: %s"), key);
                return -CONFIG_NO_SECTION_OR_NAME;
        }
 
@@ -372,13 +375,13 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele
                        if (!iskeychar(c) ||
                            (i == baselen + 1 && !isalpha(c))) {
                                if (!quiet)
-                                       error("invalid key: %s", key);
+                                       error(_("invalid key: %s"), key);
                                goto out_free_ret_1;
                        }
                        c = tolower(c);
                } else if (c == '\n') {
                        if (!quiet)
-                               error("invalid key (newline): %s", key);
+                               error(_("invalid key (newline): %s"), key);
                        goto out_free_ret_1;
                }
                if (store_key)
@@ -414,7 +417,7 @@ int git_config_parse_parameter(const char *text,
 
        pair = strbuf_split_str(text, '=', 2);
        if (!pair[0])
-               return error("bogus config parameter: %s", text);
+               return error(_("bogus config parameter: %s"), text);
 
        if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') {
                strbuf_setlen(pair[0], pair[0]->len - 1);
@@ -426,7 +429,7 @@ int git_config_parse_parameter(const char *text,
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
-               return error("bogus config parameter: %s", text);
+               return error(_("bogus config parameter: %s"), text);
        }
 
        if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
@@ -461,7 +464,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
        envw = xstrdup(env);
 
        if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
-               ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+               ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT);
                goto out;
        }
 
@@ -811,10 +814,21 @@ static int git_parse_source(config_fn_t fn, void *data,
                                      cf->linenr, cf->name);
        }
 
-       if (cf->die_on_error)
+       switch (opts && opts->error_action ?
+               opts->error_action :
+               cf->default_error_action) {
+       case CONFIG_ERROR_DIE:
                die("%s", error_msg);
-       else
+               break;
+       case CONFIG_ERROR_ERROR:
                error_return = error("%s", error_msg);
+               break;
+       case CONFIG_ERROR_SILENT:
+               error_return = -1;
+               break;
+       case CONFIG_ERROR_UNSET:
+               BUG("config error action unset");
+       }
 
        free(error_msg);
        return error_return;
@@ -923,7 +937,7 @@ int git_parse_ulong(const char *value, unsigned long *ret)
        return 1;
 }
 
-static int git_parse_ssize_t(const char *value, ssize_t *ret)
+int git_parse_ssize_t(const char *value, ssize_t *ret)
 {
        intmax_t tmp;
        if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
@@ -1070,7 +1084,16 @@ int git_config_expiry_date(timestamp_t *timestamp, const char *var, const char *
        return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value)
+int git_config_color(char *dest, const char *var, const char *value)
+{
+       if (!value)
+               return config_error_nonbool(var);
+       if (color_parse(value, dest) < 0)
+               return -1;
+       return 0;
+}
+
+static int git_default_core_config(const char *var, const char *value, void *cb)
 {
        /* This needs a better name */
        if (!strcmp(var, "core.filemode")) {
@@ -1147,7 +1170,7 @@ static int git_default_core_config(const char *var, const char *value)
                else {
                        int abbrev = git_config_int(var, value);
                        if (abbrev < minimum_abbrev || abbrev > 40)
-                               return error("abbrev length out of range: %d", abbrev);
+                               return error(_("abbrev length out of range: %d"), abbrev);
                        default_abbrev = abbrev;
                }
                return 0;
@@ -1219,11 +1242,14 @@ static int git_default_core_config(const char *var, const char *value)
        }
 
        if (!strcmp(var, "core.safecrlf")) {
+               int eol_rndtrp_die;
                if (value && !strcasecmp(value, "warn")) {
-                       safe_crlf = SAFE_CRLF_WARN;
+                       global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
                        return 0;
                }
-               safe_crlf = git_config_bool(var, value);
+               eol_rndtrp_die = git_config_bool(var, value);
+               global_conv_flags_eol = eol_rndtrp_die ?
+                       CONV_EOL_RNDTRP_DIE : 0;
                return 0;
        }
 
@@ -1239,6 +1265,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.checkroundtripencoding")) {
+               check_roundtrip_encoding = xstrdup(value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.notesref")) {
                notes_ref_name = xstrdup(value);
                return 0;
@@ -1256,7 +1287,7 @@ static int git_default_core_config(const char *var, const char *value)
                        comment_line_char = value[0];
                        auto_comment_line_char = 0;
                } else
-                       return error("core.commentChar should only be one character");
+                       return error(_("core.commentChar should only be one character"));
                return 0;
        }
 
@@ -1313,16 +1344,18 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
-       if (!strcmp(var, "core.hidedotfiles")) {
-               if (value && !strcasecmp(value, "dotgitonly"))
-                       hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
-               else
-                       hide_dotfiles = git_config_bool(var, value);
+       if (!strcmp(var, "core.partialclonefilter")) {
+               return git_config_string(&core_partial_clone_filter_default,
+                                        var, value);
+       }
+
+       if (!strcmp(var, "core.usereplacerefs")) {
+               read_replace_refs = git_config_bool(var, value);
                return 0;
        }
 
        /* Add other config variables here and to Documentation/config.txt. */
-       return 0;
+       return platform_core_config(var, value, cb);
 }
 
 static int git_default_i18n_config(const char *var, const char *value)
@@ -1359,7 +1392,7 @@ static int git_default_branch_config(const char *var, const char *value)
                else if (!strcmp(value, "always"))
                        autorebase = AUTOREBASE_ALWAYS;
                else
-                       return error("malformed value for %s", var);
+                       return error(_("malformed value for %s"), var);
                return 0;
        }
 
@@ -1385,9 +1418,9 @@ static int git_default_push_config(const char *var, const char *value)
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
-                       error("malformed value for %s: %s", var, value);
-                       return error("Must be one of nothing, matching, simple, "
-                                    "upstream or current.");
+                       error(_("malformed value for %s: %s"), var, value);
+                       return error(_("must be one of nothing, matching, simple, "
+                                      "upstream or current"));
                }
                return 0;
        }
@@ -1407,13 +1440,13 @@ static int git_default_mailmap_config(const char *var, const char *value)
        return 0;
 }
 
-int git_default_config(const char *var, const char *value, void *dummy)
+int git_default_config(const char *var, const char *value, void *cb)
 {
        if (starts_with(var, "core."))
-               return git_default_core_config(var, value);
+               return git_default_core_config(var, value, cb);
 
        if (starts_with(var, "user."))
-               return git_ident_config(var, value, dummy);
+               return git_ident_config(var, value, cb);
 
        if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
@@ -1427,7 +1460,7 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (starts_with(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
-       if (starts_with(var, "advice."))
+       if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
                return git_default_advice_config(var, value);
 
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
@@ -1489,17 +1522,21 @@ static int do_config_from_file(config_fn_t fn,
                void *data, const struct config_options *opts)
 {
        struct config_source top;
+       int ret;
 
        top.u.file = f;
        top.origin_type = origin_type;
        top.name = name;
        top.path = path;
-       top.die_on_error = 1;
+       top.default_error_action = CONFIG_ERROR_DIE;
        top.do_fgetc = config_file_fgetc;
        top.do_ungetc = config_file_ungetc;
        top.do_ftell = config_file_ftell;
 
-       return do_config_from(&top, fn, data, opts);
+       flockfile(f);
+       ret = do_config_from(&top, fn, data, opts);
+       funlockfile(f);
+       return ret;
 }
 
 static int git_config_from_stdin(config_fn_t fn, void *data)
@@ -1517,10 +1554,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
 
        f = fopen_or_warn(filename, "r");
        if (f) {
-               flockfile(f);
                ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
                                          filename, f, data, opts);
-               funlockfile(f);
                fclose(f);
        }
        return ret;
@@ -1531,8 +1566,10 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        return git_config_from_file_with_options(fn, filename, data, NULL);
 }
 
-int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
-                       const char *name, const char *buf, size_t len, void *data)
+int git_config_from_mem(config_fn_t fn,
+                       const enum config_origin_type origin_type,
+                       const char *name, const char *buf, size_t len,
+                       void *data, const struct config_options *opts)
 {
        struct config_source top;
 
@@ -1542,12 +1579,12 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
        top.origin_type = origin_type;
        top.name = name;
        top.path = NULL;
-       top.die_on_error = 0;
+       top.default_error_action = CONFIG_ERROR_ERROR;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
        top.do_ftell = config_buf_ftell;
 
-       return do_config_from(&top, fn, data, NULL);
+       return do_config_from(&top, fn, data, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
@@ -1560,15 +1597,16 @@ int git_config_from_blob_oid(config_fn_t fn,
        unsigned long size;
        int ret;
 
-       buf = read_sha1_file(oid->hash, &type, &size);
+       buf = read_object_file(oid, &type, &size);
        if (!buf)
-               return error("unable to load config blob object '%s'", name);
+               return error(_("unable to load config blob object '%s'"), name);
        if (type != OBJ_BLOB) {
                free(buf);
-               return error("reference '%s' does not point to a blob", name);
+               return error(_("reference '%s' does not point to a blob"), name);
        }
 
-       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
+       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size,
+                                 data, NULL);
        free(buf);
 
        return ret;
@@ -1581,7 +1619,7 @@ static int git_config_from_blob_ref(config_fn_t fn,
        struct object_id oid;
 
        if (get_oid(name, &oid) < 0)
-               return error("unable to resolve config blob '%s'", name);
+               return error(_("unable to resolve config blob '%s'"), name);
        return git_config_from_blob_oid(fn, name, &oid, data);
 }
 
@@ -1611,7 +1649,7 @@ unsigned long git_env_ulong(const char *k, unsigned long val)
 {
        const char *v = getenv(k);
        if (v && !git_parse_ulong(v, &val))
-               die("failed to parse %s", k);
+               die(_("failed to parse %s"), k);
        return val;
 }
 
@@ -1630,6 +1668,8 @@ static int do_git_config_sequence(const struct config_options *opts,
 
        if (opts->commondir)
                repo_config = mkpathdup("%s/config", opts->commondir);
+       else if (opts->git_dir)
+               BUG("git_dir without commondir");
        else
                repo_config = NULL;
 
@@ -1649,6 +1689,17 @@ static int do_git_config_sequence(const struct config_options *opts,
        if (repo_config && !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
 
+       /*
+        * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
+        * But let's not complicate things before it's actually needed.
+        */
+       if (repository_format_worktree_config) {
+               char *path = git_pathdup("config.worktree");
+               if (!access_or_die(path, R_OK, 0))
+                       ret += git_config_from_file(fn, path, data);
+               free(path);
+       }
+
        current_parsing_scope = CONFIG_SCOPE_CMDLINE;
        if (git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
@@ -1787,7 +1838,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
        l_item->value_index = e->value_list.nr - 1;
 
        if (!cf)
-               die("BUG: configset_add_value has no source");
+               BUG("configset_add_value has no source");
        if (cf->name) {
                kv_info->filename = strintern(cf->name);
                kv_info->linenr = cf->linenr;
@@ -2145,23 +2196,6 @@ int git_config_get_pathname(const char *key, const char **dest)
        return repo_config_get_pathname(the_repository, key, dest);
 }
 
-/*
- * Note: This function exists solely to maintain backward compatibility with
- * 'fetch' and 'update_clone' storing configuration in '.gitmodules' and should
- * NOT be used anywhere else.
- *
- * Runs the provided config function on the '.gitmodules' file found in the
- * working directory.
- */
-void config_from_gitmodules(config_fn_t fn, void *data)
-{
-       if (the_repository->worktree) {
-               char *file = repo_worktree_path(the_repository, GITMODULES_FILE);
-               git_config_from_file(fn, file, data);
-               free(file);
-       }
-}
-
 int git_config_get_expiry(const char *key, const char **output)
 {
        int ret = git_config_get_string_const(key, output);
@@ -2249,7 +2283,7 @@ int git_config_get_max_percent_split_change(void)
 int git_config_get_fsmonitor(void)
 {
        if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
-               core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
+               core_fsmonitor = getenv("GIT_TEST_FSMONITOR");
 
        if (core_fsmonitor && !*core_fsmonitor)
                core_fsmonitor = NULL;
@@ -2260,6 +2294,27 @@ int git_config_get_fsmonitor(void)
        return 0;
 }
 
+int git_config_get_index_threads(int *dest)
+{
+       int is_bool, val;
+
+       val = git_env_ulong("GIT_TEST_INDEX_THREADS", 0);
+       if (val) {
+               *dest = val;
+               return 0;
+       }
+
+       if (!git_config_get_bool_or_int("index.threads", &is_bool, &val)) {
+               if (is_bool)
+                       *dest = val ? 0 : 1;
+               else
+                       *dest = val;
+               return 0;
+       }
+
+       return 1;
+}
+
 NORETURN
 void git_die_config_linenr(const char *key, const char *filename, int linenr)
 {
@@ -2306,6 +2361,19 @@ struct config_store_data {
        unsigned int key_seen:1, section_seen:1, is_keys_section:1;
 };
 
+static void config_store_data_clear(struct config_store_data *store)
+{
+       free(store->key);
+       if (store->value_regex != NULL &&
+           store->value_regex != CONFIG_REGEX_NONE) {
+               regfree(store->value_regex);
+               free(store->value_regex);
+       }
+       free(store->parsed);
+       free(store->seen);
+       memset(store, 0, sizeof(*store));
+}
+
 static int matches(const char *key, const char *value,
                   const struct config_store_data *store)
 {
@@ -2334,7 +2402,7 @@ static int store_aux_event(enum config_event_t type,
                int (*cmpfn)(const char *, const char *, size_t);
 
                if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
-                       return error("invalid section name '%s'", cf->var.buf);
+                       return error(_("invalid section name '%s'"), cf->var.buf);
 
                if (cf->subsection_case_sensitive)
                        cmpfn = strncasecmp;
@@ -2395,7 +2463,7 @@ static int store_aux(const char *key, const char *value, void *cb)
 
 static int write_error(const char *filename)
 {
-       error("failed to write new configuration file %s", filename);
+       error(_("failed to write new configuration file %s"), filename);
 
        /* Same error code as "failed to rename". */
        return 4;
@@ -2646,8 +2714,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
         */
        fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (fd < 0) {
-               error_errno("could not lock config file %s", config_filename);
-               free(store.key);
+               error_errno(_("could not lock config file %s"), config_filename);
                ret = CONFIG_NO_LOCK;
                goto out_free;
        }
@@ -2657,10 +2724,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
         */
        in_fd = open(config_filename, O_RDONLY);
        if ( in_fd < 0 ) {
-               free(store.key);
-
                if ( ENOENT != errno ) {
-                       error_errno("opening %s", config_filename);
+                       error_errno(_("opening %s"), config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
@@ -2670,7 +2735,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                        goto out_free;
                }
 
-               store.key = (char *)key;
+               free(store.key);
+               store.key = xstrdup(key);
                if (write_section(fd, key, &store) < 0 ||
                    write_pair(fd, key, value, &store) < 0)
                        goto write_err_out;
@@ -2694,8 +2760,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                        store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
                        if (regcomp(store.value_regex, value_regex,
                                        REG_EXTENDED)) {
-                               error("invalid pattern: %s", value_regex);
-                               free(store.value_regex);
+                               error(_("invalid pattern: %s"), value_regex);
+                               FREE_AND_NULL(store.value_regex);
                                ret = CONFIG_INVALID_PATTERN;
                                goto out_free;
                        }
@@ -2719,24 +2785,11 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                if (git_config_from_file_with_options(store_aux,
                                                      config_filename,
                                                      &store, &opts)) {
-                       error("invalid config file %s", config_filename);
-                       free(store.key);
-                       if (store.value_regex != NULL &&
-                           store.value_regex != CONFIG_REGEX_NONE) {
-                               regfree(store.value_regex);
-                               free(store.value_regex);
-                       }
+                       error(_("invalid config file %s"), config_filename);
                        ret = CONFIG_INVALID_FILE;
                        goto out_free;
                }
 
-               free(store.key);
-               if (store.value_regex != NULL &&
-                   store.value_regex != CONFIG_REGEX_NONE) {
-                       regfree(store.value_regex);
-                       free(store.value_regex);
-               }
-
                /* if nothing to unset, or too many matches, error out */
                if ((store.seen_nr == 0 && value == NULL) ||
                    (store.seen_nr > 1 && multi_replace == 0)) {
@@ -2756,7 +2809,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
-                       error_errno("unable to mmap '%s'", config_filename);
+                       error_errno(_("unable to mmap '%s'"), config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
@@ -2765,7 +2818,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                in_fd = -1;
 
                if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
-                       error_errno("chmod on %s failed", get_lock_file_path(&lock));
+                       error_errno(_("chmod on %s failed"), get_lock_file_path(&lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2850,7 +2903,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        }
 
        if (commit_lock_file(&lock) < 0) {
-               error_errno("could not write config file %s", config_filename);
+               error_errno(_("could not write config file %s"), config_filename);
                ret = CONFIG_NO_WRITE;
                goto out_free;
        }
@@ -2867,6 +2920,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                munmap(contents, contents_sz);
        if (in_fd >= 0)
                close(in_fd);
+       config_store_data_clear(&store);
        return ret;
 
 write_err_out:
@@ -2975,7 +3029,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        memset(&store, 0, sizeof(store));
 
        if (new_name && !section_name_is_ok(new_name)) {
-               ret = error("invalid section name: %s", new_name);
+               ret = error(_("invalid section name: %s"), new_name);
                goto out_no_rollback;
        }
 
@@ -2984,7 +3038,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
 
        out_fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (out_fd < 0) {
-               ret = error("could not lock config file %s", config_filename);
+               ret = error(_("could not lock config file %s"), config_filename);
                goto out;
        }
 
@@ -3002,7 +3056,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        }
 
        if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
-               ret = error_errno("chmod on %s failed",
+               ret = error_errno(_("chmod on %s failed"),
                                  get_lock_file_path(&lock));
                goto out;
        }
@@ -3099,7 +3153,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        config_file = NULL;
 commit_and_out:
        if (commit_lock_file(&lock) < 0)
-               ret = error_errno("could not write config file %s",
+               ret = error_errno(_("could not write config file %s"),
                                  config_filename);
 out:
        if (config_file)
@@ -3107,6 +3161,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        rollback_lock_file(&lock);
 out_no_rollback:
        free(filename_buf);
+       config_store_data_clear(&store);
        return ret;
 }
 
@@ -3141,7 +3196,7 @@ int git_config_copy_section(const char *old_name, const char *new_name)
 #undef config_error_nonbool
 int config_error_nonbool(const char *var)
 {
-       return error("missing value for '%s'", var);
+       return error(_("missing value for '%s'"), var);
 }
 
 int parse_config_key(const char *var,
@@ -3188,7 +3243,7 @@ const char *current_config_origin_type(void)
        else if(cf)
                type = cf->origin_type;
        else
-               die("BUG: current_config_origin_type called outside config callback");
+               BUG("current_config_origin_type called outside config callback");
 
        switch (type) {
        case CONFIG_ORIGIN_BLOB:
@@ -3202,7 +3257,7 @@ const char *current_config_origin_type(void)
        case CONFIG_ORIGIN_CMDLINE:
                return "command line";
        default:
-               die("BUG: unknown config origin type");
+               BUG("unknown config origin type");
        }
 }
 
@@ -3214,7 +3269,7 @@ const char *current_config_name(void)
        else if (cf)
                name = cf->name;
        else
-               die("BUG: current_config_name called outside config callback");
+               BUG("current_config_name called outside config callback");
        return name ? name : "";
 }
 
@@ -3225,3 +3280,16 @@ enum config_scope current_config_scope(void)
        else
                return current_parsing_scope;
 }
+
+int lookup_config(const char **mapping, int nr_mapping, const char *var)
+{
+       int i;
+
+       for (i = 0; i < nr_mapping; i++) {
+               const char *name = mapping[i];
+
+               if (name && !strcasecmp(var, name))
+                       return i;
+       }
+       return -1;
+}