From 5a74b78deb5a9422be0ad8463e670057061de59e Mon Sep 17 00:00:00 2001 From: Shanthosh Date: Mon, 13 Jul 2026 06:33:13 +0530 Subject: [PATCH 1/3] dir: add discoverable local-only .gitlocalignore Introduce a root-level, local-only ignore file '.gitlocalignore' that is read only when core.gitLocalIgnore is enabled (off by default, to avoid surprising repositories that already contain such a file). It is self-excluding (the file itself is ignored) and ranked below .git/info/exclude but above core.excludesFile in precedence. This gives users a discoverable place for machine-local ignore rules without committing them to the repository. Signed-off-by: Shanthosh --- dir.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dir.c b/dir.c index 32430090dcdf26..68f95ba222df93 100644 --- a/dir.c +++ b/dir.c @@ -3490,6 +3490,35 @@ void setup_standard_excludes(struct dir_struct *dir) add_patterns_from_file_1(dir, excludes_file, dir->untracked ? &dir->internal.ss_excludes_file : NULL); + /* + * .gitlocalignore: a discoverable, local-only ignore file at the + * worktree root, gated on core.gitLocalIgnore (off by default, to + * avoid surprising repositories that already contain a file with + * this name). Read here, between core.excludesFile and + * info/exclude, so its precedence ranks below info/exclude but + * above core.excludesFile. + */ + if (startup_info->have_repository) { + int git_local_ignore = 0; + if (repo_config_get_bool(the_repository, "core.gitLocalIgnore", + &git_local_ignore) >= 0 && git_local_ignore) { + const char *wt = the_repository->worktree; + if (wt) { + char *gl_path = mkpathdup("%s/.gitlocalignore", wt); + if (!access_or_warn(gl_path, R_OK, 0)) + add_patterns_from_file_1(dir, gl_path, NULL); + { + struct pattern_list *self = + add_pattern_list(dir, EXC_CMDL, + ".gitlocalignore"); + add_pattern(".gitlocalignore", "", 0, + self, -1); + } + dir->internal.unmanaged_exclude_files++; + } + } + } + /* per repository user preference */ if (startup_info->have_repository) { const char *path = git_path_info_exclude(); From fd45f2ae3082792770149f926335c5b91d6bf41a Mon Sep 17 00:00:00 2001 From: Shanthosh Date: Mon, 13 Jul 2026 07:36:16 +0530 Subject: [PATCH 2/3] t: add tests for .gitlocalignore Exercise the new discoverable, local-only ignore file: gating via core.gitLocalIgnore, self-exclusion, precedence relative to info/exclude and core.excludesFile, root-only scoping, and that ignored files stay out of git status and cannot be added without -f. Signed-off-by: Shanthosh --- t/meson.build | 1 + t/t0011-gitlocalignore.sh | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100755 t/t0011-gitlocalignore.sh diff --git a/t/meson.build b/t/meson.build index 7c3c070426dc9e..604620a4f62281 100644 --- a/t/meson.build +++ b/t/meson.build @@ -84,6 +84,7 @@ integration_tests = [ 't0008-ignores.sh', 't0009-git-dir-validation.sh', 't0010-racy-git.sh', + 't0011-gitlocalignore.sh', 't0012-help.sh', 't0013-sha1dc.sh', 't0014-alias.sh', diff --git a/t/t0011-gitlocalignore.sh b/t/t0011-gitlocalignore.sh new file mode 100755 index 00000000000000..7a995e432ab72c --- /dev/null +++ b/t/t0011-gitlocalignore.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +test_description='.gitlocalignore support (core.gitLocalIgnore)' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +# off by default: .gitlocalignore is not consulted at all +test_expect_success 'off by default: .gitlocalignore is ignored' ' + echo content >a.txt && + printf "a.txt\n" >.gitlocalignore && + test_must_fail git check-ignore a.txt +' + +test_expect_success 'core.gitLocalIgnore enables .gitlocalignore' ' + test_config core.gitLocalIgnore true && + echo content >b.txt && + printf "b.txt\n" >.gitlocalignore && + git check-ignore b.txt +' + +test_expect_success '.gitlocalignore is self-excluding' ' + test_config core.gitLocalIgnore true && + printf "b.txt\n" >.gitlocalignore && + git check-ignore .gitlocalignore +' + +test_expect_success 'info/exclude outranks .gitlocalignore' ' + test_config core.gitLocalIgnore true && + mkdir -p .git/info && + echo "c.txt" >.git/info/exclude && + echo content >c.txt && + printf "c.txt\n" >.gitlocalignore && + git check-ignore -v c.txt >out && + grep "info/exclude" out +' + +test_expect_success '.gitlocalignore outranks core.excludesFile' ' + test_config core.gitLocalIgnore true && + echo content >d.txt && + printf "d.txt\n" >.gitlocalignore && + echo "d.txt" >.git/excludes-extra && + test_config core.excludesFile "$(pwd)/.git/excludes-extra" && + git check-ignore -v d.txt >out && + grep "\.gitlocalignore" out +' + +test_expect_success 'nested .gitlocalignore contents are not read' ' + test_config core.gitLocalIgnore true && + rm -f .gitlocalignore && + mkdir -p sub && + echo content >sub/e.txt && + printf "e.txt\n" >sub/.gitlocalignore && + test_must_fail git check-ignore sub/e.txt +' + +test_expect_success 'ignored files stay out of git status' ' + test_config core.gitLocalIgnore true && + echo content >f.txt && + printf "f.txt\n" >.gitlocalignore && + git status --porcelain >out && + ! grep f.txt out +' + +test_expect_success 'git add refuses .gitlocalignore unless forced' ' + test_config core.gitLocalIgnore true && + printf "f.txt\n" >.gitlocalignore && + test_must_fail git add .gitlocalignore && + git add -f .gitlocalignore && + git ls-files --error-unmatch .gitlocalignore +' + +test_done From a2bacd907bb1d219b5d084874376b54b65fa70d8 Mon Sep 17 00:00:00 2001 From: Shanthosh Date: Mon, 13 Jul 2026 07:36:16 +0530 Subject: [PATCH 3/3] Documentation: describe .gitlocalignore and core.gitLocalIgnore Document the new root-level, local-only ignore file and its core.gitLocalIgnore switch, including precedence and the fact that the file is self-excluding and never tracked. Signed-off-by: Shanthosh --- Documentation/config/core.adoc | 9 +++++++++ Documentation/gitignore.adoc | 25 ++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc index a0ebf03e2eb050..3ac8464ad338f1 100644 --- a/Documentation/config/core.adoc +++ b/Documentation/config/core.adoc @@ -493,6 +493,15 @@ core.excludesFile:: If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore` is used instead. See linkgit:gitignore[5]. +core.gitLocalIgnore:: + If set to `true`, Git additionally reads ignore patterns from + a file named `.gitlocalignore` at the root of the working tree. + This file is meant for machine-local, uncommitted ignore rules: + it is ignored by Git itself (self-excluding) and is never tracked. + Its patterns are applied with lower precedence than + `.git/info/exclude` but higher than `core.excludesFile`. + Defaults to `false`. See linkgit:gitignore[5]. + core.askPass:: Some commands (e.g. svn and http interfaces) that interactively ask for a password can be told to use an external program given diff --git a/Documentation/gitignore.adoc b/Documentation/gitignore.adoc index 7979e50f187e23..e407e7e7f55223 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_COMMON_DIR/info/exclude, .gitignore +$XDG_CONFIG_HOME/git/ignore, $GIT_COMMON_DIR/info/exclude, .gitignore, .gitlocalignore DESCRIPTION ----------- @@ -34,10 +34,17 @@ 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_COMMON_DIR/info/exclude`. + * Patterns read from `$GIT_COMMON_DIR/info/exclude`. - * Patterns read from the file specified by the configuration - variable `core.excludesFile`. + * Patterns read from a `.gitlocalignore` file at the root of the + working tree, but only when the `core.gitLocalIgnore` configuration + variable is enabled (it is `false` by default). This file is intended + for machine-local, uncommitted ignore rules: Git ignores the file + itself (self-excluding) and never tracks it. Its precedence is below + `$GIT_COMMON_DIR/info/exclude` but above `core.excludesFile`. + + * Patterns read from the file specified by the configuration + variable `core.excludesFile`. Which file to place a pattern in depends on how the pattern is meant to be used. @@ -57,7 +64,15 @@ be used. the user's editor of choice) generally go into a file specified by `core.excludesFile` in the user's `~/.gitconfig`. Its default value is $XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or - empty, $HOME/.config/git/ignore is used instead. + empty, $HOME/.config/git/ignore is used instead. + + * Patterns which apply only to your local machine and which you do + not want to share or commit (e.g., paths that exist only in your + checkout) can go into a `.gitlocalignore` file at the root of the + working tree, once `core.gitLocalIgnore` is enabled. Unlike the + other files above, `.gitlocalignore` is ignored by Git itself and + will never be tracked or pushed. + The underlying Git plumbing tools, such as 'git ls-files' and 'git read-tree', read