* [PATCH 1/8] test-lib: allow bare repository access when breaking changes are enabled
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests Johannes Schindelin via GitGitGadget
` (7 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
A future patch will change the `safe.bareRepository` default from
`all` to `explicit` under `WITH_BREAKING_CHANGES`. At that point,
every test that operates on a bare repository through implicit
discovery would fail, regardless of whether the test is actually
about discovery or about how a specific command behaves once inside
a bare repository.
The maintainer suggested [1] setting `safe.bareRepository=all` in
the test environment's global config whenever `WITH_BREAKING_CHANGES`
is in effect, rather than adjusting each affected test to access
bare repositories explicitly (via `--git-dir`, `GIT_DIR`, or
similar). This means the test suite continues to exercise only the
historical default behavior even after the user-facing default
changes, relying on a small number of dedicated tests in t0035 to
validate the new, stricter default.
Since `$HOME` points at the trash directory (which doubles as the
test repository's working tree), writing to `$HOME/.gitconfig` also
creates a file inside the working tree. Exclude it via
`.git/info/exclude` to limit the fallout, though this does not
help tests that use `git ls-files --others` without
`--exclude-standard` or `git status --ignored`; those are addressed
by subsequent commits.
[1] https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/
Original-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/test-lib.sh | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..b8726f4647 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1597,6 +1597,12 @@ cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DIRECTORY\""
TRASH_DIRECTORY=$(pwd)
HOME="$TRASH_DIRECTORY"
+if test -n "$WITH_BREAKING_CHANGES"
+then
+ git config --global safe.bareRepository all &&
+ echo "/.gitconfig" >>.git/info/exclude
+fi
+
start_test_output "$0"
# Convenience
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 1/8] test-lib: allow bare repository access when breaking changes are enabled Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 3/8] t1300: remove global config settings injected by test-lib.sh Johannes Schindelin via GitGitGadget
` (6 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
The XDG config tests for `git maintenance register/unregister`
create a fresh `$XDG_CONFIG_HOME/git/config` and expect git to use
that location. However, if `$HOME/.gitconfig` exists (which may
happen when test-lib.sh writes global config, e.g. to set
`safe.bareRepository`), git prefers `$HOME/.gitconfig` over the XDG
location, and the `maintenance.repo` entry ends up in the wrong
file.
This is an inherent consequence of setting global config in
test-lib.sh rather than adjusting individual tests: writing any
entry to `$HOME/.gitconfig` has side effects beyond the intended
setting, because the mere existence of that file changes which
global config location git prefers for all subsequent writes.
Individual per-test adjustments would not have this interaction.
Fix this by overriding `HOME` to a non-existent directory inside the
subshells that test XDG behavior. Since these subshells already
override `XDG_CONFIG_HOME`, they do not need `$HOME/.gitconfig` at
all, and the subshell scoping ensures the original `HOME` is
restored automatically.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t7900-maintenance.sh | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 4700beacc1..4358df0424 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -101,8 +101,12 @@ test_expect_success "maintenance.autoDetach overrides gc.autoDetach" '
test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
test_when_finished rm -r .config/git/config &&
(
+ # Override HOME so that .gitconfig (which test-lib.sh may
+ # have created, e.g. to set safe.bareRepository) does not
+ # take precedence over the XDG location.
+ HOME=$PWD/must-not-exist &&
XDG_CONFIG_HOME=.config &&
- export XDG_CONFIG_HOME &&
+ export HOME XDG_CONFIG_HOME &&
mkdir -p $XDG_CONFIG_HOME/git &&
>$XDG_CONFIG_HOME/git/config &&
git maintenance register &&
@@ -124,8 +128,12 @@ test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
test_when_finished rm -r .config/git/config &&
(
+ # Override HOME so that .gitconfig (which test-lib.sh may
+ # have created, e.g. to set safe.bareRepository) does not
+ # take precedence over the XDG location.
+ HOME=$PWD/must-not-exist &&
XDG_CONFIG_HOME=.config &&
- export XDG_CONFIG_HOME &&
+ export HOME XDG_CONFIG_HOME &&
mkdir -p $XDG_CONFIG_HOME/git &&
>$XDG_CONFIG_HOME/git/config &&
git maintenance register &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 3/8] t1300: remove global config settings injected by test-lib.sh
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 1/8] test-lib: allow bare repository access when breaking changes are enabled Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test Johannes Schindelin via GitGitGadget
` (5 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Since test-lib.sh now writes `safe.bareRepository=all` to the global
config when `WITH_BREAKING_CHANGES` is in effect, that entry shows
up in `git config --list` output. Tests in t1300 that expect exact
config contents then fail because of this unexpected extra line.
Unlike the working-tree contamination fixed in the preceding
commits, this is not about the file's existence but about its
content leaking into test expectations. Since t1300 does not use
bare repositories, simply remove the injected setting in a
preparatory step.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Assisted-by: Claude Opus 4.6
---
t/t1300-config.sh | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 128971ee12..11fc976f3a 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
+# test-lib.sh may have added global config (e.g. safe.bareRepository)
+# that would appear in "git config --list" output and break tests
+# that expect exact config contents.
+test_expect_success 'remove global config from test-lib.sh' '
+ test_might_fail git config --global --unset-all safe.bareRepository
+'
+
for mode in legacy subcommands
do
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (2 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 3/8] t1300: remove global config settings injected by test-lib.sh Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 5/8] t5601: restore `.gitconfig` after includeIf test Johannes Schindelin via GitGitGadget
` (4 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Earlier tests in t1305 overwrite `$HOME/.gitconfig` with their own
content as part of testing config includes. This clobbers the
`safe.bareRepository=all` entry that test-lib.sh writes when
`WITH_BREAKING_CHANGES` is in effect, causing `git -C cycle config`
to fail with "not in a git directory" when it tries to access the
bare repository created by `git init --bare cycle`.
Use `--git-dir=.` to access the bare repo explicitly, avoiding the
dependency on global config for repository discovery.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t1305-config-include.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 6e51f892f3..f3892578e4 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -350,9 +350,9 @@ test_expect_success 'conditional include, onbranch, implicit /** for /' '
test_expect_success 'include cycles are detected' '
git init --bare cycle &&
- git -C cycle config include.path cycle &&
+ git -C cycle --git-dir=. config include.path cycle &&
git config -f cycle/cycle include.path config &&
- test_must_fail git -C cycle config --get-all test.value 2>stderr &&
+ test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr &&
grep "exceeded maximum include depth" stderr
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 5/8] t5601: restore `.gitconfig` after includeIf test
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (3 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output Johannes Schindelin via GitGitGadget
` (3 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
One test in t5601 overwrites `$HOME/.gitconfig` with an `includeIf`
configuration snippet and removes the file in its cleanup. This
destroys the `safe.bareRepository=all` entry that test-lib.sh
writes when `WITH_BREAKING_CHANGES` is in effect, causing later
tests that use `git -C <bare-repo> config` to fail with "not in a
git directory".
Back up `.gitconfig` before overwriting and restore it in the
cleanup, so the global config survives into subsequent tests.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t5601-clone.sh | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index d743d986c4..3dd229c186 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -813,7 +813,9 @@ test_expect_success 'clone with includeIf' '
test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- test_when_finished "rm \"$HOME\"/.gitconfig" &&
+ test_when_finished "cp \"$HOME\"/.gitconfig.bak \
+ \"$HOME\"/.gitconfig 2>/dev/null || rm -f \"$HOME\"/.gitconfig" &&
+ cp "$HOME"/.gitconfig "$HOME"/.gitconfig.bak 2>/dev/null &&
cat >"$HOME"/.gitconfig <<-EOF &&
[includeIf "onbranch:something"]
path = /does/not/exist.inc
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (4 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 5/8] t5601: restore `.gitconfig` after includeIf test Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-26 0:44 ` Junio C Hamano
2026-04-24 15:01 ` [PATCH 7/8] status tests: filter `.gitconfig` from status output Johannes Schindelin via GitGitGadget
` (2 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
The global `safe.bareRepository=all` setting in test-lib.sh is
written to `$HOME/.gitconfig`, which unfortunately lives inside the
test repository's working tree. The `.git/info/exclude` entry added
alongside it handles most commands, but `git ls-files --others`
without `--exclude-standard` does not consult `info/exclude` at
all, so the file appears in the output.
Ideally, each test that accesses a bare repository would simply
specify `--git-dir` or `GIT_DIR` explicitly, which would require no
global config and produce no side effects in the working tree. As
that approach was not taken, filter `.gitconfig` from the output
before comparing against expected results. In t7104, the test
already uses `--exclude-standard`, so it suffices to switch from
the bare `git ls-files -o` to `git ls-files -o --exclude-standard`
which respects the `info/exclude` entry; the other tests
deliberately omit `--exclude-standard` because their purpose is to
verify unfiltered `--others` output.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t3000-ls-files-others.sh | 4 ++++
t/t3001-ls-files-others-exclude.sh | 3 +++
t/t3002-ls-files-dashpath.sh | 2 ++
t/t3009-ls-files-others-nonsubmodule.sh | 1 +
t/t3011-common-prefixes-and-directory-traversal.sh | 3 ++-
t/t7104-reset-hard.sh | 2 +-
t/test-lib-functions.sh | 8 ++++++++
7 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index b41e7f0daa..b4f0fbfc55 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -53,16 +53,19 @@ test_expect_success 'setup: expected output' '
test_expect_success 'ls-files --others' '
git ls-files --others >output &&
+ test_filter_gitconfig output &&
test_cmp expected1 output
'
test_expect_success 'ls-files --others --directory' '
git ls-files --others --directory >output &&
+ test_filter_gitconfig output &&
test_cmp expected2 output
'
test_expect_success '--no-empty-directory hides empty directory' '
git ls-files --others --directory --no-empty-directory >output &&
+ test_filter_gitconfig output &&
test_cmp expected3 output
'
@@ -70,6 +73,7 @@ test_expect_success 'ls-files --others handles non-submodule .git' '
mkdir not-a-submodule &&
echo foo >not-a-submodule/.git &&
git ls-files -o >output &&
+ test_filter_gitconfig output &&
test_cmp expected1 output
'
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 4b67646285..202fb8d9ea 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -72,6 +72,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -84,6 +85,7 @@ test_expect_success 'git ls-files --others with \r\n line endings.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -99,6 +101,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 31462cb441..6acaadbd67 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -24,6 +24,7 @@ test_expect_success 'setup' '
test_expect_success 'git ls-files without path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others >output &&
+ test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
@@ -63,6 +64,7 @@ test_expect_success 'git ls-files with path restriction with -- --.' '
test_expect_success 'git ls-files with no path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others -- >output &&
+ test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f3462b7..dc990c277b 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -36,6 +36,7 @@ test_expect_success 'setup: directories' '
test_expect_success 'ls-files --others handles untracked git repositories' '
git ls-files -o >output &&
+ test_filter_gitconfig output &&
cat >expect <<-EOF &&
nonrepo-untracked-file/untracked
output
diff --git a/t/t3011-common-prefixes-and-directory-traversal.sh b/t/t3011-common-prefixes-and-directory-traversal.sh
index 3da5b2b6e7..455e97954d 100755
--- a/t/t3011-common-prefixes-and-directory-traversal.sh
+++ b/t/t3011-common-prefixes-and-directory-traversal.sh
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
'
test_expect_success 'git ls-files -o shows the right entries' '
- cat <<-EOF >expect &&
+ cat >expect <<-EOF &&
.gitignore
actual
an_ignored_dir/ignored
@@ -39,6 +39,7 @@ test_expect_success 'git ls-files -o shows the right entries' '
untracked_repo/
EOF
git ls-files -o >actual &&
+ test_filter_gitconfig actual &&
test_cmp expect actual
'
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec392b..c23d6e3f52 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -21,7 +21,7 @@ test_expect_success setup '
rm -f hello &&
mkdir -p hello &&
>hello/world &&
- test "$(git ls-files -o)" = hello/world
+ test "$(git ls-files -o --exclude-standard)" = hello/world
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..0505da78e8 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -2069,3 +2069,11 @@ test_trailing_hash () {
test_redact_non_printables () {
tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
}
+
+# Remove .gitconfig entries from a file in place. test-lib.sh may
+# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
+# can appear in ls-files or status output.
+test_filter_gitconfig () {
+ sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
+ mv "$1.filtered" "$1"
+}
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output
2026-04-24 15:01 ` [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output Johannes Schindelin via GitGitGadget
@ 2026-04-26 0:44 ` Junio C Hamano
0 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2026-04-26 0:44 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> +# Remove .gitconfig entries from a file in place. test-lib.sh may
> +# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
> +# can appear in ls-files or status output.
> +test_filter_gitconfig () {
> + sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
> + mv "$1.filtered" "$1"
> +}
Thanks.
When I suggested the "just use the usual configuration mechanism to
keep the semantics of existing bare repository tests, and that would
be very unintrusive", I didn't think of this fallout from an extra
file getting reported by ls-files, and this helper function does
make sense. If we were starting from scratch, we probably would
have created $HOME that is separate from the test repository in the
test framework. The simplest layout would be to emulate a developer
who has repositories under their $HOME, i.e., $TRASH_DIRECTORY stays
to be the $HOME, but the initial test repository would be created as
a directory inside $TRASH_DIRECTORY instead of using the trash
itself, or something like that. It is all water under the bridge
now, unless somebody wants an opportunity to work on a non-trivial
clean-up.
By the way, when merged to 'seen', with the 'ps/test-set-e-clean'
topic already in 'next', many tests (especially the ones with early
test_done, like p4 tests in my environment where p4 does not exist)
seem to fail with WITH_BREAKING_CHANGES turned on. I don't have
enough time to be sitting in front of the keyboard to isolate the
cause, but because this series is one of the topics with biggest
impact to the t/ directory that was replaced after the last
successful run, I thought I should mention it.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 7/8] status tests: filter `.gitconfig` from status output
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (5 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-24 15:01 ` [PATCH 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Since test-lib.sh creates `$HOME/.gitconfig` when
`WITH_BREAKING_CHANGES` is in effect, the file appears in `git
status` output as either untracked (`?? .gitconfig`) or ignored
(`!! .gitconfig` / `! .gitconfig`, depending on porcelain version),
because the `.git/info/exclude` entry causes git to treat it as an
ignored file rather than hiding it entirely.
In t7061 and t7521, which are pervasively affected, introduce a
`filter_gitconfig` helper that strips all status-prefix variants of
`.gitconfig` from the output before comparison. In the remaining
scripts (t7060, t7064, t7508), apply targeted adjustments.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t7060-wtstatus.sh | 3 +--
t/t7061-wtstatus-ignore.sh | 27 +++++++++++++++++++++++++++
t/t7064-wtstatus-pv2.sh | 1 +
t/t7508-status.sh | 4 ++++
t/t7521-ignored-mode.sh | 1 +
5 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c55e..942ddbbf0e 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -9,6 +9,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success setup '
git config --global advice.statusuoption false &&
+ echo "/.gitconfig" >>.git/info/exclude &&
test_commit A &&
test_commit B oneside added &&
git checkout A^0 &&
@@ -221,7 +222,6 @@ test_expect_success 'status --branch with detached HEAD' '
git status --branch --porcelain >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
- ?? .gitconfig
?? actual
?? expect
?? expected
@@ -237,7 +237,6 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
git status --branch --porcelain=v1 >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
- ?? .gitconfig
?? actual
?? expect
?? expected
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index 2f9bea9793..14ddaba2f3 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -18,6 +18,7 @@ test_expect_success 'status untracked directory with --ignored' '
: >untracked/ignored &&
: >untracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -27,6 +28,7 @@ test_expect_success 'same with gitignore starting with BOM' '
: >untracked/ignored &&
: >untracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -40,18 +42,22 @@ test_expect_success 'status untracked files --ignored with pathspec (no match)'
test_expect_success 'status untracked files --ignored with pathspec (literal match)' '
git status --porcelain --ignored -- untracked/ignored >actual &&
echo "!! untracked/ignored" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored -- untracked/uncommitted >actual &&
echo "?? untracked/uncommitted" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
test_expect_success 'status untracked files --ignored with pathspec (glob match)' '
git status --porcelain --ignored -- untracked/i\* >actual &&
echo "!! untracked/ignored" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored -- untracked/u\* >actual &&
echo "?? untracked/uncommitted" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -65,6 +71,7 @@ EOF
test_expect_success 'status untracked directory with --ignored -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
cat >expected <<\EOF
@@ -76,9 +83,11 @@ test_expect_success 'status of untracked directory with --ignored works with or
git status --porcelain --ignored >tmp &&
grep untracked/ tmp >actual &&
rm tmp &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored untracked/ >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -89,6 +98,7 @@ EOF
test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
git status --porcelain --ignored -u untracked/ >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -104,6 +114,7 @@ test_expect_success 'status ignored directory with --ignore' '
mkdir ignored &&
: >ignored/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -116,6 +127,7 @@ EOF
test_expect_success 'status ignored directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -130,6 +142,7 @@ test_expect_success 'status empty untracked directory with --ignore' '
mkdir untracked-ignored &&
mkdir untracked-ignored/test &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -141,6 +154,7 @@ EOF
test_expect_success 'status empty untracked directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -155,6 +169,7 @@ test_expect_success 'status untracked directory with ignored files with --ignore
: >untracked-ignored/ignored &&
: >untracked-ignored/test/ignored &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -168,6 +183,7 @@ EOF
test_expect_success 'status untracked directory with ignored files with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -185,6 +201,7 @@ test_expect_success 'status ignored tracked directory with --ignore' '
git commit -m. &&
echo "tracked" >.gitignore &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -196,6 +213,7 @@ EOF
test_expect_success 'status ignored tracked directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -208,6 +226,7 @@ EOF
test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
echo "committed" >>.gitignore &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -219,6 +238,7 @@ EOF
test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -233,6 +253,7 @@ test_expect_success 'status ignored tracked directory and uncommitted file with
echo "tracked" >.gitignore &&
: >tracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -245,6 +266,7 @@ EOF
test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -260,6 +282,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in u
mkdir tracked/ignored &&
: >tracked/ignored/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -272,6 +295,7 @@ EOF
test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -287,6 +311,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in t
git add -f tracked/ignored/committed &&
git commit -m. &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -299,6 +324,7 @@ EOF
test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -310,6 +336,7 @@ test_expect_success 'status ignores submodule in excluded directory' '
git init tracked/submodule &&
test_commit -C tracked/submodule initial &&
git status --porcelain --ignored -u tracked/submodule >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 8bbc5ce6d9..be6c931a96 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -231,6 +231,7 @@ test_expect_success 'ignored files are printed with --ignored' '
EOF
git status --porcelain=v2 --ignored --untracked-files=all >actual &&
+ test_filter_gitconfig actual &&
test_cmp expect actual
'
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..5f76ec62d8 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -263,6 +263,7 @@ test_expect_success 'status with gitignore' '
!! untracked
EOF
git status -s --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output &&
cat >expect <<\EOF &&
@@ -296,6 +297,7 @@ Ignored files:
EOF
git status --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -328,6 +330,7 @@ test_expect_success 'status with gitignore (nothing untracked)' '
!! untracked
EOF
git status -s --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output &&
cat >expect <<\EOF &&
@@ -358,6 +361,7 @@ Ignored files:
EOF
git status --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
index a88b02b06e..7ea0b0d2f2 100755
--- a/t/t7521-ignored-mode.sh
+++ b/t/t7521-ignored-mode.sh
@@ -30,6 +30,7 @@ test_expect_success 'Verify behavior of status on directories with ignored files
dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (6 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 7/8] status tests: filter `.gitconfig` from status output Johannes Schindelin via GitGitGadget
@ 2026-04-24 15:01 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
8 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-24 15:01 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
When an attacker can convince a user to clone a crafted repository
that contains an embedded bare repository with malicious hooks, any Git
command the user runs after entering that subdirectory will discover
the bare repository and execute the hooks. The user does not even need
to run a Git command explicitly: many shell prompts run `git status`
in the background to display branch and dirty state information, and
`git status` in turn may invoke the fsmonitor hook if so configured,
making the user vulnerable the moment they `cd` into the directory. The
`safe.bareRepository` configuration variable (introduced in 8959555cee7e
(setup_git_directory(): add an owner check for the top-level directory,
2022-03-02)) already provides protection against this attack vector by
allowing users to set it to "explicit", but the default remained "all"
for backwards compatibility.
Since Git 3.0 is the natural point to change defaults to safer
values, flip the default from "all" to "explicit" when built with
`WITH_BREAKING_CHANGES`. This means Git will refuse to work with bare
repositories that are discovered implicitly by walking up the directory
tree. Bare repositories specified via `--git-dir` or `GIT_DIR` continue
to work, and directories that look like `.git`, worktrees, or submodule
directories are unaffected (the existing `is_implicit_bare_repo()`
whitelist handles those cases).
Users who rely on implicit bare repository discovery can restore the
previous behavior by setting `safe.bareRepository=all` in their global
or system configuration.
The test for the "safe.bareRepository in the repository" scenario
needed a more involved fix: it writes a `safe.bareRepository=all`
entry into the bare repository's own config to verify that repo-local
config does not override the protected (global) setting. Previously,
`test_config -C` was used to write that entry, but its cleanup runs `git
-C <bare-repo> config --unset`, which itself fails when the default is
"explicit" and the global config has already been cleaned up. Switching
to direct git config --file access avoids going through repository
discovery entirely.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
Documentation/BreakingChanges.adoc | 24 ++++++++++++++++++++++++
Documentation/config/safe.adoc | 10 ++++++++--
setup.c | 4 ++++
t/t0035-safe-bare-repository.sh | 10 ++++++++--
4 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index af59c43f42..73bb939359 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -216,6 +216,30 @@ would be significant, we may decide to defer this change to a subsequent minor
release. This evaluation will also take into account our own experience with
how painful it is to keep Rust an optional component.
+* The default value of `safe.bareRepository` will change from `all` to
+ `explicit`. It is all too easy for an attacker to trick a user into cloning a
+ repository that contains an embedded bare repository with malicious hooks
+ configured. If the user enters that subdirectory and runs any Git command, Git
+ discovers the bare repository and the hooks fire. The user does not even need
+ to run a Git command explicitly: many shell prompts run `git status` in the
+ background to display branch and dirty state information, and `git status` in
+ turn may invoke the fsmonitor hook if so configured, making the user
+ vulnerable the moment they `cd` into the directory. The `safe.bareRepository`
+ configuration variable was introduced in 8959555cee (setup_git_directory():
+ add an owner check for the top-level directory, 2022-03-02) with a default of
+ `all` to preserve backwards compatibility.
++
+Changing the default to `explicit` means that Git will refuse to work with bare
+repositories that are discovered implicitly by walking up the directory tree.
+Bare repositories specified explicitly via the `--git-dir` command-line option
+or the `GIT_DIR` environment variable continue to work regardless of this
+setting. Repositories that look like a `.git` directory, a worktree, or a
+submodule directory are also unaffected.
++
+Users who rely on implicit discovery of bare repositories can restore the
+previous behavior by setting `safe.bareRepository=all` in their global or
+system configuration.
+
=== Removals
* Support for grafting commits has long been superseded by git-replace(1).
diff --git a/Documentation/config/safe.adoc b/Documentation/config/safe.adoc
index 2d45c98b12..5b1690aebe 100644
--- a/Documentation/config/safe.adoc
+++ b/Documentation/config/safe.adoc
@@ -2,10 +2,12 @@ safe.bareRepository::
Specifies which bare repositories Git will work with. The currently
supported values are:
+
-* `all`: Git works with all bare repositories. This is the default.
+* `all`: Git works with all bare repositories. This is the default in
+ Git 2.x.
* `explicit`: Git only works with bare repositories specified via
the top-level `--git-dir` command-line option, or the `GIT_DIR`
- environment variable (see linkgit:git[1]).
+ environment variable (see linkgit:git[1]). This will be the default
+ in Git 3.0.
+
If you do not use bare repositories in your workflow, then it may be
beneficial to set `safe.bareRepository` to `explicit` in your global
@@ -13,6 +15,10 @@ config. This will protect you from attacks that involve cloning a
repository that contains a bare repository and running a Git command
within that directory.
+
+If you use bare repositories regularly and want to preserve the current
+behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
+in your global or system config.
++
This config setting is only respected in protected configuration (see
<<SCOPES>>). This prevents untrusted repositories from tampering with
this value.
diff --git a/setup.c b/setup.c
index 7ec4427368..17c0662076 100644
--- a/setup.c
+++ b/setup.c
@@ -1485,7 +1485,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,
static enum allowed_bare_repo get_allowed_bare_repo(void)
{
+#ifdef WITH_BREAKING_CHANGES
+ enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
+#else
enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
+#endif
git_protected_config(allowed_bare_repo_cb, &result);
return result;
}
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index ae7ef092ab..1d3d19f5b4 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -44,11 +44,16 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
test_path_is_dir outer-repo/.git/modules/subn
'
-test_expect_success 'safe.bareRepository unset' '
+test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
test_unconfig --global safe.bareRepository &&
expect_accepted_implicit -C outer-repo/bare-repo
'
+test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
+ test_unconfig --global safe.bareRepository &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
test_expect_success 'safe.bareRepository=all' '
test_config_global safe.bareRepository all &&
expect_accepted_implicit -C outer-repo/bare-repo
@@ -63,7 +68,8 @@ test_expect_success 'safe.bareRepository in the repository' '
# safe.bareRepository must not be "explicit", otherwise
# git config fails with "fatal: not in a git directory" (like
# safe.directory)
- test_config -C outer-repo/bare-repo safe.bareRepository all &&
+ test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
+ git config --file outer-repo/bare-repo/config safe.bareRepository all &&
test_config_global safe.bareRepository explicit &&
expect_rejected -C outer-repo/bare-repo
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
2026-04-24 15:01 [PATCH 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
` (7 preceding siblings ...)
2026-04-24 15:01 ` [PATCH 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 1/8] test-lib: allow bare repository access when breaking changes are enabled Johannes Schindelin via GitGitGadget
` (7 more replies)
8 siblings, 8 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin
This supersedes my earlier series [*1*] which took the approach of adjusting
individual tests to access bare repositories explicitly.
As Junio suggested [*2*], this series instead takes the approach of setting
safe.bareRepository=all in the test environment's global config whenever
WITH_BREAKING_CHANGES is in effect, so that existing tests continue to work
without individual modifications.
Implementing this turned out to require a number of follow-up adjustments,
because writing to $HOME/.gitconfig has side effects beyond the intended
setting: $HOME is the trash directory, which doubles as the test
repository's working tree, so the file shows up in ls-files and status
output, and tests that manipulate $HOME/.gitconfig for their own purposes
can clobber or remove the setting. Patches 2 through 7 address these
interactions in the affected test scripts.
The final patch flips the safe.bareRepository default to "explicit" under
WITH_BREAKING_CHANGES.
Footnote [*1*]:
https://lore.kernel.org/git/pull.2076.git.1775140403.gitgitgadget@gmail.com/
Footnote [*2*]: https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/
Changes since v1:
* Made it compatible with Patrick Steinhardt's set -e work.
Johannes Schindelin (8):
test-lib: allow bare repository access when breaking changes are
enabled
t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
t1300: remove global config settings injected by test-lib.sh
t1305: use `--git-dir=.` for bare repo in include cycle test
t5601: restore `.gitconfig` after includeIf test
ls-files tests: filter `.gitconfig` from `--others` output
status tests: filter `.gitconfig` from status output
safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
Documentation/BreakingChanges.adoc | 24 +++++++++++++++++
Documentation/config/safe.adoc | 10 +++++--
setup.c | 4 +++
t/t0035-safe-bare-repository.sh | 10 +++++--
t/t1300-config.sh | 7 +++++
t/t1305-config-include.sh | 4 +--
t/t3000-ls-files-others.sh | 4 +++
t/t3001-ls-files-others-exclude.sh | 3 +++
t/t3002-ls-files-dashpath.sh | 2 ++
t/t3009-ls-files-others-nonsubmodule.sh | 1 +
...common-prefixes-and-directory-traversal.sh | 3 ++-
t/t5601-clone.sh | 4 ++-
t/t7060-wtstatus.sh | 3 +--
t/t7061-wtstatus-ignore.sh | 27 +++++++++++++++++++
t/t7064-wtstatus-pv2.sh | 1 +
t/t7104-reset-hard.sh | 2 +-
t/t7508-status.sh | 4 +++
t/t7521-ignored-mode.sh | 1 +
t/t7900-maintenance.sh | 12 +++++++--
t/test-lib-functions.sh | 8 ++++++
t/test-lib.sh | 13 +++++++++
21 files changed, 134 insertions(+), 13 deletions(-)
base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2098%2Fdscho%2Fsafe-bare-repo-default-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2098/dscho/safe-bare-repo-default-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2098
Range-diff vs v1:
1: 62707b4109 ! 1: 179fcf5369 test-lib: allow bare repository access when breaking changes are enabled
@@ Commit message
`--exclude-standard` or `git status --ignored`; those are addressed
by subsequent commits.
+ The `.git/info/exclude` write is guarded by `test -d .git/info`
+ rather than using `mkdir -p`, because some tests (e.g. t0008)
+ expect to create `.git/info/` themselves and would fail with
+ Patrick Steinhardt's `set -e` preparation (ps/test-set-e-clean) if
+ the directory already existed. For tests using `TEST_NO_CREATE_REPO`
+ (where no `.git/` exists at all), the guard also handles that case.
+
[1] https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/
Original-patch-by: Junio C Hamano <gitster@pobox.com>
@@ t/test-lib.sh: cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DI
+if test -n "$WITH_BREAKING_CHANGES"
+then
+ git config --global safe.bareRepository all &&
-+ echo "/.gitconfig" >>.git/info/exclude
++ # Only write to .git/info/exclude when the directory exists
++ # (i.e. when git init created the repo). If we mkdir -p it
++ # ourselves, tests that expect to create .git/info/ themselves
++ # (e.g. t0008) would fail.
++ if test -d .git/info
++ then
++ echo "/.gitconfig" >>.git/info/exclude
++ fi
+fi
+
start_test_output "$0"
2: d9a2e76f3c = 2: 4dc5151e59 t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
3: 9c10e72eed = 3: 7d68155805 t1300: remove global config settings injected by test-lib.sh
4: 092ec11621 = 4: 5ff48e0892 t1305: use `--git-dir=.` for bare repo in include cycle test
5: 3aca302275 = 5: ed7294ace3 t5601: restore `.gitconfig` after includeIf test
6: ef57244778 = 6: 556db0eabe ls-files tests: filter `.gitconfig` from `--others` output
7: 56fe902644 = 7: ac4da79eac status tests: filter `.gitconfig` from status output
8: 64db45e385 = 8: 73bb1aa171 safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
--
gitgitgadget
^ permalink raw reply [flat|nested] 19+ messages in thread* [PATCH v2 1/8] test-lib: allow bare repository access when breaking changes are enabled
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests Johannes Schindelin via GitGitGadget
` (6 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
A future patch will change the `safe.bareRepository` default from
`all` to `explicit` under `WITH_BREAKING_CHANGES`. At that point,
every test that operates on a bare repository through implicit
discovery would fail, regardless of whether the test is actually
about discovery or about how a specific command behaves once inside
a bare repository.
The maintainer suggested [1] setting `safe.bareRepository=all` in
the test environment's global config whenever `WITH_BREAKING_CHANGES`
is in effect, rather than adjusting each affected test to access
bare repositories explicitly (via `--git-dir`, `GIT_DIR`, or
similar). This means the test suite continues to exercise only the
historical default behavior even after the user-facing default
changes, relying on a small number of dedicated tests in t0035 to
validate the new, stricter default.
Since `$HOME` points at the trash directory (which doubles as the
test repository's working tree), writing to `$HOME/.gitconfig` also
creates a file inside the working tree. Exclude it via
`.git/info/exclude` to limit the fallout, though this does not
help tests that use `git ls-files --others` without
`--exclude-standard` or `git status --ignored`; those are addressed
by subsequent commits.
The `.git/info/exclude` write is guarded by `test -d .git/info`
rather than using `mkdir -p`, because some tests (e.g. t0008)
expect to create `.git/info/` themselves and would fail with
Patrick Steinhardt's `set -e` preparation (ps/test-set-e-clean) if
the directory already existed. For tests using `TEST_NO_CREATE_REPO`
(where no `.git/` exists at all), the guard also handles that case.
[1] https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/
Original-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/test-lib.sh | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..72ed87b781 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1597,6 +1597,19 @@ cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DIRECTORY\""
TRASH_DIRECTORY=$(pwd)
HOME="$TRASH_DIRECTORY"
+if test -n "$WITH_BREAKING_CHANGES"
+then
+ git config --global safe.bareRepository all &&
+ # Only write to .git/info/exclude when the directory exists
+ # (i.e. when git init created the repo). If we mkdir -p it
+ # ourselves, tests that expect to create .git/info/ themselves
+ # (e.g. t0008) would fail.
+ if test -d .git/info
+ then
+ echo "/.gitconfig" >>.git/info/exclude
+ fi
+fi
+
start_test_output "$0"
# Convenience
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 1/8] test-lib: allow bare repository access when breaking changes are enabled Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 3/8] t1300: remove global config settings injected by test-lib.sh Johannes Schindelin via GitGitGadget
` (5 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
The XDG config tests for `git maintenance register/unregister`
create a fresh `$XDG_CONFIG_HOME/git/config` and expect git to use
that location. However, if `$HOME/.gitconfig` exists (which may
happen when test-lib.sh writes global config, e.g. to set
`safe.bareRepository`), git prefers `$HOME/.gitconfig` over the XDG
location, and the `maintenance.repo` entry ends up in the wrong
file.
This is an inherent consequence of setting global config in
test-lib.sh rather than adjusting individual tests: writing any
entry to `$HOME/.gitconfig` has side effects beyond the intended
setting, because the mere existence of that file changes which
global config location git prefers for all subsequent writes.
Individual per-test adjustments would not have this interaction.
Fix this by overriding `HOME` to a non-existent directory inside the
subshells that test XDG behavior. Since these subshells already
override `XDG_CONFIG_HOME`, they do not need `$HOME/.gitconfig` at
all, and the subshell scoping ensures the original `HOME` is
restored automatically.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t7900-maintenance.sh | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 4700beacc1..4358df0424 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -101,8 +101,12 @@ test_expect_success "maintenance.autoDetach overrides gc.autoDetach" '
test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
test_when_finished rm -r .config/git/config &&
(
+ # Override HOME so that .gitconfig (which test-lib.sh may
+ # have created, e.g. to set safe.bareRepository) does not
+ # take precedence over the XDG location.
+ HOME=$PWD/must-not-exist &&
XDG_CONFIG_HOME=.config &&
- export XDG_CONFIG_HOME &&
+ export HOME XDG_CONFIG_HOME &&
mkdir -p $XDG_CONFIG_HOME/git &&
>$XDG_CONFIG_HOME/git/config &&
git maintenance register &&
@@ -124,8 +128,12 @@ test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
test_when_finished rm -r .config/git/config &&
(
+ # Override HOME so that .gitconfig (which test-lib.sh may
+ # have created, e.g. to set safe.bareRepository) does not
+ # take precedence over the XDG location.
+ HOME=$PWD/must-not-exist &&
XDG_CONFIG_HOME=.config &&
- export XDG_CONFIG_HOME &&
+ export HOME XDG_CONFIG_HOME &&
mkdir -p $XDG_CONFIG_HOME/git &&
>$XDG_CONFIG_HOME/git/config &&
git maintenance register &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 3/8] t1300: remove global config settings injected by test-lib.sh
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 1/8] test-lib: allow bare repository access when breaking changes are enabled Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test Johannes Schindelin via GitGitGadget
` (4 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Since test-lib.sh now writes `safe.bareRepository=all` to the global
config when `WITH_BREAKING_CHANGES` is in effect, that entry shows
up in `git config --list` output. Tests in t1300 that expect exact
config contents then fail because of this unexpected extra line.
Unlike the working-tree contamination fixed in the preceding
commits, this is not about the file's existence but about its
content leaking into test expectations. Since t1300 does not use
bare repositories, simply remove the injected setting in a
preparatory step.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Assisted-by: Claude Opus 4.6
---
t/t1300-config.sh | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 128971ee12..11fc976f3a 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
+# test-lib.sh may have added global config (e.g. safe.bareRepository)
+# that would appear in "git config --list" output and break tests
+# that expect exact config contents.
+test_expect_success 'remove global config from test-lib.sh' '
+ test_might_fail git config --global --unset-all safe.bareRepository
+'
+
for mode in legacy subcommands
do
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
` (2 preceding siblings ...)
2026-04-26 14:38 ` [PATCH v2 3/8] t1300: remove global config settings injected by test-lib.sh Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 5/8] t5601: restore `.gitconfig` after includeIf test Johannes Schindelin via GitGitGadget
` (3 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Earlier tests in t1305 overwrite `$HOME/.gitconfig` with their own
content as part of testing config includes. This clobbers the
`safe.bareRepository=all` entry that test-lib.sh writes when
`WITH_BREAKING_CHANGES` is in effect, causing `git -C cycle config`
to fail with "not in a git directory" when it tries to access the
bare repository created by `git init --bare cycle`.
Use `--git-dir=.` to access the bare repo explicitly, avoiding the
dependency on global config for repository discovery.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t1305-config-include.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 6e51f892f3..f3892578e4 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -350,9 +350,9 @@ test_expect_success 'conditional include, onbranch, implicit /** for /' '
test_expect_success 'include cycles are detected' '
git init --bare cycle &&
- git -C cycle config include.path cycle &&
+ git -C cycle --git-dir=. config include.path cycle &&
git config -f cycle/cycle include.path config &&
- test_must_fail git -C cycle config --get-all test.value 2>stderr &&
+ test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr &&
grep "exceeded maximum include depth" stderr
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 5/8] t5601: restore `.gitconfig` after includeIf test
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
` (3 preceding siblings ...)
2026-04-26 14:38 ` [PATCH v2 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 6/8] ls-files tests: filter `.gitconfig` from `--others` output Johannes Schindelin via GitGitGadget
` (2 subsequent siblings)
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
One test in t5601 overwrites `$HOME/.gitconfig` with an `includeIf`
configuration snippet and removes the file in its cleanup. This
destroys the `safe.bareRepository=all` entry that test-lib.sh
writes when `WITH_BREAKING_CHANGES` is in effect, causing later
tests that use `git -C <bare-repo> config` to fail with "not in a
git directory".
Back up `.gitconfig` before overwriting and restore it in the
cleanup, so the global config survives into subsequent tests.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t5601-clone.sh | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index d743d986c4..3dd229c186 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -813,7 +813,9 @@ test_expect_success 'clone with includeIf' '
test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- test_when_finished "rm \"$HOME\"/.gitconfig" &&
+ test_when_finished "cp \"$HOME\"/.gitconfig.bak \
+ \"$HOME\"/.gitconfig 2>/dev/null || rm -f \"$HOME\"/.gitconfig" &&
+ cp "$HOME"/.gitconfig "$HOME"/.gitconfig.bak 2>/dev/null &&
cat >"$HOME"/.gitconfig <<-EOF &&
[includeIf "onbranch:something"]
path = /does/not/exist.inc
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 6/8] ls-files tests: filter `.gitconfig` from `--others` output
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
` (4 preceding siblings ...)
2026-04-26 14:38 ` [PATCH v2 5/8] t5601: restore `.gitconfig` after includeIf test Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 7/8] status tests: filter `.gitconfig` from status output Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
The global `safe.bareRepository=all` setting in test-lib.sh is
written to `$HOME/.gitconfig`, which unfortunately lives inside the
test repository's working tree. The `.git/info/exclude` entry added
alongside it handles most commands, but `git ls-files --others`
without `--exclude-standard` does not consult `info/exclude` at
all, so the file appears in the output.
Ideally, each test that accesses a bare repository would simply
specify `--git-dir` or `GIT_DIR` explicitly, which would require no
global config and produce no side effects in the working tree. As
that approach was not taken, filter `.gitconfig` from the output
before comparing against expected results. In t7104, the test
already uses `--exclude-standard`, so it suffices to switch from
the bare `git ls-files -o` to `git ls-files -o --exclude-standard`
which respects the `info/exclude` entry; the other tests
deliberately omit `--exclude-standard` because their purpose is to
verify unfiltered `--others` output.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t3000-ls-files-others.sh | 4 ++++
t/t3001-ls-files-others-exclude.sh | 3 +++
t/t3002-ls-files-dashpath.sh | 2 ++
t/t3009-ls-files-others-nonsubmodule.sh | 1 +
t/t3011-common-prefixes-and-directory-traversal.sh | 3 ++-
t/t7104-reset-hard.sh | 2 +-
t/test-lib-functions.sh | 8 ++++++++
7 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index b41e7f0daa..b4f0fbfc55 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -53,16 +53,19 @@ test_expect_success 'setup: expected output' '
test_expect_success 'ls-files --others' '
git ls-files --others >output &&
+ test_filter_gitconfig output &&
test_cmp expected1 output
'
test_expect_success 'ls-files --others --directory' '
git ls-files --others --directory >output &&
+ test_filter_gitconfig output &&
test_cmp expected2 output
'
test_expect_success '--no-empty-directory hides empty directory' '
git ls-files --others --directory --no-empty-directory >output &&
+ test_filter_gitconfig output &&
test_cmp expected3 output
'
@@ -70,6 +73,7 @@ test_expect_success 'ls-files --others handles non-submodule .git' '
mkdir not-a-submodule &&
echo foo >not-a-submodule/.git &&
git ls-files -o >output &&
+ test_filter_gitconfig output &&
test_cmp expected1 output
'
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 4b67646285..202fb8d9ea 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -72,6 +72,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -84,6 +85,7 @@ test_expect_success 'git ls-files --others with \r\n line endings.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -99,6 +101,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 31462cb441..6acaadbd67 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -24,6 +24,7 @@ test_expect_success 'setup' '
test_expect_success 'git ls-files without path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others >output &&
+ test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
@@ -63,6 +64,7 @@ test_expect_success 'git ls-files with path restriction with -- --.' '
test_expect_success 'git ls-files with no path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others -- >output &&
+ test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f3462b7..dc990c277b 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -36,6 +36,7 @@ test_expect_success 'setup: directories' '
test_expect_success 'ls-files --others handles untracked git repositories' '
git ls-files -o >output &&
+ test_filter_gitconfig output &&
cat >expect <<-EOF &&
nonrepo-untracked-file/untracked
output
diff --git a/t/t3011-common-prefixes-and-directory-traversal.sh b/t/t3011-common-prefixes-and-directory-traversal.sh
index 3da5b2b6e7..455e97954d 100755
--- a/t/t3011-common-prefixes-and-directory-traversal.sh
+++ b/t/t3011-common-prefixes-and-directory-traversal.sh
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
'
test_expect_success 'git ls-files -o shows the right entries' '
- cat <<-EOF >expect &&
+ cat >expect <<-EOF &&
.gitignore
actual
an_ignored_dir/ignored
@@ -39,6 +39,7 @@ test_expect_success 'git ls-files -o shows the right entries' '
untracked_repo/
EOF
git ls-files -o >actual &&
+ test_filter_gitconfig actual &&
test_cmp expect actual
'
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec392b..c23d6e3f52 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -21,7 +21,7 @@ test_expect_success setup '
rm -f hello &&
mkdir -p hello &&
>hello/world &&
- test "$(git ls-files -o)" = hello/world
+ test "$(git ls-files -o --exclude-standard)" = hello/world
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..0505da78e8 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -2069,3 +2069,11 @@ test_trailing_hash () {
test_redact_non_printables () {
tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
}
+
+# Remove .gitconfig entries from a file in place. test-lib.sh may
+# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
+# can appear in ls-files or status output.
+test_filter_gitconfig () {
+ sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
+ mv "$1.filtered" "$1"
+}
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 7/8] status tests: filter `.gitconfig` from status output
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
` (5 preceding siblings ...)
2026-04-26 14:38 ` [PATCH v2 6/8] ls-files tests: filter `.gitconfig` from `--others` output Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
2026-04-26 14:38 ` [PATCH v2 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES Johannes Schindelin via GitGitGadget
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Since test-lib.sh creates `$HOME/.gitconfig` when
`WITH_BREAKING_CHANGES` is in effect, the file appears in `git
status` output as either untracked (`?? .gitconfig`) or ignored
(`!! .gitconfig` / `! .gitconfig`, depending on porcelain version),
because the `.git/info/exclude` entry causes git to treat it as an
ignored file rather than hiding it entirely.
In t7061 and t7521, which are pervasively affected, introduce a
`filter_gitconfig` helper that strips all status-prefix variants of
`.gitconfig` from the output before comparison. In the remaining
scripts (t7060, t7064, t7508), apply targeted adjustments.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t7060-wtstatus.sh | 3 +--
t/t7061-wtstatus-ignore.sh | 27 +++++++++++++++++++++++++++
t/t7064-wtstatus-pv2.sh | 1 +
t/t7508-status.sh | 4 ++++
t/t7521-ignored-mode.sh | 1 +
5 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c55e..942ddbbf0e 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -9,6 +9,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success setup '
git config --global advice.statusuoption false &&
+ echo "/.gitconfig" >>.git/info/exclude &&
test_commit A &&
test_commit B oneside added &&
git checkout A^0 &&
@@ -221,7 +222,6 @@ test_expect_success 'status --branch with detached HEAD' '
git status --branch --porcelain >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
- ?? .gitconfig
?? actual
?? expect
?? expected
@@ -237,7 +237,6 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
git status --branch --porcelain=v1 >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
- ?? .gitconfig
?? actual
?? expect
?? expected
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index 2f9bea9793..14ddaba2f3 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -18,6 +18,7 @@ test_expect_success 'status untracked directory with --ignored' '
: >untracked/ignored &&
: >untracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -27,6 +28,7 @@ test_expect_success 'same with gitignore starting with BOM' '
: >untracked/ignored &&
: >untracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -40,18 +42,22 @@ test_expect_success 'status untracked files --ignored with pathspec (no match)'
test_expect_success 'status untracked files --ignored with pathspec (literal match)' '
git status --porcelain --ignored -- untracked/ignored >actual &&
echo "!! untracked/ignored" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored -- untracked/uncommitted >actual &&
echo "?? untracked/uncommitted" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
test_expect_success 'status untracked files --ignored with pathspec (glob match)' '
git status --porcelain --ignored -- untracked/i\* >actual &&
echo "!! untracked/ignored" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored -- untracked/u\* >actual &&
echo "?? untracked/uncommitted" >expected &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -65,6 +71,7 @@ EOF
test_expect_success 'status untracked directory with --ignored -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
cat >expected <<\EOF
@@ -76,9 +83,11 @@ test_expect_success 'status of untracked directory with --ignored works with or
git status --porcelain --ignored >tmp &&
grep untracked/ tmp >actual &&
rm tmp &&
+ test_filter_gitconfig actual &&
test_cmp expected actual &&
git status --porcelain --ignored untracked/ >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -89,6 +98,7 @@ EOF
test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
git status --porcelain --ignored -u untracked/ >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -104,6 +114,7 @@ test_expect_success 'status ignored directory with --ignore' '
mkdir ignored &&
: >ignored/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -116,6 +127,7 @@ EOF
test_expect_success 'status ignored directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -130,6 +142,7 @@ test_expect_success 'status empty untracked directory with --ignore' '
mkdir untracked-ignored &&
mkdir untracked-ignored/test &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -141,6 +154,7 @@ EOF
test_expect_success 'status empty untracked directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -155,6 +169,7 @@ test_expect_success 'status untracked directory with ignored files with --ignore
: >untracked-ignored/ignored &&
: >untracked-ignored/test/ignored &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -168,6 +183,7 @@ EOF
test_expect_success 'status untracked directory with ignored files with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -185,6 +201,7 @@ test_expect_success 'status ignored tracked directory with --ignore' '
git commit -m. &&
echo "tracked" >.gitignore &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -196,6 +213,7 @@ EOF
test_expect_success 'status ignored tracked directory with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -208,6 +226,7 @@ EOF
test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
echo "committed" >>.gitignore &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -219,6 +238,7 @@ EOF
test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -233,6 +253,7 @@ test_expect_success 'status ignored tracked directory and uncommitted file with
echo "tracked" >.gitignore &&
: >tracked/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -245,6 +266,7 @@ EOF
test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -260,6 +282,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in u
mkdir tracked/ignored &&
: >tracked/ignored/uncommitted &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -272,6 +295,7 @@ EOF
test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -287,6 +311,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in t
git add -f tracked/ignored/committed &&
git commit -m. &&
git status --porcelain --ignored >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -299,6 +324,7 @@ EOF
test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
git status --porcelain --ignored -u >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
@@ -310,6 +336,7 @@ test_expect_success 'status ignores submodule in excluded directory' '
git init tracked/submodule &&
test_commit -C tracked/submodule initial &&
git status --porcelain --ignored -u tracked/submodule >actual &&
+ test_filter_gitconfig actual &&
test_cmp expected actual
'
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 8bbc5ce6d9..be6c931a96 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -231,6 +231,7 @@ test_expect_success 'ignored files are printed with --ignored' '
EOF
git status --porcelain=v2 --ignored --untracked-files=all >actual &&
+ test_filter_gitconfig actual &&
test_cmp expect actual
'
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..5f76ec62d8 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -263,6 +263,7 @@ test_expect_success 'status with gitignore' '
!! untracked
EOF
git status -s --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output &&
cat >expect <<\EOF &&
@@ -296,6 +297,7 @@ Ignored files:
EOF
git status --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
@@ -328,6 +330,7 @@ test_expect_success 'status with gitignore (nothing untracked)' '
!! untracked
EOF
git status -s --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output &&
cat >expect <<\EOF &&
@@ -358,6 +361,7 @@ Ignored files:
EOF
git status --ignored >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
index a88b02b06e..7ea0b0d2f2 100755
--- a/t/t7521-ignored-mode.sh
+++ b/t/t7521-ignored-mode.sh
@@ -30,6 +30,7 @@ test_expect_success 'Verify behavior of status on directories with ignored files
dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_filter_gitconfig output &&
test_cmp expect output
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v2 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
2026-04-26 14:38 ` [PATCH v2 0/8] " Johannes Schindelin via GitGitGadget
` (6 preceding siblings ...)
2026-04-26 14:38 ` [PATCH v2 7/8] status tests: filter `.gitconfig` from status output Johannes Schindelin via GitGitGadget
@ 2026-04-26 14:38 ` Johannes Schindelin via GitGitGadget
7 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
To: git; +Cc: Johannes Schindelin, Johannes Schindelin
From: Johannes Schindelin <johannes.schindelin@gmx.de>
When an attacker can convince a user to clone a crafted repository
that contains an embedded bare repository with malicious hooks, any Git
command the user runs after entering that subdirectory will discover
the bare repository and execute the hooks. The user does not even need
to run a Git command explicitly: many shell prompts run `git status`
in the background to display branch and dirty state information, and
`git status` in turn may invoke the fsmonitor hook if so configured,
making the user vulnerable the moment they `cd` into the directory. The
`safe.bareRepository` configuration variable (introduced in 8959555cee7e
(setup_git_directory(): add an owner check for the top-level directory,
2022-03-02)) already provides protection against this attack vector by
allowing users to set it to "explicit", but the default remained "all"
for backwards compatibility.
Since Git 3.0 is the natural point to change defaults to safer
values, flip the default from "all" to "explicit" when built with
`WITH_BREAKING_CHANGES`. This means Git will refuse to work with bare
repositories that are discovered implicitly by walking up the directory
tree. Bare repositories specified via `--git-dir` or `GIT_DIR` continue
to work, and directories that look like `.git`, worktrees, or submodule
directories are unaffected (the existing `is_implicit_bare_repo()`
whitelist handles those cases).
Users who rely on implicit bare repository discovery can restore the
previous behavior by setting `safe.bareRepository=all` in their global
or system configuration.
The test for the "safe.bareRepository in the repository" scenario
needed a more involved fix: it writes a `safe.bareRepository=all`
entry into the bare repository's own config to verify that repo-local
config does not override the protected (global) setting. Previously,
`test_config -C` was used to write that entry, but its cleanup runs `git
-C <bare-repo> config --unset`, which itself fails when the default is
"explicit" and the global config has already been cleaned up. Switching
to direct git config --file access avoids going through repository
discovery entirely.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
Documentation/BreakingChanges.adoc | 24 ++++++++++++++++++++++++
Documentation/config/safe.adoc | 10 ++++++++--
setup.c | 4 ++++
t/t0035-safe-bare-repository.sh | 10 ++++++++--
4 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index af59c43f42..73bb939359 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -216,6 +216,30 @@ would be significant, we may decide to defer this change to a subsequent minor
release. This evaluation will also take into account our own experience with
how painful it is to keep Rust an optional component.
+* The default value of `safe.bareRepository` will change from `all` to
+ `explicit`. It is all too easy for an attacker to trick a user into cloning a
+ repository that contains an embedded bare repository with malicious hooks
+ configured. If the user enters that subdirectory and runs any Git command, Git
+ discovers the bare repository and the hooks fire. The user does not even need
+ to run a Git command explicitly: many shell prompts run `git status` in the
+ background to display branch and dirty state information, and `git status` in
+ turn may invoke the fsmonitor hook if so configured, making the user
+ vulnerable the moment they `cd` into the directory. The `safe.bareRepository`
+ configuration variable was introduced in 8959555cee (setup_git_directory():
+ add an owner check for the top-level directory, 2022-03-02) with a default of
+ `all` to preserve backwards compatibility.
++
+Changing the default to `explicit` means that Git will refuse to work with bare
+repositories that are discovered implicitly by walking up the directory tree.
+Bare repositories specified explicitly via the `--git-dir` command-line option
+or the `GIT_DIR` environment variable continue to work regardless of this
+setting. Repositories that look like a `.git` directory, a worktree, or a
+submodule directory are also unaffected.
++
+Users who rely on implicit discovery of bare repositories can restore the
+previous behavior by setting `safe.bareRepository=all` in their global or
+system configuration.
+
=== Removals
* Support for grafting commits has long been superseded by git-replace(1).
diff --git a/Documentation/config/safe.adoc b/Documentation/config/safe.adoc
index 2d45c98b12..5b1690aebe 100644
--- a/Documentation/config/safe.adoc
+++ b/Documentation/config/safe.adoc
@@ -2,10 +2,12 @@ safe.bareRepository::
Specifies which bare repositories Git will work with. The currently
supported values are:
+
-* `all`: Git works with all bare repositories. This is the default.
+* `all`: Git works with all bare repositories. This is the default in
+ Git 2.x.
* `explicit`: Git only works with bare repositories specified via
the top-level `--git-dir` command-line option, or the `GIT_DIR`
- environment variable (see linkgit:git[1]).
+ environment variable (see linkgit:git[1]). This will be the default
+ in Git 3.0.
+
If you do not use bare repositories in your workflow, then it may be
beneficial to set `safe.bareRepository` to `explicit` in your global
@@ -13,6 +15,10 @@ config. This will protect you from attacks that involve cloning a
repository that contains a bare repository and running a Git command
within that directory.
+
+If you use bare repositories regularly and want to preserve the current
+behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
+in your global or system config.
++
This config setting is only respected in protected configuration (see
<<SCOPES>>). This prevents untrusted repositories from tampering with
this value.
diff --git a/setup.c b/setup.c
index 7ec4427368..17c0662076 100644
--- a/setup.c
+++ b/setup.c
@@ -1485,7 +1485,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,
static enum allowed_bare_repo get_allowed_bare_repo(void)
{
+#ifdef WITH_BREAKING_CHANGES
+ enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
+#else
enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
+#endif
git_protected_config(allowed_bare_repo_cb, &result);
return result;
}
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index ae7ef092ab..1d3d19f5b4 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -44,11 +44,16 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
test_path_is_dir outer-repo/.git/modules/subn
'
-test_expect_success 'safe.bareRepository unset' '
+test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
test_unconfig --global safe.bareRepository &&
expect_accepted_implicit -C outer-repo/bare-repo
'
+test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
+ test_unconfig --global safe.bareRepository &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
test_expect_success 'safe.bareRepository=all' '
test_config_global safe.bareRepository all &&
expect_accepted_implicit -C outer-repo/bare-repo
@@ -63,7 +68,8 @@ test_expect_success 'safe.bareRepository in the repository' '
# safe.bareRepository must not be "explicit", otherwise
# git config fails with "fatal: not in a git directory" (like
# safe.directory)
- test_config -C outer-repo/bare-repo safe.bareRepository all &&
+ test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
+ git config --file outer-repo/bare-repo/config safe.bareRepository all &&
test_config_global safe.bareRepository explicit &&
expect_rejected -C outer-repo/bare-repo
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 19+ messages in thread