Subject: Re: [PATCH v2 1/3] config: add "worktree" and "worktree/i" includeIf conditions

From: Junio C Hamano <[email-masked]>
Date: Thu, 02 Apr 2026 14:22:22 -0700

Chen Linxuan via B4 Relay <[email-masked]>
writes:

> From: Chen Linxuan <[email-masked]>
>
> The `includeIf` mechanism already supports matching on the `.git`
> directory path (`gitdir`) and the currently checked out branch
> (`onbranch`).  But in multi-worktree setups the `.git` directory of a
> linked worktree points into the main repository's `.git/worktrees/`
> area, which makes `gitdir` patterns cumbersome when one wants to
> include config based on the working tree's checkout path instead.
>
> Introduce two new condition keywords:
>
>   - `worktree:<pattern>` matches the realpath of the current worktree's
>     working directory (i.e. `repo_get_work_tree()`) against a glob
>     pattern.  This is the path returned by `git rev-parse
>     --show-toplevel`.
>
>   - `worktree/i:<pattern>` is the case-insensitive variant.
>
> The implementation follows the same structure as `include_by_gitdir()`:
> the worktree path is resolved via `strbuf_realpath()`, the condition
> pattern is prepared with `prepare_include_condition_pattern()` (which
> handles `~` expansion, `./` relative paths, `**/` prefix insertion and
> trailing-`/` expansion), and matching is done with `wildmatch()`.  A
> second attempt using `strbuf_add_absolute_path()` is performed to
> handle symlinked paths.
>
> The condition never matches in bare repositories (where there is no
> worktree) or during early config reading (where no repository is
> available).
>
> Signed-off-by: Chen Linxuan <[email-masked]>
> ---
>  config.c | 25 ++++++++++++++-----------
>  1 file changed, 14 insertions(+), 11 deletions(-)

This probably should be further split into two commits, one that
introduces the "by-path" helper function and reimplements the
"gitdir" and "gitdir/i" support with it (i.e., essentially it
amounts to dropping of "struct config_options *" and using "const
char *path" in place of it, I presume), and another patch that adds
worktree support in terms of the "by-path" helper.  

The documentation updates and additional test should probably be
done as part of the second half of this patch, unless the second half
of this patch to add "worktree" and "worktree/i" is too big to be
reviewed standalone (which I do not think would be the case).

> diff --git a/config.c b/config.c
> index 156f2a24fa00..6d0c2d0725e4 100644
> --- a/config.c
> +++ b/config.c
> @@ -235,23 +235,20 @@ static int prepare_include_condition_pattern(const struct key_value_info *kvi,
>  	return 0;
>  }
>  
> -static int include_by_gitdir(const struct key_value_info *kvi,
> -			     const struct config_options *opts,
> -			     const char *cond, size_t cond_len, int icase)
> +static int include_by_path(const struct key_value_info *kvi,
> +			   const char *path,
> +			   const char *cond, size_t cond_len, int icase)
>  {
>  	struct strbuf text = STRBUF_INIT;
>  	struct strbuf pattern = STRBUF_INIT;
>  	size_t prefix;
>  	int ret = 0;
> -	const char *git_dir;
>  	int already_tried_absolute = 0;
>  
> -	if (opts->git_dir)
> -		git_dir = opts->git_dir;
> -	else
> +	if (!path)
>  		goto done;
>  
> -	strbuf_realpath(&text, git_dir, 1);
> +	strbuf_realpath(&text, path, 1);
>  	strbuf_add(&pattern, cond, cond_len);
>  	ret = prepare_include_condition_pattern(kvi, &pattern, &prefix);
>  	if (ret < 0)
> @@ -284,7 +281,7 @@ static int include_by_gitdir(const struct key_value_info *kvi,
>  		 * which'll do the right thing
>  		 */
>  		strbuf_reset(&text);
> -		strbuf_add_absolute_path(&text, git_dir);
> +		strbuf_add_absolute_path(&text, path);
>  		already_tried_absolute = 1;
>  		goto again;
>  	}
> @@ -400,9 +397,15 @@ static int include_condition_is_true(const struct key_value_info *kvi,
>  	const struct config_options *opts = inc->opts;
>  
>  	if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
> -		return include_by_gitdir(kvi, opts, cond, cond_len, 0);
> +		return include_by_path(kvi, opts->git_dir, cond, cond_len, 0);
>  	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
> -		return include_by_gitdir(kvi, opts, cond, cond_len, 1);
> +		return include_by_path(kvi, opts->git_dir, cond, cond_len, 1);
> +	else if (skip_prefix_mem(cond, cond_len, "worktree:", &cond, &cond_len))
> +		return include_by_path(kvi, inc->repo ? repo_get_work_tree(inc->repo) : NULL,
> +				       cond, cond_len, 0);
> +	else if (skip_prefix_mem(cond, cond_len, "worktree/i:", &cond, &cond_len))
> +		return include_by_path(kvi, inc->repo ? repo_get_work_tree(inc->repo) : NULL,
> +				       cond, cond_len, 1);
>  	else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
>  		return include_by_branch(inc, cond, cond_len);
>  	else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,