*
*/
#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;
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;
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;
}
}
#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;
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;
/*
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)
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");
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))
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;
}
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)
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);
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)) {
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;
}
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;
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)))
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")) {
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;
}
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;
}
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;
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;
}
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)
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;
}
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;
}
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);
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")) {
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)
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;
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;
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,
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;
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);
}
{
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;
}
if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir);
+ else if (opts->git_dir)
+ BUG("git_dir without commondir");
else
repo_config = NULL;
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"));
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;
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);
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;
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)
{
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)
{
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;
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;
*/
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;
}
*/
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;
}
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;
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;
}
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)) {
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;
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;
}
}
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;
}
munmap(contents, contents_sz);
if (in_fd >= 0)
close(in_fd);
+ config_store_data_clear(&store);
return ret;
write_err_out:
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;
}
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;
}
}
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;
}
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)
rollback_lock_file(&lock);
out_no_rollback:
free(filename_buf);
+ config_store_data_clear(&store);
return ret;
}
#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,
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:
case CONFIG_ORIGIN_CMDLINE:
return "command line";
default:
- die("BUG: unknown config origin type");
+ BUG("unknown config origin type");
}
}
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 : "";
}
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;
+}