Commit 071b6c06 by Patrick Steinhardt

config_file: implement conditional "gitdir" includes

Upstream git.git has implemented the ability to include other
configuration files based on conditions. Right now, this only includes
the ability to include a file based on the gitdir-location of the
repository the currently parsed configuration file belongs to. This
commit implements handling these conditional includes for the
case-sensitive "gitdir" condition.
parent 9d7a75be
......@@ -1612,6 +1612,86 @@ static int parse_include(struct reader *reader,
return result;
}
static int conditional_match_gitdir(
int *matches,
const git_repository *repo,
const char *cfg_file,
const char *value)
{
git_buf path = GIT_BUF_INIT;
int error, fnmatch_flags;
if (value[0] == '.' && git_path_is_dirsep(value[1])) {
git_path_dirname_r(&path, cfg_file);
git_buf_joinpath(&path, path.ptr, value + 2);
} else if (value[0] == '~' && git_path_is_dirsep(value[1]))
git_sysdir_expand_global_file(&path, value + 1);
else if (!git_path_is_absolute(value))
git_buf_joinpath(&path, "**", value);
else
git_buf_sets(&path, value);
if (git_buf_oom(&path)) {
error = -1;
goto out;
}
if (git_path_is_dirsep(value[strlen(value) - 1]))
git_buf_puts(&path, "**");
fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
goto out;
*matches = (error == 0);
out:
git_buf_free(&path);
return error;
}
static const struct {
const char *prefix;
int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
} conditions[] = {
{ "gitdir:", conditional_match_gitdir }
};
static int parse_conditional_include(struct reader *reader,
struct parse_data *parse_data, const char *section, const char *file)
{
char *condition;
size_t i;
int error = 0, matches;
if (!parse_data->repo)
return 0;
condition = git__substrdup(section + strlen("includeIf."),
strlen(section) - strlen("includeIf.") - strlen(".path"));
for (i = 0; i < ARRAY_SIZE(conditions); i++) {
if (git__prefixcmp(condition, conditions[i].prefix))
continue;
if ((error = conditions[i].matches(&matches,
parse_data->repo,
parse_data->file_path,
condition + strlen(conditions[i].prefix))) < 0)
break;
if (matches)
error = parse_include(reader, parse_data, file);
break;
}
git__free(condition);
return error;
}
static int read_on_variable(
struct reader *reader,
const char *current_section,
......@@ -1656,6 +1736,11 @@ static int read_on_variable(
/* Add or append the new config option */
if (!git__strcmp(var->entry->name, "include.path"))
result = parse_include(reader, parse_data, var->entry->value);
else if (!git__prefixcmp(var->entry->name, "includeif.") &&
!git__suffixcmp(var->entry->name, ".path"))
result = parse_conditional_include(reader, parse_data,
var->entry->name, var->entry->value);
return result;
}
......
#include "clar_libgit2.h"
#include "buffer.h"
#include "fileops.h"
#ifdef GIT_WIN32
# define ROOT_PREFIX "C:"
#else
# define ROOT_PREFIX
#endif
static git_repository *_repo;
void test_config_conditionals__initialize(void)
{
_repo = cl_git_sandbox_init("empty_standard_repo");
}
void test_config_conditionals__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void assert_condition_includes(const char *keyword, const char *path, bool expected)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
git_buf_puts(&buf, "path = other\n");
cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
_repo = cl_git_sandbox_reopen();
cl_git_pass(git_repository_config(&cfg, _repo));
if (expected) {
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
} else {
cl_git_fail_with(GIT_ENOTFOUND,
git_config_get_string_buf(&buf, cfg, "foo.bar"));
}
git_buf_free(&buf);
git_config_free(cfg);
}
void test_config_conditionals__gitdir(void)
{
git_buf path = GIT_BUF_INIT;
assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
assert_condition_includes("gitdir", "empty_standard_repo", true);
assert_condition_includes("gitdir", "empty_standard_repo/", true);
assert_condition_includes("gitdir", "./", true);
assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
assert_condition_includes("gitdir", "empty_stand", false);
assert_condition_includes("gitdir", "~/empty_standard_repo", false);
git_buf_joinpath(&path, clar_sandbox_path(), "/");
assert_condition_includes("gitdir", path.ptr, true);
git_buf_joinpath(&path, clar_sandbox_path(), "/*");
assert_condition_includes("gitdir", path.ptr, true);
git_buf_joinpath(&path, clar_sandbox_path(), "empty_standard_repo");
assert_condition_includes("gitdir", path.ptr, true);
git_buf_joinpath(&path, clar_sandbox_path(), "Empty_Standard_Repo");
assert_condition_includes("gitdir", path.ptr, false);
git_buf_free(&path);
}
void test_config_conditionals__invalid_conditional_fails(void)
{
assert_condition_includes("foobar", ".git", false);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment