Merge branch 'ab/config-multi-and-nonbool'

Assorted config API updates.

* ab/config-multi-and-nonbool:
  for-each-repo: with bad config, don't conflate <path> and <cmd>
  config API: add "string" version of *_value_multi(), fix segfaults
  config API users: test for *_get_value_multi() segfaults
  for-each-repo: error on bad --config
  config API: have *_multi() return an "int" and take a "dest"
  versioncmp.c: refactor config reading next commit
  config API: add and use a "git_config_get()" family of functions
  config tests: add "NULL" tests for *_get_value_multi()
  config tests: cover blind spots in git_die_config() tests
This commit is contained in:
Junio C Hamano 2023-04-06 13:38:28 -07:00
commit 87daf40750
21 changed files with 480 additions and 71 deletions

View File

@ -32,6 +32,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
static const char *config_key = NULL; static const char *config_key = NULL;
int i, result = 0; int i, result = 0;
const struct string_list *values; const struct string_list *values;
int err;
const struct option options[] = { const struct option options[] = {
OPT_STRING(0, "config", &config_key, N_("config"), OPT_STRING(0, "config", &config_key, N_("config"),
@ -45,14 +46,11 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
if (!config_key) if (!config_key)
die(_("missing --config=<config>")); die(_("missing --config=<config>"));
values = repo_config_get_value_multi(the_repository, err = repo_config_get_string_multi(the_repository, config_key, &values);
config_key); if (err < 0)
usage_msg_optf(_("got bad config --config=%s"),
/* for_each_repo_usage, options, config_key);
* Do nothing on an empty list, which is equivalent to the case else if (err)
* where the config variable does not exist at all.
*/
if (!values)
return 0; return 0;
for (i = 0; !result && i < values->nr; i++) for (i = 0; !result && i < values->nr; i++)

View File

@ -1494,7 +1494,6 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
}; };
int found = 0; int found = 0;
const char *key = "maintenance.repo"; const char *key = "maintenance.repo";
char *config_value;
char *maintpath = get_maintpath(); char *maintpath = get_maintpath();
struct string_list_item *item; struct string_list_item *item;
const struct string_list *list; const struct string_list *list;
@ -1509,13 +1508,10 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
git_config_set("maintenance.auto", "false"); git_config_set("maintenance.auto", "false");
/* Set maintenance strategy, if unset */ /* Set maintenance strategy, if unset */
if (!git_config_get_string("maintenance.strategy", &config_value)) if (git_config_get("maintenance.strategy"))
free(config_value);
else
git_config_set("maintenance.strategy", "incremental"); git_config_set("maintenance.strategy", "incremental");
list = git_config_get_value_multi(key); if (!git_config_get_string_multi(key, &list)) {
if (list) {
for_each_string_list_item(item, list) { for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) { if (!strcmp(maintpath, item->string)) {
found = 1; found = 1;
@ -1581,11 +1577,10 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
if (config_file) { if (config_file) {
git_configset_init(&cs); git_configset_init(&cs);
git_configset_add_file(&cs, config_file); git_configset_add_file(&cs, config_file);
list = git_configset_get_value_multi(&cs, key);
} else {
list = git_config_get_value_multi(key);
} }
if (list) { if (!(config_file
? git_configset_get_string_multi(&cs, key, &list)
: git_config_get_string_multi(key, &list))) {
for_each_string_list_item(item, list) { for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) { if (!strcmp(maintpath, item->string)) {
found = 1; found = 1;

View File

@ -185,10 +185,10 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
int i; int i;
char *value = NULL; char *value = NULL;
struct string_list *include = decoration_filter->include_ref_pattern; struct string_list *include = decoration_filter->include_ref_pattern;
const struct string_list *config_exclude = const struct string_list *config_exclude;
git_config_get_value_multi("log.excludeDecoration");
if (config_exclude) { if (!git_config_get_string_multi("log.excludeDecoration",
&config_exclude)) {
struct string_list_item *item; struct string_list_item *item;
for_each_string_list_item(item, config_exclude) for_each_string_list_item(item, config_exclude)
string_list_append(decoration_filter->exclude_ref_config_pattern, string_list_append(decoration_filter->exclude_ref_config_pattern,

View File

@ -559,7 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
* If there are no path args and submodule.active is set then, * If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules. * by default, only initialize 'active' modules.
*/ */
if (!argc && git_config_get_value_multi("submodule.active")) if (!argc && !git_config_get("submodule.active"))
module_list_active(&list); module_list_active(&list);
info.prefix = prefix; info.prefix = prefix;
@ -2745,7 +2745,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
* If there are no path args and submodule.active is set then, * If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules. * by default, only initialize 'active' modules.
*/ */
if (!argc && git_config_get_value_multi("submodule.active")) if (!argc && !git_config_get("submodule.active"))
module_list_active(&list); module_list_active(&list);
info.prefix = opt.prefix; info.prefix = opt.prefix;
@ -3142,7 +3142,6 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
static void configure_added_submodule(struct add_data *add_data) static void configure_added_submodule(struct add_data *add_data)
{ {
char *key; char *key;
const char *val;
struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_submod = CHILD_PROCESS_INIT;
struct child_process add_gitmodules = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT;
@ -3187,7 +3186,7 @@ static void configure_added_submodule(struct add_data *add_data)
* is_submodule_active(), since that function needs to find * is_submodule_active(), since that function needs to find
* out the value of "submodule.active" again anyway. * out the value of "submodule.active" again anyway.
*/ */
if (!git_config_get_string_tmp("submodule.active", &val)) { if (!git_config_get("submodule.active")) {
/* /*
* If the submodule being added isn't already covered by the * If the submodule being added isn't already covered by the
* current configured pathspec, set the submodule's active flag * current configured pathspec, set the submodule's active flag

View File

@ -320,7 +320,6 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
if (file_exists(from_file)) { if (file_exists(from_file)) {
struct config_set cs = { { 0 } }; struct config_set cs = { { 0 } };
const char *core_worktree;
int bare; int bare;
if (safe_create_leading_directories(to_file) || if (safe_create_leading_directories(to_file) ||
@ -339,7 +338,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
to_file, "core.bare", NULL, "true", 0)) to_file, "core.bare", NULL, "true", 0))
error(_("failed to unset '%s' in '%s'"), error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file); "core.bare", to_file);
if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) && if (!git_configset_get(&cs, "core.worktree") &&
git_config_set_in_file_gently(to_file, git_config_set_in_file_gently(to_file,
"core.worktree", NULL)) "core.worktree", NULL))
error(_("failed to unset '%s' in '%s'"), error(_("failed to unset '%s' in '%s'"),

109
config.c
View File

@ -2292,23 +2292,29 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts); config_with_options(cb, data, NULL, &opts);
} }
static struct config_set_element *configset_find_element(struct config_set *cs, const char *key) RESULT_MUST_BE_USED
static int configset_find_element(struct config_set *cs, const char *key,
struct config_set_element **dest)
{ {
struct config_set_element k; struct config_set_element k;
struct config_set_element *found_entry; struct config_set_element *found_entry;
char *normalized_key; char *normalized_key;
int ret;
/* /*
* `key` may come from the user, so normalize it before using it * `key` may come from the user, so normalize it before using it
* for querying entries from the hashmap. * for querying entries from the hashmap.
*/ */
if (git_config_parse_key(key, &normalized_key, NULL)) ret = git_config_parse_key(key, &normalized_key, NULL);
return NULL; if (ret)
return ret;
hashmap_entry_init(&k.ent, strhash(normalized_key)); hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key; k.key = normalized_key;
found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL); found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
free(normalized_key); free(normalized_key);
return found_entry; *dest = found_entry;
return 0;
} }
static int configset_add_value(struct config_set *cs, const char *key, const char *value) static int configset_add_value(struct config_set *cs, const char *key, const char *value)
@ -2317,8 +2323,11 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
struct string_list_item *si; struct string_list_item *si;
struct configset_list_item *l_item; struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info)); struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
int ret;
e = configset_find_element(cs, key); ret = configset_find_element(cs, key, &e);
if (ret)
return ret;
/* /*
* Since the keys are being fed by git_config*() callback mechanism, they * Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging. * are already normalized. So simply add them without any further munging.
@ -2412,24 +2421,65 @@ int git_configset_add_file(struct config_set *cs, const char *filename)
int git_configset_get_value(struct config_set *cs, const char *key, const char **value) int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
{ {
const struct string_list *values = NULL; const struct string_list *values = NULL;
int ret;
/* /*
* Follows "last one wins" semantic, i.e., if there are multiple matches for the * Follows "last one wins" semantic, i.e., if there are multiple matches for the
* queried key in the files of the configset, the value returned will be the last * queried key in the files of the configset, the value returned will be the last
* value in the value list for that key. * value in the value list for that key.
*/ */
values = git_configset_get_value_multi(cs, key); if ((ret = git_configset_get_value_multi(cs, key, &values)))
return ret;
if (!values)
return 1;
assert(values->nr > 0); assert(values->nr > 0);
*value = values->items[values->nr - 1].string; *value = values->items[values->nr - 1].string;
return 0; return 0;
} }
const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key) int git_configset_get_value_multi(struct config_set *cs, const char *key,
const struct string_list **dest)
{ {
struct config_set_element *e = configset_find_element(cs, key); struct config_set_element *e;
return e ? &e->value_list : NULL; int ret;
if ((ret = configset_find_element(cs, key, &e)))
return ret;
else if (!e)
return 1;
*dest = &e->value_list;
return 0;
}
static int check_multi_string(struct string_list_item *item, void *util)
{
return item->string ? 0 : config_error_nonbool(util);
}
int git_configset_get_string_multi(struct config_set *cs, const char *key,
const struct string_list **dest)
{
int ret;
if ((ret = git_configset_get_value_multi(cs, key, dest)))
return ret;
if ((ret = for_each_string_list((struct string_list *)*dest,
check_multi_string, (void *)key)))
return ret;
return 0;
}
int git_configset_get(struct config_set *cs, const char *key)
{
struct config_set_element *e;
int ret;
if ((ret = configset_find_element(cs, key, &e)))
return ret;
else if (!e)
return 1;
return 0;
} }
int git_configset_get_string(struct config_set *cs, const char *key, char **dest) int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
@ -2568,6 +2618,12 @@ void repo_config(struct repository *repo, config_fn_t fn, void *data)
configset_iter(repo->config, fn, data); configset_iter(repo->config, fn, data);
} }
int repo_config_get(struct repository *repo, const char *key)
{
git_config_check_init(repo);
return git_configset_get(repo->config, key);
}
int repo_config_get_value(struct repository *repo, int repo_config_get_value(struct repository *repo,
const char *key, const char **value) const char *key, const char **value)
{ {
@ -2575,11 +2631,18 @@ int repo_config_get_value(struct repository *repo,
return git_configset_get_value(repo->config, key, value); return git_configset_get_value(repo->config, key, value);
} }
const struct string_list *repo_config_get_value_multi(struct repository *repo, int repo_config_get_value_multi(struct repository *repo, const char *key,
const char *key) const struct string_list **dest)
{ {
git_config_check_init(repo); git_config_check_init(repo);
return git_configset_get_value_multi(repo->config, key); return git_configset_get_value_multi(repo->config, key, dest);
}
int repo_config_get_string_multi(struct repository *repo, const char *key,
const struct string_list **dest)
{
git_config_check_init(repo);
return git_configset_get_string_multi(repo->config, key, dest);
} }
int repo_config_get_string(struct repository *repo, int repo_config_get_string(struct repository *repo,
@ -2682,14 +2745,25 @@ void git_config_clear(void)
repo_config_clear(the_repository); repo_config_clear(the_repository);
} }
int git_config_get(const char *key)
{
return repo_config_get(the_repository, key);
}
int git_config_get_value(const char *key, const char **value) int git_config_get_value(const char *key, const char **value)
{ {
return repo_config_get_value(the_repository, key, value); return repo_config_get_value(the_repository, key, value);
} }
const struct string_list *git_config_get_value_multi(const char *key) int git_config_get_value_multi(const char *key, const struct string_list **dest)
{ {
return repo_config_get_value_multi(the_repository, key); return repo_config_get_value_multi(the_repository, key, dest);
}
int git_config_get_string_multi(const char *key,
const struct string_list **dest)
{
return repo_config_get_string_multi(the_repository, key, dest);
} }
int git_config_get_string(const char *key, char **dest) int git_config_get_string(const char *key, char **dest)
@ -2836,7 +2910,8 @@ void git_die_config(const char *key, const char *err, ...)
error_fn(err, params); error_fn(err, params);
va_end(params); va_end(params);
} }
values = git_config_get_value_multi(key); if (git_config_get_value_multi(key, &values))
BUG("for key '%s' we must have a value to report on", key);
kv_info = values->items[values->nr - 1].util; kv_info = values->items[values->nr - 1].util;
git_die_config_linenr(key, kv_info->filename, kv_info->linenr); git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
} }

View File

@ -450,10 +450,31 @@ int git_configset_add_file(struct config_set *cs, const char *filename);
/** /**
* Finds and returns the value list, sorted in order of increasing priority * Finds and returns the value list, sorted in order of increasing priority
* for the configuration variable `key` and config set `cs`. When the * for the configuration variable `key` and config set `cs`. When the
* configuration variable `key` is not found, returns NULL. The caller * configuration variable `key` is not found, returns 1 without touching
* should not free or modify the returned pointer, as it is owned by the cache. * `value`.
*
* The key will be parsed for validity with git_config_parse_key(), on
* error a negative value will be returned.
*
* The caller should not free or modify the returned pointer, as it is
* owned by the cache.
*/ */
const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key); RESULT_MUST_BE_USED
int git_configset_get_value_multi(struct config_set *cs, const char *key,
const struct string_list **dest);
/**
* A validation wrapper for git_configset_get_value_multi() which does
* for it what git_configset_get_string() does for
* git_configset_get_value().
*
* The configuration syntax allows for "[section] key", which will
* give us a NULL entry in the "struct string_list", as opposed to
* "[section] key =" which is the empty string. Most users of the API
* are not prepared to handle NULL in a "struct string_list".
*/
int git_configset_get_string_multi(struct config_set *cs, const char *key,
const struct string_list **dest);
/** /**
* Clears `config_set` structure, removes all saved variable-value pairs. * Clears `config_set` structure, removes all saved variable-value pairs.
@ -465,6 +486,13 @@ void git_configset_clear(struct config_set *cs);
* value in the 'dest' pointer. * value in the 'dest' pointer.
*/ */
/**
* git_configset_get() returns negative values on error, see
* repo_config_get() below.
*/
RESULT_MUST_BE_USED
int git_configset_get(struct config_set *cs, const char *key);
/* /*
* Finds the highest-priority value for the configuration variable `key` * Finds the highest-priority value for the configuration variable `key`
* and config set `cs`, stores the pointer to it in `value` and returns 0. * and config set `cs`, stores the pointer to it in `value` and returns 0.
@ -485,10 +513,22 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
/* Functions for reading a repository's config */ /* Functions for reading a repository's config */
struct repository; struct repository;
void repo_config(struct repository *repo, config_fn_t fn, void *data); void repo_config(struct repository *repo, config_fn_t fn, void *data);
/**
* Run only the discover part of the repo_config_get_*() functions
* below, in addition to 1 if not found, returns negative values on
* error (e.g. if the key itself is invalid).
*/
RESULT_MUST_BE_USED
int repo_config_get(struct repository *repo, const char *key);
int repo_config_get_value(struct repository *repo, int repo_config_get_value(struct repository *repo,
const char *key, const char **value); const char *key, const char **value);
const struct string_list *repo_config_get_value_multi(struct repository *repo, RESULT_MUST_BE_USED
const char *key); int repo_config_get_value_multi(struct repository *repo, const char *key,
const struct string_list **dest);
RESULT_MUST_BE_USED
int repo_config_get_string_multi(struct repository *repo, const char *key,
const struct string_list **dest);
int repo_config_get_string(struct repository *repo, int repo_config_get_string(struct repository *repo,
const char *key, char **dest); const char *key, char **dest);
int repo_config_get_string_tmp(struct repository *repo, int repo_config_get_string_tmp(struct repository *repo,
@ -521,8 +561,15 @@ void git_protected_config(config_fn_t fn, void *data);
* manner, the config API provides two functions `git_config_get_value` * manner, the config API provides two functions `git_config_get_value`
* and `git_config_get_value_multi`. They both read values from an internal * and `git_config_get_value_multi`. They both read values from an internal
* cache generated previously from reading the config files. * cache generated previously from reading the config files.
*
* For those git_config_get*() functions that aren't documented,
* consult the corresponding repo_config_get*() function's
* documentation.
*/ */
RESULT_MUST_BE_USED
int git_config_get(const char *key);
/** /**
* Finds the highest-priority value for the configuration variable `key`, * Finds the highest-priority value for the configuration variable `key`,
* stores the pointer to it in `value` and returns 0. When the * stores the pointer to it in `value` and returns 0. When the
@ -535,10 +582,17 @@ int git_config_get_value(const char *key, const char **value);
/** /**
* Finds and returns the value list, sorted in order of increasing priority * Finds and returns the value list, sorted in order of increasing priority
* for the configuration variable `key`. When the configuration variable * for the configuration variable `key`. When the configuration variable
* `key` is not found, returns NULL. The caller should not free or modify * `key` is not found, returns 1 without touching `value`.
* the returned pointer, as it is owned by the cache. *
* The caller should not free or modify the returned pointer, as it is
* owned by the cache.
*/ */
const struct string_list *git_config_get_value_multi(const char *key); RESULT_MUST_BE_USED
int git_config_get_value_multi(const char *key,
const struct string_list **dest);
RESULT_MUST_BE_USED
int git_config_get_string_multi(const char *key,
const struct string_list **dest);
/** /**
* Resets and invalidates the config cache. * Resets and invalidates the config cache.

View File

@ -2318,7 +2318,11 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git)
const struct string_list *bitmap_preferred_tips(struct repository *r) const struct string_list *bitmap_preferred_tips(struct repository *r)
{ {
return repo_config_get_value_multi(r, "pack.preferbitmaptips"); const struct string_list *dest;
if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
return dest;
return NULL;
} }
int bitmap_is_preferred_refname(struct repository *r, const char *refname) int bitmap_is_preferred_refname(struct repository *r, const char *refname)

View File

@ -275,8 +275,7 @@ int is_tree_submodule_active(struct repository *repo,
free(key); free(key);
/* submodule.active is set */ /* submodule.active is set */
sl = repo_config_get_value_multi(repo, "submodule.active"); if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
if (sl) {
struct pathspec ps; struct pathspec ps;
struct strvec args = STRVEC_INIT; struct strvec args = STRVEC_INIT;
const struct string_list_item *item; const struct string_list_item *item;

View File

@ -14,6 +14,8 @@
* get_value_multi -> prints all values for the entered key in increasing order * get_value_multi -> prints all values for the entered key in increasing order
* of priority * of priority
* *
* get -> print return value for the entered key
*
* get_int -> print integer value for the entered key or die * get_int -> print integer value for the entered key or die
* *
* get_bool -> print bool value for the entered key or die * get_bool -> print bool value for the entered key or die
@ -95,8 +97,7 @@ int cmd__config(int argc, const char **argv)
goto exit1; goto exit1;
} }
} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
strptr = git_config_get_value_multi(argv[2]); if (!git_config_get_value_multi(argv[2], &strptr)) {
if (strptr) {
for (i = 0; i < strptr->nr; i++) { for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string; v = strptr->items[i].string;
if (!v) if (!v)
@ -109,6 +110,26 @@ int cmd__config(int argc, const char **argv)
printf("Value not found for \"%s\"\n", argv[2]); printf("Value not found for \"%s\"\n", argv[2]);
goto exit1; goto exit1;
} }
} else if (argc == 3 && !strcmp(argv[1], "get")) {
int ret;
if (!(ret = git_config_get(argv[2])))
goto exit0;
else if (ret == 1)
printf("Value not found for \"%s\"\n", argv[2]);
else if (ret == -CONFIG_INVALID_KEY)
printf("Key \"%s\" is invalid\n", argv[2]);
else if (ret == -CONFIG_NO_SECTION_OR_NAME)
printf("Key \"%s\" has no section\n", argv[2]);
else
/*
* A normal caller should just check "ret <
* 0", but for our own tests let's BUG() if
* our whitelist of git_config_parse_key()
* return values isn't exhaustive.
*/
BUG("Key \"%s\" has unknown return %d", argv[2], ret);
goto exit1;
} else if (argc == 3 && !strcmp(argv[1], "get_int")) { } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
if (!git_config_get_int(argv[2], &val)) { if (!git_config_get_int(argv[2], &val)) {
printf("%d\n", val); printf("%d\n", val);
@ -159,8 +180,7 @@ int cmd__config(int argc, const char **argv)
goto exit2; goto exit2;
} }
} }
strptr = git_configset_get_value_multi(&cs, argv[2]); if (!git_configset_get_value_multi(&cs, argv[2], &strptr)) {
if (strptr) {
for (i = 0; i < strptr->nr; i++) { for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string; v = strptr->items[i].string;
if (!v) if (!v)

View File

@ -40,4 +40,23 @@ test_expect_success 'do nothing on empty config' '
git for-each-repo --config=bogus.config -- help --no-such-option git for-each-repo --config=bogus.config -- help --no-such-option
' '
test_expect_success 'error on bad config keys' '
test_expect_code 129 git for-each-repo --config=a &&
test_expect_code 129 git for-each-repo --config=a.b. &&
test_expect_code 129 git for-each-repo --config="'\''.b"
'
test_expect_success 'error on NULL value for config keys' '
cat >>.git/config <<-\EOF &&
[empty]
key
EOF
cat >expect <<-\EOF &&
error: missing value for '\''empty.key'\''
EOF
test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
grep ^error actual.raw >actual &&
test_cmp expect actual
'
test_done test_done

View File

@ -58,6 +58,8 @@ test_expect_success 'setup default config' '
skin = false skin = false
nose = 1 nose = 1
horns horns
[value]
less
EOF EOF
' '
@ -116,6 +118,45 @@ test_expect_success 'find value with the highest priority' '
check_config get_value case.baz "hask" check_config get_value case.baz "hask"
' '
test_expect_success 'return value for an existing key' '
test-tool config get lamb.chop >out 2>err &&
test_must_be_empty out &&
test_must_be_empty err
'
test_expect_success 'return value for value-less key' '
test-tool config get value.less >out 2>err &&
test_must_be_empty out &&
test_must_be_empty err
'
test_expect_success 'return value for a missing key' '
cat >expect <<-\EOF &&
Value not found for "missing.key"
EOF
test_expect_code 1 test-tool config get missing.key >actual 2>err &&
test_cmp actual expect &&
test_must_be_empty err
'
test_expect_success 'return value for a bad key: CONFIG_INVALID_KEY' '
cat >expect <<-\EOF &&
Key "fails.iskeychar.-" is invalid
EOF
test_expect_code 1 test-tool config get fails.iskeychar.- >actual 2>err &&
test_cmp actual expect &&
test_must_be_empty out
'
test_expect_success 'return value for a bad key: CONFIG_NO_SECTION_OR_NAME' '
cat >expect <<-\EOF &&
Key "keynosection" has no section
EOF
test_expect_code 1 test-tool config get keynosection >actual 2>err &&
test_cmp actual expect &&
test_must_be_empty out
'
test_expect_success 'find integer value for a key' ' test_expect_success 'find integer value for a key' '
check_config get_int lamb.chop 65 check_config get_int lamb.chop 65
' '
@ -146,6 +187,71 @@ test_expect_success 'find multiple values' '
check_config get_value_multi case.baz sam bat hask check_config get_value_multi case.baz sam bat hask
' '
test_NULL_in_multi () {
local op="$1" &&
local file="$2" &&
test_expect_success "$op: NULL value in config${file:+ in $file}" '
config="$file" &&
if test -z "$config"
then
config=.git/config &&
test_when_finished "mv $config.old $config" &&
mv "$config" "$config".old
fi &&
# Value-less in the middle of a list
cat >"$config" <<-\EOF &&
[a]key=x
[a]key
[a]key=y
EOF
case "$op" in
*_multi)
cat >expect <<-\EOF
x
(NULL)
y
EOF
;;
*)
cat >expect <<-\EOF
y
EOF
;;
esac &&
test-tool config "$op" a.key $file >actual &&
test_cmp expect actual &&
# Value-less at the end of a least
cat >"$config" <<-\EOF &&
[a]key=x
[a]key=y
[a]key
EOF
case "$op" in
*_multi)
cat >expect <<-\EOF
x
y
(NULL)
EOF
;;
*)
cat >expect <<-\EOF
(NULL)
EOF
;;
esac &&
test-tool config "$op" a.key $file >actual &&
test_cmp expect actual
'
}
test_NULL_in_multi "get_value_multi"
test_NULL_in_multi "configset_get_value" "my.config"
test_NULL_in_multi "configset_get_value_multi" "my.config"
test_expect_success 'find value from a configset' ' test_expect_success 'find value from a configset' '
cat >config2 <<-\EOF && cat >config2 <<-\EOF &&
[case] [case]
@ -207,7 +313,7 @@ test_expect_success 'proper error on error in default config files' '
cp .git/config .git/config.old && cp .git/config .git/config.old &&
test_when_finished "mv .git/config.old .git/config" && test_when_finished "mv .git/config.old .git/config" &&
echo "[" >>.git/config && echo "[" >>.git/config &&
echo "fatal: bad config line 34 in file .git/config" >expect && echo "fatal: bad config line 36 in file .git/config" >expect &&
test_expect_code 128 test-tool config get_value foo.bar 2>actual && test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
test_cmp expect actual test_cmp expect actual
' '

View File

@ -360,7 +360,12 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' ' test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
git config core.notesRef refs/notes/y && git config core.notesRef refs/notes/y &&
test_must_fail git -c notes.mergeStrategy="foo" notes merge z && cat >expect <<-\EOF &&
error: unknown notes merge strategy foo
fatal: unable to parse '\''notes.mergeStrategy'\'' from command-line config
EOF
test_must_fail git -c notes.mergeStrategy="foo" notes merge z 2>actual &&
test_cmp expect actual &&
# Verify no changes (y) # Verify no changes (y)
verify_notes y y verify_notes y y
' '

View File

@ -835,6 +835,21 @@ test_expect_success 'log.decorate configuration' '
' '
test_expect_success 'parse log.excludeDecoration with no value' '
cp .git/config .git/config.orig &&
test_when_finished mv .git/config.orig .git/config &&
cat >>.git/config <<-\EOF &&
[log]
excludeDecoration
EOF
cat >expect <<-\EOF &&
error: missing value for '\''log.excludeDecoration'\''
EOF
git log --decorate=short 2>actual &&
test_cmp expect actual
'
test_expect_success 'decorate-refs with glob' ' test_expect_success 'decorate-refs with glob' '
cat >expect.decorate <<-\EOF && cat >expect.decorate <<-\EOF &&
Merge-tag-reach Merge-tag-reach

View File

@ -72,8 +72,16 @@ test_expect_success 'gc: implicit prune --expire' '
' '
test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' ' test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
git config gc.pruneExpire invalid && test_when_finished "rm -rf repo" &&
test_must_fail git gc git init repo &&
>repo/.git/config &&
git -C repo config gc.pruneExpire invalid &&
cat >expect <<-\EOF &&
error: Invalid gc.pruneexpire: '\''invalid'\''
fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
EOF
test_must_fail git -C repo gc 2>actual &&
test_cmp expect actual
' '
test_expect_success 'gc: start with ok gc.pruneExpire' ' test_expect_success 'gc: start with ok gc.pruneExpire' '

View File

@ -404,6 +404,26 @@ test_bitmap_cases () {
) )
' '
test_expect_success 'pack.preferBitmapTips' '
git init repo &&
test_when_finished "rm -rf repo" &&
(
cd repo &&
git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
test_commit_bulk --message="%s" 103 &&
cat >>.git/config <<-\EOF &&
[pack]
preferBitmapTips
EOF
cat >expect <<-\EOF &&
error: missing value for '\''pack.preferbitmaptips'\''
EOF
git repack -adb 2>actual &&
test_cmp expect actual
)
'
test_expect_success 'complains about multiple pack bitmaps' ' test_expect_success 'complains about multiple pack bitmaps' '
rm -fr repo && rm -fr repo &&
git init repo && git init repo &&

View File

@ -3,6 +3,22 @@
test_description='test skipping fetch negotiator' test_description='test skipping fetch negotiator'
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'fetch.negotiationalgorithm config' '
test_when_finished "rm -rf repo" &&
git init repo &&
cat >repo/.git/config <<-\EOF &&
[fetch]
negotiationAlgorithm
EOF
cat >expect <<-\EOF &&
error: missing value for '\''fetch.negotiationalgorithm'\''
fatal: bad config variable '\''fetch.negotiationalgorithm'\'' in file '\''.git/config'\'' at line 2
EOF
test_expect_code 128 git -C repo fetch >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
'
have_sent () { have_sent () {
while test "$#" -ne 0 while test "$#" -ne 0
do do

View File

@ -1871,6 +1871,23 @@ test_expect_success 'invalid sort parameter in configuratoin' '
test_must_fail git tag -l "foo*" test_must_fail git tag -l "foo*"
' '
test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
cp .git/config .git/config.orig &&
test_when_finished mv .git/config.orig .git/config &&
cat >>.git/config <<-\EOF &&
[versionsort]
prereleaseSuffix
suffix
EOF
cat >expect <<-\EOF &&
error: missing value for '\''versionsort.suffix'\''
error: missing value for '\''versionsort.prereleasesuffix'\''
EOF
git tag -l --sort=version:refname 2>actual &&
test_cmp expect actual
'
test_expect_success 'version sort with prerelease reordering' ' test_expect_success 'version sort with prerelease reordering' '
test_config versionsort.prereleaseSuffix -rc && test_config versionsort.prereleaseSuffix -rc &&
git tag foo1.6-rc1 && git tag foo1.6-rc1 &&

View File

@ -51,6 +51,22 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
test-tool -C super submodule is-active sub1 test-tool -C super submodule is-active sub1
' '
test_expect_success 'is-active handles submodule.active config missing a value' '
cp super/.git/config super/.git/config.orig &&
test_when_finished mv super/.git/config.orig super/.git/config &&
cat >>super/.git/config <<-\EOF &&
[submodule]
active
EOF
cat >expect <<-\EOF &&
error: missing value for '\''submodule.active'\''
EOF
test-tool -C super submodule is-active sub1 2>actual &&
test_cmp expect actual
'
test_expect_success 'is-active works with basic submodule.active config' ' test_expect_success 'is-active works with basic submodule.active config' '
test_when_finished "git -C super config submodule.sub1.URL ../sub" && test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
test_when_finished "git -C super config --unset-all submodule.active" && test_when_finished "git -C super config --unset-all submodule.active" &&

View File

@ -524,6 +524,44 @@ test_expect_success 'register and unregister' '
git maintenance unregister --config-file ./other --force git maintenance unregister --config-file ./other --force
' '
test_expect_success 'register with no value for maintenance.repo' '
cp .git/config .git/config.orig &&
test_when_finished mv .git/config.orig .git/config &&
cat >>.git/config <<-\EOF &&
[maintenance]
repo
EOF
cat >expect <<-\EOF &&
error: missing value for '\''maintenance.repo'\''
EOF
git maintenance register 2>actual &&
test_cmp expect actual &&
git config maintenance.repo
'
test_expect_success 'unregister with no value for maintenance.repo' '
cp .git/config .git/config.orig &&
test_when_finished mv .git/config.orig .git/config &&
cat >>.git/config <<-\EOF &&
[maintenance]
repo
EOF
cat >expect <<-\EOF &&
error: missing value for '\''maintenance.repo'\''
EOF
test_expect_code 128 git maintenance unregister 2>actual.raw &&
grep ^error actual.raw >actual &&
test_cmp expect actual &&
git config maintenance.repo &&
git maintenance unregister --force 2>actual.raw &&
grep ^error actual.raw >actual &&
test_cmp expect actual &&
git config maintenance.repo
'
test_expect_success !MINGW 'register and unregister with regex metacharacters' ' test_expect_success !MINGW 'register and unregister with regex metacharacters' '
META="a+b*c" && META="a+b*c" &&
git init "$META" && git init "$META" &&

View File

@ -160,15 +160,21 @@ int versioncmp(const char *s1, const char *s2)
} }
if (!initialized) { if (!initialized) {
const struct string_list *deprecated_prereleases; const char *const newk = "versionsort.suffix";
const char *const oldk = "versionsort.prereleasesuffix";
const struct string_list *newl;
const struct string_list *oldl;
int new = git_config_get_string_multi(newk, &newl);
int old = git_config_get_string_multi(oldk, &oldl);
if (!new && !old)
warning("ignoring %s because %s is set", oldk, newk);
if (!new)
prereleases = newl;
else if (!old)
prereleases = oldl;
initialized = 1; initialized = 1;
prereleases = git_config_get_value_multi("versionsort.suffix");
deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
if (prereleases) {
if (deprecated_prereleases)
warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
} else
prereleases = deprecated_prereleases;
} }
if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1, if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
&diff)) &diff))