From: Chen Linxuan via B4 Relay Date: Fri, 10 Jul 2026 06:43:28 GMT Subject: [PATCH v8 0/2] includeIf: add "worktree" condition for matching working tree path Message-ID: <20260710-includeif-worktree-v8-0-04686d8a616c@black-desk.cn> 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:` matches the working directory of the current worktree against a glob pattern. - `worktree/i:` 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 --- 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: matches the working directory of the current - worktree (the path returned by git rev-parse --show-toplevel) - against a glob pattern. + - worktree: 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: 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 @@ 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