Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/config_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,12 +651,64 @@ static int conditional_match_gitdir_i(
return do_match_gitdir(matches, repo, cfg_file, value, true);
}

static int conditional_match_onbranch(
int *matches,
const git_repository *repo,
const char *cfg_file,
const char *condition)
{
git_buf reference = GIT_BUF_INIT, buf = GIT_BUF_INIT;
int error;

GIT_UNUSED(cfg_file);

/*
* NOTE: you cannot use `git_repository_head` here. Looking up the
* HEAD reference will create the ODB, which causes us to read the
* repo's config for keys like core.precomposeUnicode. As we're
* just parsing the config right now, though, this would result in
* an endless recursion.
*/

if ((error = git_buf_joinpath(&buf, git_repository_path(repo), GIT_HEAD_FILE)) < 0 ||
(error = git_futils_readbuffer(&reference, buf.ptr)) < 0)
goto out;
git_buf_rtrim(&reference);

if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
goto out;
git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));

if (git__strncmp(reference.ptr, GIT_REFS_HEADS_DIR, strlen(GIT_REFS_HEADS_DIR)))
goto out;
git_buf_consume(&reference, reference.ptr + strlen(GIT_REFS_HEADS_DIR));

/*
* If the condition ends with a '/', then we should treat it as if
* it had '**' appended.
*/
if ((error = git_buf_sets(&buf, condition)) < 0)
goto out;
if (git_path_is_dirsep(condition[strlen(condition) - 1]) &&
(error = git_buf_puts(&buf, "**")) < 0)
goto out;

*matches = wildmatch(buf.ptr, reference.ptr, WM_PATHNAME) == WM_MATCH;
out:
git_buf_dispose(&reference);
git_buf_dispose(&buf);

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 },
{ "gitdir/i:", conditional_match_gitdir_i }
{ "gitdir/i:", conditional_match_gitdir_i },
{ "onbranch:", conditional_match_onbranch }
};

static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file)
Expand Down
46 changes: 43 additions & 3 deletions tests/config/conditionals.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "futils.h"
#include "repository.h"

#ifdef GIT_WIN32
# define ROOT_PREFIX "C:"
Expand All @@ -22,11 +23,11 @@ void test_config_conditionals__cleanup(void)

static void assert_condition_includes(const char *keyword, const char *path, bool expected)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
git_config *cfg;

git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
git_buf_puts(&buf, "path = other\n");
cl_git_pass(git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path));
cl_git_pass(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");
Expand Down Expand Up @@ -106,3 +107,42 @@ void test_config_conditionals__invalid_conditional_fails(void)
{
assert_condition_includes("foobar", ".git", false);
}

static void set_head(git_repository *repo, const char *name)
{
cl_git_pass(git_repository_create_head(git_repository_path(repo), name));
}

void test_config_conditionals__onbranch(void)
{
assert_condition_includes("onbranch", "master", true);
assert_condition_includes("onbranch", "m*", true);
assert_condition_includes("onbranch", "*", true);
assert_condition_includes("onbranch", "master/", false);
assert_condition_includes("onbranch", "foo", false);

set_head(_repo, "foo");
assert_condition_includes("onbranch", "master", false);
assert_condition_includes("onbranch", "foo", true);
assert_condition_includes("onbranch", "f*o", true);

set_head(_repo, "dir/ref");
assert_condition_includes("onbranch", "dir/ref", true);
assert_condition_includes("onbranch", "dir/", true);
assert_condition_includes("onbranch", "dir/*", true);
assert_condition_includes("onbranch", "dir/**", true);
assert_condition_includes("onbranch", "**", true);
assert_condition_includes("onbranch", "dir", false);
assert_condition_includes("onbranch", "dir*", false);

set_head(_repo, "dir/subdir/ref");
assert_condition_includes("onbranch", "dir/subdir/", true);
assert_condition_includes("onbranch", "dir/subdir/*", true);
assert_condition_includes("onbranch", "dir/subdir/ref", true);
assert_condition_includes("onbranch", "dir/", true);
assert_condition_includes("onbranch", "dir/**", true);
assert_condition_includes("onbranch", "**", true);
assert_condition_includes("onbranch", "dir", false);
assert_condition_includes("onbranch", "dir*", false);
assert_condition_includes("onbranch", "dir/*", false);
}
30 changes: 30 additions & 0 deletions tests/config/include.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,33 @@ void test_config_include__included_variables_cannot_be_modified(void)
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}

