summaryrefslogtreecommitdiff
path: root/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'config.c')
-rw-r--r--config.c962
1 files changed, 361 insertions, 601 deletions
diff --git a/config.c b/config.c
index a11bb85da3..71b136bf7f 100644
--- a/config.c
+++ b/config.c
@@ -6,31 +6,27 @@
*
*/
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
#include "date.h"
#include "branch.h"
#include "config.h"
+#include "dir.h"
#include "parse.h"
#include "convert.h"
#include "environment.h"
#include "gettext.h"
-#include "ident.h"
+#include "git-zlib.h"
#include "repository.h"
#include "lockfile.h"
-#include "mailmap.h"
-#include "attr.h"
#include "exec-cmd.h"
#include "strbuf.h"
#include "quote.h"
#include "hashmap.h"
#include "string-list.h"
#include "object-name.h"
-#include "object-store-ll.h"
-#include "pager.h"
+#include "odb.h"
#include "path.h"
#include "utf8.h"
#include "color.h"
@@ -39,7 +35,6 @@
#include "strvec.h"
#include "trace2.h"
#include "wildmatch.h"
-#include "ws.h"
#include "write-or-die.h"
struct config_source {
@@ -54,7 +49,6 @@ struct config_source {
} u;
enum config_origin_type origin_type;
const char *name;
- const char *path;
enum config_error_action default_error_action;
int linenr;
int eof;
@@ -69,9 +63,6 @@ struct config_source {
};
#define CONFIG_SOURCE_INIT { 0 }
-static int pack_compression_seen;
-static int zlib_compression_seen;
-
/*
* Config that comes from trusted scopes, namely:
* - CONFIG_SCOPE_SYSTEM (e.g. /etc/gitconfig)
@@ -171,14 +162,14 @@ static int handle_path_include(const struct key_value_info *kvi,
if (!is_absolute_path(path)) {
char *slash;
- if (!kvi || !kvi->path) {
+ if (!kvi || kvi->origin_type != CONFIG_ORIGIN_FILE) {
ret = error(_("relative config includes must come from files"));
goto cleanup;
}
- slash = find_last_dir_sep(kvi->path);
+ slash = find_last_dir_sep(kvi->filename);
if (slash)
- strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
+ strbuf_add(&buf, kvi->filename, slash - kvi->filename + 1);
strbuf_addstr(&buf, path);
path = buf.buf;
}
@@ -206,11 +197,12 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
}
static int prepare_include_condition_pattern(const struct key_value_info *kvi,
- struct strbuf *pat)
+ struct strbuf *pat,
+ size_t *out)
{
struct strbuf path = STRBUF_INIT;
char *expanded;
- int prefix = 0;
+ size_t prefix = 0;
expanded = interpolate_path(pat->buf, 1);
if (expanded) {
@@ -222,11 +214,11 @@ static int prepare_include_condition_pattern(const struct key_value_info *kvi,
if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
const char *slash;
- if (!kvi || !kvi->path)
+ if (!kvi || kvi->origin_type != CONFIG_ORIGIN_FILE)
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_realpath(&path, kvi->path, 1);
+ strbuf_realpath(&path, kvi->filename, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
BUG("how is this possible?");
@@ -237,8 +229,10 @@ static int prepare_include_condition_pattern(const struct key_value_info *kvi,
add_trailing_starstar_for_dir(pat);
+ *out = prefix;
+
strbuf_release(&path);
- return prefix;
+ return 0;
}
static int include_by_gitdir(const struct key_value_info *kvi,
@@ -247,7 +241,8 @@ static int include_by_gitdir(const struct key_value_info *kvi,
{
struct strbuf text = STRBUF_INIT;
struct strbuf pattern = STRBUF_INIT;
- int ret = 0, prefix;
+ size_t prefix;
+ int ret = 0;
const char *git_dir;
int already_tried_absolute = 0;
@@ -258,12 +253,11 @@ static int include_by_gitdir(const struct key_value_info *kvi,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(kvi, &pattern);
-
-again:
- if (prefix < 0)
+ ret = prepare_include_condition_pattern(kvi, &pattern, &prefix);
+ if (ret < 0)
goto done;
+again:
if (prefix > 0) {
/*
* perform literal matching on the prefix part so that
@@ -631,38 +625,34 @@ void kvi_from_param(struct key_value_info *out)
out->linenr = -1;
out->origin_type = CONFIG_ORIGIN_CMDLINE;
out->scope = CONFIG_SCOPE_COMMAND;
- out->path = NULL;
}
int git_config_parse_parameter(const char *text,
config_fn_t fn, void *data)
{
const char *value;
- struct strbuf **pair;
+ struct string_list pair = STRING_LIST_INIT_DUP;
int ret;
struct key_value_info kvi = KVI_INIT;
kvi_from_param(&kvi);
- pair = strbuf_split_str(text, '=', 2);
- if (!pair[0])
+ string_list_split(&pair, text, "=", 1);
+ if (!pair.nr)
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);
- value = pair[1] ? pair[1]->buf : "";
- } else {
+ if (pair.nr == 1)
value = NULL;
- }
+ else
+ value = pair.items[1].string;
- strbuf_trim(pair[0]);
- if (!pair[0]->len) {
- strbuf_list_free(pair);
+ if (!*pair.items[0].string) {
+ string_list_clear(&pair, 0);
return error(_("bogus config parameter: %s"), text);
}
- ret = config_parse_pair(pair[0]->buf, value, &kvi, fn, data);
- strbuf_list_free(pair);
+ ret = config_parse_pair(pair.items[0].string, value, &kvi, fn, data);
+ string_list_clear(&pair, 0);
return ret;
}
@@ -732,7 +722,6 @@ int git_config_from_parameters(config_fn_t fn, void *data)
if (env) {
unsigned long count;
char *endp;
- int i;
count = strtoul(env, &endp, 10);
if (*endp) {
@@ -744,10 +733,10 @@ int git_config_from_parameters(config_fn_t fn, void *data)
goto out;
}
- for (i = 0; i < count; i++) {
+ for (unsigned long i = 0; i < count; i++) {
const char *key, *value;
- strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i);
+ strbuf_addf(&envvar, "GIT_CONFIG_KEY_%lu", i);
key = getenv_safe(&to_free, envvar.buf);
if (!key) {
ret = error(_("missing config key %s"), envvar.buf);
@@ -755,7 +744,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
}
strbuf_reset(&envvar);
- strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i);
+ strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%lu", i);
value = getenv_safe(&to_free, envvar.buf);
if (!value) {
ret = error(_("missing config value %s"), envvar.buf);
@@ -1034,7 +1023,6 @@ static void kvi_from_source(struct config_source *cs,
out->origin_type = cs->origin_type;
out->linenr = cs->linenr;
out->scope = scope;
- out->path = cs->path;
}
static int git_parse_source(struct config_source *cs, config_fn_t fn,
@@ -1260,80 +1248,6 @@ double git_config_double(const char *name, const char *value,
return ret;
}
-static const struct fsync_component_name {
- const char *name;
- enum fsync_component component_bits;
-} fsync_component_names[] = {
- { "loose-object", FSYNC_COMPONENT_LOOSE_OBJECT },
- { "pack", FSYNC_COMPONENT_PACK },
- { "pack-metadata", FSYNC_COMPONENT_PACK_METADATA },
- { "commit-graph", FSYNC_COMPONENT_COMMIT_GRAPH },
- { "index", FSYNC_COMPONENT_INDEX },
- { "objects", FSYNC_COMPONENTS_OBJECTS },
- { "reference", FSYNC_COMPONENT_REFERENCE },
- { "derived-metadata", FSYNC_COMPONENTS_DERIVED_METADATA },
- { "committed", FSYNC_COMPONENTS_COMMITTED },
- { "added", FSYNC_COMPONENTS_ADDED },
- { "all", FSYNC_COMPONENTS_ALL },
-};
-
-static enum fsync_component parse_fsync_components(const char *var, const char *string)
-{
- enum fsync_component current = FSYNC_COMPONENTS_PLATFORM_DEFAULT;
- enum fsync_component positive = 0, negative = 0;
-
- while (string) {
- int i;
- size_t len;
- const char *ep;
- int negated = 0;
- int found = 0;
-
- string = string + strspn(string, ", \t\n\r");
- ep = strchrnul(string, ',');
- len = ep - string;
- if (!strcmp(string, "none")) {
- current = FSYNC_COMPONENT_NONE;
- goto next_name;
- }
-
- if (*string == '-') {
- negated = 1;
- string++;
- len--;
- if (!len)
- warning(_("invalid value for variable %s"), var);
- }
-
- if (!len)
- break;
-
- for (i = 0; i < ARRAY_SIZE(fsync_component_names); ++i) {
- const struct fsync_component_name *n = &fsync_component_names[i];
-
- if (strncmp(n->name, string, len))
- continue;
-
- found = 1;
- if (negated)
- negative |= n->component_bits;
- else
- positive |= n->component_bits;
- }
-
- if (!found) {
- char *component = xstrndup(string, len);
- warning(_("ignoring unknown core.fsync component '%s'"), component);
- free(component);
- }
-
-next_name:
- string = ep;
- }
-
- return (current & ~negative) | positive;
-}
-
int git_config_bool_or_int(const char *name, const char *value,
const struct key_value_info *kvi, int *is_bool)
{
@@ -1364,11 +1278,23 @@ int git_config_string(char **dest, const char *var, const char *value)
int git_config_pathname(char **dest, const char *var, const char *value)
{
+ int is_optional;
+ char *path;
+
if (!value)
return config_error_nonbool(var);
- *dest = interpolate_path(value, 0);
- if (!*dest)
+
+ is_optional = skip_prefix(value, ":(optional)", &value);
+ path = interpolate_path(value, 0);
+ if (!path)
die(_("failed to expand user dir in: '%s'"), value);
+
+ if (is_optional && is_missing_file(path)) {
+ free(path);
+ return 0;
+ }
+
+ *dest = path;
return 0;
}
@@ -1391,470 +1317,6 @@ 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,
- const struct config_context *ctx, void *cb)
-{
- /* This needs a better name */
- if (!strcmp(var, "core.filemode")) {
- trust_executable_bit = git_config_bool(var, value);
- return 0;
- }
- if (!strcmp(var, "core.trustctime")) {
- trust_ctime = git_config_bool(var, value);
- return 0;
- }
- if (!strcmp(var, "core.checkstat")) {
- if (!value)
- return config_error_nonbool(var);
- if (!strcasecmp(value, "default"))
- check_stat = 1;
- else if (!strcasecmp(value, "minimal"))
- check_stat = 0;
- else
- return error(_("invalid value for '%s': '%s'"),
- var, value);
- }
-
- if (!strcmp(var, "core.quotepath")) {
- quote_path_fully = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.symlinks")) {
- has_symlinks = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.ignorecase")) {
- ignore_case = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.attributesfile")) {
- FREE_AND_NULL(git_attributes_file);
- return git_config_pathname(&git_attributes_file, var, value);
- }
-
- if (!strcmp(var, "core.hookspath")) {
- FREE_AND_NULL(git_hooks_path);
- return git_config_pathname(&git_hooks_path, var, value);
- }
-
- if (!strcmp(var, "core.bare")) {
- is_bare_repository_cfg = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.ignorestat")) {
- assume_unchanged = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.abbrev")) {
- if (!value)
- return config_error_nonbool(var);
- if (!strcasecmp(value, "auto"))
- default_abbrev = -1;
- else if (!git_parse_maybe_bool_text(value))
- default_abbrev = GIT_MAX_HEXSZ;
- else {
- int abbrev = git_config_int(var, value, ctx->kvi);
- if (abbrev < minimum_abbrev)
- return error(_("abbrev length out of range: %d"), abbrev);
- default_abbrev = abbrev;
- }
- return 0;
- }
-
- if (!strcmp(var, "core.disambiguate"))
- return set_disambiguate_hint_config(var, value);
-
- if (!strcmp(var, "core.loosecompression")) {
- int level = git_config_int(var, value, ctx->kvi);
- if (level == -1)
- level = Z_DEFAULT_COMPRESSION;
- else if (level < 0 || level > Z_BEST_COMPRESSION)
- die(_("bad zlib compression level %d"), level);
- zlib_compression_level = level;
- zlib_compression_seen = 1;
- return 0;
- }
-
- if (!strcmp(var, "core.compression")) {
- int level = git_config_int(var, value, ctx->kvi);
- if (level == -1)
- level = Z_DEFAULT_COMPRESSION;
- else if (level < 0 || level > Z_BEST_COMPRESSION)
- die(_("bad zlib compression level %d"), level);
- if (!zlib_compression_seen)
- zlib_compression_level = level;
- if (!pack_compression_seen)
- pack_compression_level = level;
- return 0;
- }
-
- if (!strcmp(var, "core.packedgitwindowsize")) {
- int pgsz_x2 = getpagesize() * 2;
- packed_git_window_size = git_config_ulong(var, value, ctx->kvi);
-
- /* This value must be multiple of (pagesize * 2) */
- packed_git_window_size /= pgsz_x2;
- if (packed_git_window_size < 1)
- packed_git_window_size = 1;
- packed_git_window_size *= pgsz_x2;
- return 0;
- }
-
- if (!strcmp(var, "core.bigfilethreshold")) {
- big_file_threshold = git_config_ulong(var, value, ctx->kvi);
- return 0;
- }
-
- if (!strcmp(var, "core.packedgitlimit")) {
- packed_git_limit = git_config_ulong(var, value, ctx->kvi);
- return 0;
- }
-
- if (!strcmp(var, "core.deltabasecachelimit")) {
- delta_base_cache_limit = git_config_ulong(var, value, ctx->kvi);
- return 0;
- }
-
- if (!strcmp(var, "core.autocrlf")) {
- if (value && !strcasecmp(value, "input")) {
- auto_crlf = AUTO_CRLF_INPUT;
- return 0;
- }
- auto_crlf = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.safecrlf")) {
- int eol_rndtrp_die;
- if (value && !strcasecmp(value, "warn")) {
- global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
- return 0;
- }
- eol_rndtrp_die = git_config_bool(var, value);
- global_conv_flags_eol = eol_rndtrp_die ?
- CONV_EOL_RNDTRP_DIE : 0;
- return 0;
- }
-
- if (!strcmp(var, "core.eol")) {
- if (value && !strcasecmp(value, "lf"))
- core_eol = EOL_LF;
- else if (value && !strcasecmp(value, "crlf"))
- core_eol = EOL_CRLF;
- else if (value && !strcasecmp(value, "native"))
- core_eol = EOL_NATIVE;
- else
- core_eol = EOL_UNSET;
- return 0;
- }
-
- if (!strcmp(var, "core.checkroundtripencoding")) {
- FREE_AND_NULL(check_roundtrip_encoding);
- return git_config_string(&check_roundtrip_encoding, var, value);
- }
-
- if (!strcmp(var, "core.editor")) {
- FREE_AND_NULL(editor_program);
- return git_config_string(&editor_program, var, value);
- }
-
- if (!strcmp(var, "core.commentchar") ||
- !strcmp(var, "core.commentstring")) {
- if (!value)
- return config_error_nonbool(var);
- else if (!strcasecmp(value, "auto"))
- auto_comment_line_char = 1;
- else if (value[0]) {
- if (strchr(value, '\n'))
- return error(_("%s cannot contain newline"), var);
- comment_line_str = value;
- FREE_AND_NULL(comment_line_str_to_free);
- auto_comment_line_char = 0;
- } else
- return error(_("%s must have at least one character"), var);
- return 0;
- }
-
- if (!strcmp(var, "core.askpass")) {
- FREE_AND_NULL(askpass_program);
- return git_config_string(&askpass_program, var, value);
- }
-
- if (!strcmp(var, "core.excludesfile")) {
- FREE_AND_NULL(excludes_file);
- return git_config_pathname(&excludes_file, var, value);
- }
-
- if (!strcmp(var, "core.whitespace")) {
- if (!value)
- return config_error_nonbool(var);
- whitespace_rule_cfg = parse_whitespace_rule(value);
- return 0;
- }
-
- if (!strcmp(var, "core.fsync")) {
- if (!value)
- return config_error_nonbool(var);
- fsync_components = parse_fsync_components(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.fsyncmethod")) {
- if (!value)
- return config_error_nonbool(var);
- if (!strcmp(value, "fsync"))
- fsync_method = FSYNC_METHOD_FSYNC;
- else if (!strcmp(value, "writeout-only"))
- fsync_method = FSYNC_METHOD_WRITEOUT_ONLY;
- else if (!strcmp(value, "batch"))
- fsync_method = FSYNC_METHOD_BATCH;
- else
- warning(_("ignoring unknown core.fsyncMethod value '%s'"), value);
-
- }
-
- if (!strcmp(var, "core.fsyncobjectfiles")) {
- if (fsync_object_files < 0)
- warning(_("core.fsyncObjectFiles is deprecated; use core.fsync instead"));
- fsync_object_files = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.preloadindex")) {
- core_preload_index = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.createobject")) {
- if (!value)
- return config_error_nonbool(var);
- if (!strcmp(value, "rename"))
- object_creation_mode = OBJECT_CREATION_USES_RENAMES;
- else if (!strcmp(value, "link"))
- object_creation_mode = OBJECT_CREATION_USES_HARDLINKS;
- else
- die(_("invalid mode for object creation: %s"), value);
- return 0;
- }
-
- if (!strcmp(var, "core.sparsecheckout")) {
- core_apply_sparse_checkout = git_config_bool(var, 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;
- }
-
- if (!strcmp(var, "core.protecthfs")) {
- protect_hfs = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.protectntfs")) {
- protect_ntfs = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "core.maxtreedepth")) {
- max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
- return 0;
- }
-
- /* Add other config variables here and to Documentation/config.txt. */
- return platform_core_config(var, value, ctx, cb);
-}
-
-static int git_default_sparse_config(const char *var, const char *value)
-{
- if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) {
- sparse_expect_files_outside_of_patterns = git_config_bool(var, value);
- return 0;
- }
-
- /* Add other config variables here and to Documentation/config/sparse.txt. */
- return 0;
-}
-
-static int git_default_i18n_config(const char *var, const char *value)
-{
- if (!strcmp(var, "i18n.commitencoding")) {
- FREE_AND_NULL(git_commit_encoding);
- return git_config_string(&git_commit_encoding, var, value);
- }
-
- if (!strcmp(var, "i18n.logoutputencoding")) {
- FREE_AND_NULL(git_log_output_encoding);
- return git_config_string(&git_log_output_encoding, var, value);
- }
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-static int git_default_branch_config(const char *var, const char *value)
-{
- if (!strcmp(var, "branch.autosetupmerge")) {
- if (value && !strcmp(value, "always")) {
- git_branch_track = BRANCH_TRACK_ALWAYS;
- return 0;
- } else if (value && !strcmp(value, "inherit")) {
- git_branch_track = BRANCH_TRACK_INHERIT;
- return 0;
- } else if (value && !strcmp(value, "simple")) {
- git_branch_track = BRANCH_TRACK_SIMPLE;
- return 0;
- }
- git_branch_track = git_config_bool(var, value);
- return 0;
- }
- if (!strcmp(var, "branch.autosetuprebase")) {
- if (!value)
- return config_error_nonbool(var);
- else if (!strcmp(value, "never"))
- autorebase = AUTOREBASE_NEVER;
- else if (!strcmp(value, "local"))
- autorebase = AUTOREBASE_LOCAL;
- else if (!strcmp(value, "remote"))
- autorebase = AUTOREBASE_REMOTE;
- else if (!strcmp(value, "always"))
- autorebase = AUTOREBASE_ALWAYS;
- else
- return error(_("malformed value for %s"), var);
- return 0;
- }
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-static int git_default_push_config(const char *var, const char *value)
-{
- if (!strcmp(var, "push.default")) {
- if (!value)
- return config_error_nonbool(var);
- else if (!strcmp(value, "nothing"))
- push_default = PUSH_DEFAULT_NOTHING;
- else if (!strcmp(value, "matching"))
- push_default = PUSH_DEFAULT_MATCHING;
- else if (!strcmp(value, "simple"))
- push_default = PUSH_DEFAULT_SIMPLE;
- else if (!strcmp(value, "upstream"))
- push_default = PUSH_DEFAULT_UPSTREAM;
- else if (!strcmp(value, "tracking")) /* deprecated */
- push_default = PUSH_DEFAULT_UPSTREAM;
- 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"));
- }
- return 0;
- }
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-static int git_default_mailmap_config(const char *var, const char *value)
-{
- if (!strcmp(var, "mailmap.file")) {
- FREE_AND_NULL(git_mailmap_file);
- return git_config_pathname(&git_mailmap_file, var, value);
- }
-
- if (!strcmp(var, "mailmap.blob")) {
- FREE_AND_NULL(git_mailmap_blob);
- return git_config_string(&git_mailmap_blob, var, value);
- }
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-static int git_default_attr_config(const char *var, const char *value)
-{
- if (!strcmp(var, "attr.tree")) {
- FREE_AND_NULL(git_attr_tree);
- return git_config_string(&git_attr_tree, var, value);
- }
-
- /*
- * Add other attribute related config variables here and to
- * Documentation/config/attr.txt.
- */
- return 0;
-}
-
-int git_default_config(const char *var, const char *value,
- const struct config_context *ctx, void *cb)
-{
- if (starts_with(var, "core."))
- return git_default_core_config(var, value, ctx, cb);
-
- if (starts_with(var, "user.") ||
- starts_with(var, "author.") ||
- starts_with(var, "committer."))
- return git_ident_config(var, value, ctx, cb);
-
- if (starts_with(var, "i18n."))
- return git_default_i18n_config(var, value);
-
- if (starts_with(var, "branch."))
- return git_default_branch_config(var, value);
-
- if (starts_with(var, "push."))
- return git_default_push_config(var, value);
-
- if (starts_with(var, "mailmap."))
- return git_default_mailmap_config(var, value);
-
- if (starts_with(var, "attr."))
- return git_default_attr_config(var, value);
-
- 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")) {
- pager_use_color = git_config_bool(var,value);
- return 0;
- }
-
- if (!strcmp(var, "pack.packsizelimit")) {
- pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi);
- return 0;
- }
-
- if (!strcmp(var, "pack.compression")) {
- int level = git_config_int(var, value, ctx->kvi);
- if (level == -1)
- level = Z_DEFAULT_COMPRESSION;
- else if (level < 0 || level > Z_BEST_COMPRESSION)
- die(_("bad pack compression level %d"), level);
- pack_compression_level = level;
- pack_compression_seen = 1;
- return 0;
- }
-
- if (starts_with(var, "sparse."))
- return git_default_sparse_config(var, value);
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
/*
* All source specific fields in the union, die_on_error, name and the callbacks
* fgetc, ungetc, ftell of top need to be initialized before calling
@@ -1885,17 +1347,19 @@ static int do_config_from(struct config_source *top, config_fn_t fn,
static int do_config_from_file(config_fn_t fn,
const enum config_origin_type origin_type,
- const char *name, const char *path, FILE *f,
- void *data, enum config_scope scope,
+ const char *name, FILE *f, void *data,
+ enum config_scope scope,
const struct config_options *opts)
{
struct config_source top = CONFIG_SOURCE_INIT;
int ret;
+ if (origin_type == CONFIG_ORIGIN_FILE && (!name || !*name))
+ BUG("missing filename for CONFIG_ORIGIN_FILE");
+
top.u.file = f;
top.origin_type = origin_type;
top.name = name;
- top.path = path;
top.default_error_action = CONFIG_ERROR_DIE;
top.do_fgetc = config_file_fgetc;
top.do_ungetc = config_file_ungetc;
@@ -1910,8 +1374,8 @@ static int do_config_from_file(config_fn_t fn,
static int git_config_from_stdin(config_fn_t fn, void *data,
enum config_scope scope)
{
- return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
- data, scope, NULL);
+ return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", stdin, data,
+ scope, NULL);
}
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -1926,7 +1390,7 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
f = fopen_or_warn(filename, "r");
if (f) {
ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
- filename, f, data, scope, opts);
+ f, data, scope, opts);
fclose(f);
}
return ret;
@@ -1951,7 +1415,6 @@ int git_config_from_mem(config_fn_t fn,
top.u.buf.pos = 0;
top.origin_type = origin_type;
top.name = name;
- top.path = NULL;
top.default_error_action = CONFIG_ERROR_ERROR;
top.do_fgetc = config_buf_fgetc;
top.do_ungetc = config_buf_ungetc;
@@ -1972,7 +1435,7 @@ int git_config_from_blob_oid(config_fn_t fn,
unsigned long size;
int ret;
- buf = repo_read_object_file(repo, oid, &type, &size);
+ buf = odb_read_object(repo->objects, oid, &type, &size);
if (!buf)
return error(_("unable to load config blob object '%s'"), name);
if (type != OBJ_BLOB) {
@@ -2160,13 +1623,13 @@ int config_with_options(config_fn_t fn, void *data,
static void configset_iter(struct config_set *set, config_fn_t fn, void *data)
{
- int i, value_index;
+ int value_index;
struct string_list *values;
struct config_set_element *entry;
struct configset_list *list = &set->list;
struct config_context ctx = CONFIG_CONTEXT_INIT;
- for (i = 0; i < list->nr; i++) {
+ for (size_t i = 0; i < list->nr; i++) {
entry = list->items[i].e;
value_index = list->items[i].value_index;
values = &entry->value_list;
@@ -2499,10 +1962,290 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d
return 1;
}
+struct comment_char_config {
+ unsigned last_key_id;
+ bool auto_set;
+ bool auto_set_in_file;
+ struct strintmap key_flags;
+ size_t alloc, nr;
+ struct comment_char_config_item {
+ unsigned key_id;
+ char *path;
+ enum config_scope scope;
+ } *item;
+};
+
+#define COMMENT_CHAR_CFG_INIT { \
+ .key_flags = STRINTMAP_INIT, \
+ }
+
+static void comment_char_config_release(struct comment_char_config *config)
+{
+ strintmap_clear(&config->key_flags);
+ for (size_t i = 0; i < config->nr; i++)
+ free(config->item[i].path);
+ free(config->item);
+}
+
+/* Used to track whether the key occurs more than once in a given file */
+#define KEY_SEEN_ONCE 1u
+#define KEY_SEEN_TWICE 2u
+#define COMMENT_KEY_SHIFT(id) (2 * (id))
+#define COMMENT_KEY_MASK(id) (3u << COMMENT_KEY_SHIFT(id))
+
+static void set_comment_key_flags(struct comment_char_config *config,
+ const char *path, unsigned id, unsigned value)
+{
+ unsigned old = strintmap_get(&config->key_flags, path);
+ unsigned new = (old & ~COMMENT_KEY_MASK(id)) |
+ value << COMMENT_KEY_SHIFT(id);
+
+ strintmap_set(&config->key_flags, path, new);
+}
+
+static unsigned get_comment_key_flags(struct comment_char_config *config,
+ const char *path, unsigned id)
+{
+ unsigned value = strintmap_get(&config->key_flags, path);
+
+ return (value & COMMENT_KEY_MASK(id)) >> COMMENT_KEY_SHIFT(id);
+}
+
+static const char *comment_key_name(unsigned id)
+{
+ static const char *name[] = {
+ "core.commentChar",
+ "core.commentString",
+ };
+
+ if (id >= ARRAY_SIZE(name))
+ BUG("invalid comment key id");
+
+ return name[id];
+}
+
+static void comment_char_callback(const char *key, const char *value,
+ const struct config_context *ctx, void *data)
+{
+ struct comment_char_config *config = data;
+ const struct key_value_info *kvi = ctx->kvi;
+ unsigned key_id;
+
+ if (!strcmp(key, "core.commentchar"))
+ key_id = 0;
+ else if (!strcmp(key, "core.commentstring"))
+ key_id = 1;
+ else
+ return;
+
+ config->last_key_id = key_id;
+ config->auto_set = value && !strcmp(value, "auto");
+ if (kvi->origin_type != CONFIG_ORIGIN_FILE) {
+ return;
+ } else if (get_comment_key_flags(config, kvi->filename, key_id)) {
+ set_comment_key_flags(config, kvi->filename, key_id,
+ KEY_SEEN_TWICE);
+ } else {
+ struct comment_char_config_item *item;
+
+ ALLOC_GROW_BY(config->item, config->nr, 1, config->alloc);
+ item = &config->item[config->nr - 1];
+ item->key_id = key_id;
+ item->scope = kvi->scope;
+ item->path = xstrdup(kvi->filename);
+ set_comment_key_flags(config, kvi->filename, key_id,
+ KEY_SEEN_ONCE);
+ }
+ config->auto_set_in_file = config->auto_set;
+}
+
+static void add_config_scope_arg(struct repository *repo, struct strbuf *buf,
+ struct comment_char_config_item *item)
+{
+ char *global_config = git_global_config();
+ char *system_config = git_system_config();
+
+ if (item->scope == CONFIG_SCOPE_SYSTEM && access(item->path, W_OK)) {
+ /*
+ * If the user cannot write to the system config recommend
+ * setting the global config instead.
+ */
+ strbuf_addstr(buf, "--global ");
+ } else if (fspatheq(item->path, system_config)) {
+ strbuf_addstr(buf, "--system ");
+ } else if (fspatheq(item->path, global_config)) {
+ strbuf_addstr(buf, "--global ");
+ } else if (fspatheq(item->path,
+ mkpath("%s/config",
+ repo_get_git_dir(repo)))) {
+ ; /* --local is the default */
+ } else if (fspatheq(item->path,
+ mkpath("%s/config.worktree",
+ repo_get_common_dir(repo)))) {
+ strbuf_addstr(buf, "--worktree ");
+ } else {
+ const char *path = item->path;
+ const char *home = getenv("HOME");
+
+ strbuf_addstr(buf, "--file ");
+ if (home && !fspathncmp(path, home, strlen(home))) {
+ path += strlen(home);
+ if (!fspathncmp(path, "/", 1))
+ path++;
+ strbuf_addstr(buf, "~/");
+ }
+ sq_quote_buf_pretty(buf, path);
+ strbuf_addch(buf, ' ');
+ }
+
+ free(global_config);
+ free(system_config);
+}
+
+static bool can_unset_comment_char_config(struct comment_char_config *config)
+{
+ for (size_t i = 0; i < config->nr; i++) {
+ struct comment_char_config_item *item = &config->item[i];
+
+ if (item->scope == CONFIG_SCOPE_SYSTEM &&
+ access(item->path, W_OK))
+ return false;
+ }
+
+ return true;
+}
+
+static void add_unset_auto_comment_char_advice(struct repository *repo,
+ struct comment_char_config *config)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!can_unset_comment_char_config(config))
+ return;
+
+ for (size_t i = 0; i < config->nr; i++) {
+ struct comment_char_config_item *item = &config->item[i];
+
+ strbuf_addstr(&buf, " git config unset ");
+ add_config_scope_arg(repo, &buf, item);
+ if (get_comment_key_flags(config, item->path, item->key_id) == KEY_SEEN_TWICE)
+ strbuf_addstr(&buf, "--all ");
+ strbuf_addf(&buf, "%s\n", comment_key_name(item->key_id));
+ }
+ advise(_("\nTo use the default comment string (#) please run\n\n%s"),
+ buf.buf);
+ strbuf_release(&buf);
+}
+
+static void add_comment_char_advice(struct repository *repo,
+ struct comment_char_config *config)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct comment_char_config_item *item;
+ /* TRANSLATORS this is a place holder for the value of core.commentString */
+ const char *placeholder = _("<comment string>");
+
+ /*
+ * If auto is set in the last file that we saw advise the user how to
+ * update their config.
+ */
+ if (!config->auto_set_in_file)
+ return;
+
+ add_unset_auto_comment_char_advice(repo, config);
+ item = &config->item[config->nr - 1];
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, " git config set ");
+ add_config_scope_arg(repo, &buf, item);
+ strbuf_addf(&buf, "%s %s\n", comment_key_name(item->key_id),
+ placeholder);
+ advise(_("\nTo set a custom comment string please run\n\n"
+ "%s\nwhere '%s' is the string you wish to use.\n"),
+ buf.buf, placeholder);
+ strbuf_release(&buf);
+}
+
+#undef KEY_SEEN_ONCE
+#undef KEY_SEEN_TWICE
+#undef COMMENT_KEY_SHIFT
+#undef COMMENT_KEY_MASK
+
+struct repo_config {
+ struct repository *repo;
+ struct comment_char_config comment_char_config;
+};
+
+#define REPO_CONFIG_INIT(repo_) { \
+ .comment_char_config = COMMENT_CHAR_CFG_INIT, \
+ .repo = repo_, \
+ };
+
+static void repo_config_release(struct repo_config *config)
+{
+ comment_char_config_release(&config->comment_char_config);
+}
+
+#ifdef WITH_BREAKING_CHANGES
+static void check_auto_comment_char_config(struct repository *repo,
+ struct comment_char_config *config)
+{
+ if (!config->auto_set)
+ return;
+
+ die_message(_("Support for '%s=auto' has been removed in Git 3.0"),
+ comment_key_name(config->last_key_id));
+ add_comment_char_advice(repo, config);
+ die(NULL);
+}
+#else
+static void check_auto_comment_char_config(struct repository *repo,
+ struct comment_char_config *config)
+{
+ extern bool warn_on_auto_comment_char;
+ const char *DEPRECATED_CONFIG_ENV =
+ "GIT_AUTO_COMMENT_CHAR_CONFIG_WARNING_GIVEN";
+
+ if (!config->auto_set || !warn_on_auto_comment_char)
+ return;
+
+ /*
+ * Use an environment variable to ensure that subprocesses do not repeat
+ * the warning.
+ */
+ if (git_env_bool(DEPRECATED_CONFIG_ENV, false))
+ return;
+
+ setenv(DEPRECATED_CONFIG_ENV, "true", true);
+
+ warning(_("Support for '%s=auto' is deprecated and will be removed in "
+ "Git 3.0"), comment_key_name(config->last_key_id));
+ add_comment_char_advice(repo, config);
+}
+#endif /* WITH_BREAKING_CHANGES */
+
+static void check_deprecated_config(struct repo_config *config)
+{
+ if (!config->repo->check_deprecated_config)
+ return;
+
+ check_auto_comment_char_config(config->repo,
+ &config->comment_char_config);
+}
+
+static int repo_config_callback(const char *key, const char *value,
+ const struct config_context *ctx, void *data)
+{
+ struct repo_config *config = data;
+
+ comment_char_callback(key, value, ctx, &config->comment_char_config);
+ return config_set_callback(key, value, ctx, config->repo->config);
+}
+
/* Functions use to read configuration from a repository */
static void repo_read_config(struct repository *repo)
{
struct config_options opts = { 0 };
+ struct repo_config config = REPO_CONFIG_INIT(repo);
opts.respect_includes = 1;
opts.commondir = repo->commondir;
@@ -2514,8 +2257,8 @@ static void repo_read_config(struct repository *repo)
git_configset_clear(repo->config);
git_configset_init(repo->config);
- if (config_with_options(config_set_callback, repo->config, NULL,
- repo, &opts) < 0)
+ if (config_with_options(repo_config_callback, &config, NULL, repo,
+ &opts) < 0)
/*
* config_with_options() normally returns only
* zero, as most errors are fatal, and
@@ -2528,6 +2271,8 @@ static void repo_read_config(struct repository *repo)
* immediately.
*/
die(_("unknown error occurred while reading the configuration files"));
+ check_deprecated_config(&config);
+ repo_config_release(&config);
}
static void git_config_check_init(struct repository *repo)
@@ -2546,6 +2291,10 @@ void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
+ if (!repo) {
+ read_very_early_config(fn, data);
+ return;
+ }
git_config_check_init(repo);
configset_iter(repo->config, fn, data);
}
@@ -2779,7 +2528,7 @@ void git_die_config(struct repository *r, const char *key, const char *err, ...)
}
/*
- * Find all the stuff for git_config_set() below.
+ * Find all the stuff for repo_config_set() below.
*/
struct config_store_data {
@@ -2966,7 +2715,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
if (value[0] == ' ')
quote = "\"";
for (i = 0; value[i]; i++)
- if (value[i] == ';' || value[i] == '#')
+ if (value[i] == ';' || value[i] == '#' || value[i] == '\r')
quote = "\"";
if (i && value[i - 1] == ' ')
quote = "\"";
@@ -3012,10 +2761,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
*/
static void maybe_remove_section(struct config_store_data *store,
size_t *begin_offset, size_t *end_offset,
- int *seen_ptr)
+ unsigned *seen_ptr)
{
size_t begin;
- int i, seen, section_seen = 0;
+ int section_seen = 0;
+ unsigned int i, seen;
/*
* First, ensure that this is the first key, and that there are no
@@ -3210,6 +2960,14 @@ int repo_config_set_multivar_in_file_gently(struct repository *r,
char *contents = NULL;
size_t contents_sz;
struct config_store_data store = CONFIG_STORE_INIT;
+ bool saved_check_deprecated_config = r->check_deprecated_config;
+
+ /*
+ * Do not warn or die if there are deprecated config settings as
+ * we want the user to be able to change those settings by running
+ * "git config".
+ */
+ r->check_deprecated_config = false;
validate_comment_string(comment);
@@ -3258,7 +3016,8 @@ int repo_config_set_multivar_in_file_gently(struct repository *r,
} else {
struct stat st;
size_t copy_begin, copy_end;
- int i, new_line = 0;
+ unsigned i;
+ int new_line = 0;
struct config_options opts;
if (!value_pattern)
@@ -3440,6 +3199,7 @@ out_free:
if (in_fd >= 0)
close(in_fd);
config_store_data_clear(&store);
+ r->check_deprecated_config = saved_check_deprecated_config;
return ret;
write_err_out: