From: Phillip Wood <phillip.wood123@gmail.com>
To: "D. Ben Knoble" <ben.knoble+github@gmail.com>, git@vger.kernel.org
Cc: "brian m . carlson" <sandals@crustytoothpaste.net>,
Patrick Steinhardt <ps@pks.im>, Taylor Blau <me@ttaylorr.com>,
Caleb White <cdwhite3@pm.me>, Calvin Wan <calvinwan@google.com>,
Junio C Hamano <gitster@pobox.com>,
Elijah Newren <newren@gmail.com>,
Andrew Berry <andrew@furrypaws.ca>, Jeff King <peff@peff.net>,
Derrick Stolee <stolee@gmail.com>
Subject: Re: [PATCH] dir: use per-worktree repository ignore patterns upon request
Date: Fri, 24 Apr 2026 20:53:31 +0100 [thread overview]
Message-ID: <4a8d1289-2e2b-4fd6-8ada-143992dd7c4d@gmail.com> (raw)
In-Reply-To: <e3ee0a11b566dd2cc605447c111ae4620bce0fe6.1777050300.git.ben.knoble+github@gmail.com>
Hi Ben
On 24/04/2026 18:09, D. Ben Knoble wrote:
> Today we have $GIT_DIR/info/exclude for the main worktree, but $(git
> rev-parse --git-dir)/info/exclude for secondary worktrees does not
> actually contribute to ignore specs; instead, secondary worktrees also
> use $GIT_COMMON_DIR/info/exclude.
Sharing the same set of exclude patterns between all worktrees seems
like a reasonable thing to do - it would be a pain to have to populate
them every time a new worktree was created.
git rev-parse --git-path info/exclude
always returns the correct path because it uses repo_git_path() which
knows whether a particular path is per-worktree or resides under
$GIT_COMMON_DIR. It is best to avoid constructing paths manually using
"git rev-parse --git-dir"
> Some users may prefer each worktree use its own ignore file; some may
> prefer both; some may prefer the current behavior.
This sounds quite hypothetical - do we have a concrete use case for
per-worktree exclude files?
If there is a use for per-worktree excludes it would be much better to
use a different path such as "info.worktree/exclude" so that the main
worktree can have both shared and per-worktree excludes. We could use
"exclude.worktree" like "config.worktree" but then we'd be in danger of
adding new ".worktree" files in the future if we want to add
per-worktree versions of other files that live under "info/"
Thanks
Phillip
> Add, test, and document extensions.worktreeIgnore that controls which
> set of ignore files to use for worktrees.
>
> Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
> ---
>
> Notes (benknoble/commits):
> Discussed briefly at https://lore.kernel.org/git/CALnO6CCXmA+ATT7CuyWkU6P8qmLCCpMi5Ppr1c78s0heznpVyw@mail.gmail.com/T
>
> This is based on next (4f69b47b94 (Merge branch 'ps/test-set-e-clean' into
> next, 2026-04-23)) but cleanly applies to master (94f057755b (Git 2.54,
> 2026-04-19)) and seen (50541634cb (Merge branch
> 'js/parseopt-subcommand-autocorrection' into seen, 2026-04-23)).
>
> Documentation/config/extensions.adoc | 7 ++++
> Documentation/git-worktree.adoc | 3 ++
> Documentation/gitignore.adoc | 6 ++--
> dir.c | 48 +++++++++++++++++++++++---
> t/meson.build | 1 +
> t/t2408-worktree-ignore.sh | 50 ++++++++++++++++++++++++++++
> 6 files changed, 109 insertions(+), 6 deletions(-)
> create mode 100755 t/t2408-worktree-ignore.sh
>
> diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc
> index be6678bb5b..5bfb06a54b 100644
> --- a/Documentation/config/extensions.adoc
> +++ b/Documentation/config/extensions.adoc
> @@ -148,3 +148,10 @@ details.
> +
> For historical reasons, this extension is respected regardless of the
> `core.repositoryFormatVersion` setting.
> +
> +worktreeIgnore:::
> + If enabled, then worktrees will load per-repository ignore files from
> + `$GIT_DIR/info/exclude` (that is,
> + `$GIT_COMMON_DIR/worktrees/<id>/info/exclude`). If set to `merge`, then
> + both `$GIT_COMMON_DIR/info/exclude` and `$GIT_DIR/info/exclude` are
> + used.
> diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
> index fbf8426cd9..611c1e06b0 100644
> --- a/Documentation/git-worktree.adoc
> +++ b/Documentation/git-worktree.adoc
> @@ -412,6 +412,9 @@ linkgit:gitrepository-layout[5] for details.
> When `extensions.worktreeConfig` is enabled, the config file
> `.git/worktrees/<id>/config.worktree` is read after `.git/config` is.
>
> +See `extensions.worktreeIgnore` in linkgit:git-config[1] to control how
> +per-repository ignore files are found in worktrees.
> +
> LIST OUTPUT FORMAT
> ------------------
> The `worktree list` command has two output formats. The default format shows the
> diff --git a/Documentation/gitignore.adoc b/Documentation/gitignore.adoc
> index a3d24e5c34..d6976a44e4 100644
> --- a/Documentation/gitignore.adoc
> +++ b/Documentation/gitignore.adoc
> @@ -7,7 +7,7 @@ gitignore - Specifies intentionally untracked files to ignore
>
> SYNOPSIS
> --------
> -$XDG_CONFIG_HOME/git/ignore, $GIT_DIR/info/exclude, .gitignore
> +$XDG_CONFIG_HOME/git/ignore, $GIT_COMMON_DIR/info/exclude, .gitignore
>
> DESCRIPTION
> -----------
> @@ -34,7 +34,9 @@ precedence, the last matching pattern decides the outcome):
> includes such `.gitignore` files in its repository, containing patterns for
> files generated as part of the project build.
>
> - * Patterns read from `$GIT_DIR/info/exclude`.
> + * Patterns read from `$GIT_COMMON_DIR/info/exclude`. (See
> + `extensions.worktreeIgnore` in linkgit:git-config[1] to change how this
> + applies to worktrees.)
>
> * Patterns read from the file specified by the configuration
> variable `core.excludesFile`.
> diff --git a/dir.c b/dir.c
> index fcb8f6dd2a..9592eb0062 100644
> --- a/dir.c
> +++ b/dir.c
> @@ -36,6 +36,7 @@
> #include "trace2.h"
> #include "tree.h"
> #include "hex.h"
> +#include "worktree.h"
>
> /*
> * The maximum size of a pattern/exclude file. If the file exceeds this size
> @@ -3478,6 +3479,23 @@ int remove_dir_recursively(struct strbuf *path, int flag)
> }
>
> static GIT_PATH_FUNC(git_path_info_exclude, "info/exclude")
> +static const char *git_worktree_info_exclude(void)
> +{
> + static const char *ret;
> + if (!ret)
> + {
> + const struct worktree *wt = get_worktree_from_repository(the_repository);
> + ret = worktree_git_path(wt, "info/exclude");
> + }
> + return ret;
> +}
> +
> +static void standard_exclude_from_info(const char *path, struct dir_struct *dir)
> +{
> + struct oid_stat *oid_stat = dir->untracked ? &dir->internal.ss_info_exclude : NULL;
> + if (!access_or_warn(path, R_OK, 0))
> + add_patterns_from_file_1(dir, path, oid_stat);
> +}
>
> void setup_standard_excludes(struct dir_struct *dir)
> {
> @@ -3492,10 +3510,32 @@ void setup_standard_excludes(struct dir_struct *dir)
>
> /* per repository user preference */
> if (startup_info->have_repository) {
> - const char *path = git_path_info_exclude();
> - if (!access_or_warn(path, R_OK, 0))
> - add_patterns_from_file_1(dir, path,
> - dir->untracked ? &dir->internal.ss_info_exclude : NULL);
> + /* extensions.worktreeIgnore determines which includes we add */
> + const char *wt_ignore;
> + int do_wt_ignore;
> + if (repo_config_get_value(the_repository,
> + "extensions.worktreeIgnore",
> + &wt_ignore)) {
> + /* unset: use main/.git/info/exclude */
> + standard_exclude_from_info(git_path_info_exclude(), dir);
> + return;
> + }
> + do_wt_ignore = git_parse_maybe_bool(wt_ignore);
> + if (!do_wt_ignore) {
> + /* false: as above */
> + standard_exclude_from_info(git_path_info_exclude(), dir);
> + } else if (do_wt_ignore > 0 || !strcmp(wt_ignore, "worktree")) {
> + /* true/worktree: use worktree/.git‡/info/exclude
> + * ‡: resolving .git */
> + standard_exclude_from_info(git_worktree_info_exclude(), dir);
> + } else if (!strcmp(wt_ignore, "merge")) {
> + /* merge both! worktree last */
> + standard_exclude_from_info(git_path_info_exclude(), dir);
> + standard_exclude_from_info(git_worktree_info_exclude(), dir);
> + } else {
> + die(_("invalid value for '%s': '%s'"),
> + "extensions.worktreeIgnore", wt_ignore);
> + }
> }
> }
>
> diff --git a/t/meson.build b/t/meson.build
> index 7528e5cda5..30624b59e8 100644
> --- a/t/meson.build
> +++ b/t/meson.build
> @@ -307,6 +307,7 @@ integration_tests = [
> 't2405-worktree-submodule.sh',
> 't2406-worktree-repair.sh',
> 't2407-worktree-heads.sh',
> + 't2408-worktree-ignore.sh',
> 't2500-untracked-overwriting.sh',
> 't2501-cwd-empty.sh',
> 't3000-ls-files-others.sh',
> diff --git a/t/t2408-worktree-ignore.sh b/t/t2408-worktree-ignore.sh
> new file mode 100755
> index 0000000000..67644ddd8d
> --- /dev/null
> +++ b/t/t2408-worktree-ignore.sh
> @@ -0,0 +1,50 @@
> +#!/bin/sh
> +
> +test_description='extensions.worktreeIgnore'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> + test_commit init &&
> + git worktree add wt &&
> + echo main >.git/info/exclude &&
> + echo contents >main &&
> + echo contents >wt/main &&
> + wt_exclude="$(git -C wt rev-parse --git-dir)"/info/exclude &&
> + mkdir -p "$(dirname "$wt_exclude")" &&
> + echo worktree >"$wt_exclude" &&
> + echo contents >worktree &&
> + echo contents >wt/worktree
> +'
> +
> +test_ignore_main() {
> + git check-ignore main &&
> + git -C wt check-ignore main
> +}
> +
> +test_expect_success 'ignores main items by default' '
> + test_ignore_main
> +'
> +
> +test_expect_success 'ignores main items with extensions.worktreeIgnore=no' '
> + test_config extensions.worktreeIgnore no &&
> + test_ignore_main
> +'
> +
> +test_expect_success 'ignores worktree items with extensions.worktreeIgnore=worktree' '
> + test_config extensions.worktreeIgnore worktree &&
> + git check-ignore main &&
> + ! git check-ignore worktree &&
> + ! git -C wt check-ignore main &&
> + git -C wt check-ignore worktree
> +'
> +
> +test_expect_success 'ignores all items with extensions.worktreeIgnore=merge' '
> + test_config extensions.worktreeIgnore merge &&
> + git check-ignore main &&
> + ! git check-ignore worktree &&
> + git -C wt check-ignore main &&
> + git -C wt check-ignore worktree
> +'
> +
> +test_done
>
> base-commit: 4f69b47b940100b02630f745a52f9d9850f122b2
next prev parent reply other threads:[~2026-04-24 19:53 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-24 17:09 [PATCH] dir: use per-worktree repository ignore patterns upon request D. Ben Knoble
2026-04-24 19:35 ` brian m. carlson
2026-04-24 19:53 ` Phillip Wood [this message]
2026-04-25 3:06 ` Junio C Hamano
2026-05-08 14:14 ` [PATCH v2] ignore: note info/exclude lives in GIT_COMMON_DIR, not GIT_DIR D. Ben Knoble
2026-05-09 14:08 ` brian m. carlson
2026-05-11 10:30 ` Phillip Wood
2026-05-11 19:55 ` D. Ben Knoble
2026-05-12 21:21 ` [PATCH v3] " D. Ben Knoble
2026-05-13 14:02 ` Phillip Wood
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=4a8d1289-2e2b-4fd6-8ada-143992dd7c4d@gmail.com \
--to=phillip.wood123@gmail.com \
--cc=andrew@furrypaws.ca \
--cc=ben.knoble+github@gmail.com \
--cc=calvinwan@google.com \
--cc=cdwhite3@pm.me \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=me@ttaylorr.com \
--cc=newren@gmail.com \
--cc=peff@peff.net \
--cc=phillip.wood@dunelm.org.uk \
--cc=ps@pks.im \
--cc=sandals@crustytoothpaste.net \
--cc=stolee@gmail.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.