From: Chen Linxuan via B4 Relay <devnull+me.black-desk.cn@kernel.org>
To: git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
Junio C Hamano <gitster@pobox.com>,
Patrick Steinhardt <ps@pks.im>, Chen Linxuan <me@black-desk.cn>,
Phillip Wood <phillip.wood@dunelm.org.uk>
Subject: [PATCH v8 0/2] includeIf: add "worktree" condition for matching working tree path
Date: Fri, 10 Jul 2026 14:43:28 +0800 [thread overview]
Message-ID: <20260710-includeif-worktree-v8-0-04686d8a616c@black-desk.cn> (raw)
In-Reply-To: <20260709-includeif-worktree-v7-0-e87e705e8df6@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 working directory of the current
worktree against a glob pattern.
- `worktree/i:<pattern>` is the case-insensitive variant.
Supported pattern features: glob wildcards, `**/` and `/**`, `~`
expansion, `./` relative paths, and trailing-`/` prefix matching.
The condition never matches in a bare repository.
Signed-off-by: Chen Linxuan <me@black-desk.cn>
---
Changes in v8:
- Drop the v7 symlink-preserving worktree path implementation. Patrick
pointed out that the setup-side plumbing was too invasive and likely to
conflict with the ongoing setup discovery work.
- Document the current limitation instead: includeIf "worktree:" matches
the realpath-resolved worktree location, so symlink spellings may not
match.
- Return the series to two patches, based on v6 plus the documentation
update.
- Link to v7: https://lore.kernel.org/r/20260709-includeif-worktree-v7-0-e87e705e8df6@black-desk.cn
Changes in v7:
- Preserve the symlinked spelling of the worktree path and match
includeIf "worktree:" against it, so the condition now matches both
the symlinked and the real path, consistent with "gitdir:"
(Patrick Steinhardt, v6 review).
- Split the work into a preparatory commit that stores a non-realpath
worktree path and a follow-up that wires it into includeIf.
- Extend symlink test coverage to subdirectories and linked worktrees.
- Link to v6: https://lore.kernel.org/r/20260703-includeif-worktree-v6-0-a13893ad9a7f@black-desk.cn
Changes in v6:
- Rebase onto current `master` at Git 2.55.
- Add an in-code comment explaining why the non-repository worktree
tests use the loose `**.path` pattern (suggested by Junio C Hamano).
- Link to v5: https://lore.kernel.org/r/20260525-includeif-worktree-v5-0-1efe525d025a@black-desk.cn
Changes in v5:
- Fix Windows CI failure: use `**` glob pattern instead of `/` in the
"worktree without repository" tests, since `/` as a path pattern is
Unix-specific and does not match Windows paths.
Github CI pass: https://github.com/black-desk/git/actions/runs/26380466288
- Add a test verifying case-sensitive matching by default, with the
`!CASE_INSENSITIVE_FS` prerequisite (suggested by Patrick Steinhardt).
- Link to v4: https://lore.kernel.org/r/20260513-includeif-worktree-v4-0-f8e6212d1fba@black-desk.cn
Changes in v4:
- Deduplicate the worktree pattern documentation by referencing the
gitdir syntax instead of repeating the full pattern description
(suggested by Patrick Steinhardt).
- Add documentation comparing includeIf "worktree:" with
extensions.worktreeConfig, including a concrete use case example
(suggested by Phillip Wood, Junio C Hamano).
- Add a test verifying that the worktree condition does not match
during early config reading (suggested by Patrick Steinhardt).
- Add tests for the non-repository (nongit) scenario (suggested by
Patrick Steinhardt).
- Add a test for the case-insensitive "worktree/i" variant
- Link to v3: https://lore.kernel.org/r/20260403-includeif-worktree-v3-0-109ce5782b03@black-desk.cn
Changes in v3:
- Apply Junio's suggestion.
- Link to v2: https://lore.kernel.org/r/20260402-includeif-worktree-v2-0-36e339b898d7@black-desk.cn
Changes in v2:
- Add missing signed-off-by lines.
- Link to v1: https://lore.kernel.org/r/20260401-includeif-worktree-v1-0-906db69f2c79@black-desk.cn
---
Chen Linxuan (2):
config: refactor include_by_gitdir() into include_by_path()
config: add "worktree" and "worktree/i" includeIf conditions
Documentation/config.adoc | 53 +++++++++++++++++++
config.c | 25 +++++----
t/t1305-config-include.sh | 128 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 195 insertions(+), 11 deletions(-)
Range-diff versus v7:
1: 510f28d207f8 = 1: 731d928b1dfa config: refactor include_by_gitdir() into include_by_path()
2: 0f83ffee0338 < -: ------------ repository: keep a symlink-preserving copy of the worktree path
3: 18d1abc325fc ! 2: 56c792090625 config: add "worktree" and "worktree/i" includeIf conditions
@@ Commit message
Introduce two new condition keywords:
- - worktree:<pattern> matches the working directory of the current
- worktree (the path returned by git rev-parse --show-toplevel)
- against a glob pattern.
+ - 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 reuses the include_by_path() helper, passing
- repo_get_work_tree_original() (added in the previous commit; it keeps
- the symlink-preserving spelling of the worktree path) in place of the
- gitdir. As with gitdir, include_by_path() then matches both the
- realpath and the original spelling, so a pattern may use either. The
- condition never matches in bare repositories (where there is no
- worktree) or during early config reading (where no repository is
- available).
+ 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).
Add documentation describing the new conditions, including a comparison
- with extensions.worktreeConfig. Add tests covering bare repositories,
- multiple worktrees, symlinked and subdir-of-symlinked worktree paths,
- case-sensitive and case-insensitive matching, early config reading,
+ with extensions.worktreeConfig and a note that worktree matching currently
+ uses the realpath-resolved worktree location. Add tests covering bare
+ repositories, multiple worktrees, realpath-resolved symlinked worktree
+ paths, case-sensitive and case-insensitive matching, early config reading,
and non-repository scenarios.
Signed-off-by: Chen Linxuan <me@black-desk.cn>
@@ Documentation/config.adoc: refer to linkgit:gitignore[5] for details. For conven
+`**/`, and trailing-`/` prefix matching). This condition will never match
+in a bare repository (which has no worktree).
++
++Unlike `gitdir`, the `worktree` condition currently matches only the
++realpath-resolved worktree location. If the working tree was entered via a
++symbolic link, a pattern that uses the symbolic-link spelling may not match;
++use the real path instead.
+++
+This is useful when you want to apply configuration based on where the
+working tree is located on the filesystem. For example, a contributor who
+works on the same project both personally and as an employee can use
@@ config.c: static int include_condition_is_true(const struct key_value_info *kvi,
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_original(inc->repo) : NULL,
++ 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_original(inc->repo) : NULL,
++ 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);
@@ t/t1305-config-include.sh: test_expect_success 'onbranch without repository but
+ test_must_fail git -C wt-prefix/linked config test.linkedvar
+'
+
-+test_expect_success SYMLINKS 'conditional include, worktree matching symlink' '
-+ mkdir sym-real &&
-+ ln -s sym-real sym-link &&
-+ git init sym-link/repo &&
-+ (
-+ cd sym-link/repo &&
-+ link_path="$(pwd)" &&
-+ real_path="$(test-tool path-utils real_path "$link_path")" &&
-+ cat >>.git/config <<-EOF &&
-+ [includeIf "gitdir:$link_path/.git"]
-+ path = gitdir-link
-+ [includeIf "gitdir:$real_path/.git"]
-+ path = gitdir-real
-+ [includeIf "worktree:$link_path"]
-+ path = worktree-link
-+ [includeIf "worktree:$real_path"]
-+ path = worktree-real
-+ EOF
-+ echo "[test]gitdirlink=1" >.git/gitdir-link &&
-+ echo "[test]gitdirreal=1" >.git/gitdir-real &&
-+ echo "[test]worktreelink=1" >.git/worktree-link &&
-+ echo "[test]worktreereal=1" >.git/worktree-real &&
-+ git config get test.gitdirlink &&
-+ git config get test.gitdirreal &&
-+ git config get test.worktreelink &&
-+ git config get test.worktreereal &&
-+ # from a subdirectory, the logical worktree path is recovered by
-+ # stripping the below-root suffix, so both spellings still match
-+ mkdir d &&
-+ cd d &&
-+ git config get test.worktreelink &&
-+ git config get test.worktreereal
-+ )
-+'
-+
-+test_expect_success SYMLINKS 'conditional include, worktree matching symlink of a linked worktree' '
-+ git init wt-main &&
-+ ( cd wt-main && test_commit initial ) &&
-+ git -C wt-main worktree add --detach ../wt-real &&
-+ ln -s wt-real wt-link &&
-+ wt_main="$(cd wt-main && pwd)" &&
++test_expect_success SYMLINKS 'conditional include, worktree resolves symlinks' '
++ mkdir real-wt &&
++ ln -s real-wt link-wt &&
++ git init link-wt/repo &&
+ (
-+ cd wt-link &&
-+ link_path="$(pwd)" &&
-+ real_path="$(test-tool path-utils real_path "$link_path")" &&
-+ cat >>"$wt_main/.git/config" <<-EOF &&
-+ [includeIf "worktree:$link_path"]
-+ path = wt-link
-+ [includeIf "worktree:$real_path"]
-+ path = wt-real
-+ EOF
-+ echo "[test]wtlink=1" >"$wt_main/.git/wt-link" &&
-+ echo "[test]wtreal=1" >"$wt_main/.git/wt-real" &&
-+ test "$(git config get test.wtlink)" = "1" &&
-+ test "$(git config get test.wtreal)" = "1"
++ cd link-wt/repo &&
++ # repo->worktree resolves symlinks, so use real path in pattern
++ echo "[includeIf \"worktree:**/real-wt/repo\"]path=bar-link" >>.git/config &&
++ echo "[test]wtlink=2" >.git/bar-link &&
++ echo 2 >expect &&
++ git config test.wtlink >actual &&
++ test_cmp expect actual
+ )
+'
+
---
base-commit: f85a7e662054a7b0d9070e432508831afa214b47
next prev parent reply other threads:[~2026-07-10 6:44 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 2:41 [PATCH v7 0/3] includeIf: add "worktree" condition for matching working tree path Chen Linxuan via B4 Relay
2026-07-09 2:41 ` [PATCH v7 1/3] config: refactor include_by_gitdir() into include_by_path() Chen Linxuan via B4 Relay
2026-07-09 2:41 ` [PATCH v7 2/3] repository: keep a symlink-preserving copy of the worktree path Chen Linxuan via B4 Relay
2026-07-09 10:09 ` Patrick Steinhardt
2026-07-09 2:41 ` [PATCH v7 3/3] config: add "worktree" and "worktree/i" includeIf conditions Chen Linxuan via B4 Relay
2026-07-09 10:09 ` [PATCH v7 0/3] includeIf: add "worktree" condition for matching working tree path Patrick Steinhardt
2026-07-09 20:40 ` Junio C Hamano
2026-07-10 6:43 ` Chen Linxuan via B4 Relay [this message]
2026-07-10 6:43 ` [PATCH v8 1/2] config: refactor include_by_gitdir() into include_by_path() Chen Linxuan via B4 Relay
2026-07-10 6:43 ` [PATCH v8 2/2] config: add "worktree" and "worktree/i" includeIf conditions Chen Linxuan via B4 Relay
2026-07-13 11:16 ` Patrick Steinhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260710-includeif-worktree-v8-0-04686d8a616c@black-desk.cn \
--to=devnull+me.black-desk.cn@kernel.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=me@black-desk.cn \
--cc=phillip.wood@dunelm.org.uk \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox