]> git.ipfire.org Git - thirdparty/git.git/blobdiff - config.c
Merge branch 'km/submodule-doc-use-sm-path' into maint
[thirdparty/git.git] / config.c
index 3461993f0af665d64b48d16915fc2cd017634377..d75f88ca0ce31f1a9b6b3a8a2198c4f94a4196b5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -19,6 +19,7 @@
 #include "utf8.h"
 #include "dir.h"
 #include "color.h"
+#include "refs.h"
 
 struct config_source {
        struct config_source *prev;
@@ -170,6 +171,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        return ret;
 }
 
+static void add_trailing_starstar_for_dir(struct strbuf *pat)
+{
+       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+               strbuf_addstr(pat, "**");
+}
+
 static int prepare_include_condition_pattern(struct strbuf *pat)
 {
        struct strbuf path = STRBUF_INIT;
@@ -199,8 +206,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        } else if (!is_absolute_path(pat->buf))
                strbuf_insert(pat, 0, "**/", 3);
 
-       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
-               strbuf_addstr(pat, "**");
+       add_trailing_starstar_for_dir(pat);
 
        strbuf_release(&path);
        return prefix;
@@ -242,7 +248,7 @@ again:
        }
 
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
-                        icase ? WM_CASEFOLD : 0);
+                        WM_PATHNAME | (icase ? WM_CASEFOLD : 0));
 
        if (!ret && !already_tried_absolute) {
                /*
@@ -264,6 +270,26 @@ done:
        return ret;
 }
 
+static int include_by_branch(const char *cond, size_t cond_len)
+{
+       int flags;
+       int ret;
+       struct strbuf pattern = STRBUF_INIT;
+       const char *refname = !the_repository->gitdir ?
+               NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+       const char *shortname;
+
+       if (!refname || !(flags & REF_ISSYMREF) ||
+                       !skip_prefix(refname, "refs/heads/", &shortname))
+               return 0;
+
+       strbuf_add(&pattern, cond, cond_len);
+       add_trailing_starstar_for_dir(&pattern);
+       ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
+       strbuf_release(&pattern);
+       return ret;
+}
+
 static int include_condition_is_true(const struct config_options *opts,
                                     const char *cond, size_t cond_len)
 {
@@ -272,6 +298,8 @@ static int include_condition_is_true(const struct config_options *opts,
                return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
                return include_by_gitdir(opts, cond, cond_len, 1);
+       else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
+               return include_by_branch(cond, cond_len);
 
        /* unknown conditionals are always false */
        return 0;
@@ -834,22 +862,16 @@ static int git_parse_source(config_fn_t fn, void *data,
        return error_return;
 }
 
-static int parse_unit_factor(const char *end, uintmax_t *val)
+static uintmax_t get_unit_factor(const char *end)
 {
        if (!*end)
                return 1;
-       else if (!strcasecmp(end, "k")) {
-               *val *= 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "m")) {
-               *val *= 1024 * 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "g")) {
-               *val *= 1024 * 1024 * 1024;
-               return 1;
-       }
+       else if (!strcasecmp(end, "k"))
+               return 1024;
+       else if (!strcasecmp(end, "m"))
+               return 1024 * 1024;
+       else if (!strcasecmp(end, "g"))
+               return 1024 * 1024 * 1024;
        return 0;
 }
 
@@ -859,19 +881,20 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
                char *end;
                intmax_t val;
                uintmax_t uval;
-               uintmax_t factor = 1;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               uval = labs(val);
-               uval *= factor;
-               if (uval > max || labs(val) > uval) {
+               uval = val < 0 ? -val : val;
+               if (unsigned_mult_overflows(factor, uval) ||
+                   factor * uval > max) {
                        errno = ERANGE;
                        return 0;
                }
@@ -888,21 +911,23 @@ static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
        if (value && *value) {
                char *end;
                uintmax_t val;
-               uintmax_t oldval;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoumax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               oldval = val;
-               if (!parse_unit_factor(end, &val)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               if (val > max || oldval > val) {
+               if (unsigned_mult_overflows(factor, val) ||
+                   factor * val > max) {
                        errno = ERANGE;
                        return 0;
                }
+               val *= factor;
                *ret = val;
                return 1;
        }
@@ -949,34 +974,44 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
 NORETURN
 static void die_bad_number(const char *name, const char *value)
 {
-       const char * error_type = (errno == ERANGE)? _("out of range"):_("invalid unit");
+       const char *error_type = (errno == ERANGE) ?
+               N_("out of range") : N_("invalid unit");
+       const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
 
        if (!value)
                value = "";
 
+       if (!strcmp(name, "GIT_TEST_GETTEXT_POISON"))
+               /*
+                * We explicitly *don't* use _() here since it would
+                * cause an infinite loop with _() needing to call
+                * use_gettext_poison(). This is why marked up
+                * translations with N_() above.
+                */
+               die(bad_numeric, value, name, error_type);
+
        if (!(cf && cf->name))
-               die(_("bad numeric config value '%s' for '%s': %s"),
-                   value, name, error_type);
+               die(_(bad_numeric), value, name, _(error_type));
 
        switch (cf->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
-                   value, name, cf->name, error_type);
+                   value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_FILE:
                die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
-                   value, name, cf->name, error_type);
+                   value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_STDIN:
                die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
-                   value, name, error_type);
+                   value, name, _(error_type));
        case CONFIG_ORIGIN_SUBMODULE_BLOB:
                die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
-                   value, name, cf->name, error_type);
+                   value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_CMDLINE:
                die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
-                   value, name, cf->name, error_type);
+                   value, name, cf->name, _(error_type));
        default:
                die(_("bad numeric config value '%s' for '%s' in %s: %s"),
-                   value, name, cf->name, error_type);
+                   value, name, cf->name, _(error_type));
        }
 }
 
@@ -1093,7 +1128,7 @@ int git_config_color(char *dest, const char *var, const char *value)
        return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value)
+static int git_default_core_config(const char *var, const char *value, void *cb)
 {
        /* This needs a better name */
        if (!strcmp(var, "core.filemode")) {
@@ -1169,7 +1204,7 @@ static int git_default_core_config(const char *var, const char *value)
                        default_abbrev = -1;
                else {
                        int abbrev = git_config_int(var, value);
-                       if (abbrev < minimum_abbrev || abbrev > 40)
+                       if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
                                return error(_("abbrev length out of range: %d"), abbrev);
                        default_abbrev = abbrev;
                }
@@ -1329,6 +1364,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.sparsecheckoutcone")) {
+               core_sparse_checkout_cone = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.precomposeunicode")) {
                precomposed_unicode = git_config_bool(var, value);
                return 0;
@@ -1344,26 +1384,13 @@ 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);
-               return 0;
-       }
-
-       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)
@@ -1448,13 +1475,15 @@ 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);
+       if (starts_with(var, "user.") ||
+           starts_with(var, "author.") ||
+           starts_with(var, "committer."))
+               return git_ident_config(var, value, cb);
 
        if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
@@ -1676,11 +1705,15 @@ 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;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
-       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
+       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
+                                                 opts->system_gently ?
+                                                 ACCESS_EACCES_OK : 0))
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
 
