Skip to content

Commit da1f520

Browse files
black-deskgitster
authored andcommitted
config: add "worktree" and "worktree/i" includeIf conditions
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. 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 and their supported pattern features (glob wildcards, **/ and /**, ~ expansion, ./ relative paths, and trailing-/ prefix matching). Add tests covering bare repositories, multiple worktrees, and symlinked worktree paths. Signed-off-by: Chen Linxuan <me@black-desk.cn> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f7d92bc commit da1f520

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

Documentation/config.adoc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,48 @@ refer to linkgit:gitignore[5] for details. For convenience:
146146
This is the same as `gitdir` except that matching is done
147147
case-insensitively (e.g. on case-insensitive file systems)
148148

149+
`worktree`::
150+
The data that follows the keyword `worktree` and a colon is used as a
151+
glob pattern. If the working directory of the current worktree matches
152+
the pattern, the include condition is met.
153+
+
154+
The worktree location is the path where files are checked out (as returned
155+
by `git rev-parse --show-toplevel`). This is different from `gitdir`, which
156+
matches the `.git` directory path. In a linked worktree, the worktree path
157+
is the directory where that worktree's files are located, not the main
158+
repository's `.git` directory.
159+
+
160+
The pattern can contain standard globbing wildcards and two additional
161+
ones, `**/` and `/**`, that can match multiple path components. Please
162+
refer to linkgit:gitignore[5] for details. For convenience:
163+
164+
* If the pattern starts with `~/`, `~` will be substituted with the
165+
content of the environment variable `HOME`.
166+
167+
* If the pattern starts with `./`, it is replaced with the directory
168+
containing the current config file.
169+
170+
* If the pattern does not start with either `~/`, `./` or `/`, `**/`
171+
will be automatically prepended. For example, the pattern `foo/bar`
172+
becomes `**/foo/bar` and would match `/any/path/to/foo/bar`.
173+
174+
* If the pattern ends with `/`, `**` will be automatically added. For
175+
example, the pattern `foo/` becomes `foo/**`. In other words, it
176+
matches "foo" and everything inside, recursively.
177+
+
178+
This condition will never match in a bare repository (which has no worktree).
179+
+
180+
This is useful when you need to use different `user.name`, `user.email`, or
181+
GPG keys in different worktrees of the same repository. While
182+
`extensions.worktreeConfig` also allows per-worktree configuration, it
183+
requires changes inside each repository. This condition can be set in the
184+
user's global configuration file (e.g. `~/.config/git/config`) and applies
185+
to multiple repositories at once.
186+
187+
`worktree/i`::
188+
This is the same as `worktree` except that matching is done
189+
case-insensitively (e.g. on case-insensitive file systems)
190+
149191
`onbranch`::
150192
The data that follows the keyword `onbranch` and a colon is taken to be a
151193
pattern with standard globbing wildcards and two additional
@@ -244,6 +286,14 @@ Example
244286
[includeIf "gitdir:~/to/group/"]
245287
path = /path/to/foo.inc
246288
289+
; include if the worktree is at /path/to/project-build
290+
[includeIf "worktree:/path/to/project-build"]
291+
path = build-config.inc
292+
293+
; include for all worktrees inside /path/to/group
294+
[includeIf "worktree:/path/to/group/"]
295+
path = group-config.inc
296+
247297
; relative paths are always relative to the including
248298
; file (if the condition is true); their location is not
249299
; affected by the condition

config.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,12 @@ static int include_condition_is_true(const struct key_value_info *kvi,
400400
return include_by_path(kvi, opts->git_dir, cond, cond_len, 0);
401401
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
402402
return include_by_path(kvi, opts->git_dir, cond, cond_len, 1);
403+
else if (skip_prefix_mem(cond, cond_len, "worktree:", &cond, &cond_len))
404+
return include_by_path(kvi, inc->repo ? repo_get_work_tree(inc->repo) : NULL,
405+
cond, cond_len, 0);
406+
else if (skip_prefix_mem(cond, cond_len, "worktree/i:", &cond, &cond_len))
407+
return include_by_path(kvi, inc->repo ? repo_get_work_tree(inc->repo) : NULL,
408+
cond, cond_len, 1);
403409
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
404410
return include_by_branch(inc, cond, cond_len);
405411
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,

t/t1305-config-include.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,70 @@ test_expect_success 'onbranch without repository but explicit nonexistent Git di
396396
test_must_fail nongit git --git-dir=nonexistent config get foo.bar
397397
'
398398

399+
# worktree: conditional include tests
400+
401+
test_expect_success 'conditional include, worktree bare repo' '
402+
git init --bare wt-bare &&
403+
(
404+
cd wt-bare &&
405+
echo "[includeIf \"worktree:/\"]path=bar-bare" >>config &&
406+
echo "[test]wtbare=1" >bar-bare &&
407+
test_must_fail git config test.wtbare
408+
)
409+
'
410+
411+
test_expect_success 'conditional include, worktree multiple worktrees' '
412+
git init wt-multi &&
413+
(
414+
cd wt-multi &&
415+
test_commit initial &&
416+
git worktree add -b linked-branch ../wt-linked HEAD &&
417+
git worktree add -b prefix-branch ../wt-prefix/linked HEAD
418+
) &&
419+
wt_main="$(cd wt-multi && pwd)" &&
420+
wt_linked="$(cd wt-linked && pwd)" &&
421+
wt_prefix_parent="$(cd wt-prefix && pwd)" &&
422+
cat >>wt-multi/.git/config <<-EOF &&
423+
[includeIf "worktree:$wt_main"]
424+
path = main-config
425+
[includeIf "worktree:$wt_linked"]
426+
path = linked-config
427+
[includeIf "worktree:$wt_prefix_parent/"]
428+
path = prefix-config
429+
EOF
430+
echo "[test]mainvar=main" >wt-multi/.git/main-config &&
431+
echo "[test]linkedvar=linked" >wt-multi/.git/linked-config &&
432+
echo "[test]prefixvar=prefix" >wt-multi/.git/prefix-config &&
433+
echo main >expect &&
434+
git -C wt-multi config test.mainvar >actual &&
435+
test_cmp expect actual &&
436+
test_must_fail git -C wt-multi config test.linkedvar &&
437+
test_must_fail git -C wt-multi config test.prefixvar &&
438+
echo linked >expect &&
439+
git -C wt-linked config test.linkedvar >actual &&
440+
test_cmp expect actual &&
441+
test_must_fail git -C wt-linked config test.mainvar &&
442+
test_must_fail git -C wt-linked config test.prefixvar &&
443+
echo prefix >expect &&
444+
git -C wt-prefix/linked config test.prefixvar >actual &&
445+
test_cmp expect actual &&
446+
test_must_fail git -C wt-prefix/linked config test.mainvar &&
447+
test_must_fail git -C wt-prefix/linked config test.linkedvar
448+
'
449+
450+
test_expect_success SYMLINKS 'conditional include, worktree resolves symlinks' '
451+
mkdir real-wt &&
452+
ln -s real-wt link-wt &&
453+
git init link-wt/repo &&
454+
(
455+
cd link-wt/repo &&
456+
# repo->worktree resolves symlinks, so use real path in pattern
457+
echo "[includeIf \"worktree:**/real-wt/repo\"]path=bar-link" >>.git/config &&
458+
echo "[test]wtlink=2" >.git/bar-link &&
459+
echo 2 >expect &&
460+
git config test.wtlink >actual &&
461+
test_cmp expect actual
462+
)
463+
'
464+
399465
test_done

0 commit comments

Comments
 (0)