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 <pclo...@gmail.com>
---
 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< --

Reply via email to