@@ -1692,11 +1725,23 @@ static int do_git_config_sequence(const struct config_options *opts,
                ret += git_config_from_file(fn, user_config, data);
 
        current_parsing_scope = CONFIG_SCOPE_REPO;
-       if (repo_config && !access_or_die(repo_config, R_OK, 0))
+       if (!opts->ignore_repo && 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 (!opts->ignore_worktree && 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)
+       if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 
        current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
@@ -1787,6 +1832,23 @@ void read_early_config(config_fn_t cb, void *data)
        strbuf_release(&gitdir);
 }
 
+/*
+ * Read config but only enumerate system and global settings.
+ * Omit any repo-local, worktree-local, or command-line settings.
+ */
+void read_very_early_config(config_fn_t cb, void *data)
+{
+       struct config_options opts = { 0 };
+
+       opts.respect_includes = 1;
+       opts.ignore_repo = 1;
+       opts.ignore_worktree = 1;
+       opts.ignore_cmdline = 1;
+       opts.system_gently = 1;
+
+       config_with_options(cb, data, NULL, &opts);
+}
+
 static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
 {
        struct config_set_element k;
@@ -1799,9 +1861,9 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
        if (git_config_parse_key(key, &normalized_key, NULL))
                return NULL;
 
-       hashmap_entry_init(&k, strhash(normalized_key));
+       hashmap_entry_init(&k.ent, strhash(normalized_key));
        k.key = normalized_key;
-       found_entry = hashmap_get(&cs->config_hash, &k, NULL);
+       found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
        free(normalized_key);
        return found_entry;
 }
@@ -1820,10 +1882,10 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
         */
        if (!e) {
                e = xmalloc(sizeof(*e));
-               hashmap_entry_init(e, strhash(key));
+               hashmap_entry_init(&e->ent, strhash(key));
                e->key = xstrdup(key);
                string_list_init(&e->value_list, 1);
-               hashmap_add(&cs->config_hash, e);
+               hashmap_add(&cs->config_hash, &e->ent);
        }
        si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
 
@@ -1851,12 +1913,14 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
 }
 
 static int config_set_element_cmp(const void *unused_cmp_data,
-                                 const void *entry,
-                                 const void *entry_or_key,
+                                 const struct hashmap_entry *eptr,
+                                 const struct hashmap_entry *entry_or_key,
                                  const void *unused_keydata)
 {
-       const struct config_set_element *e1 = entry;
-       const struct config_set_element *e2 = entry_or_key;
+       const struct config_set_element *e1, *e2;
+
+       e1 = container_of(eptr, const struct config_set_element, ent);
+       e2 = container_of(entry_or_key, const struct config_set_element, ent);
 
        return strcmp(e1->key, e2->key);
 }
@@ -1877,12 +1941,12 @@ void git_configset_clear(struct config_set *cs)
        if (!cs->hash_initialized)
                return;
 
-       hashmap_iter_init(&cs->config_hash, &iter);
-       while ((entry = hashmap_iter_next(&iter))) {
+       hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+                               ent /* member name */) {
                free(entry->key);
                string_list_clear(&entry->value_list, 1);
        }
-       hashmap_free(&cs->config_hash, 1);
+       hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
        cs->hash_initialized = 0;
        free(cs->list.items);
        cs->list.nr = 0;
@@ -2004,7 +2068,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
 /* Functions use to read configuration from a repository */
 static void repo_read_config(struct repository *repo)
 {
-       struct config_options opts;
+       struct config_options opts = { 0 };
 
        opts.respect_includes = 1;
        opts.commondir = repo->commondir;
@@ -2226,30 +2290,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestam
        return -1; /* thing exists but cannot be parsed */
 }
 
-int git_config_get_untracked_cache(void)
-{
-       int val = -1;
-       const char *v;
-
-       /* Hack for test programs like test-dump-untracked-cache */
-       if (ignore_untracked_cache_config)
-               return -1;
-
-       if (!git_config_get_maybe_bool("core.untrackedcache", &val))
-               return val;
-
-       if (!git_config_get_value("core.untrackedcache", &v)) {
-               if (!strcasecmp(v, "keep"))
-                       return -1;
-
-               error(_("unknown core.untrackedCache value '%s'; "
-                       "using 'keep' default value"), v);
-               return -1;
-       }
-
-       return -1; /* default value */
-}
-
 int git_config_get_split_index(void)
 {
        int val;
@@ -2278,7 +2318,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;
@@ -2289,6 +2329,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)
 {
@@ -2539,7 +2600,6 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
  * entry (which all are to be removed).
  */
 static void maybe_remove_section(struct config_store_data *store,
-                                const char *contents,
                                 size_t *begin_offset, size_t *end_offset,
                                 int *seen_ptr)
 {
@@ -2630,6 +2690,8 @@ int git_config_set_gently(const char *key, const char *value)
 void git_config_set(const char *key, const char *value)
 {
        git_config_set_multivar(key, value, NULL, 0);
+
+       trace2_cmd_set_config(key, value);
 }
 
 /*
@@ -2824,7 +2886,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                                replace_end = store.parsed[j].end;
                                copy_end = store.parsed[j].begin;
                                if (!value)
-                                       maybe_remove_section(&store, contents,
+                                       maybe_remove_section(&store,
                                                             &copy_end,
                                                             &replace_end, &i);
                                /*