Git development
 help / color / mirror / Atom feed
* [PATCH v7 0/3] includeIf: add "worktree" condition for matching working tree path
@ 2026-07-09  2:41 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
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Chen Linxuan via B4 Relay @ 2026-07-09  2:41 UTC (permalink / raw)
  To: git
  Cc: Kristoffer Haugsbakk, Junio C Hamano, Patrick Steinhardt,
	Chen Linxuan, Phillip Wood

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 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 (3):
      config: refactor include_by_gitdir() into include_by_path()
      repository: keep a symlink-preserving copy of the worktree path
      config: add "worktree" and "worktree/i" includeIf conditions

 Documentation/config.adoc |  48 +++++++++++++
 config.c                  |  25 ++++---
 repository.c              |  26 +++++++
 repository.h              |  10 +++
 setup.c                   |  86 ++++++++++++++++++++++-
 t/t1305-config-include.sh | 171 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 354 insertions(+), 12 deletions(-)

Range-diff versus v6:

1:  faa10baf8deb = 1:  47ee82d6a5bd config: refactor include_by_gitdir() into include_by_path()
-:  ------------ > 2:  367d61f9b55f repository: keep a symlink-preserving copy of the worktree path
2:  ee81d7aeee56 ! 3:  67d9c31d3817 config: add "worktree" and "worktree/i" includeIf conditions
    @@ Commit message
     
         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:<pattern> matches the working directory of the current
    +        worktree (the path returned by git rev-parse --show-toplevel)
    +        against a glob pattern.
     
           - worktree/i:<pattern> is the case-insensitive variant.
     
    -    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).
    +    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).
     
         Add documentation describing the new conditions, including a comparison
         with extensions.worktreeConfig.  Add tests covering bare repositories,
    -    multiple worktrees, symlinked worktree paths, case-sensitive and
    -    case-insensitive matching, early config reading, and non-repository
    -    scenarios.
    +    multiple worktrees, symlinked and subdir-of-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>
     
    @@ 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(inc->repo) : NULL,
    ++		return include_by_path(kvi, inc->repo ? repo_get_work_tree_original(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,
    ++		return include_by_path(kvi, inc->repo ? repo_get_work_tree_original(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 resolves symlinks' '
    -+	mkdir real-wt &&
    -+	ln -s real-wt link-wt &&
    -+	git init link-wt/repo &&
    ++test_expect_success SYMLINKS 'conditional include, worktree matching symlink' '
    ++	mkdir sym-real &&
    ++	ln -s sym-real sym-link &&
    ++	git init sym-link/repo &&
     +	(
    -+		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
    ++		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)" &&
    ++	(
    ++		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"
     +	)
     +'
     +

---
base-commit: f85a7e662054a7b0d9070e432508831afa214b47



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-07-13 11:16 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH v8 0/2] " Chen Linxuan via B4 Relay
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox