Skip to content

Commit aaa48d0

Browse files
authored
Merge pull request libgit2#5196 from pks-t/pks/config-include-onbranch
config: implement "onbranch" conditional
2 parents 4e20c7b + 722ba93 commit aaa48d0

File tree

4 files changed

+187
-41
lines changed

4 files changed

+187
-41
lines changed

src/config_file.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,12 +651,64 @@ static int conditional_match_gitdir_i(
651651
return do_match_gitdir(matches, repo, cfg_file, value, true);
652652
}
653653

654+
static int conditional_match_onbranch(
655+
int *matches,
656+
const git_repository *repo,
657+
const char *cfg_file,
658+
const char *condition)
659+
{
660+
git_buf reference = GIT_BUF_INIT, buf = GIT_BUF_INIT;
661+
int error;
662+
663+
GIT_UNUSED(cfg_file);
664+
665+
/*
666+
* NOTE: you cannot use `git_repository_head` here. Looking up the
667+
* HEAD reference will create the ODB, which causes us to read the
668+
* repo's config for keys like core.precomposeUnicode. As we're
669+
* just parsing the config right now, though, this would result in
670+
* an endless recursion.
671+
*/
672+
673+
if ((error = git_buf_joinpath(&buf, git_repository_path(repo), GIT_HEAD_FILE)) < 0 ||
674+
(error = git_futils_readbuffer(&reference, buf.ptr)) < 0)
675+
goto out;
676+
git_buf_rtrim(&reference);
677+
678+
if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
679+
goto out;
680+
git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
681+
682+
if (git__strncmp(reference.ptr, GIT_REFS_HEADS_DIR, strlen(GIT_REFS_HEADS_DIR)))
683+
goto out;
684+
git_buf_consume(&reference, reference.ptr + strlen(GIT_REFS_HEADS_DIR));
685+
686+
/*
687+
* If the condition ends with a '/', then we should treat it as if
688+
* it had '**' appended.
689+
*/
690+
if ((error = git_buf_sets(&buf, condition)) < 0)
691+
goto out;
692+
if (git_path_is_dirsep(condition[strlen(condition) - 1]) &&
693+
(error = git_buf_puts(&buf, "**")) < 0)
694+
goto out;
695+
696+
*matches = wildmatch(buf.ptr, reference.ptr, WM_PATHNAME) == WM_MATCH;
697+
out:
698+
git_buf_dispose(&reference);
699+
git_buf_dispose(&buf);
700+
701+
return error;
702+
703+
}
704+
654705
static const struct {
655706
const char *prefix;
656707
int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
657708
} conditions[] = {
658709
{ "gitdir:", conditional_match_gitdir },
659-
{ "gitdir/i:", conditional_match_gitdir_i }
710+
{ "gitdir/i:", conditional_match_gitdir_i },
711+
{ "onbranch:", conditional_match_onbranch }
660712
};
661713

662714
static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file)

tests/config/conditionals.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "clar_libgit2.h"
22
#include "buffer.h"
33
#include "futils.h"
4+
#include "repository.h"
45

56
#ifdef GIT_WIN32
67
# define ROOT_PREFIX "C:"
@@ -22,11 +23,11 @@ void test_config_conditionals__cleanup(void)
2223

2324
static void assert_condition_includes(const char *keyword, const char *path, bool expected)
2425
{
25-
git_config *cfg;
2626
git_buf buf = GIT_BUF_INIT;
27+
git_config *cfg;
2728

28-
git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
29-
git_buf_puts(&buf, "path = other\n");
29+
cl_git_pass(git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path));
30+
cl_git_pass(git_buf_puts(&buf, "path = other\n"));
3031

3132
cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
3233
cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
@@ -106,3 +107,42 @@ void test_config_conditionals__invalid_conditional_fails(void)
106107
{
107108
assert_condition_includes("foobar", ".git", false);
108109
}
110+
111+
static void set_head(git_repository *repo, const char *name)
112+
{
113+
cl_git_pass(git_repository_create_head(git_repository_path(repo), name));
114+
}
115+
116+
void test_config_conditionals__onbranch(void)
117+
{
118+
assert_condition_includes("onbranch", "master", true);
119+
assert_condition_includes("onbranch", "m*", true);
120+
assert_condition_includes("onbranch", "*", true);
121+
assert_condition_includes("onbranch", "master/", false);
122+
assert_condition_includes("onbranch", "foo", false);
123+
124+
set_head(_repo, "foo");
125+
assert_condition_includes("onbranch", "master", false);
126+
assert_condition_includes("onbranch", "foo", true);
127+
assert_condition_includes("onbranch", "f*o", true);
128+
129+
set_head(_repo, "dir/ref");
130+
assert_condition_includes("onbranch", "dir/ref", true);
131+
assert_condition_includes("onbranch", "dir/", true);
132+
assert_condition_includes("onbranch", "dir/*", true);
133+
assert_condition_includes("onbranch", "dir/**", true);
134+
assert_condition_includes("onbranch", "**", true);
135+
assert_condition_includes("onbranch", "dir", false);
136+
assert_condition_includes("onbranch", "dir*", false);
137+
138+
set_head(_repo, "dir/subdir/ref");
139+
assert_condition_includes("onbranch", "dir/subdir/", true);
140+
assert_condition_includes("onbranch", "dir/subdir/*", true);
141+
assert_condition_includes("onbranch", "dir/subdir/ref", true);
142+
assert_condition_includes("onbranch", "dir/", true);
143+
assert_condition_includes("onbranch", "dir/**", true);
144+
assert_condition_includes("onbranch", "**", true);
145+
assert_condition_includes("onbranch", "dir", false);
146+
assert_condition_includes("onbranch", "dir*", false);
147+
assert_condition_includes("onbranch", "dir/*", false);
148+
}