void test_config_include__variables_in_included_override_including(void)
{
int i;

cl_git_mkfile("top-level", "[foo]\nbar = 1\n[include]\npath = included");
cl_git_mkfile("included", "[foo]\nbar = 2");

cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
cl_assert_equal_i(i, 2);

cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}

void test_config_include__variables_in_including_override_included(void)
{
int i;

cl_git_mkfile("top-level", "[include]\npath = included\n[foo]\nbar = 1");
cl_git_mkfile("included", "[foo]\nbar = 2");

cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
cl_assert_equal_i(i, 1);

cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
}
98 changes: 61 additions & 37 deletions tests/config/snapshot.c
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
#include "clar_libgit2.h"

void test_config_snapshot__create_snapshot(void)
{
int32_t tmp;
git_config *cfg, *snapshot, *new_snapshot;
const char *filename = "config-ext-change";
static git_config *cfg;
static git_config *snapshot;

cl_git_mkfile(filename, "[old]\nvalue = 5\n");
void test_config_snapshot__cleanup(void)
{
git_config_free(cfg);
cfg = NULL;
git_config_free(snapshot);
snapshot = NULL;
}

cl_git_pass(git_config_open_ondisk(&cfg, filename));
void test_config_snapshot__create_snapshot(void)
{
int32_t i;

cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
cl_assert_equal_i(5, tmp);
cl_git_mkfile("config", "[old]\nvalue = 5\n");
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
cl_assert_equal_i(5, i);

cl_git_pass(git_config_snapshot(&snapshot, cfg));

/* Change the value on the file itself (simulate external process) */
cl_git_mkfile(filename, "[old]\nvalue = 56\n");
cl_git_mkfile("config", "[old]\nvalue = 56\n");

cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
cl_assert_equal_i(56, tmp);

cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value"));
cl_assert_equal_i(5, tmp);
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
cl_assert_equal_i(56, i);
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(5, i);

/* Change the value on the file itself (simulate external process) */
cl_git_mkfile(filename, "[old]\nvalue = 999\n");
cl_git_mkfile("config", "[old]\nvalue = 999\n");

cl_git_pass(git_config_snapshot(&new_snapshot, cfg));
/* Old snapshot should still have the old value */
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(5, i);

/* New snapshot should see new value */
cl_git_pass(git_config_get_int32(&tmp, new_snapshot, "old.value"));
cl_assert_equal_i(999, tmp);

/* Old snapshot should still have the old value */
cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value"));
cl_assert_equal_i(5, tmp);

git_config_free(new_snapshot);
git_config_free(snapshot);
git_config_free(cfg);
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
cl_assert_equal_i(999, i);

cl_git_pass(p_unlink("config"));
}

static int count_me(const git_config_entry *entry, void *payload)
Expand All @@ -55,24 +59,44 @@ static int count_me(const git_config_entry *entry, void *payload)

void test_config_snapshot__multivar(void)
{
int count = 0;
git_config *cfg, *snapshot;
const char *filename = "config-file";

cl_git_mkfile(filename, "[old]\nvalue = 5\nvalue = 6\n");
int count;

cl_git_pass(git_config_open_ondisk(&cfg, filename));
count = 0;
cl_git_mkfile("config", "[old]\nvalue = 5\nvalue = 6\n");
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count));
cl_assert_equal_i(2, count);

count = 0;
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
cl_assert_equal_i(2, count);

cl_git_pass(p_unlink("config"));
}

void test_config_snapshot__includes(void)
{
int i;

cl_git_mkfile("including", "[include]\npath = included");
cl_git_mkfile("included", "[section]\nkey = 1\n");

cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
git_config_free(cfg);

count = 0;
cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);

cl_assert_equal_i(2, count);
/* Rewrite "included" config */
cl_git_mkfile("included", "[section]\nkey = 11\n");

git_config_free(snapshot);
/* Assert that the live config changed, but snapshot remained the same */
cl_git_pass(git_config_get_int32(&i, cfg, "section.key"));
cl_assert_equal_i(i, 11);
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);

cl_git_pass(p_unlink("including"));
cl_git_pass(p_unlink("included"));
}