All of lore.kernel.org
 help / color / mirror / Atom feed
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


  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.