Thread (12 messages) 12 messages, 4 authors, 2019-06-06

Re: [PATCH v2] config: learn the "onbranch:" includeIf condition

From: Johannes Schindelin <hidden>
Date: 2019-05-31 20:15:26

Hi Denton,

On Fri, 31 May 2019, Denton Liu wrote:
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.
This looks good to me.

Thanks,
Dscho
quoted hunk ↗ jump to hunk
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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help