Web lists-archives.com

Re: feature request: git-config: Add conditional include for gitbranch




On Thu, Mar 08, 2018 at 07:23:00PM -0500, Jeremy Bicha wrote:
> Use Case
> ======
> Jeremy is a developer for Debian and Ubuntu. The same repository is
> used for both Debian and Ubuntu packaging but with different branches.
> For commits to the debian/master branch, Jeremy wants to use his
> @debian.org email address. For commits to the ubuntu/master branch,
> Jeremy wants to use his @ubuntu.com email.
> 
> Proposal
> =======
> The includeIf feature of git-config could be extended to offer a
> gitbranch conditional include in addition to the gitdir conditional
> include it already offers.

Interesting. It looks quite simple to do this. My prototype looks like
this.

-- 8< --
Subject: [PATCH] config: support conditional include by matching ref pattern

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx>
---
 Documentation/config.txt  | 12 ++++++++++++
 config.c                  | 30 ++++++++++++++++++++++++++++++
 t/t1305-config-include.sh | 26 ++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index ce9102cea8..4e8fb6d99c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -143,6 +143,18 @@ refer to linkgit:gitignore[5] for details. For convenience:
 	This is the same as `gitdir` except that matching is done
 	case-insensitively (e.g. on case-insensitive file sytems)
 
+`ref`::
+	The data that follows the keyword `ref:` is used as a glob
+	pattern that matches against the current branch. `*` in the
+	pattern does not match `/` and `**` matches multiple levels,
+	the same as .gitignore syntax. The branch is a full reference
+	(i.e. with `refs/heads/` prefix). If HEAD is detached, the
+	pattern will be matched against the value "HEAD".
+
+`ref/i`::
+	This is the same as `ref` except that matching is done
+	case-insensitively
+
 A few more notes on matching via `gitdir` and `gitdir/i`:
 
  * Symlinks in `$GIT_DIR` are not resolved before matching.
diff --git a/config.c b/config.c
index b0c20e6cb8..72ff2da667 100644
--- a/config.c
+++ b/config.c
@@ -16,6 +16,7 @@
 #include "string-list.h"
 #include "utf8.h"
 #include "dir.h"
+#include "refs.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -202,6 +203,31 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
 	return prefix;
 }
 
+static int include_by_ref(const struct config_options *opts,
+			  const char *cond, size_t cond_len, int icase)
+{
+	struct strbuf pattern = STRBUF_INIT;
+	char *branch;
+	unsigned flags = WM_PATHNAME;
+	int ret;
+
+	if (!opts->git_dir)
+		return 0;
+
+	branch = resolve_refdup("HEAD", 0, NULL, NULL);
+	if (!branch)
+		return 0;
+
+	if (icase)
+		flags |= WM_CASEFOLD;
+	strbuf_add(&pattern,  cond, cond_len);
+	ret = !wildmatch(pattern.buf, branch, flags);
+
+	free(branch);
+	strbuf_release(&pattern);
+	return ret;
+}
+
 static int include_by_gitdir(const struct config_options *opts,
 			     const char *cond, size_t cond_len, int icase)
 {
@@ -268,6 +294,10 @@ static int include_condition_is_true(const struct config_options *opts,
 		return include_by_gitdir(opts, cond, cond_len, 0);
 	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
 		return include_by_gitdir(opts, cond, cond_len, 1);
+	else if (skip_prefix_mem(cond, cond_len, "ref:", &cond, &cond_len))
+		return include_by_ref(opts, cond, cond_len, 0);
+	else if (skip_prefix_mem(cond, cond_len, "ref/i:", &cond, &cond_len))
+		return include_by_ref(opts, cond, cond_len, 1);
 
 	/* unknown conditionals are always false */
 	return 0;
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index d9d2f545a4..27ecfc74b7 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -296,6 +296,32 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas
 	)
 '
 
+test_expect_success 'conditional include by refs' '
+	git init inc-by-ref &&
+	(
+		check() {
+			echo "ref: $1" >.git/HEAD &&
+			echo "[includeIf \"ref:$2\"]path=bar8" >.git/config &&
+			git config test.var >actual &&
+			test_cmp expect actual
+		}
+		cd inc-by-ref &&
+		echo "[test]var=matched" >.git/bar8 &&
+		echo matched >expect &&
+
+		check refs/heads/foo refs/heads/foo &&
+		check refs/heads/foo "refs/heads/*" &&
+		check refs/heads/foo "refs/heads/f*" &&
+		check refs/heads/deep/in/foo "refs/heads/**/foo" &&
+
+		test_commit one &&
+		git checkout --detach &&
+		echo "[includeIf \"ref:HEAD\"]path=bar8" >.git/config &&
+		git config test.var >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'include cycles are detected' '
 	cat >.gitconfig <<-\EOF &&
 	[test]value = gitconfig
-- 
2.16.2.903.gd04caf5039

-- 8< --