Commit 722ba93f6f813669ff9ea2359d3dfbbd662283fc

Patrick Steinhardt 2019-08-01T15:14:06

config: implement "onbranch" conditional With Git v2.23.0, the conditional include mechanism gained another new conditional "onbranch". As the name says, it will cause a file to be included if the "onbranch" pattern matches the currently checked out branch. Implement this new condition and add a bunch of tests.

diff --git a/src/config_file.c b/src/config_file.c
index 3e8e30c..849096d 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -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)
diff --git a/tests/config/conditionals.c b/tests/config/conditionals.c
index e2c74c1..5858782 100644
--- a/tests/config/conditionals.c
+++ b/tests/config/conditionals.c
@@ -1,6 +1,7 @@
 #include "clar_libgit2.h"
 #include "buffer.h"
 #include "futils.h"
+#include "repository.h"
 
 #ifdef GIT_WIN32
 # define ROOT_PREFIX "C:"
@@ -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);
+}