Merge branch 'js/adjust-tests-to-explicitly-access-bare-repo'

Some tests assume that bare repository accesses are by default
allowed; rewrite some of them to avoid the assumption, rewrite
others to explicitly set safe.bareRepository to allow them.

* js/adjust-tests-to-explicitly-access-bare-repo:
  safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
  status tests: filter `.gitconfig` from status output
  ls-files tests: filter `.gitconfig` from `--others` output
  t5601: restore `.gitconfig` after includeIf test
  t1305: use `--git-dir=.` for bare repo in include cycle test
  t1300: remove global config settings injected by test-lib.sh
  t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
  test-lib: allow bare repository access when breaking changes are enabled
This commit is contained in:
Junio C Hamano
2026-05-20 10:30:56 +09:00
21 changed files with 134 additions and 13 deletions
+24
View File
@@ -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).
+8 -2
View File
@@ -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.
+4
View File
@@ -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;
}
+8 -2
View File
@@ -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
'
+7
View File
@@ -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
+2 -2
View File
@@ -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
'
+4
View File
@@ -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
'
+3
View File
@@ -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
'
+2
View File
@@ -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
+1
View File
@@ -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
@@ -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
'
+3 -1
View File
@@ -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
+1 -2
View File
@@ -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
+27
View File
@@ -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
'
+1
View File
@@ -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
'
+1 -1
View File
@@ -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
'
+4
View File
@@ -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
'
+1
View File
@@ -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
'
+10 -2
View File
@@ -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 &&
+8
View File
@@ -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"
}
+13
View File
@@ -1624,6 +1624,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