* [PATCH 00/12] t: detect errors outside of test cases
@ 2026-04-13 9:49 Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (17 more replies)
0 siblings, 18 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
t/lib-git-daemon.sh | 13 ++++++++++---
t/lib-git-svn.sh | 3 +--
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 2 +-
t/test-lib-functions.sh | 12 ++++++++----
t/test-lib.sh | 8 ++++++--
19 files changed, 76 insertions(+), 56 deletions(-)
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 16:26 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (16 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..74c2a27972 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..8e68a00dcb 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 16:33 ` Junio C Hamano
2026-04-14 6:23 ` Jeff King
2026-04-13 9:49 ` [PATCH 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (15 subsequent siblings)
17 siblings, 2 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by marking the
command part of a condition.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..f8bc77619b 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,12 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+ if "$@" 2>&7
+ then
+ exit_code=0
+ else
+ exit_code=$?
+ fi
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 16:53 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (14 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by making the call to `wait` part of a condition
and by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..91bcdd0bf7 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,21 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ if wait "$GIT_DAEMON_PID" >&3 2>&4
+ then
+ ret=0
+ else
+ ret=$?
+ fi
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 16:59 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (13 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 05/12] t: prepare conditional test execution for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 17:04 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (12 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 17:09 ` Junio C Hamano
2026-04-13 22:32 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (11 subsequent siblings)
17 siblings, 2 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 3 +--
t/lib-httpd.sh | 3 +--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib.sh | 4 ++--
7 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..07d86ea244 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..81380fe978 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 17:23 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (10 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f8bc77619b..0eac676109 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1516,7 +1516,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1544,7 +1544,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 17:28 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (9 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..1218005b54 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || true)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (8 subsequent siblings)
17 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..7f920d7b9e 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || true
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 10/12] t6002: fix use of `expr` with `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 11/12] t9902: fix use of `read` " Patrick Steinhardt
` (7 subsequent siblings)
17 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 11/12] t9902: fix use of `read` with `set -e`
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 12/12] t: detect errors outside of test cases Patrick Steinhardt
` (6 subsequent siblings)
17 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. There is a gotcha though: when the delimiter
isn't found at all, then the read builtin will return an error. This
hasn't been an issue until now as we didn't run with `set -e`, but
that'll change in a subsequent commit.
Prepare for this change by silencing the error.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..e3a7df7691 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,7 +590,7 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
+read -r -d "" refs <<-\EOF || :
main
maint
next
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH 12/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-13 9:49 ` Patrick Steinhardt
2026-04-13 17:29 ` Junio C Hamano
2026-04-13 21:33 ` [PATCH 00/12] " Junio C Hamano
` (5 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-13 9:49 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib.sh | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 81380fe978..c493e3c768 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case.
+set -e
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.rc0.707.g0fbf48f4d6.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-13 9:49 ` [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-13 16:26 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 16:26 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> We have a couple of calls to `test_match_signal ()` where we execute a
> Git command and expect it to die with a specific signal. These calls
> will essentially execute the process in a subshell via `foo; echo $?`,
> but as we expect `foo` to fail this will cause the overall subshell to
> fail once we `set -e`.
>
> Fix this issue by using `foo || echo $?` instead.
Hmph, if the 'foo' command that is expected to fail succeeds by a
bug, we won't see 0 on the standard output anymore. Comparing 13
with 0 or 13 with an empty string "" would fail either way, so that
may not give us a practical difference, but this somehow leaves a
foul small in my nose.
The technique used in <20260325062114.2067946-2-gitster@pobox.com>
would give us something ugly like
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((st=0; large_git || st=$?; echo $st 1>&3) | :) 3>&1 ) &&
and it ensures that we give 0 when we succeed, but I am not sure if
this is worth doing.
Or perhaps
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=${OUT:-0} &&
I dunno.
If "large_git" somehow writes into file descriptor #3, then OUT will
be contaminated with something other than $?, so the additional "if
empty substitute with 0" would not work without doing something like
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git 3>/dev/null || echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=${OUT:-0} &&
I guess. For that matter, if large_git spits out "13" to its file
descriptor #3, I do not know what would have happened ;-)
> test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
> - OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
> test_match_signal 13 "$OUT"
> '
>
> test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
> - OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((trap "" PIPE && large_git || echo $? 1>&3) | :) 3>&1 ) &&
> test_match_signal 13 "$OUT"
> '
>
> diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
> index 1f16e6b522..8e68a00dcb 100755
> --- a/t/t3600-rm.sh
> +++ b/t/t3600-rm.sh
> @@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
>
> test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
> choke_git_rm_setup &&
> - OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((trap "" PIPE && git rm -n "some-file-*" || echo $? 1>&3) | :) 3>&1 ) &&
> test_match_signal 13 "$OUT" &&
> test_path_is_missing .git/index.lock
> '
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-13 9:49 ` [PATCH 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-13 16:33 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 6:23 ` Jeff King
1 sibling, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 16:33 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> The helper function `test_must_fail ()` executes a specific Git command
> that may or may not fail in a specific way. This is done by executing
> the command in question and then comparing its exit code against a set
> of conditions.
>
> This works, but once we run our test suite with `set -e` we may bail out
> of `test_must_fail ()` early in case the command actually fails, even
> though we expect it to fail. Prepare for this change by marking the
> command part of a condition.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/test-lib-functions.sh | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> index f3af10fb7e..f8bc77619b 100644
> --- a/t/test-lib-functions.sh
> +++ b/t/test-lib-functions.sh
> @@ -1195,8 +1195,12 @@ test_must_fail () {
> echo >&7 "test_must_fail: only 'git' is allowed: $*"
> return 1
> fi
> - "$@" 2>&7
> - exit_code=$?
> + if "$@" 2>&7
> + then
> + exit_code=0
> + else
> + exit_code=$?
> + fi
This is obvious and clear. Alternatively
exit_code=0; "$@" 2>&7 || exit_code=$?
would be more assuring to readers who (with less fluency in Bourne
shells) wonder, upon seeing the else clause, how far "$?" traveled
and it still holds the status of "$@".
> if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
> then
> echo >&4 "test_must_fail: command succeeded: $*"
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-13 9:49 ` [PATCH 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-13 16:53 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 16:53 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> We have a couple of calls to `stop_git_daemon ()` outside of specific
> test cases that will kill a backgrounded git-daemon(1) process and
> expect the process with a specific error code. While these function
> calls do end up killing git-daemon(1), the error handling we have in
> those contexts is basically ineffective. So while we expect the process
> to exit with a specific error code, we will just continue with any error
> in case it doesn't.
>
> This will change once we enable `set -e` in a subsequent commit. There's
> two issues though that will make this _always_ fail:
>
> - Our call to `wait` is expected to fail, but because it's not part of
> a condition it will cause us to bail out immediately with `set -e`.
>
> - We try to kill git-daemon(1) a second time via the pidfile. We can
> generally expect that this is the same PID though as we had in the
> "GIT_DAEMON_PID" environment variable, and thus it's more likely
> than not that we have already killed it, and the call to kill will
> fail.
>
> Prepare for this change by making the call to `wait` part of a condition
> and by silencing failures of the second call to `kill`.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/lib-git-daemon.sh | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
The same comment as [02/12] applies to the early part.
Ignoring the exit status from "kill" in case that the daemon has
already exited with " || :" totally makes sense.
> diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
> index e62569222b..91bcdd0bf7 100644
> --- a/t/lib-git-daemon.sh
> +++ b/t/lib-git-daemon.sh
> @@ -85,14 +85,21 @@ stop_git_daemon() {
>
> # kill git-daemon child of git
> say >&3 "Stopping git daemon ..."
> +
> kill "$GIT_DAEMON_PID"
> - wait "$GIT_DAEMON_PID" >&3 2>&4
> - ret=$?
> + if wait "$GIT_DAEMON_PID" >&3 2>&4
> + then
> + ret=0
> + else
> + ret=$?
> + fi
> +
> if ! test_match_signal 15 $ret
> then
> error "git daemon exited with status: $ret"
> fi
> - kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
> +
> + kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
> GIT_DAEMON_PID=
> rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
> }
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-13 9:49 ` [PATCH 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-13 16:59 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 16:59 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> We have a couple of calls to `git config --unset` that ultimately end up
> as no-ops as the configuration variables aren't set (anymore) in the
> first place. These calls are mostly intended to recover unconditionally
> from tests that may have executed only partially, but they'll ultimately
> fail during a normal test run.
>
> This hasn't been a problem until now as we aren't running tests with
> `set -e`. This is about to change though, so let's silence the case
> where we cannot unset the config keys.
They all look good. A bare "unset" in the shell also can fail, and
that is where our sane_unset comes from. It is where the "it is an
error to try unsetting a config" comes from, but given that only
small number of places we need these changes to, it wouldn't not be
worth adding "git config --sane-unset" that does not fail ;-)
Looking good.
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/t4032-diff-inter-hunk-context.sh | 2 +-
> t/t7508-status.sh | 4 ++--
> t/t9138-git-svn-authors-prog.sh | 4 ++--
> 3 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
> index bada0cbd32..c98eb6abb2 100755
> --- a/t/t4032-diff-inter-hunk-context.sh
> +++ b/t/t4032-diff-inter-hunk-context.sh
> @@ -17,7 +17,7 @@ f() {
>
> t() {
> use_config=
> - git config --unset diff.interHunkContext
> + git config --unset diff.interHunkContext || :
>
> case $# in
> 4) hunks=$4; cmd="diff -U$3";;
> diff --git a/t/t7508-status.sh b/t/t7508-status.sh
> index a5e21bf8bf..1167b835a4 100755
> --- a/t/t7508-status.sh
> +++ b/t/t7508-status.sh
> @@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
> '
>
> # recover unconditionally from color tests
> -git config --unset color.status
> -git config --unset color.ui
> +git config --unset color.status || :
> +git config --unset color.ui || :
>
> test_expect_success 'status --porcelain respects -b' '
>
> diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
> index 784ec7fc2d..5bb38cb23a 100755
> --- a/t/t9138-git-svn-authors-prog.sh
> +++ b/t/t9138-git-svn-authors-prog.sh
> @@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
> )
> '
>
> -git --git-dir=x/.git config --unset svn.authorsfile
> -git --git-dir=x/.git config --unset svn.authorsprog
> +git --git-dir=x/.git config --unset svn.authorsfile || :
> +git --git-dir=x/.git config --unset svn.authorsprog || :
>
> test_expect_success 'authors-prog imported user without email' '
> svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 05/12] t: prepare conditional test execution for `set -e`
2026-04-13 9:49 ` [PATCH 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-13 17:04 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 17:04 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> We have some test in our test suite where we use the pattern of
> `test ... && test_expect_succeess` to conditionally execute a test. The
> problem is that when we decide to not execute the test, we'll indeed
> skip the test, but the overall statement will also be unsuccessful. This
> will become a problem once we enable `set -e`.
>
> Prepare for this future by turning this into a proper conditional, which
> is also a bit easier to read overall.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
> t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
> 2 files changed, 20 insertions(+), 16 deletions(-)
Unlike the ones that involve $?, I very much prefer the explicit
uses of if/then/else in these changes. Compared to a potential
alternative to rewrite the existing
check &&
test_expect_success ...
into
! check ||
test_expect_success ...
it is much more clear to explicitly say
if check
then
test_expect_success ...
fi
> diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
> index c98eb6abb2..2d216fb70f 100755
> --- a/t/t4032-diff-inter-hunk-context.sh
> +++ b/t/t4032-diff-inter-hunk-context.sh
> @@ -40,11 +40,13 @@ t() {
> test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
> "
>
> - test -f $expected &&
> - test_expect_success "$label: check output" "
> - git $cmd $file | grep -v '^index ' >actual &&
> - test_cmp $expected actual
> - "
> + if test -f $expected
> + then
> + test_expect_success "$label: check output" "
> + git $cmd $file | grep -v '^index ' >actual &&
> + test_cmp $expected actual
> + "
> + fi
> }
>
> cat <<EOF >expected.f1.0.1 || exit 1
> diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
> index f512eed278..8cc86522b2 100755
> --- a/t/t7450-bad-git-dotfiles.sh
> +++ b/t/t7450-bad-git-dotfiles.sh
> @@ -220,17 +220,19 @@ check_dotx_symlink () {
> )
> '
>
> - test -n "$refuse_index" &&
> - test_expect_success "refuse to load symlinked $name into index ($type)" '
> - test_must_fail \
> - git -C $dir \
> - -c core.protectntfs \
> - -c core.protecthfs \
> - read-tree $tree 2>err &&
> - grep "invalid path.*$name" err &&
> - git -C $dir ls-files -s >out &&
> - test_must_be_empty out
> - '
> + if test -n "$refuse_index"
> + then
> + test_expect_success "refuse to load symlinked $name into index ($type)" '
> + test_must_fail \
> + git -C $dir \
> + -c core.protectntfs \
> + -c core.protecthfs \
> + read-tree $tree 2>err &&
> + grep "invalid path.*$name" err &&
> + git -C $dir ls-files -s >out &&
> + test_must_be_empty out
> + '
> + fi
> }
>
> check_dotx_symlink gitmodules vanilla .gitmodules
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-13 9:49 ` [PATCH 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-13 17:09 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-13 22:32 ` Junio C Hamano
1 sibling, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 17:09 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> Several of our tests verify whether a certain binary can be executed,
> potentially skipping tests in case we cannot, for example because the
> binary doesn't exist. In those cases we often run the binary outside of
> any conditionally.
>
> This will start to fail once we enable `set -e`, as that will cause us
> to bail out the test immediately. Improve these tests by executing them
> inside of a conditional instead.
OK. "svn help" and "cvs version" do exit with status 0 and the
rewrites that use them make sense. I wonder if we can do something
similar to "git" instead of relying on "git<RETURN>" to exit with 1,
perhaps ...
$ git version >/dev/null; echo $?
0
... by using "git version" in the test-lib.sh change?
Other than that looking very good.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/lib-git-svn.sh | 3 +--
> t/lib-httpd.sh | 3 +--
> t/t9200-git-cvsexportcommit.sh | 3 +--
> t/t9400-git-cvsserver-server.sh | 5 +++--
> t/t9401-git-cvsserver-crlf.sh | 4 ++--
> t/t9402-git-cvsserver-refs.sh | 4 ++--
> t/test-lib.sh | 4 ++--
> 7 files changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
> index 2fde2353fd..07d86ea244 100644
> --- a/t/lib-git-svn.sh
> +++ b/t/lib-git-svn.sh
> @@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
> SVN_TREE=$GIT_SVN_DIR/svn-tree
> test_set_port SVNSERVE_PORT
>
> -svn >/dev/null 2>&1
> -if test $? -ne 1
> +if ! svn help >/dev/null 2>&1
> then
> skip_all='skipping git svn tests, svn not found'
> test_done
> diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
> index 4c76e813e3..fc646447d5 100644
> --- a/t/lib-httpd.sh
> +++ b/t/lib-httpd.sh
> @@ -235,11 +235,10 @@ start_httpd() {
>
> test_atexit stop_httpd
>
> - "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
> + if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
> -f "$TEST_PATH/apache.conf" $HTTPD_PARA \
> -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
> >&3 2>&4
> - if test $? -ne 0
> then
> cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
> test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
> diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
> index 14cbe96527..581cf3d28f 100755
> --- a/t/t9200-git-cvsexportcommit.sh
> +++ b/t/t9200-git-cvsexportcommit.sh
> @@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
> test_done
> fi
>
> -cvs >/dev/null 2>&1
> -if test $? -ne 1
> +if ! cvs version >/dev/null 2>&1
> then
> skip_all='skipping git cvsexportcommit tests, cvs not found'
> test_done
> diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
> index e499c7f955..4b45398bab 100755
> --- a/t/t9400-git-cvsserver-server.sh
> +++ b/t/t9400-git-cvsserver-server.sh
> @@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
> skip_all='skipping git cvsserver tests, perl not available'
> test_done
> fi
> -cvs >/dev/null 2>&1
> -if test $? -ne 1
> +
> +if ! cvs version >/dev/null 2>&1
> then
> skip_all='skipping git-cvsserver tests, cvs not found'
> test_done
> fi
> +
> perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
> skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
> test_done
> diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
> index a34805acdc..6b4cbb1651 100755
> --- a/t/t9401-git-cvsserver-crlf.sh
> +++ b/t/t9401-git-cvsserver-crlf.sh
> @@ -60,12 +60,12 @@ check_status_options() {
> return $stat
> }
>
> -cvs >/dev/null 2>&1
> -if test $? -ne 1
> +if ! cvs version >/dev/null 2>&1
> then
> skip_all='skipping git-cvsserver tests, cvs not found'
> test_done
> fi
> +
> if ! test_have_prereq PERL
> then
> skip_all='skipping git-cvsserver tests, perl not available'
> diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
> index 2ee41f9443..65f2ceedec 100755
> --- a/t/t9402-git-cvsserver-refs.sh
> +++ b/t/t9402-git-cvsserver-refs.sh
> @@ -68,12 +68,12 @@ check_diff() {
>
> #########
>
> -cvs >/dev/null 2>&1
> -if test $? -ne 1
> +if ! cvs version >/dev/null 2>&1
> then
> skip_all='skipping git-cvsserver tests, cvs not found'
> test_done
> fi
> +
> if ! test_have_prereq PERL
> then
> skip_all='skipping git-cvsserver tests, perl not available'
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 70fd3e9baf..81380fe978 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
> ################################################################
> # It appears that people try to run tests without building...
> GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
> -"$GIT_BINARY" >/dev/null
> -if test $? != 1
> +
> +if ! "$GIT_BINARY" version >/dev/null
> then
> if test -n "$GIT_TEST_INSTALLED"
> then
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-13 9:49 ` [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-13 17:23 ` Junio C Hamano
2026-04-14 7:24 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 17:23 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> Both `test_when_finished ()` and `test_atexit ()` build up a chain of
> cleanup commands by prepending each new command to the existing cleanup
> string. To preserve the exit code of the test body across cleanup
> execution, we append the following logic:
>
> } && (exit "$eval_ret"); eval_ret=$?; ...
>
> The intent of this is to run the cleanup block and then unconditionally
> restore `eval_ret`. The original behaviour of this is is:
>
> +------------------+---------+------------------------------------+
> |test body │ cleanup │ old behaviour │
> +------------------+---------+------------------------------------+
> │pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
> +------------------+---------+------------------------------------+
> │pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
> +------------------+---------+------------------------------------+
> │fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
> +------------------+---------+------------------------------------+
> │fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
> +------------------+---------+------------------------------------+
>
> This logic will start to fail once we enable `set -e`. When `$eval_ret`
> is non-zero, the subshell we create will fail, and with `set -e` we'll
> thus bail out without evaluating the logic after the semicolon.
>
> Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
> a bit simpler, it also retains the original behaviour:
>
> +------------------+---------+------------------------------------+
> |test body │ cleanup │ old behaviour │
> +------------------+---------+------------------------------------+
> │pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
> +------------------+---------+------------------------------------+
> │pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
> +------------------+---------+------------------------------------+
> │fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
> +------------------+---------+------------------------------------+
> │fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
> +------------------+---------+------------------------------------+
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/test-lib-functions.sh | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> index f8bc77619b..0eac676109 100644
> --- a/t/test-lib-functions.sh
> +++ b/t/test-lib-functions.sh
> @@ -1516,7 +1516,7 @@ test_when_finished () {
> test "${BASH_SUBSHELL-0}" = 0 ||
> BUG "test_when_finished does nothing in a subshell"
> test_cleanup="{ $*
> - } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
> + } || eval_ret=\$?; $test_cleanup"
Hmph, it seems this "&& (exit $eval_ret)" pattern has been with us
forever since it was introduced at 3bf78867 (test-lib: Let tests
specify commands to be run at end of test, 2010-05-02), and survived
a slight modification to work around issues on FreeBSD done in
b6b0afdc (test-lib: some shells do not let $? propagate into an
eval, 2010-05-06). When a major part of test-lib.sh was split into
test-lib-functions.s at 12a29b1a (Move the user-facing test library
to test-lib-functions.sh, 2012-02-17), this part was copied intact.
Does this interact well with the glitch b6b0afdc (test-lib: some
shells do not let $? propagate into an eval, 2010-05-06) tried to
work around, by the way?
Thanks.
> }
>
> # This function can be used to schedule some commands to be run
> @@ -1544,7 +1544,7 @@ test_atexit () {
> test "${BASH_SUBSHELL-0}" = 0 ||
> BUG "test_atexit does nothing in a subshell"
> test_atexit_cleanup="{ $*
> - } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
> + } || eval_ret=\$?; $test_atexit_cleanup"
> }
>
> # Deprecated wrapper for "git init", use "git init" directly instead
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-13 9:49 ` [PATCH 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-13 17:28 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 17:28 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> In t0008 we use `grep -v` in a subshell, but expect that this command
> will sometimes not match anything. This would cause grep(1) to return an
> error code, but given that we don't run with `set -e` we swallow this
> error.
>
> We're about to enable `set -e`. Prepare for this by ignoring any errors.
It is curious that true is explicitly spelled out, unlike the
earlier steps in the series that used ":" in "|| :".
Loss of extra spaces around the statement inside $() is a good
touch.
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/t0008-ignores.sh | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
> index e716b5cdfa..1218005b54 100755
> --- a/t/t0008-ignores.sh
> +++ b/t/t0008-ignores.sh
> @@ -122,8 +122,8 @@ test_expect_success_multiple () {
> fi
> testname="$1" expect_all="$2" code="$3"
>
> - expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
> - expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
> + expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || true)
> + expect=$(echo "$expect_verbose" | sed -e 's/.* //')
>
> test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
> expect "$expect" &&
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 12/12] t: detect errors outside of test cases
2026-04-13 9:49 ` [PATCH 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-13 17:29 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 17:29 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> We have recently merged a patch series that had a simple misspelling of
> `test_expect_success`. Instead of making our tests fail though, this
> typo went completely undetected and all of our tests passed, which is of
> course unfortunate. This is a more general issue with our test suite:
> all commands that run outside of a specific test case can fail, and if
> we don't explicitly check for such failure then this failure will be
> silently ignored.
>
> Improve the status quo by enabling the errexit option so that any such
> unchecked failures will cause us to abort immediately.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/test-lib.sh | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 81380fe978..c493e3c768 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -15,6 +15,10 @@
> # You should have received a copy of the GNU General Public License
> # along with this program. If not, see https://www.gnu.org/licenses/ .
>
> +# Enable the use of errexit so that any unexpected failures will cause us to
> +# abort tests, even when outside of a specific test case.
> +set -e
Yay. Very nice.
> # Test the binaries we have just built. The tests are kept in
> # t/ subdirectory and are run in 'trash directory' subdirectory.
> if test -z "$TEST_DIRECTORY"
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (11 preceding siblings ...)
2026-04-13 9:49 ` [PATCH 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-13 21:33 ` Junio C Hamano
2026-04-13 21:46 ` Junio C Hamano
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (4 subsequent siblings)
17 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 21:33 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> this is a follow-up to the recent discussion we had around `set -e` to
> make our tests more robust and basically supersedes Junio's [1].
>
> I've tested the patches with both Bash and Dash, and all tests are
> passing on my machine with both of them. CI seems to be happy, as
> well. But I would expect that this change probably has some fallout,
> even though I hope that it's generally going to be small and contained.
>
> This series is based on 8c9303b1ff (Merge branch
> 'jc/no-writev-does-not-work', 2026-04-10).
This unfortunately breaks svn related tests big time for me, as I
deliberately do not install Perl modules that are needed for git-svn.
$ cd t && sh t9152-svn-empty-dirs-after-gc.sh -i -v
Initialized empty Git repository in /home/gitster/git/t/trash directory.t9152-svn-empty-dirs-after-gc/.git/
Can't locate SVN/Core.pm in @INC (you may need to install the SVN::Core module) (@INC entries checked: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.40.1 /usr/local/share/perl/5.40.1 /usr/lib/x86_64-linux-gnu/perl5/5.40 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.40 /usr/share/perl/5.40 /usr/local/lib/site_perl) at -e line 2.
BEGIN failed--compilation aborted at -e line 2.
FATAL: Unexpected exit with code 2
$ cd t && ls -d trash\ directory.t*
trash directory.t9100-git-svn-basic
trash directory.t9101-git-svn-props
trash directory.t9102-git-svn-deep-rmdir
trash directory.t9103-git-svn-tracked-directory-removed
trash directory.t9104-git-svn-follow-parent
trash directory.t9105-git-svn-commit-diff
trash directory.t9106-git-svn-commit-diff-clobber
trash directory.t9107-git-svn-migrate
trash directory.t9108-git-svn-glob
trash directory.t9109-git-svn-multi-glob
trash directory.t9110-git-svn-use-svm-props
trash directory.t9111-git-svn-use-svnsync-props
trash directory.t9112-git-svn-md5less-file
trash directory.t9113-git-svn-dcommit-new-file
trash directory.t9114-git-svn-dcommit-merge
trash directory.t9115-git-svn-dcommit-funky-renames
trash directory.t9116-git-svn-log
trash directory.t9117-git-svn-init-clone
trash directory.t9118-git-svn-funky-branch-names
trash directory.t9119-git-svn-info
trash directory.t9120-git-svn-clone-with-percent-escapes
trash directory.t9121-git-svn-fetch-renamed-dir
trash directory.t9122-git-svn-author
trash directory.t9123-git-svn-rebuild-with-rewriteroot
trash directory.t9124-git-svn-dcommit-auto-props
trash directory.t9125-git-svn-multi-glob-branch-names
trash directory.t9126-git-svn-follow-deleted-readded-directory
trash directory.t9127-git-svn-partial-rebuild
trash directory.t9128-git-svn-cmd-branch
trash directory.t9129-git-svn-i18n-commitencoding
trash directory.t9130-git-svn-authors-file
trash directory.t9131-git-svn-empty-symlink
trash directory.t9132-git-svn-broken-symlink
trash directory.t9133-git-svn-nested-git-repo
trash directory.t9134-git-svn-ignore-paths
trash directory.t9135-git-svn-moved-branch-empty-file
trash directory.t9136-git-svn-recreated-branch-empty-file
trash directory.t9137-git-svn-dcommit-clobber-series
trash directory.t9138-git-svn-authors-prog
trash directory.t9139-git-svn-non-utf8-commitencoding
trash directory.t9140-git-svn-reset
trash directory.t9141-git-svn-multiple-branches
trash directory.t9142-git-svn-shallow-clone
trash directory.t9143-git-svn-gc
trash directory.t9144-git-svn-old-rev_map
trash directory.t9145-git-svn-master-branch
trash directory.t9146-git-svn-empty-dirs
trash directory.t9147-git-svn-include-paths
trash directory.t9148-git-svn-propset
trash directory.t9150-svk-mergetickets
trash directory.t9151-svn-mergeinfo
trash directory.t9152-svn-empty-dirs-after-gc
trash directory.t9153-git-svn-rewrite-uuid
trash directory.t9154-git-svn-fancy-glob
trash directory.t9155-git-svn-fetch-deleted-tag
trash directory.t9156-git-svn-fetch-deleted-tag-2
trash directory.t9157-git-svn-fetch-merge
trash directory.t9158-git-svn-mergeinfo
trash directory.t9159-git-svn-no-parent-mergeinfo
trash directory.t9160-git-svn-preserve-empty-dirs
trash directory.t9161-git-svn-mergeinfo-push
trash directory.t9162-git-svn-dcommit-interactive
trash directory.t9163-git-svn-reset-clears-caches
trash directory.t9164-git-svn-dcommit-concurrent
trash directory.t9165-git-svn-fetch-merge-branch-of-branch
trash directory.t9166-git-svn-fetch-merge-branch-of-branch2
trash directory.t9167-git-svn-cmd-branch-subproject
trash directory.t9168-git-svn-partially-globbed-names
trash directory.t9169-git-svn-dcommit-crlf
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 00/12] t: detect errors outside of test cases
2026-04-13 21:33 ` [PATCH 00/12] " Junio C Hamano
@ 2026-04-13 21:46 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 21:46 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Junio C Hamano <gitster@pobox.com> writes:
> Patrick Steinhardt <ps@pks.im> writes:
>
>> this is a follow-up to the recent discussion we had around `set -e` to
>> make our tests more robust and basically supersedes Junio's [1].
>>
>> I've tested the patches with both Bash and Dash, and all tests are
>> passing on my machine with both of them. CI seems to be happy, as
>> well. But I would expect that this change probably has some fallout,
>> even though I hope that it's generally going to be small and contained.
>>
>> This series is based on 8c9303b1ff (Merge branch
>> 'jc/no-writev-does-not-work', 2026-04-10).
>
> This unfortunately breaks svn related tests big time for me, as I
> deliberately do not install Perl modules that are needed for git-svn.
>
>
> $ cd t && sh t9152-svn-empty-dirs-after-gc.sh -i -v
> Initialized empty Git repository in /home/gitster/git/t/trash directory.t9152-svn-empty-dirs-after-gc/.git/
> Can't locate SVN/Core.pm in @INC (you may need to install the SVN::Core module) (@INC entries checked: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.40.1 /usr/local/share/perl/5.40.1 /usr/lib/x86_64-linux-gnu/perl5/5.40 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.40 /usr/share/perl/5.40 /usr/local/lib/site_perl) at -e line 2.
> BEGIN failed--compilation aborted at -e line 2.
> FATAL: Unexpected exit with code 2
With the merge of this topic reverted out of 'seen', here is how it
used to end:
$ cd t && sh t9152-svn-empty-dirs-after-gc.sh -i -v; echo $?
Initialized empty Git repository in /home/gitster/git/t/trash directory.t9152-svn-empty-dirs-after-gc/.git/
Can't locate SVN/Core.pm in @INC (you may need to install the SVN::Core module) (@INC entries checked: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.40.1 /usr/local/share/perl/5.40.1 /usr/lib/x86_64-linux-gnu/perl5/5.40 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.40 /usr/share/perl/5.40 /usr/local/lib/site_perl) at -e line 2.
BEGIN failed--compilation aborted at -e line 2.
1..0 # SKIP Perl SVN libraries not found or unusable
0
The largest difference being that we used to exit with 0 so the
overall "make test" passed, but it no longer is the case.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-13 9:49 ` [PATCH 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-13 17:09 ` Junio C Hamano
@ 2026-04-13 22:32 ` Junio C Hamano
2026-04-14 1:09 ` Junio C Hamano
1 sibling, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-13 22:32 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
> index 2fde2353fd..07d86ea244 100644
> --- a/t/lib-git-svn.sh
> +++ b/t/lib-git-svn.sh
> @@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
> SVN_TREE=$GIT_SVN_DIR/svn-tree
> test_set_port SVNSERVE_PORT
>
> -svn >/dev/null 2>&1
> -if test $? -ne 1
> +if ! svn help >/dev/null 2>&1
> then
> skip_all='skipping git svn tests, svn not found'
> test_done
I think I know what is lacking in this patch. Following the above
section (which is a good conversion), there is this bit that needs a
similar handling.
t/lib-git-svn.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git c/t/lib-git-svn.sh w/t/lib-git-svn.sh
index 2fde2353fd..24c15d17eb 100644
--- c/t/lib-git-svn.sh
+++ w/t/lib-git-svn.sh
@@ -27,13 +27,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-13 22:32 ` Junio C Hamano
@ 2026-04-14 1:09 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 7:23 ` Patrick Steinhardt
0 siblings, 2 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-14 1:09 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Junio C Hamano <gitster@pobox.com> writes:
> I think I know what is lacking in this patch. Following the above
> section (which is a good conversion), there is this bit that needs a
> similar handling.
>
> t/lib-git-svn.sh | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git c/t/lib-git-svn.sh w/t/lib-git-svn.sh
> index 2fde2353fd..24c15d17eb 100644
> --- c/t/lib-git-svn.sh
> +++ w/t/lib-git-svn.sh
> @@ -27,13 +27,13 @@ export svnrepo
> svnconf=$PWD/svnconf
> export svnconf
>
> +x=0
> perl -w -e "
> use SVN::Core;
> use SVN::Repos;
> \$SVN::Core::VERSION gt '1.1.0' or exit(42);
> system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
> -" >&3 2>&4
> -x=$?
> +" >&3 2>&4 || x=$?
> if test $x -ne 0
> then
> if test $x -eq 42; then
The above is queued as a squash fix-up on top of the topic, but with
the topic merged to 'seen', we seem to be getting a CI failure that
appears specific to macOS. Compare the failing
https://github.com/git/git/actions/runs/24371204585 (aa13593)
with the same tree without the topic
https://github.com/git/git/actions/runs/24369661492 (ad8b884)
The only differences between the commits are
$ git diff --compact-summary ad8b884 aa13593
t/lib-git-daemon.sh | 13 ++++++++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 2 +-
t/test-lib-functions.sh | 12 ++++++++----
t/test-lib.sh | 8 ++++++--
19 files changed, 78 insertions(+), 58 deletions(-)
which does match what is contained in this topic.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-13 9:49 ` [PATCH 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-13 16:33 ` Junio C Hamano
@ 2026-04-14 6:23 ` Jeff King
2026-04-14 17:41 ` Junio C Hamano
1 sibling, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-14 6:23 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano
On Mon, Apr 13, 2026 at 11:49:23AM +0200, Patrick Steinhardt wrote:
> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> index f3af10fb7e..f8bc77619b 100644
> --- a/t/test-lib-functions.sh
> +++ b/t/test-lib-functions.sh
> @@ -1195,8 +1195,12 @@ test_must_fail () {
> echo >&7 "test_must_fail: only 'git' is allowed: $*"
> return 1
> fi
> - "$@" 2>&7
> - exit_code=$?
> + if "$@" 2>&7
> + then
> + exit_code=0
> + else
> + exit_code=$?
> + fi
One subtle interaction here is that the command in "$@" will be run with
"set -e" suppressed if it's a shell function (even if the body
explicitly says "set -e").
I think it's mostly academic since we don't tend to use "set -e" in the
tests at all, but it may be an eventual gotcha.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-13 16:33 ` Junio C Hamano
@ 2026-04-14 7:23 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 09:33:59AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > The helper function `test_must_fail ()` executes a specific Git command
> > that may or may not fail in a specific way. This is done by executing
> > the command in question and then comparing its exit code against a set
> > of conditions.
> >
> > This works, but once we run our test suite with `set -e` we may bail out
> > of `test_must_fail ()` early in case the command actually fails, even
> > though we expect it to fail. Prepare for this change by marking the
> > command part of a condition.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > t/test-lib-functions.sh | 8 ++++++--
> > 1 file changed, 6 insertions(+), 2 deletions(-)
> >
> > diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> > index f3af10fb7e..f8bc77619b 100644
> > --- a/t/test-lib-functions.sh
> > +++ b/t/test-lib-functions.sh
> > @@ -1195,8 +1195,12 @@ test_must_fail () {
> > echo >&7 "test_must_fail: only 'git' is allowed: $*"
> > return 1
> > fi
> > - "$@" 2>&7
> > - exit_code=$?
> > + if "$@" 2>&7
> > + then
> > + exit_code=0
> > + else
> > + exit_code=$?
> > + fi
>
> This is obvious and clear. Alternatively
>
> exit_code=0; "$@" 2>&7 || exit_code=$?
>
> would be more assuring to readers who (with less fluency in Bourne
> shells) wonder, upon seeing the else clause, how far "$?" traveled
> and it still holds the status of "$@".
Fair enough, will change.
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-13 17:28 ` Junio C Hamano
@ 2026-04-14 7:23 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 10:28:00AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > In t0008 we use `grep -v` in a subshell, but expect that this command
> > will sometimes not match anything. This would cause grep(1) to return an
> > error code, but given that we don't run with `set -e` we swallow this
> > error.
> >
> > We're about to enable `set -e`. Prepare for this by ignoring any errors.
>
> It is curious that true is explicitly spelled out, unlike the
> earlier steps in the series that used ":" in "|| :".
That's fair. Will adapt.
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-13 16:26 ` Junio C Hamano
@ 2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 17:49 ` Junio C Hamano
0 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 09:26:13AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > We have a couple of calls to `test_match_signal ()` where we execute a
> > Git command and expect it to die with a specific signal. These calls
> > will essentially execute the process in a subshell via `foo; echo $?`,
> > but as we expect `foo` to fail this will cause the overall subshell to
> > fail once we `set -e`.
> >
> > Fix this issue by using `foo || echo $?` instead.
>
> Hmph, if the 'foo' command that is expected to fail succeeds by a
> bug, we won't see 0 on the standard output anymore. Comparing 13
> with 0 or 13 with an empty string "" would fail either way, so that
> may not give us a practical difference, but this somehow leaves a
> foul small in my nose.
Hm, true, it is a wee bit ugly.
> The technique used in <20260325062114.2067946-2-gitster@pobox.com>
> would give us something ugly like
>
> - OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((st=0; large_git || st=$?; echo $st 1>&3) | :) 3>&1 ) &&
>
> and it ensures that we give 0 when we succeed, but I am not sure if
> this is worth doing.
>
> Or perhaps
>
> - OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=${OUT:-0} &&
>
> I dunno.
>
> If "large_git" somehow writes into file descriptor #3, then OUT will
> be contaminated with something other than $?, so the additional "if
> empty substitute with 0" would not work without doing something like
>
> - OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((large_git 3>/dev/null || echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=${OUT:-0} &&
>
> I guess. For that matter, if large_git spits out "13" to its file
> descriptor #3, I do not know what would have happened ;-)
How about the below patch? It's a mouthful, but the intent is quite
explicit and it's overall not too bad, if you ask me.
Patrick
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 74c2a27972..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git || echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 8e68a00dcb..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*" || echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 1:09 ` Junio C Hamano
@ 2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 7:23 ` Patrick Steinhardt
1 sibling, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 06:09:08PM -0700, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
> > I think I know what is lacking in this patch. Following the above
> > section (which is a good conversion), there is this bit that needs a
> > similar handling.
> >
> > t/lib-git-svn.sh | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git c/t/lib-git-svn.sh w/t/lib-git-svn.sh
> > index 2fde2353fd..24c15d17eb 100644
> > --- c/t/lib-git-svn.sh
> > +++ w/t/lib-git-svn.sh
> > @@ -27,13 +27,13 @@ export svnrepo
> > svnconf=$PWD/svnconf
> > export svnconf
> >
> > +x=0
> > perl -w -e "
> > use SVN::Core;
> > use SVN::Repos;
> > \$SVN::Core::VERSION gt '1.1.0' or exit(42);
> > system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
> > -" >&3 2>&4
> > -x=$?
> > +" >&3 2>&4 || x=$?
> > if test $x -ne 0
> > then
> > if test $x -eq 42; then
Thanks, I will squash this fix-up into my branch.
> The above is queued as a squash fix-up on top of the topic, but with
> the topic merged to 'seen', we seem to be getting a CI failure that
> appears specific to macOS. Compare the failing
>
> https://github.com/git/git/actions/runs/24371204585 (aa13593)
>
> with the same tree without the topic
>
> https://github.com/git/git/actions/runs/24369661492 (ad8b884)
>
> The only differences between the commits are
>
> $ git diff --compact-summary ad8b884 aa13593
> t/lib-git-daemon.sh | 13 ++++++++++---
> t/lib-git-svn.sh | 7 +++----
> t/lib-httpd.sh | 3 +--
> t/t0005-signals.sh | 4 ++--
> t/t0008-ignores.sh | 4 ++--
> t/t1301-shared-repo.sh | 2 +-
> t/t3600-rm.sh | 2 +-
> t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
> t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
> t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
> t/t7508-status.sh | 4 ++--
> t/t9138-git-svn-authors-prog.sh | 4 ++--
> t/t9200-git-cvsexportcommit.sh | 3 +--
> t/t9400-git-cvsserver-server.sh | 5 +++--
> t/t9401-git-cvsserver-crlf.sh | 4 ++--
> t/t9402-git-cvsserver-refs.sh | 4 ++--
> t/t9902-completion.sh | 2 +-
> t/test-lib-functions.sh | 12 ++++++++----
> t/test-lib.sh | 8 ++++++--
> 19 files changed, 78 insertions(+), 58 deletions(-)
I'll investigate, thanks for the hint!
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 1:09 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
@ 2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 13:40 ` Junio C Hamano
2026-04-14 22:03 ` Jeff King
1 sibling, 2 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 06:09:08PM -0700, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
> > I think I know what is lacking in this patch. Following the above
> > section (which is a good conversion), there is this bit that needs a
> > similar handling.
> >
> > t/lib-git-svn.sh | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git c/t/lib-git-svn.sh w/t/lib-git-svn.sh
> > index 2fde2353fd..24c15d17eb 100644
> > --- c/t/lib-git-svn.sh
> > +++ w/t/lib-git-svn.sh
> > @@ -27,13 +27,13 @@ export svnrepo
> > svnconf=$PWD/svnconf
> > export svnconf
> >
> > +x=0
> > perl -w -e "
> > use SVN::Core;
> > use SVN::Repos;
> > \$SVN::Core::VERSION gt '1.1.0' or exit(42);
> > system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
> > -" >&3 2>&4
> > -x=$?
> > +" >&3 2>&4 || x=$?
> > if test $x -ne 0
> > then
> > if test $x -eq 42; then
Thanks, I've queued that change locally.
> The above is queued as a squash fix-up on top of the topic, but with
> the topic merged to 'seen', we seem to be getting a CI failure that
> appears specific to macOS. Compare the failing
>
> https://github.com/git/git/actions/runs/24371204585 (aa13593)
>
> with the same tree without the topic
>
> https://github.com/git/git/actions/runs/24369661492 (ad8b884)
>
> The only differences between the commits are
>
> $ git diff --compact-summary ad8b884 aa13593
> t/lib-git-daemon.sh | 13 ++++++++++---
> t/lib-git-svn.sh | 7 +++----
> t/lib-httpd.sh | 3 +--
> t/t0005-signals.sh | 4 ++--
> t/t0008-ignores.sh | 4 ++--
> t/t1301-shared-repo.sh | 2 +-
> t/t3600-rm.sh | 2 +-
> t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
> t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
> t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
> t/t7508-status.sh | 4 ++--
> t/t9138-git-svn-authors-prog.sh | 4 ++--
> t/t9200-git-cvsexportcommit.sh | 3 +--
> t/t9400-git-cvsserver-server.sh | 5 +++--
> t/t9401-git-cvsserver-crlf.sh | 4 ++--
> t/t9402-git-cvsserver-refs.sh | 4 ++--
> t/t9902-completion.sh | 2 +-
> t/test-lib-functions.sh | 12 ++++++++----
> t/test-lib.sh | 8 ++++++--
> 19 files changed, 78 insertions(+), 58 deletions(-)
>
> which does match what is contained in this topic.
I knew it was a bad idea to not also run tests on GitHub Actions :) I'll
do that before the next reroll.
Anyway, looking at the failing test t9501, I assume that Perl isn't able
to enable the DATE_PARSER prerequisite. So something like the below
patch might hopefully fix it.
Thanks!
Patrick
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df..3acb58125b 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -15,12 +15,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./lib-gitweb.sh
-#
# Gitweb only provides the functionality tested by the 'modification times'
# tests if it can access a date parser from one of these modules:
-#
-perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
-perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
+test_lazy_prereq DATE_PARSER '
+ perl -MHTTP::Date -e 0 ||
+ perl -MTime::ParseDate -e 0
+'
# ----------------------------------------------------------------------
# snapshot settings
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-13 17:09 ` Junio C Hamano
@ 2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 13:41 ` Junio C Hamano
0 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 10:09:27AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > Several of our tests verify whether a certain binary can be executed,
> > potentially skipping tests in case we cannot, for example because the
> > binary doesn't exist. In those cases we often run the binary outside of
> > any conditionally.
> >
> > This will start to fail once we enable `set -e`, as that will cause us
> > to bail out the test immediately. Improve these tests by executing them
> > inside of a conditional instead.
>
> OK. "svn help" and "cvs version" do exit with status 0 and the
> rewrites that use them make sense. I wonder if we can do something
> similar to "git" instead of relying on "git<RETURN>" to exit with 1,
> perhaps ...
>
> $ git version >/dev/null; echo $?
> 0
>
> ... by using "git version" in the test-lib.sh change?
>
> Other than that looking very good.
I'm a bit confused. We do exactly that in "test-lib.sh" now, see the
below hunk that is part of this patch. Am I missing something?
Patrick
> > diff --git a/t/test-lib.sh b/t/test-lib.sh
> > index 70fd3e9baf..81380fe978 100644
> > --- a/t/test-lib.sh
> > +++ b/t/test-lib.sh
> > @@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
> > ################################################################
> > # It appears that people try to run tests without building...
> > GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
> > -"$GIT_BINARY" >/dev/null
> > -if test $? != 1
> > +
> > +if ! "$GIT_BINARY" version >/dev/null
> > then
> > if test -n "$GIT_TEST_INSTALLED"
> > then
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-13 17:23 ` Junio C Hamano
@ 2026-04-14 7:24 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-14 7:24 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Mon, Apr 13, 2026 at 10:23:37AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > Both `test_when_finished ()` and `test_atexit ()` build up a chain of
> > cleanup commands by prepending each new command to the existing cleanup
> > string. To preserve the exit code of the test body across cleanup
> > execution, we append the following logic:
> >
> > } && (exit "$eval_ret"); eval_ret=$?; ...
> >
> > The intent of this is to run the cleanup block and then unconditionally
> > restore `eval_ret`. The original behaviour of this is is:
> >
> > +------------------+---------+------------------------------------+
> > |test body │ cleanup │ old behaviour │
> > +------------------+---------+------------------------------------+
> > │pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
> > +------------------+---------+------------------------------------+
> > │pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
> > +------------------+---------+------------------------------------+
> > │fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
> > +------------------+---------+------------------------------------+
> > │fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
> > +------------------+---------+------------------------------------+
> >
> > This logic will start to fail once we enable `set -e`. When `$eval_ret`
> > is non-zero, the subshell we create will fail, and with `set -e` we'll
> > thus bail out without evaluating the logic after the semicolon.
> >
> > Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
> > a bit simpler, it also retains the original behaviour:
> >
> > +------------------+---------+------------------------------------+
> > |test body │ cleanup │ old behaviour │
> > +------------------+---------+------------------------------------+
> > │pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
> > +------------------+---------+------------------------------------+
> > │pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
> > +------------------+---------+------------------------------------+
> > │fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
> > +------------------+---------+------------------------------------+
> > │fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
> > +------------------+---------+------------------------------------+
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > t/test-lib-functions.sh | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> > index f8bc77619b..0eac676109 100644
> > --- a/t/test-lib-functions.sh
> > +++ b/t/test-lib-functions.sh
> > @@ -1516,7 +1516,7 @@ test_when_finished () {
> > test "${BASH_SUBSHELL-0}" = 0 ||
> > BUG "test_when_finished does nothing in a subshell"
> > test_cleanup="{ $*
> > - } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
> > + } || eval_ret=\$?; $test_cleanup"
>
> Hmph, it seems this "&& (exit $eval_ret)" pattern has been with us
> forever since it was introduced at 3bf78867 (test-lib: Let tests
> specify commands to be run at end of test, 2010-05-02), and survived
> a slight modification to work around issues on FreeBSD done in
> b6b0afdc (test-lib: some shells do not let $? propagate into an
> eval, 2010-05-06). When a major part of test-lib.sh was split into
> test-lib-functions.s at 12a29b1a (Move the user-facing test library
> to test-lib-functions.sh, 2012-02-17), this part was copied intact.
>
> Does this interact well with the glitch b6b0afdc (test-lib: some
> shells do not let $? propagate into an eval, 2010-05-06) tried to
> work around, by the way?
>
> Thanks.
Ah, that's a very good question. I'll spin up a FreeBSD VM to give this
a test, thanks!
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 7:23 ` Patrick Steinhardt
@ 2026-04-14 13:40 ` Junio C Hamano
2026-04-14 22:03 ` Jeff King
1 sibling, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-14 13:40 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> ...
> Anyway, looking at the failing test t9501, I assume that Perl isn't able
> to enable the DATE_PARSER prerequisite. So something like the below
> patch might hopefully fix it.
Ahhhh. This is exactly the kind of test breakage we _were_ hoping
to find and fix with "set -e" series. It has been happily failing
to set DATE_PARSER on macOS, but now with "set -e", it is unhappy.
Thanks.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 7:23 ` Patrick Steinhardt
@ 2026-04-14 13:41 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-14 13:41 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> I'm a bit confused. We do exactly that in "test-lib.sh" now, see the
> below hunk that is part of this patch. Am I missing something?
No, I was misreading the patch and "$GIT_BINARY" >/dev/null in *preimage*
was pulling my attention away from the fixed one with "version".
Thanks for correcting me.
>
> Patrick
>
>> > diff --git a/t/test-lib.sh b/t/test-lib.sh
>> > index 70fd3e9baf..81380fe978 100644
>> > --- a/t/test-lib.sh
>> > +++ b/t/test-lib.sh
>> > @@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>> > ################################################################
>> > # It appears that people try to run tests without building...
>> > GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
>> > -"$GIT_BINARY" >/dev/null
>> > -if test $? != 1
>> > +
>> > +if ! "$GIT_BINARY" version >/dev/null
>> > then
>> > if test -n "$GIT_TEST_INSTALLED"
>> > then
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-14 6:23 ` Jeff King
@ 2026-04-14 17:41 ` Junio C Hamano
2026-04-15 6:58 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-14 17:41 UTC (permalink / raw)
To: Jeff King; +Cc: Patrick Steinhardt, git
Jeff King <peff@peff.net> writes:
> On Mon, Apr 13, 2026 at 11:49:23AM +0200, Patrick Steinhardt wrote:
>
>> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
>> index f3af10fb7e..f8bc77619b 100644
>> --- a/t/test-lib-functions.sh
>> +++ b/t/test-lib-functions.sh
>> @@ -1195,8 +1195,12 @@ test_must_fail () {
>> echo >&7 "test_must_fail: only 'git' is allowed: $*"
>> return 1
>> fi
>> - "$@" 2>&7
>> - exit_code=$?
>> + if "$@" 2>&7
>> + then
>> + exit_code=0
>> + else
>> + exit_code=$?
>> + fi
>
> One subtle interaction here is that the command in "$@" will be run with
> "set -e" suppressed if it's a shell function (even if the body
> explicitly says "set -e").
>
> I think it's mostly academic since we don't tend to use "set -e" in the
> tests at all, but it may be an eventual gotcha.
Yeah, ugly, but this is tricky that they felt the need to explain it
with an example in the informative section X-<.
"set" is described in
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_26
and the description for "-e" does not even talk about any function.
The application usage notes associated with the "set" gives this
Application writers should avoid relying on set -e within
functions. For example, in the following script:
set -e
start() {
some_server
echo some_server started successfully
}
start || echo >&2 some_server failed
the -e setting is ignored within the function body (because the
function is a command in an AND-OR list other than the
last). Therefore, if some_server fails, the function carries on to
echo "some_server started successfully", and the exit status of the
function is zero (which means "some_server failed" is not output).
which greatly helps.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-14 7:23 ` Patrick Steinhardt
@ 2026-04-14 17:49 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-14 17:49 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git
Patrick Steinhardt <ps@pks.im> writes:
> How about the below patch? It's a mouthful, but the intent is quite
> explicit and it's overall not too bad, if you ask me.
>
> test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
> - OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
> + OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
Ah, I like it slightly better than "st=0; large_git || st=$?"
pattern, but yes, it is mouthful.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 13:40 ` Junio C Hamano
@ 2026-04-14 22:03 ` Jeff King
2026-04-14 22:52 ` Jeff King
1 sibling, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-14 22:03 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, git
On Tue, Apr 14, 2026 at 09:23:45AM +0200, Patrick Steinhardt wrote:
> diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
> index 32814e75df..3acb58125b 100755
> --- a/t/t9501-gitweb-standalone-http-status.sh
> +++ b/t/t9501-gitweb-standalone-http-status.sh
> @@ -15,12 +15,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>
> . ./lib-gitweb.sh
>
> -#
> # Gitweb only provides the functionality tested by the 'modification times'
> # tests if it can access a date parser from one of these modules:
> -#
> -perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
> -perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
> +test_lazy_prereq DATE_PARSER '
> + perl -MHTTP::Date -e 0 ||
> + perl -MTime::ParseDate -e 0
> +'
Maybe I am being dense, but I don't see how the original would have
problems with "set -e". The perl command is on the left-hand side of an
&&, so "set -e" will be suppressed.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 22:03 ` Jeff King
@ 2026-04-14 22:52 ` Jeff King
2026-04-14 23:08 ` Jeff King
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-14 22:52 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, git
On Tue, Apr 14, 2026 at 06:03:47PM -0400, Jeff King wrote:
> > -perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
> > -perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER
> > +test_lazy_prereq DATE_PARSER '
> > + perl -MHTTP::Date -e 0 ||
> > + perl -MTime::ParseDate -e 0
> > +'
>
> Maybe I am being dense, but I don't see how the original would have
> problems with "set -e". The perl command is on the left-hand side of an
> &&, so "set -e" will be suppressed.
Joy of joys, it looks like a bash bug. Using "command" re-enables "set
-e", even on the left-hand side of an &&.
With bash 5.3.9 on my Debian system:
$ bash -ec 'command false && echo one; echo two'
two
On bash 3.2.57, tmate'd into the GitHub Actions macOS image:
$ bash -ec 'command false && echo one; echo two'
[no output, we exited after command failed]
It triggers in this case because perl in our test suite is a shell
function which runs "command $PERL_PATH".
But I don't think switching to an if/then conditional helps. Doing:
bash -ec 'if command false; then echo one; fi; echo two'
likewise exits early (but prints "two" on modern versions).
Sadly I do not think we'll see a fixed version anytime soon. Apple is
sticking with ancient bash because of licensing, IIRC. I don't know if
they're backporting any fixes (and even if they wanted to, there are
probably license complications).
Short of requiring a third-party shell, the only workaround I can think
of is to manually "set +e" before using "command", and then restore it
with "set -e". Gross.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 22:52 ` Jeff King
@ 2026-04-14 23:08 ` Jeff King
2026-04-15 6:48 ` Patrick Steinhardt
2026-04-15 15:31 ` Junio C Hamano
0 siblings, 2 replies; 133+ messages in thread
From: Jeff King @ 2026-04-14 23:08 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, git
On Tue, Apr 14, 2026 at 06:52:07PM -0400, Jeff King wrote:
> Short of requiring a third-party shell, the only workaround I can think
> of is to manually "set +e" before using "command", and then restore it
> with "set -e". Gross.
I guess one other option is to avoid turning on "set -e" at all for
known-buggy shells. We are not relying on it working everywhere, but
rather hoping that if at least one platform uses it, it will find
programming errors in the test script.
Personally, I am still skeptical that all of this is worth it versus
just checking stderr.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 23:08 ` Jeff King
@ 2026-04-15 6:48 ` Patrick Steinhardt
2026-04-16 5:49 ` Jeff King
2026-04-15 15:31 ` Junio C Hamano
1 sibling, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 6:48 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
On Tue, Apr 14, 2026 at 07:08:10PM -0400, Jeff King wrote:
> On Tue, Apr 14, 2026 at 06:52:07PM -0400, Jeff King wrote:
>
> > Short of requiring a third-party shell, the only workaround I can think
> > of is to manually "set +e" before using "command", and then restore it
> > with "set -e". Gross.
>
> I guess one other option is to avoid turning on "set -e" at all for
> known-buggy shells. We are not relying on it working everywhere, but
> rather hoping that if at least one platform uses it, it will find
> programming errors in the test script.
>
> Personally, I am still skeptical that all of this is worth it versus
> just checking stderr.
Yeah, the more I dive into this topic the more sceptical I get, as well,
as shells behave wildly different around `set -e`. So I'm starting to
feel somewhat uncomfortable with the idea of blanket-enabling it for all
shells, as that will for sure lead to lots of fallout on platforms that
we're not testing.
Maybe we should really only do this for an allow-listed set of shells.
Starting with Bash 5 and newer might be good enough, and given that we
use Bash for some of our CI jobs we can assume that this would weed out
failures anyway.
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-14 17:41 ` Junio C Hamano
@ 2026-04-15 6:58 ` Patrick Steinhardt
2026-04-16 5:40 ` Jeff King
0 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 6:58 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git
On Tue, Apr 14, 2026 at 10:41:29AM -0700, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> > On Mon, Apr 13, 2026 at 11:49:23AM +0200, Patrick Steinhardt wrote:
> >
> >> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> >> index f3af10fb7e..f8bc77619b 100644
> >> --- a/t/test-lib-functions.sh
> >> +++ b/t/test-lib-functions.sh
> >> @@ -1195,8 +1195,12 @@ test_must_fail () {
> >> echo >&7 "test_must_fail: only 'git' is allowed: $*"
> >> return 1
> >> fi
> >> - "$@" 2>&7
> >> - exit_code=$?
> >> + if "$@" 2>&7
> >> + then
> >> + exit_code=0
> >> + else
> >> + exit_code=$?
> >> + fi
> >
> > One subtle interaction here is that the command in "$@" will be run with
> > "set -e" suppressed if it's a shell function (even if the body
> > explicitly says "set -e").
> >
> > I think it's mostly academic since we don't tend to use "set -e" in the
> > tests at all, but it may be an eventual gotcha.
>
> Yeah, ugly, but this is tricky that they felt the need to explain it
> with an example in the informative section X-<.
>
> "set" is described in
>
> https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_26
>
> and the description for "-e" does not even talk about any function.
> The application usage notes associated with the "set" gives this
>
> Application writers should avoid relying on set -e within
> functions. For example, in the following script:
>
> set -e
> start() {
> some_server
> echo some_server started successfully
> }
> start || echo >&2 some_server failed
>
> the -e setting is ignored within the function body (because the
> function is a command in an AND-OR list other than the
> last). Therefore, if some_server fails, the function carries on to
> echo "some_server started successfully", and the exit status of the
> function is zero (which means "some_server failed" is not output).
>
> which greatly helps.
The funny thing is that I discovered shells where this is not the case,
and `set -e` _does_ extend into functions. Anyway, I think I'll follow
Peff's suggestion and just enable `set -e` for Bash 5 and newer. This
should ensure that the behaviour is a lot more uniform without having
negative fallout on other platforms that don't use Bash, but we'd still
get a failing CI as we use Bash in some of the jobs.
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v2 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (12 preceding siblings ...)
2026-04-13 21:33 ` [PATCH 00/12] " Junio C Hamano
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (11 more replies)
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (3 subsequent siblings)
17 siblings, 12 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
that these changes work on both platforms.
Changes in v2:
- Use `ret=0; $command || ret=$?` pattern.
- Restore `echo 0` in SIGPIPE tests.
- Fix "lib-git-svn.sh" to gracefully handle the case where SVN Perl
modules aren't installed.
- Use `|| :` consistently instead of `|| true`.
- Fix up a couple of tests that fail on FreeBSD 15. The test suite is
now passing on this system, too.
- Only enable `set -e` on Bash 5 and newer.
- Link to v1: https://patch.msgid.link/20260413-b4-pks-tests-with-set-e-v1-0-5b83763a0e84@pks.im
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
[2]: https://gitlab.com/gitlab-org/git/-/merge_requests/541
[3]: https://github.com/git/git/pull/2270
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
t/lib-git-daemon.sh | 8 +++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t5000-tar-tree.sh | 4 ++--
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7422-submodule-output.sh | 2 +-
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 2 +-
t/test-lib-functions.sh | 12 ++++++------
t/test-lib.sh | 19 +++++++++++++++----
22 files changed, 85 insertions(+), 66 deletions(-)
Range-diff versus v1:
1: 210ccb018c ! 1: 6e3147dbb1 t: prepare `test_match_signal ()` calls for `set -e`
@@ Commit message
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
- Fix this issue by using `foo || echo $?` instead.
+ Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
@@ t/t0005-signals.sh: test_expect_success 'create blob' '
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
-+ OUT=$( ((large_git || echo $? 1>&3) | :) 3>&1 ) &&
++ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
-+ OUT=$( ((trap "" PIPE && large_git || echo $? 1>&3) | :) 3>&1 ) &&
++ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
@@ t/t3600-rm.sh: test_expect_success 'choking "git rm" should not let it die with
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
-+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" || echo $? 1>&3) | :) 3>&1 ) &&
++ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
2: c056357f6d < -: ---------- t: prepare `test_must_fail ()` for `set -e`
-: ---------- > 2: 393374871a t: prepare `test_must_fail ()` for `set -e`
3: d9076a67ba ! 3: 2ff2e3fb7d t: prepare `stop_git_daemon ()` for `set -e`
@@ Commit message
than not that we have already killed it, and the call to kill will
fail.
- Prepare for this change by making the call to `wait` part of a condition
- and by silencing failures of the second call to `kill`.
+ Prepare for this change by handling the failure of `wait` with `||` and
+ by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
@@ t/lib-git-daemon.sh: stop_git_daemon() {
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
-+ if wait "$GIT_DAEMON_PID" >&3 2>&4
-+ then
-+ ret=0
-+ else
-+ ret=$?
-+ fi
++ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
4: 50be774536 = 4: 2c51b9d9fa t: prepare `git config --unset` calls for `set -e`
5: b1ac21d4dd = 5: adba2b830f t: prepare conditional test execution for `set -e`
6: 19518eeac5 ! 6: 61f949e1fb t: prepare execution of potentially failing commands for `set -e`
@@ t/lib-git-svn.sh: GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
then
skip_all='skipping git svn tests, svn not found'
test_done
+@@ t/lib-git-svn.sh: export svnrepo
+ svnconf=$PWD/svnconf
+ export svnconf
+
++x=0
+ perl -w -e "
+ use SVN::Core;
+ use SVN::Repos;
+ \$SVN::Core::VERSION gt '1.1.0' or exit(42);
+ system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
+-" >&3 2>&4
+-x=$?
++" >&3 2>&4 || x=$?
+ if test $x -ne 0
+ then
+ if test $x -eq 42; then
## t/lib-httpd.sh ##
@@ t/lib-httpd.sh: start_httpd() {
@@ t/lib-httpd.sh: start_httpd() {
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
+ ## t/t3901-i18n-patch.sh ##
+@@ t/t3901-i18n-patch.sh: check_encoding () {
+ 8859)
+ grep "^encoding ISO8859-1" ;;
+ *)
+- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
++ ret=0; grep "^encoding ISO8859-1" || ret=$?
++ test "$ret" != 0 ;;
+ esac || return 1
+ j=$i
+ i=$(($i+1))
+
+ ## t/t5000-tar-tree.sh ##
+@@ t/t5000-tar-tree.sh: test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
+ # would generate the whole 64GB).
+ test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
+ {
+- git archive HEAD
+- echo $? >exit-code
++ { ret=0 && git archive HEAD || ret=$?; } &&
++ echo "$ret" >exit-code
+ } | test_copy_bytes 4096 >huge.tar &&
+ echo 141 >expect &&
+ test_cmp expect exit-code
+
+ ## t/t7422-submodule-output.sh ##
+@@ t/t7422-submodule-output.sh: test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
+ (
+ cd repo &&
+ GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
+- { git submodule status --recursive 2>err; echo $?>status; } |
++ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
+ grep -q recursive-submodule-path-1 &&
+ test_must_be_empty err &&
+ test_match_signal 13 "$(cat status)"
+
## t/t9200-git-cvsexportcommit.sh ##
@@ t/t9200-git-cvsexportcommit.sh: if ! test_have_prereq PERL; then
test_done
@@ t/t9402-git-cvsserver-refs.sh: check_diff() {
then
skip_all='skipping git-cvsserver tests, perl not available'
+ ## t/test-lib-functions.sh ##
+@@ t/test-lib-functions.sh: test_might_fail () {
+ test_expect_code () {
+ want_code=$1
+ shift
+- "$@" 2>&7
+- exit_code=$?
++ exit_code=0; "$@" 2>&7 || exit_code=$?
+ if test $exit_code = $want_code
+ then
+ return 0
+
## t/test-lib.sh ##
@@ t/test-lib.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
@@ t/test-lib.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
then
if test -n "$GIT_TEST_INSTALLED"
then
+@@ t/test-lib.sh: then
+ # from any previous runs.
+ >"$GIT_TEST_TEE_OUTPUT_FILE"
+
+- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
+- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
++ (
++ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
++ echo "$ret" >"$TEST_RESULTS_BASE.exit"
++ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
+ exit
+ fi
7: 7d7583d1ea = 7: 697830e576 t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
8: 749a350716 ! 8: d5d1ea03ab t0008: silence error in subshell when using `grep -v`
@@ t/t0008-ignores.sh: test_expect_success_multiple () {
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
-+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || true)
++ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
9: 14c8dd5148 ! 9: 75a150e2dd t1301: don't fail in case setfacl(1) doesn't exist or fails
@@ t/t1301-shared-repo.sh: TEST_CREATE_REPO_NO_TEMPLATE=1
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
-+setfacl -k . 2>/dev/null || true
++setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
10: a81e602616 = 10: ba22bab22d t6002: fix use of `expr` with `set -e`
11: dcf5c849e9 = 11: 5a8e2df836 t9902: fix use of `read` with `set -e`
12: 691e1c9b58 ! 12: 8266ee6035 t: detect errors outside of test cases
@@ Commit message
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
+ Note that for now, we only enable this option for Bash 5 and newer. This
+ is because other shells have wildly different behaviour, and older
+ versions of Bash (especially on macOS) are buggy. The list of enabled
+ shells may be extended going forward.
+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
## t/test-lib.sh ##
@@ t/test-lib.sh
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
-+# abort tests, even when outside of a specific test case.
-+set -e
++# abort tests, even when outside of a specific test case. Note that we only
++# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
++# across shells. The list of allowed shells may be extended going forward.
++if test "${BASH_VERSINFO:=0}" -ge 5
++then
++ set -e
++fi
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v2 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (10 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (9 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..5fd5494ef1 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+
+ exit_code=0; "$@" 2>&7 || exit_code=$?
+
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (8 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..d172aa51f0 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (7 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 05/12] t: prepare conditional test execution for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (6 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (5 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t3901-i18n-patch.sh | 3 ++-
t/t5000-tar-tree.sh | 4 ++--
t/t7422-submodule-output.sh | 2 +-
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib-functions.sh | 3 +--
t/test-lib.sh | 10 ++++++----
11 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..52843f667d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b49a..ef7d7e1edc 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ ret=0; grep "^encoding ISO8859-1" || ret=$?
+ test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054f17..a8c28533dc 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
- git archive HEAD
- echo $? >exit-code
+ { ret=0 && git archive HEAD || ret=$?; } &&
+ echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf117..852136fdfd 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
- { git submodule status --recursive 2>err; echo $?>status; } |
+ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5fd5494ef1..879ee1ee59 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1248,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
- "$@" 2>&7
- exit_code=$?
+ exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..de7d9e7b92 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@@ -454,8 +454,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ (
+ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+ echo "$ret" >"$TEST_RESULTS_BASE.exit"
+ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (4 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 879ee1ee59..502bb0ddcb 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (3 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..d77a179bdd 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (2 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..0e0d07a1a1 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 10/12] t6002: fix use of `expr` with `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 11/12] t9902: fix use of `read` with `set -e`
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. There is a gotcha though: when the delimiter
isn't found at all, then the read builtin will return an error. This
hasn't been an issue until now as we didn't run with `set -e`, but
that'll change in a subsequent commit.
Prepare for this change by silencing the error.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..e3a7df7691 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,7 +590,7 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
+read -r -d "" refs <<-\EOF || :
main
maint
next
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v2 12/12] t: detect errors outside of test cases
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-15 13:06 ` Patrick Steinhardt
2026-04-16 6:00 ` Jeff King
11 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-15 13:06 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib.sh | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index de7d9e7b92..1f7868c537 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case. Note that we only
+# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
+# across shells. The list of allowed shells may be extended going forward.
+if test "${BASH_VERSINFO:=0}" -ge 5
+then
+ set -e
+fi
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-14 23:08 ` Jeff King
2026-04-15 6:48 ` Patrick Steinhardt
@ 2026-04-15 15:31 ` Junio C Hamano
1 sibling, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-15 15:31 UTC (permalink / raw)
To: Jeff King; +Cc: Patrick Steinhardt, git
Jeff King <peff@peff.net> writes:
> I guess one other option is to avoid turning on "set -e" at all for
> known-buggy shells. We are not relying on it working everywhere, but
> rather hoping that if at least one platform uses it, it will find
> programming errors in the test script.
>
> Personally, I am still skeptical that all of this is worth it versus
> just checking stderr.
Not having to "check" stderr is powerful, when you generally just
run your tests with the output sent to your terminal.
I agree that it is a good workaround to use 'set -e' only where we
know it works.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-15 6:58 ` Patrick Steinhardt
@ 2026-04-16 5:40 ` Jeff King
0 siblings, 0 replies; 133+ messages in thread
From: Jeff King @ 2026-04-16 5:40 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, git
On Wed, Apr 15, 2026 at 08:58:40AM +0200, Patrick Steinhardt wrote:
> > "set" is described in
> >
> > https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_26
> >
> > and the description for "-e" does not even talk about any function.
> > The application usage notes associated with the "set" gives this
> >
> > Application writers should avoid relying on set -e within
> > functions. For example, in the following script:
> >
> > set -e
> > start() {
> > some_server
> > echo some_server started successfully
> > }
> > start || echo >&2 some_server failed
> >
> > the -e setting is ignored within the function body (because the
> > function is a command in an AND-OR list other than the
> > last). Therefore, if some_server fails, the function carries on to
> > echo "some_server started successfully", and the exit status of the
> > function is zero (which means "some_server failed" is not output).
> >
> > which greatly helps.
>
> The funny thing is that I discovered shells where this is not the case,
> and `set -e` _does_ extend into functions.
It should extend into functions for all shells. It is just that the
suppression via "if" (or "&&", "||", etc) is counter-intuitive because
it applies to individual commands within the function, not just the
return value of the function as a whole.
So if you have seen shells where "set -e" is respected within a function
even when that function is part of a conditional, that would be
noteworthy. But mostly only for our own curiosity at this point, I
think, as we'd be restricting the set of shells we'd even try to use
"set -e" with.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-15 6:48 ` Patrick Steinhardt
@ 2026-04-16 5:49 ` Jeff King
2026-04-16 8:03 ` Patrick Steinhardt
2026-04-16 14:34 ` Junio C Hamano
0 siblings, 2 replies; 133+ messages in thread
From: Jeff King @ 2026-04-16 5:49 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, git
On Wed, Apr 15, 2026 at 08:48:15AM +0200, Patrick Steinhardt wrote:
> > Personally, I am still skeptical that all of this is worth it versus
> > just checking stderr.
>
> Yeah, the more I dive into this topic the more sceptical I get, as well,
> as shells behave wildly different around `set -e`. So I'm starting to
> feel somewhat uncomfortable with the idea of blanket-enabling it for all
> shells, as that will for sure lead to lots of fallout on platforms that
> we're not testing.
>
> Maybe we should really only do this for an allow-listed set of shells.
> Starting with Bash 5 and newer might be good enough, and given that we
> use Bash for some of our CI jobs we can assume that this would weed out
> failures anyway.
Yeah, an allow-list is probably much better than trying to come up with
a list of buggy shells. But that only helps with portability.
I'm still concerned that this approach is going to create extra friction
for test writers down the road. This series needed to clean up several
spots to avoid false positives, and some of the spots were non-trivial.
Now that was the accumulated cruft of 20 years of test-writing, so it's
not clear to me how often new test-writers will run into this. But when
they do, I worry that it may be hard to even figure out what is going
on.
But I've said as much in earlier rounds, and I'm not sure Junio agrees.
So we can note my dissent in the captain's log, and I can reserve the
right to told-you-so later if need be. ;)
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v2 12/12] t: detect errors outside of test cases
2026-04-15 13:06 ` [PATCH v2 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-16 6:00 ` Jeff King
2026-04-16 10:46 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-16 6:00 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano
On Wed, Apr 15, 2026 at 03:06:45PM +0200, Patrick Steinhardt wrote:
> Improve the status quo by enabling the errexit option so that any such
> unchecked failures will cause us to abort immediately.
>
> Note that for now, we only enable this option for Bash 5 and newer. This
> is because other shells have wildly different behaviour, and older
> versions of Bash (especially on macOS) are buggy. The list of enabled
> shells may be extended going forward.
OK, we know that this does not cause false positives because all of the
tests should pass. It would be nice if we could verify that it catches
bugs, too. Doing this:
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index e4d32bb4d2..5521f21e64 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -980,4 +980,6 @@ test_expect_success 're-init reads matching includeIf.onbranch' '
test_cmp expect err
'
+test_expect_foobar 'baz'
+
test_done
will fail for me, but only if I specially ask to use bash, either
manually or by setting TEST_SHELL_PATH (since /bin/sh is dash on
Debian). Is there something in both GitHub and GitLab CI that will
reliably use an acceptable version of bash?
I guess perhaps Windows, though I don't know what version is used there.
But should we maybe set TEST_SHELL_PATH in at least one of the linux
builds?
-Peff
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-16 5:49 ` Jeff King
@ 2026-04-16 8:03 ` Patrick Steinhardt
2026-04-16 14:34 ` Junio C Hamano
1 sibling, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 8:03 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
On Thu, Apr 16, 2026 at 01:49:24AM -0400, Jeff King wrote:
> On Wed, Apr 15, 2026 at 08:48:15AM +0200, Patrick Steinhardt wrote:
>
> > > Personally, I am still skeptical that all of this is worth it versus
> > > just checking stderr.
> >
> > Yeah, the more I dive into this topic the more sceptical I get, as well,
> > as shells behave wildly different around `set -e`. So I'm starting to
> > feel somewhat uncomfortable with the idea of blanket-enabling it for all
> > shells, as that will for sure lead to lots of fallout on platforms that
> > we're not testing.
> >
> > Maybe we should really only do this for an allow-listed set of shells.
> > Starting with Bash 5 and newer might be good enough, and given that we
> > use Bash for some of our CI jobs we can assume that this would weed out
> > failures anyway.
>
> Yeah, an allow-list is probably much better than trying to come up with
> a list of buggy shells. But that only helps with portability.
>
> I'm still concerned that this approach is going to create extra friction
> for test writers down the road. This series needed to clean up several
> spots to avoid false positives, and some of the spots were non-trivial.
>
> Now that was the accumulated cruft of 20 years of test-writing, so it's
> not clear to me how often new test-writers will run into this. But when
> they do, I worry that it may be hard to even figure out what is going
> on.
>
> But I've said as much in earlier rounds, and I'm not sure Junio agrees.
> So we can note my dissent in the captain's log, and I can reserve the
> right to told-you-so later if need be. ;)
I don't necessarily disagree with your take, I do think this has the
potential to cause some pain. I guess the question is _how_ painful it
will get, and whether that additional pain is worth it to help us avoid
silent breakage like we recently had.
So I'd take a "merge and see" approach here, and if we ever notice that
it's too annoying we simply revert the last commit that introduced `set
-e`. It doesn't have to be a one-way decision.
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v2 12/12] t: detect errors outside of test cases
2026-04-16 6:00 ` Jeff King
@ 2026-04-16 10:46 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 10:46 UTC (permalink / raw)
To: Jeff King; +Cc: git, Junio C Hamano
On Thu, Apr 16, 2026 at 02:00:59AM -0400, Jeff King wrote:
> On Wed, Apr 15, 2026 at 03:06:45PM +0200, Patrick Steinhardt wrote:
>
> > Improve the status quo by enabling the errexit option so that any such
> > unchecked failures will cause us to abort immediately.
> >
> > Note that for now, we only enable this option for Bash 5 and newer. This
> > is because other shells have wildly different behaviour, and older
> > versions of Bash (especially on macOS) are buggy. The list of enabled
> > shells may be extended going forward.
>
> OK, we know that this does not cause false positives because all of the
> tests should pass. It would be nice if we could verify that it catches
> bugs, too. Doing this:
>
> diff --git a/t/t0001-init.sh b/t/t0001-init.sh
> index e4d32bb4d2..5521f21e64 100755
> --- a/t/t0001-init.sh
> +++ b/t/t0001-init.sh
> @@ -980,4 +980,6 @@ test_expect_success 're-init reads matching includeIf.onbranch' '
> test_cmp expect err
> '
>
> +test_expect_foobar 'baz'
> +
> test_done
>
> will fail for me, but only if I specially ask to use bash, either
> manually or by setting TEST_SHELL_PATH (since /bin/sh is dash on
> Debian). Is there something in both GitHub and GitLab CI that will
> reliably use an acceptable version of bash?
>
> I guess perhaps Windows, though I don't know what version is used there.
> But should we maybe set TEST_SHELL_PATH in at least one of the linux
> builds?
Our Fedora-based builds use Bash 5.3.0, so we at least have some test
coverage [1]. But I agree that it would make sense to maybe also make
one of our Ubuntu-based builds use Bash explicitly instead of Dash.
Patrick
[1]: https://gitlab.com/gitlab-org/git/-/jobs/13947942805
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v3 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (13 preceding siblings ...)
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (11 more replies)
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (2 subsequent siblings)
17 siblings, 12 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
that these changes work on both platforms.
Changes in v3:
- Adapt `linux-TEST-vars` job to use Bash instead of Dash. Ubuntu
packet mirrors seem to be having problems, so I wasn't able to get
past installing dependencies in any jobs. All to say that I couldn't
verify that this works as expected :/
- Link to v2: https://patch.msgid.link/20260415-b4-pks-tests-with-set-e-v2-0-4e4904a96f15@pks.im
Changes in v2:
- Use `ret=0; $command || ret=$?` pattern.
- Restore `echo 0` in SIGPIPE tests.
- Fix "lib-git-svn.sh" to gracefully handle the case where SVN Perl
modules aren't installed.
- Use `|| :` consistently instead of `|| true`.
- Fix up a couple of tests that fail on FreeBSD 15. The test suite is
now passing on this system, too.
- Only enable `set -e` on Bash 5 and newer.
- Link to v1: https://patch.msgid.link/20260413-b4-pks-tests-with-set-e-v1-0-5b83763a0e84@pks.im
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
[2]: https://gitlab.com/gitlab-org/git/-/merge_requests/541
[3]: https://github.com/git/git/pull/2270
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
ci/run-build-and-tests.sh | 5 +++++
t/lib-git-daemon.sh | 8 +++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t5000-tar-tree.sh | 4 ++--
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7422-submodule-output.sh | 2 +-
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 2 +-
t/test-lib-functions.sh | 12 ++++++------
t/test-lib.sh | 19 +++++++++++++++----
23 files changed, 90 insertions(+), 66 deletions(-)
Range-diff versus v2:
1: 5685131f51 = 1: 97c51d03f7 t: prepare `test_match_signal ()` calls for `set -e`
2: 7459f569ee = 2: e1e2f83d1d t: prepare `test_must_fail ()` for `set -e`
3: 4077ee4b7f = 3: 441444895e t: prepare `stop_git_daemon ()` for `set -e`
4: 095c9b5b38 = 4: 5084b4627a t: prepare `git config --unset` calls for `set -e`
5: 0cdf15d405 = 5: 4608aef6ee t: prepare conditional test execution for `set -e`
6: 12f85b2c18 = 6: 9f10b21410 t: prepare execution of potentially failing commands for `set -e`
7: 50dc57081d = 7: c9584e9b56 t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
8: e223383e82 = 8: 778a555bb2 t0008: silence error in subshell when using `grep -v`
9: ad084a9d37 = 9: ba4b513722 t1301: don't fail in case setfacl(1) doesn't exist or fails
10: 39a01a5e06 = 10: 5f5db73fdb t6002: fix use of `expr` with `set -e`
11: 060d094d3f = 11: 03d0e9c089 t9902: fix use of `read` with `set -e`
12: 73a21590b1 ! 12: bd300ab2f8 t: detect errors outside of test cases
@@ Commit message
Signed-off-by: Patrick Steinhardt <ps@pks.im>
+ ## ci/run-build-and-tests.sh ##
+@@ ci/run-build-and-tests.sh: fedora-breaking-changes-musl|linux-breaking-changes)
+ MESONFLAGS="$MESONFLAGS -Drust=enabled"
+ ;;
+ linux-TEST-vars)
++ # Ubuntu uses Dash by default, but we only enable use of `set -e`
++ # when using Bash 5+. Ensure that we have at least one CI job that uses
++ # it.
++ export TEST_SHELL_PATH=/usr/bin/bash
++
+ export OPENSSL_SHA1_UNSAFE=YesPlease
+ export GIT_TEST_SPLIT_INDEX=yes
+ export GIT_TEST_FULL_IN_PACK_ARRAY=true
+
## t/test-lib.sh ##
@@
# You should have received a copy of the GNU General Public License
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v3 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (10 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (9 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..5fd5494ef1 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+
+ exit_code=0; "$@" 2>&7 || exit_code=$?
+
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (8 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..d172aa51f0 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (7 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 05/12] t: prepare conditional test execution for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (6 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (5 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t3901-i18n-patch.sh | 3 ++-
t/t5000-tar-tree.sh | 4 ++--
t/t7422-submodule-output.sh | 2 +-
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib-functions.sh | 3 +--
t/test-lib.sh | 10 ++++++----
11 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..52843f667d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b49a..ef7d7e1edc 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ ret=0; grep "^encoding ISO8859-1" || ret=$?
+ test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054f17..a8c28533dc 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
- git archive HEAD
- echo $? >exit-code
+ { ret=0 && git archive HEAD || ret=$?; } &&
+ echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf117..852136fdfd 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
- { git submodule status --recursive 2>err; echo $?>status; } |
+ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5fd5494ef1..879ee1ee59 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1248,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
- "$@" 2>&7
- exit_code=$?
+ exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..de7d9e7b92 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@@ -454,8 +454,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ (
+ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+ echo "$ret" >"$TEST_RESULTS_BASE.exit"
+ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (4 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 879ee1ee59..502bb0ddcb 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (3 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..d77a179bdd 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (2 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..0e0d07a1a1 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 10/12] t6002: fix use of `expr` with `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 11/12] t9902: fix use of `read` with `set -e`
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 20:12 ` SZEDER Gábor
2026-04-16 11:19 ` [PATCH v3 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. There is a gotcha though: when the delimiter
isn't found at all, then the read builtin will return an error. This
hasn't been an issue until now as we didn't run with `set -e`, but
that'll change in a subsequent commit.
Prepare for this change by silencing the error.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..e3a7df7691 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,7 +590,7 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
+read -r -d "" refs <<-\EOF || :
main
maint
next
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v3 12/12] t: detect errors outside of test cases
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-16 11:19 ` Patrick Steinhardt
2026-04-16 16:06 ` Junio C Hamano
11 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-16 11:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ci/run-build-and-tests.sh | 5 +++++
t/test-lib.sh | 9 +++++++++
2 files changed, 14 insertions(+)
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 28cfe730ee..f0a3597184 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -15,6 +15,11 @@ fedora-breaking-changes-musl|linux-breaking-changes)
MESONFLAGS="$MESONFLAGS -Drust=enabled"
;;
linux-TEST-vars)
+ # Ubuntu uses Dash by default, but we only enable use of `set -e`
+ # when using Bash 5+. Ensure that we have at least one CI job that uses
+ # it.
+ export TEST_SHELL_PATH=/usr/bin/bash
+
export OPENSSL_SHA1_UNSAFE=YesPlease
export GIT_TEST_SPLIT_INDEX=yes
export GIT_TEST_FULL_IN_PACK_ARRAY=true
diff --git a/t/test-lib.sh b/t/test-lib.sh
index de7d9e7b92..1f7868c537 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case. Note that we only
+# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
+# across shells. The list of allowed shells may be extended going forward.
+if test "${BASH_VERSINFO:=0}" -ge 5
+then
+ set -e
+fi
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-16 5:49 ` Jeff King
2026-04-16 8:03 ` Patrick Steinhardt
@ 2026-04-16 14:34 ` Junio C Hamano
2026-04-18 8:01 ` Jeff King
1 sibling, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-16 14:34 UTC (permalink / raw)
To: Jeff King; +Cc: Patrick Steinhardt, git
Jeff King <peff@peff.net> writes:
> I'm still concerned that this approach is going to create extra friction
> for test writers down the road. This series needed to clean up several
> spots to avoid false positives, and some of the spots were non-trivial.
>
> Now that was the accumulated cruft of 20 years of test-writing, so it's
> not clear to me how often new test-writers will run into this. But when
> they do, I worry that it may be hard to even figure out what is going
> on.
>
> But I've said as much in earlier rounds, and I'm not sure Junio agrees.
> So we can note my dissent in the captain's log, and I can reserve the
> right to told-you-so later if need be. ;)
The alternatigve to allow us to be sloppy is alluring from the point
of view of a test writer in me. But do we have an easy/canned way
to run tests and see the unexpected failures outside test_expect_foo
while ignoring all the noises from passing tests? Perhaps running
tests (with and without prove) while redirecting the standard output
stream to /dev/null or something?
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v3 12/12] t: detect errors outside of test cases
2026-04-16 11:19 ` [PATCH v3 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-16 16:06 ` Junio C Hamano
0 siblings, 0 replies; 133+ messages in thread
From: Junio C Hamano @ 2026-04-16 16:06 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Jeff King
Patrick Steinhardt <ps@pks.im> writes:
> linux-TEST-vars)
> + # Ubuntu uses Dash by default, but we only enable use of `set -e`
> + # when using Bash 5+. Ensure that we have at least one CI job that uses
> + # it.
> + export TEST_SHELL_PATH=/usr/bin/bash
> +
OK.
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index de7d9e7b92..1f7868c537 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -15,6 +15,15 @@
> # You should have received a copy of the GNU General Public License
> # along with this program. If not, see https://www.gnu.org/licenses/ .
>
> +# Enable the use of errexit so that any unexpected failures will cause us to
> +# abort tests, even when outside of a specific test case. Note that we only
> +# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
> +# across shells. The list of allowed shells may be extended going forward.
> +if test "${BASH_VERSINFO:=0}" -ge 5
> +then
> + set -e
> +fi
A possible alternative would be to actually test "$TEST_SHELL_PATH"
with the features we want to be working, e.g.,
out=$("$TEST_SHELL_PATH" -e -c '
# basics - "set -e" suppressed when "tested"
false && echo 0
# <20260414225206.GA3486072@coredump.intra.peff.net>
# ancient bash dies after "command false"
command false && echo 1
# <20260416054038.GA646814@coredump.intra.peff.net>
# failure inside a function excempt from "set -e"
f () { false ; echo 2; }
f || echo 3
')
if test "$out" = 2
then
set -e
fi
which adds some documentation value, but I think "this one is
known-good, and we know we use it somewhere" is probably good
enough.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v3 11/12] t9902: fix use of `read` with `set -e`
2026-04-16 11:19 ` [PATCH v3 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-16 20:12 ` SZEDER Gábor
2026-04-16 20:42 ` Junio C Hamano
0 siblings, 1 reply; 133+ messages in thread
From: SZEDER Gábor @ 2026-04-16 20:12 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano, Jeff King
On Thu, Apr 16, 2026 at 01:19:28PM +0200, Patrick Steinhardt wrote:
> In t9902 we're using the `read` builtin to read some values into a
> variable. This is done by using `-d ""`, which cause us to read until
> the end of the heredoc. There is a gotcha though: when the delimiter
> isn't found at all, then the read builtin will return an error.
The absence of the delimiter doesn't make "read" return an error, EOF
does (that's why a "while read ..." loop works).
> This
> hasn't been an issue until now as we didn't run with `set -e`, but
> that'll change in a subsequent commit.
>
> Prepare for this change by silencing the error.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> t/t9902-completion.sh | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
> index 2f9a597ec7..e3a7df7691 100755
> --- a/t/t9902-completion.sh
> +++ b/t/t9902-completion.sh
> @@ -590,7 +590,7 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
> __gitcomp "$invalid_variable_name"
> '
>
> -read -r -d "" refs <<-\EOF
> +read -r -d "" refs <<-\EOF || :
> main
> maint
> next
So AFAICT what this "read" does is equivalent to:
refs='main
maint
next
seen'
Isn't this much easier to read!? OK, the first ref is not aligned
with the rest... But I admit I had to look up the docs to see what
the empty string as delimiter actually does, and even after that I had
to add a printf '>%s<\n' "$refs" command to the test script to see
what's exactly going on.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v3 11/12] t9902: fix use of `read` with `set -e`
2026-04-16 20:12 ` SZEDER Gábor
@ 2026-04-16 20:42 ` Junio C Hamano
2026-04-17 9:44 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-16 20:42 UTC (permalink / raw)
To: SZEDER Gábor; +Cc: Patrick Steinhardt, git, Jeff King
SZEDER Gábor <szeder.dev@gmail.com> writes:
> On Thu, Apr 16, 2026 at 01:19:28PM +0200, Patrick Steinhardt wrote:
>> In t9902 we're using the `read` builtin to read some values into a
>> variable. This is done by using `-d ""`, which cause us to read until
>> the end of the heredoc. There is a gotcha though: when the delimiter
>> isn't found at all, then the read builtin will return an error.
>
> The absence of the delimiter doesn't make "read" return an error, EOF
> does (that's why a "while read ..." loop works).
Very true. So for this kind of "read", failing is the norm.
> So AFAICT what this "read" does is equivalent to:
>
> refs='main
> maint
> next
> seen'
>
> Isn't this much easier to read!? OK, the first ref is not aligned
> with the rest... But I admit I had to look up the docs to see what
> the empty string as delimiter actually does, and even after that I had
> to add a printf '>%s<\n' "$refs" command to the test script to see
> what's exactly going on.
Yes, whether "set -e" is used or not, not using "read" there makes a
lot more sense.
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v3 11/12] t9902: fix use of `read` with `set -e`
2026-04-16 20:42 ` Junio C Hamano
@ 2026-04-17 9:44 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 9:44 UTC (permalink / raw)
To: Junio C Hamano; +Cc: SZEDER Gábor, git, Jeff King
On Thu, Apr 16, 2026 at 01:42:26PM -0700, Junio C Hamano wrote:
> SZEDER Gábor <szeder.dev@gmail.com> writes:
>
> > On Thu, Apr 16, 2026 at 01:19:28PM +0200, Patrick Steinhardt wrote:
> >> In t9902 we're using the `read` builtin to read some values into a
> >> variable. This is done by using `-d ""`, which cause us to read until
> >> the end of the heredoc. There is a gotcha though: when the delimiter
> >> isn't found at all, then the read builtin will return an error.
> >
> > The absence of the delimiter doesn't make "read" return an error, EOF
> > does (that's why a "while read ..." loop works).
>
> Very true. So for this kind of "read", failing is the norm.
>
> > So AFAICT what this "read" does is equivalent to:
> >
> > refs='main
> > maint
> > next
> > seen'
> >
> > Isn't this much easier to read!? OK, the first ref is not aligned
> > with the rest... But I admit I had to look up the docs to see what
> > the empty string as delimiter actually does, and even after that I had
> > to add a printf '>%s<\n' "$refs" command to the test script to see
> > what's exactly going on.
>
> Yes, whether "set -e" is used or not, not using "read" there makes a
> lot more sense.
Hm, true indeed, that variant is a lot more straight-forward. Will
change, thanks!
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v4 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (14 preceding siblings ...)
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (11 more replies)
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
17 siblings, 12 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
that these changes work on both platforms.
Changes in v4:
- Simplify how we read a multi-line variable value.
- Link to v3: https://patch.msgid.link/20260416-b4-pks-tests-with-set-e-v3-0-7a90e5dccadd@pks.im
Changes in v3:
- Adapt `linux-TEST-vars` job to use Bash instead of Dash. Ubuntu
packet mirrors seem to be having problems, so I wasn't able to get
past installing dependencies in any jobs. All to say that I couldn't
verify that this works as expected :/
- Link to v2: https://patch.msgid.link/20260415-b4-pks-tests-with-set-e-v2-0-4e4904a96f15@pks.im
Changes in v2:
- Use `ret=0; $command || ret=$?` pattern.
- Restore `echo 0` in SIGPIPE tests.
- Fix "lib-git-svn.sh" to gracefully handle the case where SVN Perl
modules aren't installed.
- Use `|| :` consistently instead of `|| true`.
- Fix up a couple of tests that fail on FreeBSD 15. The test suite is
now passing on this system, too.
- Only enable `set -e` on Bash 5 and newer.
- Link to v1: https://patch.msgid.link/20260413-b4-pks-tests-with-set-e-v1-0-5b83763a0e84@pks.im
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
[2]: https://gitlab.com/gitlab-org/git/-/merge_requests/541
[3]: https://github.com/git/git/pull/2270
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
ci/run-build-and-tests.sh | 5 +++++
t/lib-git-daemon.sh | 8 +++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t5000-tar-tree.sh | 4 ++--
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7422-submodule-output.sh | 2 +-
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 6 ++----
t/test-lib-functions.sh | 12 ++++++------
t/test-lib.sh | 19 +++++++++++++++----
23 files changed, 91 insertions(+), 69 deletions(-)
Range-diff versus v3:
1: 276cd1c541 = 1: 7e57f3ba57 t: prepare `test_match_signal ()` calls for `set -e`
2: 3cbcf0298c = 2: 3b8f710de8 t: prepare `test_must_fail ()` for `set -e`
3: e97211a468 = 3: 9cf3f458b3 t: prepare `stop_git_daemon ()` for `set -e`
4: c974d59252 = 4: 8763cedd60 t: prepare `git config --unset` calls for `set -e`
5: e41064dd1b = 5: 8dc43cca62 t: prepare conditional test execution for `set -e`
6: 890c11aa7a = 6: ca0c250d39 t: prepare execution of potentially failing commands for `set -e`
7: a7b2bb9cd5 = 7: 4631ebe1d9 t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
8: 17656428f9 = 8: 64df2f3975 t0008: silence error in subshell when using `grep -v`
9: 7a6e730ba3 = 9: f79e55dd96 t1301: don't fail in case setfacl(1) doesn't exist or fails
10: b762f10ac9 = 10: fcf5ed7ced t6002: fix use of `expr` with `set -e`
11: bb588ffe22 < -: ---------- t9902: fix use of `read` with `set -e`
-: ---------- > 11: 39a5e2ffcb t9902: fix use of `read` with `set -e`
12: 9ffcb73e64 = 12: 7dfee331e9 t: detect errors outside of test cases
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v4 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (10 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (9 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..5fd5494ef1 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+
+ exit_code=0; "$@" 2>&7 || exit_code=$?
+
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (8 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..d172aa51f0 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (7 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 05/12] t: prepare conditional test execution for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (6 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (5 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t3901-i18n-patch.sh | 3 ++-
t/t5000-tar-tree.sh | 4 ++--
t/t7422-submodule-output.sh | 2 +-
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib-functions.sh | 3 +--
t/test-lib.sh | 10 ++++++----
11 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..52843f667d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b49a..ef7d7e1edc 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ ret=0; grep "^encoding ISO8859-1" || ret=$?
+ test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054f17..a8c28533dc 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
- git archive HEAD
- echo $? >exit-code
+ { ret=0 && git archive HEAD || ret=$?; } &&
+ echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf117..852136fdfd 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
- { git submodule status --recursive 2>err; echo $?>status; } |
+ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5fd5494ef1..879ee1ee59 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1248,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
- "$@" 2>&7
- exit_code=$?
+ exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..de7d9e7b92 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@@ -454,8 +454,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ (
+ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+ echo "$ret" >"$TEST_RESULTS_BASE.exit"
+ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (4 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 879ee1ee59..502bb0ddcb 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (3 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..d77a179bdd 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (2 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..0e0d07a1a1 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 10/12] t6002: fix use of `expr` with `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 11/12] t9902: fix use of `read` with `set -e`
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. As the read is terminated by EOF, the command
will end up returning a non-zero error code. This hasn't been an issue
until now as we didn't run with `set -e`, but that'll change in a
subsequent commit.
Prepare for this change by not using read at all, as we can simply store
the multi-line value directly.
Suggested-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..28f61f08fb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,12 +590,10 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
-main
+refs='main
maint
next
-seen
-EOF
+seen'
test_expect_success '__gitcomp_nl - trailing space' '
test_gitcomp_nl "m" "$refs" <<-EOF
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-17 10:50 ` Patrick Steinhardt
2026-04-18 6:50 ` Jeff King
11 siblings, 1 reply; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-17 10:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ci/run-build-and-tests.sh | 5 +++++
t/test-lib.sh | 9 +++++++++
2 files changed, 14 insertions(+)
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 28cfe730ee..f0a3597184 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -15,6 +15,11 @@ fedora-breaking-changes-musl|linux-breaking-changes)
MESONFLAGS="$MESONFLAGS -Drust=enabled"
;;
linux-TEST-vars)
+ # Ubuntu uses Dash by default, but we only enable use of `set -e`
+ # when using Bash 5+. Ensure that we have at least one CI job that uses
+ # it.
+ export TEST_SHELL_PATH=/usr/bin/bash
+
export OPENSSL_SHA1_UNSAFE=YesPlease
export GIT_TEST_SPLIT_INDEX=yes
export GIT_TEST_FULL_IN_PACK_ARRAY=true
diff --git a/t/test-lib.sh b/t/test-lib.sh
index de7d9e7b92..1f7868c537 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case. Note that we only
+# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
+# across shells. The list of allowed shells may be extended going forward.
+if test "${BASH_VERSINFO:=0}" -ge 5
+then
+ set -e
+fi
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-17 10:50 ` [PATCH v4 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-18 6:50 ` Jeff King
2026-04-18 12:17 ` Ben Knoble
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-18 6:50 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Junio C Hamano, SZEDER Gábor
On Fri, Apr 17, 2026 at 12:50:58PM +0200, Patrick Steinhardt wrote:
> --- a/ci/run-build-and-tests.sh
> +++ b/ci/run-build-and-tests.sh
> @@ -15,6 +15,11 @@ fedora-breaking-changes-musl|linux-breaking-changes)
> MESONFLAGS="$MESONFLAGS -Drust=enabled"
> ;;
> linux-TEST-vars)
> + # Ubuntu uses Dash by default, but we only enable use of `set -e`
> + # when using Bash 5+. Ensure that we have at least one CI job that uses
> + # it.
> + export TEST_SHELL_PATH=/usr/bin/bash
Thinking on this a little more, it is a shame we cannot easily enable
this for dash. That would hit most CI jobs, but also the local builds of
most developers. And finding problems early and locally often saves a
lot of time versus finding them in CI.
Unfortunately I could not find a way to detect whether we are running
dash at all, let alone a recent version. But what if we let the user
tell us? Something like:
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1f7868c537..a0d07f75fb 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -17,9 +17,10 @@
# Enable the use of errexit so that any unexpected failures will cause us to
# abort tests, even when outside of a specific test case. Note that we only
-# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
-# across shells. The list of allowed shells may be extended going forward.
-if test "${BASH_VERSINFO:=0}" -ge 5
+# enable this by default on Bash 5 and newer, as `set -e` has wildly different
+# behaviour across shells. If you trust your shell's `set -e` implementation,
+# you can set GIT_TEST_USE_SET_E manually.
+if test "$GIT_TEST_USE_SET_E" = 1 && test "${BASH_VERSINFO:=0}" -ge 5
then
set -e
fi
And then those of us who want to stick:
export GIT_TEST_USE_SET_E = 1
in our config.mak can do so, and we could even set it in the ci/ scripts
for all of the ubuntu builds.
-Peff
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-16 14:34 ` Junio C Hamano
@ 2026-04-18 8:01 ` Jeff King
0 siblings, 0 replies; 133+ messages in thread
From: Jeff King @ 2026-04-18 8:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Patrick Steinhardt, git
On Thu, Apr 16, 2026 at 07:34:26AM -0700, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> > I'm still concerned that this approach is going to create extra friction
> > for test writers down the road. This series needed to clean up several
> > spots to avoid false positives, and some of the spots were non-trivial.
> >
> > Now that was the accumulated cruft of 20 years of test-writing, so it's
> > not clear to me how often new test-writers will run into this. But when
> > they do, I worry that it may be hard to even figure out what is going
> > on.
> >
> > But I've said as much in earlier rounds, and I'm not sure Junio agrees.
> > So we can note my dissent in the captain's log, and I can reserve the
> > right to told-you-so later if need be. ;)
>
> The alternatigve to allow us to be sloppy is alluring from the point
> of view of a test writer in me. But do we have an easy/canned way
> to run tests and see the unexpected failures outside test_expect_foo
> while ignoring all the noises from passing tests? Perhaps running
> tests (with and without prove) while redirecting the standard output
> stream to /dev/null or something?
I took a stab at this. It's easy to do it hackily, but it proved a
little trickier than I'd hoped to get something elegant.
My main goals were that you would still get good output to either the
terminal or the test-results/*.out files (so we can't just redirect
stderr for all runs), and that we wouldn't spend too much extra CPU (so
just running the tests an extra time with stderr redirected is right
out).
My first attempt was to teach test-lib.sh a mode where all of the test
snippets are noops, stderr goes to a file in test-results/, and at the
end of the script we complain if our stderr file is non-empty. And then
we have a "test-lint-stderr" make target that runs each script in the
special mode. That works, but:
1. Quite a few scripts do stuff inside their test snippets that affect
the environment, and then do stuff outside of a test snippet that
depends on that. Some of it is questionable, like running git
commands outside of test snippets to generate expected output. But
there's reasonable stuff like t0012 generating the list of builtin
inside a test snippet and expecting:
while read builtin
do
test_expect_success "$builtin..." ...
done <builtins
So I think the test suite is a little too free-form for this kind
of trickery.
2. It's way too slow, anyway. Even with all of the snippets as noops,
it still takes ~13s to run all of the scripts in parallel on my
machine. The real test suite only takes ~90 seconds! There's
probably low-hanging fruit to fix there, but it's still an order of
magnitude off what I'd hope the cost would be.
So I think that's probably a dead end.
The next obvious choice is redirecting stderr via tee or similar, which
is easy-ish. We already do it for --verbose-log, but we combine it with
stdout there (and we turn on verbose mode for the tests, which is going
to generate a bunch of extra output).
I think there's probably a way to do this with an extra tee for stderr.
But actually...do we even need tee? The idea is that we are not
expecting any output here, so if there is any, we'd bail and show it
along with an error.
So I think we might able able to get by with just redirecting descriptor
2, and then making sure that test snippets stderr goes to the original
stderr (which we already save as descriptor 7). And we catch only errors
outside of the snippet.
Something like this seems to work:
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1f7868c537..6657d56e52 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -728,7 +728,7 @@ then
exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
elif test "$verbose" = "t"
then
- exec 4>&2 3>&2
+ exec 4>&7 3>&7
else
exec 4>/dev/null 3>/dev/null
fi
@@ -970,7 +970,7 @@ maybe_setup_verbose () {
test -z "$verbose_only" && return
if match_pattern_list $test_count "$verbose_only"
then
- exec 4>&2 3>&2
+ exec 4>&7 3>&7
# Emit a delimiting blank line when going from
# non-verbose to verbose. Within verbose mode the
# delimiter is printed by test_expect_*. The choice
@@ -1052,7 +1052,7 @@ test_eval_ () {
test 1 = $trace_level_ && set +x
trace_level_=$(($trace_level_-1))
fi
- } 2>/dev/null 4>&2
+ } 2>/dev/null 4>&7
if test "$test_eval_ret_" != 0 && want_trace
then
@@ -1234,6 +1234,14 @@ test_done () {
EOF
fi
+ if test -s "$TEST_RESULTS_BASE.stderr"
+ then
+ say_color >&5 error "FATAL: Unexpected output on stderr"
+ sed >&5 's/^/# /' "$TEST_RESULTS_BASE.stderr"
+ GIT_EXIT_OK=1
+ exit 1
+ fi
+
if test "$test_fixed" != 0
then
say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
@@ -1982,3 +1990,9 @@ test_lazy_prereq FSMONITOR_DAEMON '
git version --build-options >output &&
grep "feature: fsmonitor--daemon" output
'
+
+# Now that test-lib setup is done, direct our stderr to a file, as we want to
+# catch and complain if anything ends up here. We can always access the
+# original via descriptor 7.
+mkdir -p "$TEST_RESULTS_DIR"
+exec 2>"$TEST_RESULTS_BASE.stderr"
If I stick a "test_expect_foobar" into a test script, it is found:
$ ./t0001-init.sh
ok 1 - plain
[...]
ok 102 - re-init reads matching includeIf.onbranch
FATAL: Unexpected output on stderr
# ./t0001-init.sh: 983: test_expect_foobar: not found
And if I run scripts with "-v" or "-x", that output still goes to the
terminal and does not trigger the stderr checker. Likewise with
--verbose-log, the log gets the verbose bits.
It _almost_ passes a full "make test", but there's one lingering
problem: random bits of code which want to output to stderr and bail,
and use "echo >&2" to do so. But with our redirect, that stderr goes to
the log file. t0000-basic notices this, because it is testing the test
suite itself. And it notices that:
./t0000-basic.sh --run=bogus
should complain to stderr, but now doesn't (it's swallowed by our log
file). We can fix that with;
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 6657d56e52..aa6415b308 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -902,13 +902,13 @@ match_test_selector_list () {
if expr "z${selector%%-*}" : "z[0-9]*[^0-9]" >/dev/null
then
echo "error: $operation: invalid non-numeric in range" \
- "start: '$orig_selector'" >&2
+ "start: '$orig_selector'" >&7
exit 1
fi
if expr "z${selector#*-}" : "z[0-9]*[^0-9]" >/dev/null
then
echo "error: $operation: invalid non-numeric in range" \
- "end: '$orig_selector'" >&2
+ "end: '$orig_selector'" >&7
exit 1
fi
;;
but the same problem doubtless exists elsewhere. We could fix that
globally like this:
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 6657d56e52..72d60d9dac 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -762,6 +762,15 @@ die () {
# test script run with '--immediate' fails, or when the user hits
# ctrl-C, i.e. when 'test_done' is not invoked at all.
test_atexit_handler || code=$?
+
+ # Dump the stderr log if we saw anything, since otherwise random
+ # errors will never make it to the user when we do not have
+ # a clean exit.
+ if test -s "$TEST_RESULTS_BASE.stderr"
+ then
+ cat >&7 "$TEST_RESULTS_BASE.stderr"
+ fi
+
if test -n "$GIT_EXIT_OK"
then
exit $code
It's a little weird because the stderr message is delayed until we cat
it out, but it makes sense. If we are bailing immediately, then the time
between "echo" and "cat" is small. And if we are not bailing, then that
is exactly the kind of bug our stderr log is trying to catch (and we
will complain during test_done).
I am not quite sure if this is an elegant solution or a terrible hack.
-Peff
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 6:50 ` Jeff King
@ 2026-04-18 12:17 ` Ben Knoble
2026-04-18 17:44 ` Jeff King
2026-04-18 19:17 ` brian m. carlson
0 siblings, 2 replies; 133+ messages in thread
From: Ben Knoble @ 2026-04-18 12:17 UTC (permalink / raw)
To: Jeff King; +Cc: Patrick Steinhardt, git, Junio C Hamano, Gábor SZEDER
> Le 18 avr. 2026 à 02:50, Jeff King <peff@peff.net> a écrit :
>
> On Fri, Apr 17, 2026 at 12:50:58PM +0200, Patrick Steinhardt wrote:
>
>> --- a/ci/run-build-and-tests.sh
>> +++ b/ci/run-build-and-tests.sh
>> @@ -15,6 +15,11 @@ fedora-breaking-changes-musl|linux-breaking-changes)
>> MESONFLAGS="$MESONFLAGS -Drust=enabled"
>> ;;
>> linux-TEST-vars)
>> + # Ubuntu uses Dash by default, but we only enable use of `set -e`
>> + # when using Bash 5+. Ensure that we have at least one CI job that uses
>> + # it.
>> + export TEST_SHELL_PATH=/usr/bin/bash
>
> Thinking on this a little more, it is a shame we cannot easily enable
> this for dash. That would hit most CI jobs, but also the local builds of
> most developers. And finding problems early and locally often saves a
> lot of time versus finding them in CI.
>
> Unfortunately I could not find a way to detect whether we are running
> dash at all, let alone a recent version. But what if we let the user
> tell us? Something like:
I was just wishing for similar! I imagine it would be useful for folks who occasionally test Zsh’s POSIX mode and want to see how it handles -e
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 1f7868c537..a0d07f75fb 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -17,9 +17,10 @@
>
> # Enable the use of errexit so that any unexpected failures will cause us to
> # abort tests, even when outside of a specific test case. Note that we only
> -# enable this on Bash 5 and newer, as `set -e` has wildly different behaviour
> -# across shells. The list of allowed shells may be extended going forward.
> -if test "${BASH_VERSINFO:=0}" -ge 5
> +# enable this by default on Bash 5 and newer, as `set -e` has wildly different
> +# behaviour across shells. If you trust your shell's `set -e` implementation,
> +# you can set GIT_TEST_USE_SET_E manually.
> +if test "$GIT_TEST_USE_SET_E" = 1 && test "${BASH_VERSINFO:=0}" -ge 5
> then
> set -e
> fi
I guess that should be || instead of &&?
>
> And then those of us who want to stick:
>
> export GIT_TEST_USE_SET_E = 1
>
> in our config.mak can do so, and we could even set it in the ci/ scripts
> for all of the ubuntu builds.
>
> -Peff
Thanks
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 12:17 ` Ben Knoble
@ 2026-04-18 17:44 ` Jeff King
2026-04-18 19:24 ` Junio C Hamano
2026-04-18 19:17 ` brian m. carlson
1 sibling, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-18 17:44 UTC (permalink / raw)
To: Ben Knoble; +Cc: Patrick Steinhardt, git, Junio C Hamano, Gábor SZEDER
On Sat, Apr 18, 2026 at 08:17:10AM -0400, Ben Knoble wrote:
> > +if test "$GIT_TEST_USE_SET_E" = 1 && test "${BASH_VERSINFO:=0}" -ge 5
> > then
> > set -e
> > fi
>
> I guess that should be || instead of &&?
Oops, yeah. I wrote it correctly and tested it once, and then started to
rewrite it to support setting it to 0, like:
if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
then
GIT_TEST_USE_SET_E=1
fi
case "$GIT_TEST_USE_SET_E" in
1|on|true)
set -e
;;
esac
But I didn't want to get too much into details of the patch, so I went
back to the original, but obviously screwed that up. ;)
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 12:17 ` Ben Knoble
2026-04-18 17:44 ` Jeff King
@ 2026-04-18 19:17 ` brian m. carlson
2026-04-18 21:30 ` Jeff King
1 sibling, 1 reply; 133+ messages in thread
From: brian m. carlson @ 2026-04-18 19:17 UTC (permalink / raw)
To: Ben Knoble
Cc: Jeff King, Patrick Steinhardt, git, Junio C Hamano,
Gábor SZEDER
[-- Attachment #1: Type: text/plain, Size: 2158 bytes --]
On 2026-04-18 at 12:17:10, Ben Knoble wrote:
>
> > Le 18 avr. 2026 à 02:50, Jeff King <peff@peff.net> a écrit :
> > Thinking on this a little more, it is a shame we cannot easily enable
> > this for dash. That would hit most CI jobs, but also the local builds of
> > most developers. And finding problems early and locally often saves a
> > lot of time versus finding them in CI.
> >
> > Unfortunately I could not find a way to detect whether we are running
> > dash at all, let alone a recent version. But what if we let the user
> > tell us? Something like:
>
> I was just wishing for similar! I imagine it would be useful for folks
> who occasionally test Zsh’s POSIX mode and want to see how it handles
> -e
I hard-coded this on with a bunch of shells in Debian unstable using the
below script. zsh, busybox, and dash passed, while mksh, lksh, and posh
failed. (The latter are all pdksh variants, I believe, so they are an
important set of shells to consider.)
Note that the script symlinks the shell to `sh` so that everyone will be
on their best POSIX behaviour.
----
#!/bin/sh
dir=$(mktemp -d)
trap 'rm -fr "$dir"' EXIT
sh="$1"
ln -sf "$sh" "$dir/sh"
make -j12 all && (cd t && GIT_PROVE_OPTS=-j12 GIT_TEST_DEFAULT_HASH=sha256 PATH="$dir:$PATH" SHELL_PATH="$dir/sh" make prove)
----
Having said that, I actually think that mksh may be right in at least
one case. For instance, this diff seems required for mksh to pass t1410
and I believe this is actually the right thing to do:
----
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index ce71f9a30a..f289fc11e9 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -23,7 +23,7 @@ check_have () {
}
check_fsck () {
- git fsck --full >fsck.output
+ git fsck --full >fsck.output || true
case "$1" in
'')
test_must_be_empty fsck.output ;;
----
I haven't checked the other cases under mksh, but I think it may be a
fruitful source of things to look at. And if you find a bug, I'm sure
the maintainer would happily accept a bug report in the Debian BTS.
--
brian m. carlson (they/them)
Toronto, Ontario, CA
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 325 bytes --]
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 17:44 ` Jeff King
@ 2026-04-18 19:24 ` Junio C Hamano
2026-04-18 21:05 ` Jeff King
0 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-18 19:24 UTC (permalink / raw)
To: Jeff King; +Cc: Ben Knoble, Patrick Steinhardt, git, Gábor SZEDER
Jeff King <peff@peff.net> writes:
> On Sat, Apr 18, 2026 at 08:17:10AM -0400, Ben Knoble wrote:
>
>> > +if test "$GIT_TEST_USE_SET_E" = 1 && test "${BASH_VERSINFO:=0}" -ge 5
>> > then
>> > set -e
>> > fi
>>
>> I guess that should be || instead of &&?
>
> Oops, yeah. I wrote it correctly and tested it once, and then started to
> rewrite it to support setting it to 0, like:
>
> if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
> then
> GIT_TEST_USE_SET_E=1
> fi
> case "$GIT_TEST_USE_SET_E" in
> 1|on|true)
> set -e
> ;;
> esac
>
> But I didn't want to get too much into details of the patch, so I went
> back to the original, but obviously screwed that up. ;)
We could forget about "we know this is a good shell by its name and
version" and test the feature we depend on ourselves, perhaps?
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 19:24 ` Junio C Hamano
@ 2026-04-18 21:05 ` Jeff King
2026-04-20 6:11 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-18 21:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ben Knoble, Patrick Steinhardt, git, Gábor SZEDER
On Sat, Apr 18, 2026 at 12:24:53PM -0700, Junio C Hamano wrote:
> We could forget about "we know this is a good shell by its name and
> version" and test the feature we depend on ourselves, perhaps?
I looked into that but didn't get anywhere useful. You can try to test
all of the "set -e" scenarios we care about, but there are a lot of
them. For example, I would never have thought to check how "command"
behaves inside a &&-chain while "set -e" is in effect.
So you basically end up adding a test case for the bugs you find, at
which point it is not much better than blocking known-bad versions.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 19:17 ` brian m. carlson
@ 2026-04-18 21:30 ` Jeff King
2026-04-18 21:54 ` brian m. carlson
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-18 21:30 UTC (permalink / raw)
To: brian m. carlson
Cc: Ben Knoble, Patrick Steinhardt, git, Junio C Hamano,
Gábor SZEDER
On Sat, Apr 18, 2026 at 07:17:43PM +0000, brian m. carlson wrote:
> Having said that, I actually think that mksh may be right in at least
> one case. For instance, this diff seems required for mksh to pass t1410
> and I believe this is actually the right thing to do:
I think mksh is wrong here, if it is flagging this fsck call.
> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
> index ce71f9a30a..f289fc11e9 100755
> --- a/t/t1410-reflog.sh
> +++ b/t/t1410-reflog.sh
> @@ -23,7 +23,7 @@ check_have () {
> }
>
> check_fsck () {
> - git fsck --full >fsck.output
> + git fsck --full >fsck.output || true
> case "$1" in
> '')
> test_must_be_empty fsck.output ;;
If check_fsck() were run by itself then yes, this would be a problem.
But it is always run inside a test snippet, and there "set -e" should
always be suppressed because test_expect_success does:
if test_run_ "$test_body"
So we are inside a conditional, and the usual global "set -e"
suppression should happen. It sounds like it is not happening in your
version of mksh, but I was unable to get t1410 to fail at all using mksh
59c-43 (from Debian unstable) or 59c-41 (from stable).
And it is a good thing that this "if" suppression is here, or else tests
who fail the final component of the &&-chain would cause the shell to
exit. The simplest case is just:
test_expect_success 'bad' 'false'
If "set -e" were in effect, then the whole script would bail upon seeing
that "false".
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 21:30 ` Jeff King
@ 2026-04-18 21:54 ` brian m. carlson
2026-04-19 2:10 ` Jeff King
0 siblings, 1 reply; 133+ messages in thread
From: brian m. carlson @ 2026-04-18 21:54 UTC (permalink / raw)
To: Jeff King
Cc: Ben Knoble, Patrick Steinhardt, git, Junio C Hamano,
Gábor SZEDER
[-- Attachment #1: Type: text/plain, Size: 1473 bytes --]
On 2026-04-18 at 21:30:43, Jeff King wrote:
> If check_fsck() were run by itself then yes, this would be a problem.
> But it is always run inside a test snippet, and there "set -e" should
> always be suppressed because test_expect_success does:
>
> if test_run_ "$test_body"
>
> So we are inside a conditional, and the usual global "set -e"
> suppression should happen. It sounds like it is not happening in your
> version of mksh, but I was unable to get t1410 to fail at all using mksh
> 59c-43 (from Debian unstable) or 59c-41 (from stable).
It does fail with 59c-43 under `make prove` or if you do `sh ./t1410*.sh
--verbose`, assuming that `sh` points to `mksh`, but since the script
has a `/bin/sh` shebang, you need to invoke it explicitly with the shell
in question, or it will use the system `sh` (dash). (I made this
mistake when reproducing the problem.)
Note that the test in question does not exit, but returns this (with
`--verbose`):
----
Checking ref database: 100% (1/1), done.
Checking object directories: 100% (256/256), done.
not ok 7 - corrupt and check
----
and this:
----
Checking ref database: 100% (1/1), done.
Checking object directories: 100% (256/256), done.
not ok 8 - reflog expire --dry-run should not touch reflog
----
It does seem like this _is_ a bug in mksh, though, which I've reproduced
with a test script, so I'll report it there.
--
brian m. carlson (they/them)
Toronto, Ontario, CA
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 325 bytes --]
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 21:54 ` brian m. carlson
@ 2026-04-19 2:10 ` Jeff King
0 siblings, 0 replies; 133+ messages in thread
From: Jeff King @ 2026-04-19 2:10 UTC (permalink / raw)
To: brian m. carlson
Cc: Ben Knoble, Patrick Steinhardt, git, Junio C Hamano,
Gábor SZEDER
On Sat, Apr 18, 2026 at 09:54:58PM +0000, brian m. carlson wrote:
> > So we are inside a conditional, and the usual global "set -e"
> > suppression should happen. It sounds like it is not happening in your
> > version of mksh, but I was unable to get t1410 to fail at all using mksh
> > 59c-43 (from Debian unstable) or 59c-41 (from stable).
>
> It does fail with 59c-43 under `make prove` or if you do `sh ./t1410*.sh
> --verbose`, assuming that `sh` points to `mksh`, but since the script
> has a `/bin/sh` shebang, you need to invoke it explicitly with the shell
> in question, or it will use the system `sh` (dash). (I made this
> mistake when reproducing the problem.)
Doh. The problem was none of that, but that I was using Patrick's
version of the patch that only turns on "set -e" for bash.
So yeah, after actually enabling "set -e" I do see the failure.
> It does seem like this _is_ a bug in mksh, though, which I've reproduced
> with a test script, so I'll report it there.
I looked up your report in Debian's system. I think you're right that
the eval is the problem. The smallest reproduction I came up with is:
$ dash -ec 'eval "false; true" && echo ok'
ok
$ mksh -ec 'eval "false; true" && echo ok'
[no output]
So it respects "-e" within the eval, which is wrong, and then doubly
weird that "-e" bails from the eval but not the whole script.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v4 12/12] t: detect errors outside of test cases
2026-04-18 21:05 ` Jeff King
@ 2026-04-20 6:11 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 6:11 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, Ben Knoble, git, Gábor SZEDER
On Sat, Apr 18, 2026 at 05:05:18PM -0400, Jeff King wrote:
> On Sat, Apr 18, 2026 at 12:24:53PM -0700, Junio C Hamano wrote:
>
> > We could forget about "we know this is a good shell by its name and
> > version" and test the feature we depend on ourselves, perhaps?
>
> I looked into that but didn't get anywhere useful. You can try to test
> all of the "set -e" scenarios we care about, but there are a lot of
> them. For example, I would never have thought to check how "command"
> behaves inside a &&-chain while "set -e" is in effect.
>
> So you basically end up adding a test case for the bugs you find, at
> which point it is not much better than blocking known-bad versions.
Yeah, agreed. If it was only one or two cases I'd definitely agree with
Junio. But I have a feeling that every shell will behave slightly
different here, and there's even differences between versions of the
same shell.
I'll go with Peff's proposal to have an explicit opt-in, thanks!
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v5 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (15 preceding siblings ...)
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (12 more replies)
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
17 siblings, 13 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
that these changes work on both platforms.
Changes in v5:
- Allow opting in via `GIT_TEST_USE_SET_E=yes` and enable this option
for Linux CI jobs.
- Another fix for a potentially-failing command.
- Link to v4: https://patch.msgid.link/20260417-b4-pks-tests-with-set-e-v4-0-44d43efdafb1@pks.im
Changes in v4:
- Simplify how we read a multi-line variable value.
- Link to v3: https://patch.msgid.link/20260416-b4-pks-tests-with-set-e-v3-0-7a90e5dccadd@pks.im
Changes in v3:
- Adapt `linux-TEST-vars` job to use Bash instead of Dash. Ubuntu
packet mirrors seem to be having problems, so I wasn't able to get
past installing dependencies in any jobs. All to say that I couldn't
verify that this works as expected :/
- Link to v2: https://patch.msgid.link/20260415-b4-pks-tests-with-set-e-v2-0-4e4904a96f15@pks.im
Changes in v2:
- Use `ret=0; $command || ret=$?` pattern.
- Restore `echo 0` in SIGPIPE tests.
- Fix "lib-git-svn.sh" to gracefully handle the case where SVN Perl
modules aren't installed.
- Use `|| :` consistently instead of `|| true`.
- Fix up a couple of tests that fail on FreeBSD 15. The test suite is
now passing on this system, too.
- Only enable `set -e` on Bash 5 and newer.
- Link to v1: https://patch.msgid.link/20260413-b4-pks-tests-with-set-e-v1-0-5b83763a0e84@pks.im
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
[2]: https://gitlab.com/gitlab-org/git/-/merge_requests/541
[3]: https://github.com/git/git/pull/2270
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
ci/run-build-and-tests.sh | 6 ++++++
t/lib-git-daemon.sh | 8 +++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t1410-reflog.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t5000-tar-tree.sh | 4 ++--
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7422-submodule-output.sh | 2 +-
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 6 ++----
t/test-lib-functions.sh | 12 ++++++------
t/test-lib.sh | 35 +++++++++++++++++++++++++++++++----
24 files changed, 109 insertions(+), 70 deletions(-)
Range-diff versus v4:
1: c6503a6ecc = 1: 9f5238f960 t: prepare `test_match_signal ()` calls for `set -e`
2: 792e674fe2 = 2: 7c1beb70dc t: prepare `test_must_fail ()` for `set -e`
3: 106cf12fa0 = 3: f209a2ecbb t: prepare `stop_git_daemon ()` for `set -e`
4: 1a5ac9b5d8 = 4: 7b6f7091f8 t: prepare `git config --unset` calls for `set -e`
5: ecb6f07131 = 5: e265d86fa0 t: prepare conditional test execution for `set -e`
6: 4468b5ef08 ! 6: 04978dda41 t: prepare execution of potentially failing commands for `set -e`
@@ t/lib-httpd.sh: start_httpd() {
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
+ ## t/t1410-reflog.sh ##
+@@ t/t1410-reflog.sh: check_have () {
+ }
+
+ check_fsck () {
+- git fsck --full >fsck.output
++ git fsck --full >fsck.output || true
+ case "$1" in
+ '')
+ test_must_be_empty fsck.output ;;
+
## t/t3901-i18n-patch.sh ##
@@ t/t3901-i18n-patch.sh: check_encoding () {
8859)
7: a188ac9564 = 7: 3c07c82032 t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
8: 963b9f4a82 = 8: 43d67af0e5 t0008: silence error in subshell when using `grep -v`
9: 510d235080 = 9: 822857fd35 t1301: don't fail in case setfacl(1) doesn't exist or fails
10: 550d9042bc = 10: ad24432195 t6002: fix use of `expr` with `set -e`
11: 9b23e69584 = 11: 88c8b888e1 t9902: fix use of `read` with `set -e`
12: 55fc58c1ec < -: ---------- t: detect errors outside of test cases
-: ---------- > 12: 0de0c56caa t: detect errors outside of test cases
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v5 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (11 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (10 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..5fd5494ef1 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+
+ exit_code=0; "$@" 2>&7 || exit_code=$?
+
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (9 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..d172aa51f0 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (8 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 05/12] t: prepare conditional test execution for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (7 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (6 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t1410-reflog.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t5000-tar-tree.sh | 4 ++--
t/t7422-submodule-output.sh | 2 +-
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib-functions.sh | 3 +--
t/test-lib.sh | 10 ++++++----
12 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..52843f667d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index ce71f9a30a..f289fc11e9 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -23,7 +23,7 @@ check_have () {
}
check_fsck () {
- git fsck --full >fsck.output
+ git fsck --full >fsck.output || true
case "$1" in
'')
test_must_be_empty fsck.output ;;
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b49a..ef7d7e1edc 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ ret=0; grep "^encoding ISO8859-1" || ret=$?
+ test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054f17..a8c28533dc 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
- git archive HEAD
- echo $? >exit-code
+ { ret=0 && git archive HEAD || ret=$?; } &&
+ echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf117..852136fdfd 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
- { git submodule status --recursive 2>err; echo $?>status; } |
+ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5fd5494ef1..879ee1ee59 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1248,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
- "$@" 2>&7
- exit_code=$?
+ exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..de7d9e7b92 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@@ -454,8 +454,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ (
+ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+ echo "$ret" >"$TEST_RESULTS_BASE.exit"
+ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (5 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 879ee1ee59..502bb0ddcb 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (4 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..d77a179bdd 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (3 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..0e0d07a1a1 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 10/12] t6002: fix use of `expr` with `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 11/12] t9902: fix use of `read` " Patrick Steinhardt
` (2 subsequent siblings)
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 11/12] t9902: fix use of `read` with `set -e`
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-20 16:19 ` [PATCH v5 00/12] " Junio C Hamano
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. As the read is terminated by EOF, the command
will end up returning a non-zero error code. This hasn't been an issue
until now as we didn't run with `set -e`, but that'll change in a
subsequent commit.
Prepare for this change by not using read at all, as we can simply store
the multi-line value directly.
Suggested-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..28f61f08fb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,12 +590,10 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
-main
+refs='main
maint
next
-seen
-EOF
+seen'
test_expect_success '__gitcomp_nl - trailing space' '
test_gitcomp_nl "m" "$refs" <<-EOF
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v5 12/12] t: detect errors outside of test cases
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-20 7:27 ` Patrick Steinhardt
2026-04-20 16:19 ` [PATCH v5 00/12] " Junio C Hamano
12 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-20 7:27 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ci/run-build-and-tests.sh | 6 ++++++
t/test-lib.sh | 25 +++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 28cfe730ee..de08a08d59 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -7,6 +7,12 @@
export TEST_CONTRIB_TOO=yes
+case "$jobname" in
+almalinux-*|debian-*|fedora-*|linux-*)
+ export GIT_TEST_USE_SET_E=yes
+ ;;
+esac
+
case "$jobname" in
fedora-breaking-changes-musl|linux-breaking-changes)
export WITH_BREAKING_CHANGES=YesPlease
diff --git a/t/test-lib.sh b/t/test-lib.sh
index de7d9e7b92..cded7bd693 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,31 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case.
+#
+# Note that we only enable this on Bash 5 and newer, or when explicitly
+# requested by the user via `GIT_TEST_USE_SET_E=true`. This ib secause `set -e`
+# has wildly different behaviour across shells. The list of default-enabled
+# shells may be extended going forward.
+if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
+then
+ GIT_TEST_USE_SET_E=true
+fi
+
+# We cannot use `test-tool env-helper` here, as it's not yet available.
+case "${GIT_TEST_USE_SET_E:-false}" in
+1|on|true|yes)
+ set -e
+ ;;
+0|off|false|no)
+ ;;
+*)
+ echo "GIT_TEST_USE_SET_E requires a boolean" >&2
+ exit 1
+ ;;
+esac
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.rc2.529.gd9106f7525.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* Re: [PATCH v5 00/12] t: detect errors outside of test cases
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
` (11 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 12/12] t: detect errors outside of test cases Patrick Steinhardt
@ 2026-04-20 16:19 ` Junio C Hamano
2026-04-21 3:00 ` Jeff King
12 siblings, 1 reply; 133+ messages in thread
From: Junio C Hamano @ 2026-04-20 16:19 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Jeff King, SZEDER Gábor
Patrick Steinhardt <ps@pks.im> writes:
> I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
> that these changes work on both platforms.
>
> Changes in v5:
> - Allow opting in via `GIT_TEST_USE_SET_E=yes` and enable this option
> for Linux CI jobs.
> - Another fix for a potentially-failing command.
> - Link to v4: https://patch.msgid.link/20260417-b4-pks-tests-with-set-e-v4-0-44d43efdafb1@pks.im
I agree that the explicit GIT_TEST_USE_SET_E option is a good way to
go, as it would be clear which ones are (and which ones are not)
using it. I am not sure why we have check_fsck() thing? Wasn't it
determined that this would fail only with a broken shells, or is it
futureproofing just in case the function is used without being
tested?
Also you updated in your earlier round to make everybody to use "|| :"
not "|| true". Either would work, but this sticks out.
> + ## t/t1410-reflog.sh ##
> +@@ t/t1410-reflog.sh: check_have () {
> + }
> +
> + check_fsck () {
> +- git fsck --full >fsck.output
> ++ git fsck --full >fsck.output || true
> + case "$1" in
> + '')
> + test_must_be_empty fsck.output ;;
> +
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v5 00/12] t: detect errors outside of test cases
2026-04-20 16:19 ` [PATCH v5 00/12] " Junio C Hamano
@ 2026-04-21 3:00 ` Jeff King
2026-04-21 5:41 ` Patrick Steinhardt
0 siblings, 1 reply; 133+ messages in thread
From: Jeff King @ 2026-04-21 3:00 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Patrick Steinhardt, git, SZEDER Gábor
On Mon, Apr 20, 2026 at 09:19:12AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
> > that these changes work on both platforms.
> >
> > Changes in v5:
> > - Allow opting in via `GIT_TEST_USE_SET_E=yes` and enable this option
> > for Linux CI jobs.
> > - Another fix for a potentially-failing command.
> > - Link to v4: https://patch.msgid.link/20260417-b4-pks-tests-with-set-e-v4-0-44d43efdafb1@pks.im
>
> I agree that the explicit GIT_TEST_USE_SET_E option is a good way to
> go, as it would be clear which ones are (and which ones are not)
> using it. I am not sure why we have check_fsck() thing? Wasn't it
> determined that this would fail only with a broken shells, or is it
> futureproofing just in case the function is used without being
> tested?
Yes, I think it would only fail on a broken shell. It's not _wrong_ to
protect against it, but it's the tip of the iceberg. There are many
other spots that rely on "set -e" being suppressed inside test snippets,
not the least of which is every single final command in each snippet
(because it's at the end of the &&-chain).
So I think it is better to draw the line at things that actually trigger
with working shells.
-Peff
^ permalink raw reply [flat|nested] 133+ messages in thread
* Re: [PATCH v5 00/12] t: detect errors outside of test cases
2026-04-21 3:00 ` Jeff King
@ 2026-04-21 5:41 ` Patrick Steinhardt
0 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 5:41 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git, SZEDER Gábor
On Mon, Apr 20, 2026 at 11:00:45PM -0400, Jeff King wrote:
> On Mon, Apr 20, 2026 at 09:19:12AM -0700, Junio C Hamano wrote:
>
> > Patrick Steinhardt <ps@pks.im> writes:
> >
> > > I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
> > > that these changes work on both platforms.
> > >
> > > Changes in v5:
> > > - Allow opting in via `GIT_TEST_USE_SET_E=yes` and enable this option
> > > for Linux CI jobs.
> > > - Another fix for a potentially-failing command.
> > > - Link to v4: https://patch.msgid.link/20260417-b4-pks-tests-with-set-e-v4-0-44d43efdafb1@pks.im
> >
> > I agree that the explicit GIT_TEST_USE_SET_E option is a good way to
> > go, as it would be clear which ones are (and which ones are not)
> > using it. I am not sure why we have check_fsck() thing? Wasn't it
> > determined that this would fail only with a broken shells, or is it
> > futureproofing just in case the function is used without being
> > tested?
>
> Yes, I think it would only fail on a broken shell. It's not _wrong_ to
> protect against it, but it's the tip of the iceberg. There are many
> other spots that rely on "set -e" being suppressed inside test snippets,
> not the least of which is every single final command in each snippet
> (because it's at the end of the &&-chain).
>
> So I think it is better to draw the line at things that actually trigger
> with working shells.
Okay, that's fair enough. Let me drop that part again and then send out
another (hopefully final) version. Thanks!
Patrick
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v6 00/12] t: detect errors outside of test cases
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
` (16 preceding siblings ...)
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
` (11 more replies)
17 siblings, 12 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Hi,
this is a follow-up to the recent discussion we had around `set -e` to
make our tests more robust and basically supersedes Junio's [1].
I've tested the patches with both Bash and Dash, and all tests are
passing on my machine with both of them. CI seems to be happy, as
well. But I would expect that this change probably has some fallout,
even though I hope that it's generally going to be small and contained.
This series is based on 8c9303b1ff (Merge branch
'jc/no-writev-does-not-work', 2026-04-10).
I've created an MR with GitLab [2] and a PR with GitHub [3] to verify
that these changes work on both platforms.
Changes in v6:
- Drop the change in t1410, which is only required for broken shells.
- Link to v5: https://patch.msgid.link/20260420-b4-pks-tests-with-set-e-v5-0-7d3d68292f6b@pks.im
Changes in v5:
- Allow opting in via `GIT_TEST_USE_SET_E=yes` and enable this option
for Linux CI jobs.
- Another fix for a potentially-failing command.
- Link to v4: https://patch.msgid.link/20260417-b4-pks-tests-with-set-e-v4-0-44d43efdafb1@pks.im
Changes in v4:
- Simplify how we read a multi-line variable value.
- Link to v3: https://patch.msgid.link/20260416-b4-pks-tests-with-set-e-v3-0-7a90e5dccadd@pks.im
Changes in v3:
- Adapt `linux-TEST-vars` job to use Bash instead of Dash. Ubuntu
packet mirrors seem to be having problems, so I wasn't able to get
past installing dependencies in any jobs. All to say that I couldn't
verify that this works as expected :/
- Link to v2: https://patch.msgid.link/20260415-b4-pks-tests-with-set-e-v2-0-4e4904a96f15@pks.im
Changes in v2:
- Use `ret=0; $command || ret=$?` pattern.
- Restore `echo 0` in SIGPIPE tests.
- Fix "lib-git-svn.sh" to gracefully handle the case where SVN Perl
modules aren't installed.
- Use `|| :` consistently instead of `|| true`.
- Fix up a couple of tests that fail on FreeBSD 15. The test suite is
now passing on this system, too.
- Only enable `set -e` on Bash 5 and newer.
- Link to v1: https://patch.msgid.link/20260413-b4-pks-tests-with-set-e-v1-0-5b83763a0e84@pks.im
Thanks!
Patrick
[1]: <20260325062114.2067946-1-gitster@pobox.com>
[2]: https://gitlab.com/gitlab-org/git/-/merge_requests/541
[3]: https://github.com/git/git/pull/2270
---
Patrick Steinhardt (12):
t: prepare `test_match_signal ()` calls for `set -e`
t: prepare `test_must_fail ()` for `set -e`
t: prepare `stop_git_daemon ()` for `set -e`
t: prepare `git config --unset` calls for `set -e`
t: prepare conditional test execution for `set -e`
t: prepare execution of potentially failing commands for `set -e`
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
t0008: silence error in subshell when using `grep -v`
t1301: don't fail in case setfacl(1) doesn't exist or fails
t6002: fix use of `expr` with `set -e`
t9902: fix use of `read` with `set -e`
t: detect errors outside of test cases
ci/run-build-and-tests.sh | 6 ++++++
t/lib-git-daemon.sh | 8 +++++---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t0005-signals.sh | 4 ++--
t/t0008-ignores.sh | 4 ++--
t/t1301-shared-repo.sh | 2 +-
t/t3600-rm.sh | 2 +-
t/t3901-i18n-patch.sh | 3 ++-
t/t4032-diff-inter-hunk-context.sh | 14 ++++++++------
t/t5000-tar-tree.sh | 4 ++--
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
t/t7422-submodule-output.sh | 2 +-
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/t9902-completion.sh | 6 ++----
t/test-lib-functions.sh | 12 ++++++------
t/test-lib.sh | 35 +++++++++++++++++++++++++++++++----
23 files changed, 108 insertions(+), 69 deletions(-)
Range-diff versus v5:
1: a7fd95c0f8 = 1: e619a6f565 t: prepare `test_match_signal ()` calls for `set -e`
2: 1edfada458 = 2: 9002a8e77e t: prepare `test_must_fail ()` for `set -e`
3: 9c703ea3ec = 3: b375926fc4 t: prepare `stop_git_daemon ()` for `set -e`
4: 855707f648 = 4: 0d46e4212e t: prepare `git config --unset` calls for `set -e`
5: e24c272d06 = 5: 5bdb1ad025 t: prepare conditional test execution for `set -e`
6: dd89a14595 ! 6: 7654824960 t: prepare execution of potentially failing commands for `set -e`
@@ t/lib-httpd.sh: start_httpd() {
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
- ## t/t1410-reflog.sh ##
-@@ t/t1410-reflog.sh: check_have () {
- }
-
- check_fsck () {
-- git fsck --full >fsck.output
-+ git fsck --full >fsck.output || true
- case "$1" in
- '')
- test_must_be_empty fsck.output ;;
-
## t/t3901-i18n-patch.sh ##
@@ t/t3901-i18n-patch.sh: check_encoding () {
8859)
7: dfcbb1ed20 = 7: 8f5ee3234a t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
8: cef0ff3bf9 = 8: 46b4ddf1fc t0008: silence error in subshell when using `grep -v`
9: f0e43f0f54 = 9: 6e21a1af73 t1301: don't fail in case setfacl(1) doesn't exist or fails
10: b61761cfdb = 10: c1c907187f t6002: fix use of `expr` with `set -e`
11: 591e53ab47 = 11: b6df934d90 t9902: fix use of `read` with `set -e`
12: dd7a68c141 = 12: 60681e8ebe t: detect errors outside of test cases
---
base-commit: 8c9303b1ffae5b745d1b0a1f98330cf7944d8db0
change-id: 20260410-b4-pks-tests-with-set-e-3ae479b24b51
^ permalink raw reply [flat|nested] 133+ messages in thread
* [PATCH v6 01/12] t: prepare `test_match_signal ()` calls for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
` (10 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0005-signals.sh | 4 ++--
t/t3600-rm.sh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index afba0fc3fc..84319cf169 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
- OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 1f16e6b522..a371ea690e 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 02/12] t: prepare `test_must_fail ()` for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
` (9 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..5fd5494ef1 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
- "$@" 2>&7
- exit_code=$?
+
+ exit_code=0; "$@" 2>&7 || exit_code=$?
+
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 03/12] t: prepare `stop_git_daemon ()` for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
` (8 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-daemon.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index e62569222b..d172aa51f0 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -85,14 +85,16 @@ stop_git_daemon() {
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
+
kill "$GIT_DAEMON_PID"
- wait "$GIT_DAEMON_PID" >&3 2>&4
- ret=$?
+ ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?
+
if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
- kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
+
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 04/12] t: prepare `git config --unset` calls for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (2 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 05/12] t: prepare conditional test execution " Patrick Steinhardt
` (7 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 2 +-
t/t7508-status.sh | 4 ++--
t/t9138-git-svn-authors-prog.sh | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cbd32..c98eb6abb2 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -17,7 +17,7 @@ f() {
t() {
use_config=
- git config --unset diff.interHunkContext
+ git config --unset diff.interHunkContext || :
case $# in
4) hunks=$4; cmd="diff -U$3";;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..1167b835a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'
# recover unconditionally from color tests
-git config --unset color.status
-git config --unset color.ui
+git config --unset color.status || :
+git config --unset color.ui || :
test_expect_success 'status --porcelain respects -b' '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 784ec7fc2d..5bb38cb23a 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'
-git --git-dir=x/.git config --unset svn.authorsfile
-git --git-dir=x/.git config --unset svn.authorsprog
+git --git-dir=x/.git config --unset svn.authorsfile || :
+git --git-dir=x/.git config --unset svn.authorsprog || :
test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 05/12] t: prepare conditional test execution for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (3 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
` (6 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t4032-diff-inter-hunk-context.sh | 12 +++++++-----
t/t7450-bad-git-dotfiles.sh | 24 +++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index c98eb6abb2..2d216fb70f 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"
- test -f $expected &&
- test_expect_success "$label: check output" "
- git $cmd $file | grep -v '^index ' >actual &&
- test_cmp $expected actual
- "
+ if test -f $expected
+ then
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+ fi
}
cat <<EOF >expected.f1.0.1 || exit 1
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index f512eed278..8cc86522b2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -220,17 +220,19 @@ check_dotx_symlink () {
)
'
- test -n "$refuse_index" &&
- test_expect_success "refuse to load symlinked $name into index ($type)" '
- test_must_fail \
- git -C $dir \
- -c core.protectntfs \
- -c core.protecthfs \
- read-tree $tree 2>err &&
- grep "invalid path.*$name" err &&
- git -C $dir ls-files -s >out &&
- test_must_be_empty out
- '
+ if test -n "$refuse_index"
+ then
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+ fi
}
check_dotx_symlink gitmodules vanilla .gitmodules
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 06/12] t: prepare execution of potentially failing commands for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (4 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 05/12] t: prepare conditional test execution " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
` (5 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/lib-git-svn.sh | 7 +++----
t/lib-httpd.sh | 3 +--
t/t3901-i18n-patch.sh | 3 ++-
t/t5000-tar-tree.sh | 4 ++--
t/t7422-submodule-output.sh | 2 +-
t/t9200-git-cvsexportcommit.sh | 3 +--
t/t9400-git-cvsserver-server.sh | 5 +++--
t/t9401-git-cvsserver-crlf.sh | 4 ++--
t/t9402-git-cvsserver-refs.sh | 4 ++--
t/test-lib-functions.sh | 3 +--
t/test-lib.sh | 10 ++++++----
11 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..52843f667d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT
-svn >/dev/null 2>&1
-if test $? -ne 1
+if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf
+x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
-" >&3 2>&4
-x=$?
+" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 4c76e813e3..fc646447d5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -235,11 +235,10 @@ start_httpd() {
test_atexit stop_httpd
- "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
- if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index f03601b49a..ef7d7e1edc 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
- grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ ret=0; grep "^encoding ISO8859-1" || ret=$?
+ test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5465054f17..a8c28533dc 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
- git archive HEAD
- echo $? >exit-code
+ { ret=0 && git archive HEAD || ret=$?; } &&
+ echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
index aea1ddf117..852136fdfd 100755
--- a/t/t7422-submodule-output.sh
+++ b/t/t7422-submodule-output.sh
@@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
- { git submodule status --recursive 2>err; echo $?>status; } |
+ { { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 14cbe96527..581cf3d28f 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index e499c7f955..4b45398bab 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
-cvs >/dev/null 2>&1
-if test $? -ne 1
+
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805acdc..6b4cbb1651 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -60,12 +60,12 @@ check_status_options() {
return $stat
}
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 2ee41f9443..65f2ceedec 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -68,12 +68,12 @@ check_diff() {
#########
-cvs >/dev/null 2>&1
-if test $? -ne 1
+if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
+
if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5fd5494ef1..879ee1ee59 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1248,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
- "$@" 2>&7
- exit_code=$?
+ exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..de7d9e7b92 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -143,8 +143,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
-"$GIT_BINARY" >/dev/null
-if test $? != 1
+
+if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@@ -454,8 +454,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
- echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ (
+ ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
+ echo "$ret" >"$TEST_RESULTS_BASE.exit"
+ ) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 07/12] t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (5 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
` (4 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/test-lib-functions.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 879ee1ee59..502bb0ddcb 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+ } || eval_ret=\$?; $test_cleanup"
}
# This function can be used to schedule some commands to be run
@@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
- } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+ } || eval_ret=\$?; $test_atexit_cleanup"
}
# Deprecated wrapper for "git init", use "git init" directly instead
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 08/12] t0008: silence error in subshell when using `grep -v`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (6 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
` (3 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t0008-ignores.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index e716b5cdfa..d77a179bdd 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"
- expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
- expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
+ expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
+ expect=$(echo "$expect_verbose" | sed -e 's/.* //')
test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (7 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
` (2 subsequent siblings)
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t1301-shared-repo.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 630a47af21..0e0d07a1a1 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
-setfacl -k . 2>/dev/null
+setfacl -k . 2>/dev/null || :
# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 10/12] t6002: fix use of `expr` with `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (8 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t6002-rev-list-bisect.sh | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index daa009c9a1..f2de40b5ed 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
- _bisect_err=$(expr $_list_size - $_bisection_size \* 2)
- test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
- _bisect_err=$(expr $_bisect_err / 2) ; # floor
-
- test_expect_success \
- "bisection diff $_bisect_option $_head $* <= $_max_diff" \
- 'test $_bisect_err -le $_max_diff'
+ _bisect_err=$(($_list_size - $_bisection_size * 2))
+ if test "$_bisect_err" -lt 0
+ then
+ _bisect_err=$((0 - $_bisect_err))
+ fi
+ _bisect_err=$(($_bisect_err / 2)) ; # floor
+
+ test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
+ test $_bisect_err -le $_max_diff
+ '
}
date >path0
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 11/12] t9902: fix use of `read` with `set -e`
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (9 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 12/12] t: detect errors outside of test cases Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. As the read is terminated by EOF, the command
will end up returning a non-zero error code. This hasn't been an issue
until now as we didn't run with `set -e`, but that'll change in a
subsequent commit.
Prepare for this change by not using read at all, as we can simply store
the multi-line value directly.
Suggested-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
t/t9902-completion.sh | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..28f61f08fb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -590,12 +590,10 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
-read -r -d "" refs <<-\EOF
-main
+refs='main
maint
next
-seen
-EOF
+seen'
test_expect_success '__gitcomp_nl - trailing space' '
test_gitcomp_nl "m" "$refs" <<-EOF
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
* [PATCH v6 12/12] t: detect errors outside of test cases
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
` (10 preceding siblings ...)
2026-04-21 7:34 ` [PATCH v6 11/12] t9902: fix use of `read` " Patrick Steinhardt
@ 2026-04-21 7:34 ` Patrick Steinhardt
11 siblings, 0 replies; 133+ messages in thread
From: Patrick Steinhardt @ 2026-04-21 7:34 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, SZEDER Gábor
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
ci/run-build-and-tests.sh | 6 ++++++
t/test-lib.sh | 25 +++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 28cfe730ee..de08a08d59 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -7,6 +7,12 @@
export TEST_CONTRIB_TOO=yes
+case "$jobname" in
+almalinux-*|debian-*|fedora-*|linux-*)
+ export GIT_TEST_USE_SET_E=yes
+ ;;
+esac
+
case "$jobname" in
fedora-breaking-changes-musl|linux-breaking-changes)
export WITH_BREAKING_CHANGES=YesPlease
diff --git a/t/test-lib.sh b/t/test-lib.sh
index de7d9e7b92..cded7bd693 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,6 +15,31 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .
+# Enable the use of errexit so that any unexpected failures will cause us to
+# abort tests, even when outside of a specific test case.
+#
+# Note that we only enable this on Bash 5 and newer, or when explicitly
+# requested by the user via `GIT_TEST_USE_SET_E=true`. This ib secause `set -e`
+# has wildly different behaviour across shells. The list of default-enabled
+# shells may be extended going forward.
+if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
+then
+ GIT_TEST_USE_SET_E=true
+fi
+
+# We cannot use `test-tool env-helper` here, as it's not yet available.
+case "${GIT_TEST_USE_SET_E:-false}" in
+1|on|true|yes)
+ set -e
+ ;;
+0|off|false|no)
+ ;;
+*)
+ echo "GIT_TEST_USE_SET_E requires a boolean" >&2
+ exit 1
+ ;;
+esac
+
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
--
2.54.0.545.g6539524ca2.dirty
^ permalink raw reply related [flat|nested] 133+ messages in thread
end of thread, other threads:[~2026-04-21 7:34 UTC | newest]
Thread overview: 133+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-13 9:49 [PATCH 00/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-13 16:26 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 17:49 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-13 16:33 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 6:23 ` Jeff King
2026-04-14 17:41 ` Junio C Hamano
2026-04-15 6:58 ` Patrick Steinhardt
2026-04-16 5:40 ` Jeff King
2026-04-13 9:49 ` [PATCH 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-13 16:53 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-13 16:59 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-13 17:04 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-13 17:09 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 13:41 ` Junio C Hamano
2026-04-13 22:32 ` Junio C Hamano
2026-04-14 1:09 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-14 13:40 ` Junio C Hamano
2026-04-14 22:03 ` Jeff King
2026-04-14 22:52 ` Jeff King
2026-04-14 23:08 ` Jeff King
2026-04-15 6:48 ` Patrick Steinhardt
2026-04-16 5:49 ` Jeff King
2026-04-16 8:03 ` Patrick Steinhardt
2026-04-16 14:34 ` Junio C Hamano
2026-04-18 8:01 ` Jeff King
2026-04-15 15:31 ` Junio C Hamano
2026-04-13 9:49 ` [PATCH 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-13 17:23 ` Junio C Hamano
2026-04-14 7:24 ` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-13 17:28 ` Junio C Hamano
2026-04-14 7:23 ` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-13 9:49 ` [PATCH 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-13 17:29 ` Junio C Hamano
2026-04-13 21:33 ` [PATCH 00/12] " Junio C Hamano
2026-04-13 21:46 ` Junio C Hamano
2026-04-15 13:06 ` [PATCH v2 " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-15 13:06 ` [PATCH v2 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-16 6:00 ` Jeff King
2026-04-16 10:46 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 00/12] " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-16 20:12 ` SZEDER Gábor
2026-04-16 20:42 ` Junio C Hamano
2026-04-17 9:44 ` Patrick Steinhardt
2026-04-16 11:19 ` [PATCH v3 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-16 16:06 ` Junio C Hamano
2026-04-17 10:50 ` [PATCH v4 00/12] " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-17 10:50 ` [PATCH v4 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-18 6:50 ` Jeff King
2026-04-18 12:17 ` Ben Knoble
2026-04-18 17:44 ` Jeff King
2026-04-18 19:24 ` Junio C Hamano
2026-04-18 21:05 ` Jeff King
2026-04-20 6:11 ` Patrick Steinhardt
2026-04-18 19:17 ` brian m. carlson
2026-04-18 21:30 ` Jeff King
2026-04-18 21:54 ` brian m. carlson
2026-04-19 2:10 ` Jeff King
2026-04-20 7:27 ` [PATCH v5 00/12] " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-20 7:27 ` [PATCH v5 12/12] t: detect errors outside of test cases Patrick Steinhardt
2026-04-20 16:19 ` [PATCH v5 00/12] " Junio C Hamano
2026-04-21 3:00 ` Jeff King
2026-04-21 5:41 ` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 01/12] t: prepare `test_match_signal ()` calls for `set -e` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 02/12] t: prepare `test_must_fail ()` " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 03/12] t: prepare `stop_git_daemon " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 04/12] t: prepare `git config --unset` calls " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 05/12] t: prepare conditional test execution " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 06/12] t: prepare execution of potentially failing commands " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 07/12] t: prepare `test_when_finished ()`/`test_atexit()` " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 08/12] t0008: silence error in subshell when using `grep -v` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 09/12] t1301: don't fail in case setfacl(1) doesn't exist or fails Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 10/12] t6002: fix use of `expr` with `set -e` Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 11/12] t9902: fix use of `read` " Patrick Steinhardt
2026-04-21 7:34 ` [PATCH v6 12/12] t: detect errors outside of test cases Patrick Steinhardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox