Files
git/t/helper/test-config.c
Matheus Tavares 92bca22fce config: correctly read worktree configs in submodules
One of the steps in do_git_config_sequence() is to load the
worktree-specific config file. Although the function receives a git_dir
string, it relies on git_pathdup(), which uses the_repository->git_dir,
to make the path to the file. Furthermore, it also checks that
extensions.worktreeConfig is set through the
repository_format_worktree_config variable, which refers to
the_repository only. Thus, when a submodule has worktree-specific
settings, a command executed in the superproject that recurses into the
submodule won't find the said settings.

This will be especially important in the next patch: git-grep will learn
to honor sparse checkouts and, when running with --recurse-submodules,
the submodule's sparse checkout settings must be loaded. As these
settings are stored in the config.worktree file, they would be ignored
without this patch. So let's fix this by reading the right
config.worktree file and extensions.worktreeConfig setting, based on the
git_dir and commondir paths given to do_git_config_sequence(). Also
add a test to avoid any regressions.

Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-06-12 11:53:03 -07:00

252 lines
6.7 KiB
C

#include "test-tool.h"
#include "cache.h"
#include "config.h"
#include "string-list.h"
#include "submodule-config.h"
/*
* This program exposes the C API of the configuration mechanism
* as a set of simple commands in order to facilitate testing.
*
* Usage: test-tool config [--submodule=<path>] <cmd> [<args>]
*
* If --submodule=<path> is given, <cmd> will operate on the submodule at the
* given <path>. This option is not valid for the commands: read_early_config,
* configset_get_value and configset_get_value_multi.
*
* Possible cmds are:
*
* get_value -> prints the value with highest priority for the entered key
*
* get_value_multi -> prints all values for the entered key in increasing order
* of priority
*
* get_int -> print integer value for the entered key or die
*
* get_bool -> print bool value for the entered key or die
*
* get_string -> print string value for the entered key or die
*
* configset_get_value -> returns value with the highest priority for the entered key
* from a config_set constructed from files entered as arguments.
*
* configset_get_value_multi -> returns value_list for the entered key sorted in
* ascending order of priority from a config_set
* constructed from files entered as arguments.
*
* iterate -> iterate over all values using git_config(), and print some
* data for each
*
* Exit codes:
* 0: success
* 1: value not found for the given config key
* 2: config file path given as argument is inaccessible or doesn't exist
* 129: test-config usage error
*
* Note: tests may also expect 128 for die() calls in the config machinery.
*
* Examples:
*
* To print the value with highest priority for key "foo.bAr Baz.rock":
* test-tool config get_value "foo.bAr Baz.rock"
*
*/
static int iterate_cb(const char *var, const char *value, void *data)
{
static int nr;
if (nr++)
putchar('\n');
printf("key=%s\n", var);
printf("value=%s\n", value ? value : "(null)");
printf("origin=%s\n", current_config_origin_type());
printf("name=%s\n", current_config_name());
printf("lno=%d\n", current_config_line());
printf("scope=%s\n", config_scope_name(current_config_scope()));
return 0;
}
static int early_config_cb(const char *var, const char *value, void *vdata)
{
const char *key = vdata;
if (!strcmp(key, var))
printf("%s\n", value);
return 0;
}
enum test_config_exit_code {
TC_SUCCESS = 0,
TC_VALUE_NOT_FOUND = 1,
TC_CONFIG_FILE_ERROR = 2,
TC_USAGE_ERROR = 129,
};
int cmd__config(int argc, const char **argv)
{
int i, val;
const char *v;
const struct string_list *strptr;
struct config_set cs = { .hash_initialized = 0 };
enum test_config_exit_code ret = TC_SUCCESS;
struct repository *repo = the_repository;
const char *subrepo_path = NULL;
argc--; /* skip over "config" */
argv++;
if (argc == 0)
goto print_usage_error;
if (skip_prefix(*argv, "--submodule=", &subrepo_path)) {
argc--;
argv++;
if (argc == 0)
goto print_usage_error;
}
if (argc == 2 && !strcmp(argv[0], "read_early_config")) {
if (subrepo_path) {
fprintf(stderr, "Cannot use --submodule with read_early_config\n");
return TC_USAGE_ERROR;
}
read_early_config(early_config_cb, (void *)argv[1]);
return TC_SUCCESS;
}
setup_git_directory();
git_configset_init(&cs);
if (subrepo_path) {
const struct submodule *sub;
struct repository *subrepo = xcalloc(1, sizeof(*repo));
sub = submodule_from_path(the_repository, &null_oid, subrepo_path);
if (!sub || repo_submodule_init(subrepo, the_repository, sub)) {
fprintf(stderr, "Invalid argument to --submodule: '%s'\n",
subrepo_path);
free(subrepo);
ret = TC_USAGE_ERROR;
goto out;
}
repo = subrepo;
}
if (argc == 2 && !strcmp(argv[0], "get_value")) {
if (!repo_config_get_value(repo, argv[1], &v)) {
if (!v)
printf("(NULL)\n");
else
printf("%s\n", v);
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc == 2 && !strcmp(argv[0], "get_value_multi")) {
strptr = repo_config_get_value_multi(repo, argv[1]);
if (strptr) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
printf("(NULL)\n");
else
printf("%s\n", v);
}
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc == 2 && !strcmp(argv[0], "get_int")) {
if (!repo_config_get_int(repo, argv[1], &val)) {
printf("%d\n", val);
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc == 2 && !strcmp(argv[0], "get_bool")) {
if (!repo_config_get_bool(repo, argv[1], &val)) {
printf("%d\n", val);
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc == 2 && !strcmp(argv[0], "get_string")) {
if (!repo_config_get_string_const(repo, argv[1], &v)) {
printf("%s\n", v);
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc >= 2 && !strcmp(argv[0], "configset_get_value")) {
if (subrepo_path) {
fprintf(stderr, "Cannot use --submodule with configset_get_value\n");
ret = TC_USAGE_ERROR;
goto out;
}
for (i = 2; i < argc; i++) {
int err;
if ((err = git_configset_add_file(&cs, argv[i]))) {
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
ret = TC_CONFIG_FILE_ERROR;
goto out;
}
}
if (!git_configset_get_value(&cs, argv[1], &v)) {
if (!v)
printf("(NULL)\n");
else
printf("%s\n", v);
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (argc >= 2 && !strcmp(argv[0], "configset_get_value_multi")) {
if (subrepo_path) {
fprintf(stderr, "Cannot use --submodule with configset_get_value_multi\n");
ret = TC_USAGE_ERROR;
goto out;
}
for (i = 2; i < argc; i++) {
int err;
if ((err = git_configset_add_file(&cs, argv[i]))) {
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
ret = TC_CONFIG_FILE_ERROR;
goto out;
}
}
strptr = git_configset_get_value_multi(&cs, argv[1]);
if (strptr) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
printf("(NULL)\n");
else
printf("%s\n", v);
}
} else {
printf("Value not found for \"%s\"\n", argv[1]);
ret = TC_VALUE_NOT_FOUND;
}
} else if (!strcmp(argv[0], "iterate")) {
repo_config(repo, iterate_cb, NULL);
} else {
print_usage_error:
fprintf(stderr, "Invalid syntax. Usage: test-tool config"
" [--submodule=<path>] <cmd> [args]\n");
ret = TC_USAGE_ERROR;
}
out:
git_configset_clear(&cs);
if (repo != the_repository) {
repo_clear(repo);
free(repo);
}
return ret;
}