Git development
 help / color / mirror / Atom feed
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



  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