git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 2/2] config: add "worktree" and "worktree/i" includeIf conditions

From
Patrick Steinhardt <ps@pks.im>
Date
May 12, 2026, 07:14 UTC
Message-ID
<agLTO0amktCWMsiE@pks.im>
In-Reply-To
<20260403-includeif-worktree-v3-2-109ce5782b03@black-desk.cn>
On Fri, Apr 03, 2026 at 03:02:29PM +0800, Chen Linxuan via B4 Relay wrote:
Show 17 quoted lines
> From: Chen Linxuan <me@black-desk.cn>
> 
> 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.
Seems sensible.
Show 5 quoted lines
> The implementation reuses the include_by_path() helper introduced in
> the previous commit, passing the worktree path in place of the
> gitdir.  The condition never matches in bare repositories (where
> there is no worktree) or during early config reading (where no
> repository is available).

Right. This is because `repo_get_work_tree()` would return a NULL pointer in these cases, and `include_by_path()` exits early in that case.

Show 36 quoted lines
> diff --git a/Documentation/config.adoc b/Documentation/config.adoc
> index 62eebe7c5450..a4f3ec905098 100644
> --- a/Documentation/config.adoc
> +++ b/Documentation/config.adoc
> @@ -146,6 +146,48 @@ 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 systems)
>  
> +`worktree`::
> +	The data that follows the keyword `worktree` and a colon is used as a
> +	glob pattern. If the working directory of the current worktree matches
> +	the pattern, the include condition is met.
> ++
> +The worktree location is the path where files are checked out (as returned
> +by `git rev-parse --show-toplevel`). This is different from `gitdir`, which
> +matches the `.git` directory path. In a linked worktree, the worktree path
> +is the directory where that worktree's files are located, not the main
> +repository's `.git` directory.
> ++
> +The pattern can contain standard globbing wildcards and two additional
> +ones, `**/` and `/**`, that can match multiple path components. Please
> +refer to linkgit:gitignore[5] for details. For convenience:
> +
> + * If the pattern starts with `~/`, `~` will be substituted with the
> +   content of the environment variable `HOME`.
> +
> + * If the pattern starts with `./`, it is replaced with the directory
> +   containing the current config file.
> +
> + * If the pattern does not start with either `~/`, `./` or `/`, `**/`
> +   will be automatically prepended. For example, the pattern `foo/bar`
> +   becomes `**/foo/bar` and would match `/any/path/to/foo/bar`.
> +
> + * If the pattern ends with `/`, `**` will be automatically added. For
> +   example, the pattern `foo/` becomes `foo/**`. In other words, it
> +   matches "foo" and everything inside, recursively.

This whole listing here is the exact same as we have for the `gitdir` condition. Can we maybe deduplicate these into a common section?

Show 17 quoted lines
> diff --git a/config.c b/config.c
> index 7d5dae0e8450..6d0c2d0725e4 100644
> --- a/config.c
> +++ b/config.c
> @@ -400,6 +400,12 @@ static int include_condition_is_true(const struct key_value_info *kvi,
>  		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_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,

I feel like this is something that we might eventually want to convert to be table-driven. But I think that doesn't have to happen as part of this patch series.

> diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
> index 6e51f892f320..8a5ba4b884d3 100755
> --- a/t/t1305-config-include.sh
> +++ b/t/t1305-config-include.sh

Just because it was explicitly mentioned: we might also want to have a test that verifies this works with early-config parsing. We already have a similar test for "gitdir:" in "conditional include, early config reading".

And should we also have a "nongit" branch where we verify outside a repository?

Other than that this series looks good to me, thanks!
Patrick
Previous: Patrick SteinhardtNext: Phillip Wood
Message 6 of 11 in “config: refactor include_by_gitdir() into include_by_path()”
  1. 1/2 config: refactor include_by_gitdir() into include_by_path()Chen Linxuan via B4 Relay, Apr 3, 2026
  2. 0/2 includeIf: add "worktree" condition for matching working tree pathChen Linxuan via B4 Relay, Apr 3, 2026
  3. 2/2 config: add "worktree" and "worktree/i" includeIf conditionsChen Linxuan via B4 Relay, Apr 3, 2026
  4. Junio C HamanoMay 12, 2026
  5. Patrick SteinhardtMay 12, 2026
  6. Patrick SteinhardtMay 12, 2026
  7. Phillip WoodMay 12, 2026
  8. Junio C HamanoMay 12, 2026
  9. Chen LinxuanMay 13, 2026
  10. Chen LinxuanMay 13, 2026
  11. Patrick SteinhardtMay 13, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.