[PATCH v2] config: learn the "onbranch:" includeIf condition
From: Denton Liu <hidden>
Date: 2019-05-31 19:33:48
Subsystem:
documentation, the rest · Maintainers:
Jonathan Corbet, Linus Torvalds
Currently, if a user wishes to have individual settings per branch, they
are required to manually keep track of the settings in their head and
manually set the options on the command-line or change the config at
each branch.
Teach config the "onbranch:" includeIf condition so that it can
conditionally include configuration files if the branch that is checked
out in the current worktree matches the pattern given.
Signed-off-by: Denton Liu <redacted>
---
Thanks for taking a look, Johannes. I've modified the patch to match
with wildcards now.
Changes since v1:
* Allow onbranch: to specify a pattern instead of just a static
comparison
Interdiff against v1:
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 3b9fbe1860..93aa4323c6 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -145,9 +145,11 @@ refer to linkgit:gitignore[5] for details. For convenience:
case-insensitively (e.g. on case-insensitive file sytems)
`onbranch`::
- The data that follows the keyword `onbranch:` is taken to be a
- name of a local branch. If we are in a worktree where that
- branch is currently checked out, the include condition is met.
+ The data that follows the keyword `onbranch:` is taken to be a pattern
+ with standard globbing wildcards and two additional ones, `**/` and
+ `/**`, that can match multiple path components. If we are in a worktree
+ where the name of the branch that is currently checked out matches the
+ pattern, the include condition is met.
A few more notes on matching via `gitdir` and `gitdir/i`:
diff --git a/config.c b/config.c
index 954e84e13a..48bb435739 100644
--- a/config.c
+++ b/config.c
@@ -268,6 +268,8 @@ static int include_by_gitdir(const struct config_options *opts,
static int include_by_branch(const char *cond, size_t cond_len)
{
int flags;
+ int ret;
+ struct strbuf pattern = STRBUF_INIT;
const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
const char *shortname;
@@ -275,7 +277,10 @@ static int include_by_branch(const char *cond, size_t cond_len)
!skip_prefix(refname, "refs/heads/", &shortname))
return 0;
- return !strncmp(cond, shortname, cond_len);
+ strbuf_add(&pattern, cond, cond_len);
+ ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
+ strbuf_release(&pattern);
+ return ret;
}
static int include_condition_is_true(const struct config_options *opts,
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 062db08ad9..05c7def1f2 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -314,6 +314,7 @@ test_expect_success 'conditional include, onbranch' '
cd bar &&
echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
echo "[test]nine=9" >.git/bar9 &&
+ git checkout -b master &&
test_must_fail git config test.nine &&
git checkout -b foo-branch &&
echo 9 >expect &&
@@ -322,6 +323,25 @@ test_expect_success 'conditional include, onbranch' '
)
'
+test_expect_success 'conditional include, onbranch, wildcard' '
+ (
+ cd bar &&
+ echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config &&
+ echo "[test]ten=10" >.git/bar10 &&
+ git checkout -b not-foo-branch/a &&
+ test_must_fail git config test.ten &&
+
+ echo 10 >expect &&
+ git checkout -b foo-branch/a/b/c &&
+ git config test.ten >actual &&
+ test_cmp expect actual &&
+
+ git checkout -b moo-bar/a &&
+ git config test.ten >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'include cycles are detected' '
cat >.gitconfig <<-\EOF &&
[test]value = gitconfig
Documentation/config.txt | 12 ++++++++++++
config.c | 21 +++++++++++++++++++++
t/t1305-config-include.sh | 33 +++++++++++++++++++++++++++++++++
3 files changed, 66 insertions(+)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 7e2a6f61f5..93aa4323c6 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt@@ -144,6 +144,13 @@ 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) +`onbranch`:: + The data that follows the keyword `onbranch:` is taken to be a pattern + with standard globbing wildcards and two additional ones, `**/` and + `/**`, that can match multiple path components. If we are in a worktree + where the name of the branch that is currently checked out matches the + pattern, the include condition is met. + A few more notes on matching via `gitdir` and `gitdir/i`: * Symlinks in `$GIT_DIR` are not resolved before matching.
@@ -206,6 +213,11 @@ Example [includeIf "gitdir:/path/to/group/"] path = foo.inc + ; include only if we are in a worktree where foo-branch is + ; currently checked out + [includeIf "onbranch:foo-branch"] + path = foo.inc + Values ~~~~~~
diff --git a/config.c b/config.c
index 296a6d9cc4..48bb435739 100644
--- a/config.c
+++ b/config.c@@ -19,6 +19,7 @@ #include "utf8.h" #include "dir.h" #include "color.h" +#include "refs.h" struct config_source { struct config_source *prev;
@@ -264,6 +265,24 @@ static int include_by_gitdir(const struct config_options *opts, return ret; } +static int include_by_branch(const char *cond, size_t cond_len) +{ + int flags; + int ret; + struct strbuf pattern = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags); + const char *shortname; + + if (!refname || !(flags & REF_ISSYMREF) || + !skip_prefix(refname, "refs/heads/", &shortname)) + return 0; + + strbuf_add(&pattern, cond, cond_len); + ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME); + strbuf_release(&pattern); + return ret; +} + static int include_condition_is_true(const struct config_options *opts, const char *cond, size_t cond_len) {
@@ -272,6 +291,8 @@ 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, "onbranch:", &cond, &cond_len)) + return include_by_branch(cond, cond_len); /* unknown conditionals are always false */ return 0;
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 579a86b7f8..05c7def1f2 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh@@ -309,6 +309,39 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas ) ' +test_expect_success 'conditional include, onbranch' ' + ( + cd bar && + echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config && + echo "[test]nine=9" >.git/bar9 && + git checkout -b master && + test_must_fail git config test.nine && + git checkout -b foo-branch && + echo 9 >expect && + git config test.nine >actual && + test_cmp expect actual + ) +' + +test_expect_success 'conditional include, onbranch, wildcard' ' + ( + cd bar && + echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config && + echo "[test]ten=10" >.git/bar10 && + git checkout -b not-foo-branch/a && + test_must_fail git config test.ten && + + echo 10 >expect && + git checkout -b foo-branch/a/b/c && + git config test.ten >actual && + test_cmp expect actual && + + git checkout -b moo-bar/a && + git config test.ten >actual && + test_cmp expect actual + ) +' + test_expect_success 'include cycles are detected' ' cat >.gitconfig <<-\EOF && [test]value = gitconfig
--
2.22.0.rc1.169.g49223abbf8