tests/config/include.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,33 @@ void test_config_include__included_variables_cannot_be_modified(void)
202202
cl_git_pass(p_unlink("top-level"));
203203
cl_git_pass(p_unlink("included"));
204204
}
205+
206+
void test_config_include__variables_in_included_override_including(void)
207+
{
208+
int i;
209+
210+
cl_git_mkfile("top-level", "[foo]\nbar = 1\n[include]\npath = included");
211+
cl_git_mkfile("included", "[foo]\nbar = 2");
212+
213+
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
214+
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
215+
cl_assert_equal_i(i, 2);
216+
217+
cl_git_pass(p_unlink("top-level"));
218+
cl_git_pass(p_unlink("included"));
219+
}
220+
221+
void test_config_include__variables_in_including_override_included(void)
222+
{
223+
int i;
224+
225+
cl_git_mkfile("top-level", "[include]\npath = included\n[foo]\nbar = 1");
226+
cl_git_mkfile("included", "[foo]\nbar = 2");
227+
228+
cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
229+
cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
230+
cl_assert_equal_i(i, 1);
231+
232+
cl_git_pass(p_unlink("top-level"));
233+
cl_git_pass(p_unlink("included"));
234+
}

tests/config/snapshot.c

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,49 @@
11
#include "clar_libgit2.h"
22

3-
void test_config_snapshot__create_snapshot(void)
4-
{
5-
int32_t tmp;
6-
git_config *cfg, *snapshot, *new_snapshot;
7-
const char *filename = "config-ext-change";
3+
static git_config *cfg;
4+
static git_config *snapshot;
85

9-
cl_git_mkfile(filename, "[old]\nvalue = 5\n");
6+
void test_config_snapshot__cleanup(void)
7+
{
8+
git_config_free(cfg);
9+
cfg = NULL;
10+
git_config_free(snapshot);
11+
snapshot = NULL;
12+
}
1013

11-
cl_git_pass(git_config_open_ondisk(&cfg, filename));
14+
void test_config_snapshot__create_snapshot(void)
15+
{
16+
int32_t i;
1217

13-
cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
14-
cl_assert_equal_i(5, tmp);
18+
cl_git_mkfile("config", "[old]\nvalue = 5\n");
19+
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
20+
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
21+
cl_assert_equal_i(5, i);
1522

1623
cl_git_pass(git_config_snapshot(&snapshot, cfg));
1724

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

21-
cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
22-
cl_assert_equal_i(56, tmp);
23-
24-
cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value"));
25-
cl_assert_equal_i(5, tmp);
28+
cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
29+
cl_assert_equal_i(56, i);
30+
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
31+
cl_assert_equal_i(5, i);
2632

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

30-
cl_git_pass(git_config_snapshot(&new_snapshot, cfg));
36+
/* Old snapshot should still have the old value */
37+
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
38+
cl_assert_equal_i(5, i);
3139

3240
/* New snapshot should see new value */
33-
cl_git_pass(git_config_get_int32(&tmp, new_snapshot, "old.value"));
34-
cl_assert_equal_i(999, tmp);
35-
36-
/* Old snapshot should still have the old value */
37-
cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value"));
38-
cl_assert_equal_i(5, tmp);
39-
40-
git_config_free(new_snapshot);
4141
git_config_free(snapshot);
42-
git_config_free(cfg);
42+
cl_git_pass(git_config_snapshot(&snapshot, cfg));
43+
cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
44+
cl_assert_equal_i(999, i);
45+
46+
cl_git_pass(p_unlink("config"));
4347
}
4448

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

5660
void test_config_snapshot__multivar(void)
5761
{
58-
int count = 0;
59-
git_config *cfg, *snapshot;
60-
const char *filename = "config-file";
61-
62-
cl_git_mkfile(filename, "[old]\nvalue = 5\nvalue = 6\n");
62+
int count;
6363

64-
cl_git_pass(git_config_open_ondisk(&cfg, filename));
64+
count = 0;
65+
cl_git_mkfile("config", "[old]\nvalue = 5\nvalue = 6\n");
66+
cl_git_pass(git_config_open_ondisk(&cfg, "config"));
6567
cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count));
68+
cl_assert_equal_i(2, count);
6669

70+
count = 0;
71+
cl_git_pass(git_config_snapshot(&snapshot, cfg));
72+
cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
6773
cl_assert_equal_i(2, count);
6874

75+
cl_git_pass(p_unlink("config"));
76+
}
77+
78+
void test_config_snapshot__includes(void)
79+
{
80+
int i;
81+
82+
cl_git_mkfile("including", "[include]\npath = included");
83+
cl_git_mkfile("included", "[section]\nkey = 1\n");
84+
85+
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
6986
cl_git_pass(git_config_snapshot(&snapshot, cfg));
70-
git_config_free(cfg);
7187

72-
count = 0;
73-
cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
88+
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
89+
cl_assert_equal_i(i, 1);
7490

75-
cl_assert_equal_i(2, count);
91+
/* Rewrite "included" config */
92+
cl_git_mkfile("included", "[section]\nkey = 11\n");
7693

77-
git_config_free(snapshot);
94+
/* Assert that the live config changed, but snapshot remained the same */
95+
cl_git_pass(git_config_get_int32(&i, cfg, "section.key"));
96+
cl_assert_equal_i(i, 11);
97+
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
98+
cl_assert_equal_i(i, 1);
99+
100+
cl_git_pass(p_unlink("including"));
101+
cl_git_pass(p_unlink("included"));
78102
}

0 commit comments

Comments
 (0)