diff --git a/t/README b/t/README
index d5bb0c9aa1..35438bca48 100644
--- a/t/README
+++ b/t/README
@@ -168,6 +168,16 @@ appropriately before running "make".
 	Using this option with a RAM-based filesystem (such as tmpfs)
 	can massively speed up the test suite.
 
+--chain-lint::
+--no-chain-lint::
+	If --chain-lint is enabled, the test harness will check each
+	test to make sure that it properly "&&-chains" all commands (so
+	that a failure in the middle does not go unnoticed by the final
+	exit code of the test). This check is performed in addition to
+	running the tests themselves. You may also enable or disable
+	this feature by setting the GIT_TEST_CHAIN_LINT environment
+	variable to "1" or "0", respectively.
+
 You can also set the GIT_TEST_INSTALLED environment variable to
 the bindir of an existing git installation to test that installation.
 You still need to have built this git sandbox, from which various
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 071e4d7d3e..f5c01758ca 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -405,7 +405,7 @@ test_expect_success 'setup -L :regex' '
 	mv hello.c hello.orig &&
 	echo "#include <stdio.h>" >hello.c &&
 	cat hello.orig >>hello.c &&
-	tr Q "\\t" >>hello.c <<-\EOF
+	tr Q "\\t" >>hello.c <<-\EOF &&
 	void mail()
 	{
 	Qputs("mail");
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index a8c9574291..5cf74eddec 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -91,7 +91,7 @@ test_perf_create_repo_from () {
 				*/objects|*/hooks|*/config)
 					;;
 				*)
-					cp -R "$stuff" . || break
+					cp -R "$stuff" . || exit 1
 					;;
 			esac
 		done &&
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index f10ba4a01e..79b9074172 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -253,7 +253,7 @@ test_expect_success 'test --verbose' '
 	test_expect_success "failing test" false
 	test_done
 	EOF
-	mv test-verbose/out test-verbose/out+
+	mv test-verbose/out test-verbose/out+ &&
 	grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
 	check_sub_test_lib_test test-verbose <<-\EOF
 	> expecting success: true
@@ -974,7 +974,7 @@ test_expect_success 'writing this tree with --missing-ok' '
 
 ################################################################
 test_expect_success 'git read-tree followed by write-tree should be idempotent' '
-	rm -f .git/index
+	rm -f .git/index &&
 	git read-tree $tree &&
 	test -f .git/index &&
 	newtree=$(git write-tree) &&
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index aeea50c633..e7f27ebbc1 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -10,8 +10,8 @@ one
 EOF
 
 test_expect_success 'sigchain works' '
-	test-sigchain >actual
-	case "$?" in
+	{ test-sigchain >actual; ret=$?; } &&
+	case "$ret" in
 	143) true ;; # POSIX w/ SIGTERM=15
 	271) true ;; # ksh w/ SIGTERM=15
 	  3) true ;; # Windows
@@ -40,12 +40,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 "$OUT" -eq 141
 '
 
 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 "$OUT" -eq 141
 '
 
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index f97c80556f..9c217d948c 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -218,7 +218,7 @@ test_expect_success 'grow / shrink' '
 	echo size >> in &&
 	echo 64 51 >> expect &&
 	echo put key52 value52 >> in &&
-	echo NULL >> expect
+	echo NULL >> expect &&
 	echo size >> in &&
 	echo 256 52 >> expect &&
 	for n in $(test_seq 12)
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index d2e51a81bc..f94120a894 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -8,6 +8,13 @@ has_cr() {
 	tr '\015' Q <"$1" | grep Q >/dev/null
 }
 
+# add or remove CRs to disk file in-place
+# usage: munge_cr <append|remove> <file>
+munge_cr () {
+	"${1}_cr" <"$2" >tmp &&
+	mv tmp "$2"
+}
+
 test_expect_success setup '
 
 	git config core.autocrlf false &&
@@ -28,9 +35,7 @@ test_expect_success setup '
 	for w in Some extra lines here; do echo $w; done >>one &&
 	git diff >patch.file &&
 	patched=$(git hash-object --stdin <one) &&
-	git read-tree --reset -u HEAD &&
-
-	echo happy.
+	git read-tree --reset -u HEAD
 '
 
 test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
@@ -100,22 +105,11 @@ test_expect_success 'update with autocrlf=input' '
 	rm -f tmp one dir/two three &&
 	git read-tree --reset -u HEAD &&
 	git config core.autocrlf input &&
-
-	for f in one dir/two
-	do
-		append_cr <$f >tmp && mv -f tmp $f &&
-		git update-index -- $f || {
-			echo Oops
-			false
-			break
-		}
-	done &&
-
+	munge_cr append one &&
+	munge_cr append dir/two &&
+	git update-index -- one dir/two &&
 	differs=$(git diff-index --cached HEAD) &&
-	test -z "$differs" || {
-		echo Oops "$differs"
-		false
-	}
+	verbose test -z "$differs"
 
 '
 
@@ -124,22 +118,11 @@ test_expect_success 'update with autocrlf=true' '
 	rm -f tmp one dir/two three &&
 	git read-tree --reset -u HEAD &&
 	git config core.autocrlf true &&
-
-	for f in one dir/two
-	do
-		append_cr <$f >tmp && mv -f tmp $f &&
-		git update-index -- $f || {
-			echo "Oops $f"
-			false
-			break
-		}
-	done &&
-
+	munge_cr append one &&
+	munge_cr append dir/two &&
+	git update-index -- one dir/two &&
 	differs=$(git diff-index --cached HEAD) &&
-	test -z "$differs" || {
-		echo Oops "$differs"
-		false
-	}
+	verbose test -z "$differs"
 
 '
 
@@ -148,23 +131,13 @@ test_expect_success 'checkout with autocrlf=true' '
 	rm -f tmp one dir/two three &&
 	git config core.autocrlf true &&
 	git read-tree --reset -u HEAD &&
-
-	for f in one dir/two
-	do
-		remove_cr <"$f" >tmp && mv -f tmp $f &&
-		git update-index -- $f || {
-			echo "Eh? $f"
-			false
-			break
-		}
-	done &&
+	munge_cr remove one &&
+	munge_cr remove dir/two &&
+	git update-index -- one dir/two &&
 	test "$one" = $(git hash-object --stdin <one) &&
 	test "$two" = $(git hash-object --stdin <dir/two) &&
 	differs=$(git diff-index --cached HEAD) &&
-	test -z "$differs" || {
-		echo Oops "$differs"
-		false
-	}
+	verbose test -z "$differs"
 '
 
 test_expect_success 'checkout with autocrlf=input' '
@@ -172,25 +145,13 @@ test_expect_success 'checkout with autocrlf=input' '
 	rm -f tmp one dir/two three &&
 	git config core.autocrlf input &&
 	git read-tree --reset -u HEAD &&
-
-	for f in one dir/two
-	do
-		if has_cr "$f"
-		then
-			echo "Eh? $f"
-			false
-			break
-		else
-			git update-index -- $f
-		fi
-	done &&
+	test_must_fail has_cr one &&
+	test_must_fail has_cr two &&
+	git update-index -- one dir/two &&
 	test "$one" = $(git hash-object --stdin <one) &&
 	test "$two" = $(git hash-object --stdin <dir/two) &&
 	differs=$(git diff-index --cached HEAD) &&
-	test -z "$differs" || {
-		echo Oops "$differs"
-		false
-	}
+	verbose test -z "$differs"
 '
 
 test_expect_success 'apply patch (autocrlf=input)' '
@@ -200,10 +161,7 @@ test_expect_success 'apply patch (autocrlf=input)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply patch.file &&
-	test "$patched" = "$(git hash-object --stdin <one)" || {
-		echo "Eh?  apply without index"
-		false
-	}
+	verbose test "$patched" = "$(git hash-object --stdin <one)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -213,10 +171,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply --cached patch.file &&
-	test "$patched" = $(git rev-parse :one) || {
-		echo "Eh?  apply with --cached"
-		false
-	}
+	verbose test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -226,11 +181,8 @@ test_expect_success 'apply patch --index (autocrlf=input)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply --index patch.file &&
-	test "$patched" = $(git rev-parse :one) &&
-	test "$patched" = $(git hash-object --stdin <one) || {
-		echo "Eh?  apply with --index"
-		false
-	}
+	verbose test "$patched" = $(git rev-parse :one) &&
+	verbose test "$patched" = $(git hash-object --stdin <one)
 '
 
 test_expect_success 'apply patch (autocrlf=true)' '
@@ -240,10 +192,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply patch.file &&
-	test "$patched" = "$(remove_cr <one | git hash-object --stdin)" || {
-		echo "Eh?  apply without index"
-		false
-	}
+	verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -253,10 +202,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply --cached patch.file &&
-	test "$patched" = $(git rev-parse :one) || {
-		echo "Eh?  apply without index"
-		false
-	}
+	verbose test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -266,11 +212,8 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
 	git read-tree --reset -u HEAD &&
 
 	git apply --index patch.file &&
-	test "$patched" = $(git rev-parse :one) &&
-	test "$patched" = "$(remove_cr <one | git hash-object --stdin)" || {
-		echo "Eh?  apply with --index"
-		false
-	}
+	verbose test "$patched" = $(git rev-parse :one) &&
+	verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success '.gitattributes says two is binary' '
@@ -280,29 +223,9 @@ test_expect_success '.gitattributes says two is binary' '
 	git config core.autocrlf true &&
 	git read-tree --reset -u HEAD &&
 
-	if has_cr dir/two
-	then
-		echo "Huh?"
-		false
-	else
-		: happy
-	fi &&
-
-	if has_cr one
-	then
-		: happy
-	else
-		echo "Huh?"
-		false
-	fi &&
-
-	if has_cr three
-	then
-		echo "Huh?"
-		false
-	else
-		: happy
-	fi
+	test_must_fail has_cr dir/two &&
+	verbose has_cr one &&
+	test_must_fail has_cr three
 '
 
 test_expect_success '.gitattributes says two is input' '
@@ -311,13 +234,7 @@ test_expect_success '.gitattributes says two is input' '
 	echo "two crlf=input" >.gitattributes &&
 	git read-tree --reset -u HEAD &&
 
-	if has_cr dir/two
-	then
-		echo "Huh?"
-		false
-	else
-		: happy
-	fi
+	test_must_fail has_cr dir/two
 '
 
 test_expect_success '.gitattributes says two and three are text' '
@@ -326,21 +243,8 @@ test_expect_success '.gitattributes says two and three are text' '
 	echo "t* crlf" >.gitattributes &&
 	git read-tree --reset -u HEAD &&
 
-	if has_cr dir/two
-	then
-		: happy
-	else
-		echo "Huh?"
-		false
-	fi &&
-
-	if has_cr three
-	then
-		: happy
-	else
-		echo "Huh?"
-		false
-	fi
+	verbose has_cr dir/two &&
+	verbose has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (1)' '
@@ -352,17 +256,8 @@ test_expect_success 'in-tree .gitattributes (1)' '
 	rm -rf tmp one dir .gitattributes patch.file three &&
 	git read-tree --reset -u HEAD &&
 
-	if has_cr one
-	then
-		echo "Eh? one should not have CRLF"
-		false
-	else
-		: happy
-	fi &&
-	has_cr three || {
-		echo "Eh? three should still have CRLF"
-		false
-	}
+	test_must_fail has_cr one &&
+	verbose has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (2)' '
@@ -371,17 +266,8 @@ test_expect_success 'in-tree .gitattributes (2)' '
 	git read-tree --reset HEAD &&
 	git checkout-index -f -q -u -a &&
 
-	if has_cr one
-	then
-		echo "Eh? one should not have CRLF"
-		false
-	else
-		: happy
-	fi &&
-	has_cr three || {
-		echo "Eh? three should still have CRLF"
-		false
-	}
+	test_must_fail has_cr one &&
+	verbose has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (3)' '
@@ -391,17 +277,8 @@ test_expect_success 'in-tree .gitattributes (3)' '
 	git checkout-index -u .gitattributes &&
 	git checkout-index -u one dir/two three &&
 
-	if has_cr one
-	then
-		echo "Eh? one should not have CRLF"
-		false
-	else
-		: happy
-	fi &&
-	has_cr three || {
-		echo "Eh? three should still have CRLF"
-		false
-	}
+	test_must_fail has_cr one &&
+	verbose has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (4)' '
@@ -411,17 +288,8 @@ test_expect_success 'in-tree .gitattributes (4)' '
 	git checkout-index -u one dir/two three &&
 	git checkout-index -u .gitattributes &&
 
-	if has_cr one
-	then
-		echo "Eh? one should not have CRLF"
-		false
-	else
-		: happy
-	fi &&
-	has_cr three || {
-		echo "Eh? three should still have CRLF"
-		false
-	}
+	test_must_fail has_cr one &&
+	verbose has_cr three
 '
 
 test_expect_success 'checkout with existing .gitattributes' '
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index a90c86bfa3..b044785175 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -172,12 +172,9 @@ test_expect_success 'long options' '
 '
 
 test_expect_success 'missing required value' '
-	test-parse-options -s;
-	test $? = 129 &&
-	test-parse-options --string;
-	test $? = 129 &&
-	test-parse-options --file;
-	test $? = 129
+	test_expect_code 129 test-parse-options -s &&
+	test_expect_code 129 test-parse-options --string &&
+	test_expect_code 129 test-parse-options --file
 '
 
 cat > expect << EOF
@@ -227,8 +224,7 @@ test_expect_success 'unambiguously abbreviated option with "="' '
 '
 
 test_expect_success 'ambiguously abbreviated option' '
-	test-parse-options --strin 123;
-	test $? = 129
+	test_expect_code 129 test-parse-options --strin 123
 '
 
 cat > expect << EOF
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 988c3925d5..b29d749bb7 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -33,16 +33,20 @@ test_expect_success "detection of case insensitive filesystem during repo init"
 '
 else
 test_expect_success "detection of case insensitive filesystem during repo init" '
-	test_must_fail git config --bool core.ignorecase >/dev/null ||
-	test $(git config --bool core.ignorecase) = false
+	{
+		test_must_fail git config --bool core.ignorecase >/dev/null ||
+			test $(git config --bool core.ignorecase) = false
+	}
 '
 fi
 
 if test_have_prereq SYMLINKS
 then
 test_expect_success "detection of filesystem w/o symlink support during repo init" '
-	test_must_fail git config --bool core.symlinks ||
-	test "$(git config --bool core.symlinks)" = true
+	{
+		test_must_fail git config --bool core.symlinks ||
+		test "$(git config --bool core.symlinks)" = true
+	}
 '
 else
 test_expect_success "detection of filesystem w/o symlink support during repo init" '
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 3d98eb847f..113c728e67 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'revision walking can be done twice' '
-	test-revision-walking run-twice > run_twice_actual
+	test-revision-walking run-twice >run_twice_actual &&
 	test_cmp run_twice_expected run_twice_actual
 '
 
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
index 5d80a985fb..90da1c7ddc 100755
--- a/t/t0201-gettext-fallbacks.sh
+++ b/t/t0201-gettext-fallbacks.sh
@@ -50,17 +50,17 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
     cmdline="git am" &&
-    export cmdline;
+    export cmdline &&
     printf "When you have resolved this problem, run git am --resolved." >expect &&
-    eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual
+    eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual &&
     test_i18ncmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
     cmdline="git am" &&
-    export cmdline;
+    export cmdline &&
     printf "When you have resolved this problem, run \"git am --resolved\"." >expect &&
-    eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual
+    eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual &&
     test_i18ncmp expect actual
 '
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index fed877b20f..e3bf821694 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -75,8 +75,8 @@ test_expect_success \
      echo yomin >yomin &&
      git update-index --add yomin &&
      read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >4.out || return 1
-     git diff -U0 --no-index M.out 4.out >4diff.out
+     git ls-files --stage >4.out &&
+     test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
      compare_change 4diff.out expected &&
      check_cache_at yomin clean &&
      sum bozbar frotz nitfol >actual4.sum &&
@@ -94,8 +94,8 @@ test_expect_success \
      git update-index --add yomin &&
      echo yomin yomin >yomin &&
      read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >5.out || return 1
-     git diff -U0 --no-index M.out 5.out >5diff.out
+     git ls-files --stage >5.out &&
+     test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty &&
      sum bozbar frotz nitfol >actual5.sum &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index a72e700ae4..ab36b1eb72 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -274,7 +274,7 @@ test_expect_success 'setup blobs which are likely to delta' '
 '
 
 test_expect_success 'confirm that neither loose blob is a delta' '
-	cat >expect <<-EOF
+	cat >expect <<-EOF &&
 	$_z40
 	$_z40
 	EOF
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 938fc8bfd7..66dd28644f 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -677,7 +677,7 @@ test_expect_success 'invalid unit' '
 	echo 1auto >expect &&
 	git config aninvalid.unit >actual &&
 	test_cmp expect actual &&
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
 	EOF
 	test_must_fail git config --int --get aninvalid.unit 2>actual &&
@@ -1165,7 +1165,7 @@ test_expect_failure 'adding a key into an empty section reuses header' '
 	Qkey = value
 	EOF
 
-	git config section.key value
+	git config section.key value &&
 	test_cmp expect .git/config
 '
 
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 7eecfb836a..ac10875408 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -12,12 +12,11 @@ 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)' '
+	test_when_finished "rm -rf sub" &&
 	mkdir sub && (
-		cd sub && git init --shared=0400
+		cd sub &&
+		test_must_fail git init --shared=0400
 	)
-	ret="$?"
-	rm -rf sub
-	test $ret != "0"
 '
 
 modebits () {
@@ -33,7 +32,7 @@ do
 			git init --shared=1 &&
 			test 1 = "$(git config core.sharedrepository)"
 		) &&
-		actual=$(ls -l sub/.git/HEAD)
+		actual=$(ls -l sub/.git/HEAD) &&
 		case "$actual" in
 		-rw-rw-r--*)
 			: happy
@@ -90,10 +89,8 @@ do
 		rm -f .git/info/refs &&
 		git update-server-info &&
 		actual="$(modebits .git/info/refs)" &&
-		test "x$actual" = "x-$y" || {
-			ls -lt .git/info
-			false
-		}
+		verbose test "x$actual" = "x-$y"
+
 	'
 
 	umask 077 &&
@@ -102,10 +99,7 @@ do
 		rm -f .git/info/refs &&
 		git update-server-info &&
 		actual="$(modebits .git/info/refs)" &&
-		test "x$actual" = "x-$x" || {
-			ls -lt .git/info
-			false
-		}
+		verbose test "x$actual" = "x-$x"
 
 	'
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 3e500ed7da..7e10bcfe39 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -28,7 +28,7 @@ test_expect_success 'show-ref' '
 
 	>expect &&
 
-	test_must_fail git show-ref D >actual
+	test_must_fail git show-ref D >actual &&
 	test_cmp expect actual
 '
 
@@ -62,7 +62,7 @@ test_expect_success 'show-ref --verify' '
 	test_must_fail git show-ref --verify tags/A >actual &&
 	test_cmp expect actual &&
 
-	test_must_fail git show-ref --verify D >actual
+	test_must_fail git show-ref --verify D >actual &&
 	test_cmp expect actual
 '
 
@@ -78,7 +78,7 @@ test_expect_success 'show-ref --verify -q' '
 	test_must_fail git show-ref --verify -q tags/A >actual &&
 	test_cmp expect actual &&
 
-	test_must_fail git show-ref --verify -q D >actual
+	test_must_fail git show-ref --verify -q D >actual &&
 	test_cmp expect actual
 '
 
@@ -105,10 +105,10 @@ test_expect_success 'show-ref -d' '
 	test_cmp expect actual &&
 
 	git show-ref -d refs/heads/master >actual &&
-	test_cmp expect actual
+	test_cmp expect actual &&
 
 	git show-ref -d --verify refs/heads/master >actual &&
-	test_cmp expect actual
+	test_cmp expect actual &&
 
 	>expect &&
 
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 92f7298fba..193d55c3f4 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -191,7 +191,7 @@ test_expect_success 'unify index, two files remain' '
 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	one
 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0	two
 EOF
-	test_cmp ls-files.expect ls-files.actual
+	test_cmp ls-files.expect ls-files.actual &&
 
 	test-dump-split-index .git/index | sed "/^own/d" >actual &&
 	cat >expect <<EOF &&
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index b2798feef7..3fc484e8c3 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -294,7 +294,7 @@ one/a.1
 one/two/a.1
 three/a.1
 EOF
-	git ls-files -o -i --exclude "**/a.1" >actual
+	git ls-files -o -i --exclude "**/a.1" >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 6d3b828a95..580e158f99 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -55,14 +55,11 @@ test_expect_success 'git update-index --add to add various paths.' '
 	: >path9 &&
 	date >path10 &&
 	git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 &&
-	for i in 1 2
-	do
-		git init submod$i &&
-		(
-			cd submod$i && git commit --allow-empty -m "empty $i"
-		) || break
-	done &&
-	git update-index --add submod[12]
+	git init submod1 &&
+	git -C submod1 commit --allow-empty -m "empty 1" &&
+	git init submod2 &&
+	git -C submod2 commit --allow-empty -m "empty 2" &&
+	git update-index --add submod[12] &&
 	(
 		cd submod1 &&
 		git commit --allow-empty -m "empty 1 (updated)"
@@ -99,12 +96,12 @@ test_expect_success 'git ls-files -k to show killed files.' '
 '
 
 test_expect_success 'git ls-files -k output (w/o icase)' '
-	git ls-files -k >.output
+	git ls-files -k >.output &&
 	test_cmp .expected .output
 '
 
 test_expect_success 'git ls-files -k output (w/ icase)' '
-	git -c core.ignorecase=true ls-files -k >.output
+	git -c core.ignorecase=true ls-files -k >.output &&
 	test_cmp .expected .output
 '
 
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
index 7f41607c56..e59b0a32d6 100755
--- a/t/t3031-merge-criscross.sh
+++ b/t/t3031-merge-criscross.sh
@@ -32,7 +32,7 @@ test_expect_success 'setup repo with criss-cross history' '
 	do
 		echo $n > data/$n &&
 		n=$(($n+1)) ||
-		break
+		return 1
 	done &&
 
 	# check them in
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index 61c1f53d1b..44f378ce41 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -18,22 +18,16 @@ test_expect_success setup '
 
 	echo file >expected &&
 	mkdir sub &&
-	bad= &&
 	for n in 0 1 2 3 4 5
 	do
 		for m in 0 1 2 3 4 5 6 7 8 9
 		do
 			num=00$n$m &&
 			>sub/file-$num &&
-			echo file-$num >>expected || {
-				bad=t
-				break
-			}
-		done && test -z "$bad" || {
-			bad=t
-			break
-		}
-	done && test -z "$bad" &&
+			echo file-$num >>expected ||
+			return 1
+		done
+	done &&
 	git add . &&
 	git commit -m "add a bunch of files" &&
 
diff --git a/t/t3202-show-branch-octopus.sh b/t/t3202-show-branch-octopus.sh
index 0a5d5e669f..6adf47869c 100755
--- a/t/t3202-show-branch-octopus.sh
+++ b/t/t3202-show-branch-octopus.sh
@@ -19,7 +19,7 @@ test_expect_success 'setup' '
 		> file$i &&
 		git add file$i &&
 		test_tick &&
-		git commit -m branch$i || break
+		git commit -m branch$i || return 1
 	done
 
 '
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index b1ea64b213..54460beec4 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -51,15 +51,12 @@ test_expect_success 'deleting most notes with git-notes' '
 	num_notes=250 &&
 	i=0 &&
 	git rev-list HEAD |
-	while read sha1
+	while test $i -lt $num_notes && read sha1
 	do
 		i=$(($i + 1)) &&
-		if test $i -gt $num_notes
-		then
-			break
-		fi &&
 		test_tick &&
-		git notes remove "$sha1"
+		git notes remove "$sha1" ||
+		exit 1
 	done
 '
 
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 034eb35cdf..eed76cca55 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -950,7 +950,7 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
 	set_fake_editor &&
 	FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
 	FAKE_LINES="2 1" git rebase --edit-todo &&
-	git rebase --continue
+	git rebase --continue &&
 	test M = $(git cat-file commit HEAD^ | sed -ne \$p) &&
 	test L = $(git cat-file commit HEAD | sed -ne \$p)
 '
@@ -1007,7 +1007,7 @@ test_expect_success 'rebase -i with --strategy and -X' '
 '
 
 test_expect_success 'rebase -i error on commits with \ in message' '
-	current_head=$(git rev-parse HEAD)
+	current_head=$(git rev-parse HEAD) &&
 	test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&
 	test_commit TO-REMOVE will-conflict old-content &&
 	test_commit "\temp" will-conflict new-content dummy &&
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index 19eddadcf7..ff8c360cd5 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -24,7 +24,7 @@ test_expect_success setup '
 	git add file1 file2 &&
 	test_tick &&
 	git commit -m "Initial commit" &&
-	git branch diff-in-message
+	git branch diff-in-message &&
 
 	git checkout -b multi-line-subject &&
 	cat F >file2 &&
@@ -36,7 +36,7 @@ test_expect_success setup '
 
 	git checkout diff-in-message &&
 	echo "commit log message containing a diff" >G &&
-	echo "" >>G
+	echo "" >>G &&
 	cat G >file2 &&
 	git add file2 &&
 	git diff --cached >>G &&
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index 1d195fbd64..846f85c27e 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -24,7 +24,7 @@ test_expect_success 'setup of non-linear-history' '
 	test_commit c &&
 	git checkout b &&
 	test_commit d &&
-	test_commit e
+	test_commit e &&
 
 	git checkout c &&
 	test_commit g &&
@@ -33,7 +33,7 @@ test_expect_success 'setup of non-linear-history' '
 	cherry_pick gp g &&
 	test_commit i &&
 	git checkout b &&
-	test_commit f
+	test_commit f &&
 
 	git checkout d &&
 	test_commit n &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index e00d7d2b61..9d90d2c935 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -38,37 +38,37 @@ test_expect_success \
 
 test_expect_success \
     'Test that git rm --cached foo succeeds if the index matches the file' \
-    'echo content > foo
-     git add foo
+    'echo content >foo &&
+     git add foo &&
      git rm --cached foo'
 
 test_expect_success \
     'Test that git rm --cached foo succeeds if the index matches the file' \
-    'echo content > foo
-     git add foo
-     git commit -m foo
-     echo "other content" > foo
+    'echo content >foo &&
+     git add foo &&
+     git commit -m foo &&
+     echo "other content" >foo &&
      git rm --cached foo'
 
 test_expect_success \
     'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
-     echo content > foo
-     git add foo
-     git commit -m foo
-     echo "other content" > foo
-     git add foo
-     echo "yet another content" > foo
+     echo content >foo &&
+     git add foo &&
+     git commit -m foo --allow-empty &&
+     echo "other content" >foo &&
+     git add foo &&
+     echo "yet another content" >foo &&
      test_must_fail git rm --cached foo
 '
 
 test_expect_success \
     'Test that git rm --cached -f foo works in case where --cached only did not' \
-    'echo content > foo
-     git add foo
-     git commit -m foo
-     echo "other content" > foo
-     git add foo
-     echo "yet another content" > foo
+    'echo content >foo &&
+     git add foo &&
+     git commit -m foo --allow-empty &&
+     echo "other content" >foo &&
+     git add foo &&
+     echo "yet another content" >foo &&
      git rm --cached -f foo'
 
 test_expect_success \
@@ -170,7 +170,7 @@ test_expect_success 'but with -f it should work.' '
 	git rm -f foo baz &&
 	test ! -f foo &&
 	test ! -f baz &&
-	test_must_fail git ls-files --error-unmatch foo
+	test_must_fail git ls-files --error-unmatch foo &&
 	test_must_fail git ls-files --error-unmatch baz
 '
 
@@ -183,7 +183,7 @@ test_expect_success 'refuse to remove cached empty file with modifications' '
 
 test_expect_success 'remove intent-to-add file without --force' '
 	echo content >intent-to-add &&
-	git add -N intent-to-add
+	git add -N intent-to-add &&
 	git rm --cached intent-to-add
 '
 
@@ -201,7 +201,7 @@ test_expect_success 'Recursive without -r fails' '
 '
 
 test_expect_success 'Recursive with -r but dirty' '
-	echo qfwfq >>frotz/nitfol
+	echo qfwfq >>frotz/nitfol &&
 	test_must_fail git rm -r frotz &&
 	test -d frotz &&
 	test -f frotz/nitfol
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index a392f3d1d6..75cf3ff9bd 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -9,7 +9,7 @@ test_description='i18n settings and format-patch | am pipe'
 
 check_encoding () {
 	# Make sure characters are not corrupted
-	cnt="$1" header="$2" i=1 j=0 bad=0
+	cnt="$1" header="$2" i=1 j=0
 	while test "$i" -le $cnt
 	do
 		git format-patch --encoding=UTF-8 --stdout HEAD~$i..HEAD~$j |
@@ -20,14 +20,10 @@ check_encoding () {
 			grep "^encoding ISO8859-1" ;;
 		*)
 			grep "^encoding ISO8859-1"; test "$?" != 0 ;;
-		esac || {
-			bad=1
-			break
-		}
+		esac || return 1
 		j=$i
 		i=$(($i+1))
 	done
-	(exit $bad)
 }
 
 test_expect_success setup '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 256affce89..c39e50028f 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -802,7 +802,7 @@ test_expect_success '--no-signature suppresses format.signaturefile ' '
 '
 
 test_expect_success '--signature-file overrides format.signaturefile' '
-	cat >other-mail-signature <<-\EOF
+	cat >other-mail-signature <<-\EOF &&
 	Use this other signature instead of mail-signature.
 	EOF
 	test_config format.signaturefile mail-signature &&
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 2d030a4ec3..cb51d9f9d4 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -20,10 +20,7 @@ test_expect_success setup '
 test_expect_success 'detect rewrite' '
 
 	actual=$(git diff-files -B --summary test) &&
-	expr "$actual" : " rewrite test ([0-9]*%)$" || {
-		echo "Eh? <<$actual>>"
-		false
-	}
+	verbose expr "$actual" : " rewrite test ([0-9]*%)$"
 
 '
 
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index c4d733f5db..7e76018296 100755
--- a/t/t4024-diff-optimize-common.sh
+++ b/t/t4024-diff-optimize-common.sh
@@ -139,7 +139,7 @@ test_expect_success setup '
 		( printf C; zs $n ) >file-c$n &&
 		( echo D; zs $n ) >file-d$n &&
 
-		expect_pattern $n || break
+		expect_pattern $n || return 1
 
 	done >expect
 '
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index 4d20feacfe..2b32c4fbe6 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -111,9 +111,9 @@ test_expect_success 'unknown color slots are ignored (branch)' '
 '
 
 test_expect_success 'unknown color slots are ignored (status)' '
-	git config color.status.nosuchslotwilleverbedefined white || exit
-	git status
-	case $? in 0|1) : ok ;; *) false ;; esac
+	git config color.status.nosuchslotwilleverbedefined white &&
+	{ git status; ret=$?; } &&
+	case $ret in 0|1) : ok ;; *) false ;; esac
 '
 
 test_done
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index e8ae2a03fd..461f4bb583 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -29,67 +29,65 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'git diff-tree HEAD^ HEAD' '
-	git diff-tree --quiet HEAD^ HEAD >cnt
-	test $? = 1 && test_line_count = 0 cnt
+	test_expect_code 1 git diff-tree --quiet HEAD^ HEAD >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
-	git diff-tree --quiet HEAD^ HEAD -- a >cnt
-	test $? = 0 && test_line_count = 0 cnt
+	test_expect_code 0 git diff-tree --quiet HEAD^ HEAD -- a >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
-	git diff-tree --quiet HEAD^ HEAD -- b >cnt
-	test $? = 1 && test_line_count = 0 cnt
+	test_expect_code 1 git diff-tree --quiet HEAD^ HEAD -- b >cnt &&
+	test_line_count = 0 cnt
 '
 # this diff outputs one line: sha1 of the given head
 test_expect_success 'echo HEAD | git diff-tree --stdin' '
-	echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
-	test $? = 1 && test_line_count = 1 cnt
+	echo $(git rev-parse HEAD) |
+	test_expect_code 1 git diff-tree --quiet --stdin >cnt &&
+	test_line_count = 1 cnt
 '
 test_expect_success 'git diff-tree HEAD HEAD' '
-	git diff-tree --quiet HEAD HEAD >cnt
-	test $? = 0 && test_line_count = 0 cnt
+	test_expect_code 0 git diff-tree --quiet HEAD HEAD >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
-	git diff-files --quiet >cnt
-	test $? = 0 && test_line_count = 0 cnt
+	test_expect_code 0 git diff-files --quiet >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD' '
-	git diff-index --quiet --cached HEAD >cnt
-	test $? = 0 && test_line_count = 0 cnt
+	test_expect_code 0 git diff-index --quiet --cached HEAD >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
-	git diff-index --quiet --cached HEAD^ >cnt
-	test $? = 1 && test_line_count = 0 cnt
+	test_expect_code 1 git diff-index --quiet --cached HEAD^ >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
 	echo text >>b &&
 	echo 3 >c &&
-	git add . && {
-		git diff-index --quiet --cached HEAD^ >cnt
-		test $? = 1 && test_line_count = 0 cnt
-	}
+	git add . &&
+	test_expect_code 1 git diff-index --quiet --cached HEAD^ >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
-	git commit -m "text in b" && {
-		git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
-		test $? = 1 && test_line_count = 0 cnt
-	}
+	git commit -m "text in b" &&
+	test_expect_code 1 git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
-	git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
-	test $? = 0 && test_line_count = 0 cnt
+	test_expect_code 0 git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt &&
+	test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
-	echo 3 >>c && {
-		git diff-files --quiet >cnt
-		test $? = 1 && test_line_count = 0 cnt
-	}
+	echo 3 >>c &&
+	test_expect_code 1 git diff-files --quiet >cnt &&
+	test_line_count = 0 cnt
 '
+
 test_expect_success 'git diff-index --cached HEAD' '
-	git update-index c && {
-		git diff-index --quiet --cached HEAD >cnt
-		test $? = 1 && test_line_count = 0 cnt
-	}
+	git update-index c &&
+	test_expect_code 1 git diff-index --quiet --cached HEAD >cnt &&
+	test_line_count = 0 cnt
 '
 
 test_expect_success 'git diff, one file outside repo' '
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index e4328964a7..2d9731b52d 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -528,10 +528,12 @@ test_expect_success 'diff --submodule with objects referenced by alternates' '
 		sha1_before=$(git rev-parse --short HEAD)
 		echo b >b &&
 		git add b &&
-		git commit -m b
-		sha1_after=$(git rev-parse --short HEAD)
-		echo "Submodule sub $sha1_before..$sha1_after:
-  > b" >../expected
+		git commit -m b &&
+		sha1_after=$(git rev-parse --short HEAD) &&
+		{
+			echo "Submodule sub $sha1_before..$sha1_after:" &&
+			echo "  > b"
+		} >../expected
 	) &&
 	(cd super &&
 		(cd sub &&
@@ -539,7 +541,7 @@ test_expect_success 'diff --submodule with objects referenced by alternates' '
 			git checkout origin/master
 		) &&
 		git diff --submodule > ../actual
-	)
+	) &&
 	test_cmp expected actual
 '
 
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
index 25d50a654a..d0f14475ca 100755
--- a/t/t4046-diff-unmerged.sh
+++ b/t/t4046-diff-unmerged.sh
@@ -8,7 +8,7 @@ test_expect_success setup '
 	do
 		blob=$(echo $i | git hash-object --stdin) &&
 		eval "blob$i=$blob" &&
-		eval "m$i=\"100644 \$blob$i $i\"" || break
+		eval "m$i=\"100644 \$blob$i $i\"" || return 1
 	done &&
 	paths= &&
 	for b in o x
@@ -24,9 +24,9 @@ test_expect_success setup '
 				case "$b" in x) echo "$m1$p" ;; esac &&
 				case "$o" in x) echo "$m2$p" ;; esac &&
 				case "$t" in x) echo "$m3$p" ;; esac ||
-				break
-			done || break
-		done || break
+				return 1
+			done
+		done
 	done >ls-files-s.expect &&
 	git update-index --index-info <ls-files-s.expect &&
 	git ls-files -s >ls-files-s.actual &&
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 0d50dce97e..3b8b7921d6 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -374,7 +374,7 @@ test_expect_success 'later options override earlier options:' '
 	git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
 	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
 	git diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&
-	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC
+	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
 	git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&
 	test_cmp expect_diff_dirstat actual_diff_dirstat &&
 	git diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
index 5b594e878f..a34121740a 100755
--- a/t/t4049-diff-stat-count.sh
+++ b/t/t4049-diff-stat-count.sh
@@ -18,7 +18,7 @@ test_expect_success 'mode-only change show as a 0-line change' '
 	test_chmod +x b d &&
 	echo a >a &&
 	echo c >c &&
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	 a | 1 +
 	 b | 0
 	 ...
@@ -33,7 +33,7 @@ test_expect_success 'binary changes do not count in lines' '
 	echo a >a &&
 	echo c >c &&
 	cat "$TEST_DIRECTORY"/test-binary-1.png >d &&
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	 a | 1 +
 	 c | 1 +
 	 ...
@@ -55,7 +55,7 @@ test_expect_success 'exclude unmerged entries from total file count' '
 	done |
 	git update-index --index-info &&
 	echo d >d &&
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	 a | 1 +
 	 b | 1 +
 	 ...
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index babd2ead88..9f563db20a 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -94,7 +94,7 @@ EOF
 while read verb expect cmd args
 do
 	test_expect_success "$cmd $verb COLUMNS (big change)" '
-		COLUMNS=200 git $cmd $args >output
+		COLUMNS=200 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
 	'
@@ -102,7 +102,7 @@ do
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
-		COLUMNS=200 git $cmd $args --graph >output
+		COLUMNS=200 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
 	'
@@ -122,7 +122,7 @@ EOF
 while read verb expect cmd args
 do
 	test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
-		COLUMNS=40 git $cmd $args >output
+		COLUMNS=40 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
 	'
@@ -130,7 +130,7 @@ do
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
-		COLUMNS=40 git $cmd $args --graph >output
+		COLUMNS=40 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
 	'
@@ -150,7 +150,7 @@ EOF
 while read verb expect cmd args
 do
 	test_expect_success "$cmd $verb statGraphWidth config" '
-		git -c diff.statGraphWidth=26 $cmd $args >output
+		git -c diff.statGraphWidth=26 $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
 	'
@@ -158,7 +158,7 @@ do
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success "$cmd --graph $verb statGraphWidth config" '
-		git -c diff.statGraphWidth=26 $cmd $args --graph >output
+		git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
 	'
@@ -179,19 +179,19 @@ EOF
 while read cmd args
 do
 	test_expect_success "$cmd --stat=width with big change" '
-		git $cmd $args --stat=40 >output
+		git $cmd $args --stat=40 >output &&
 		grep " | " output >actual &&
 		test_cmp expect actual
 	'
 
 	test_expect_success "$cmd --stat-width=width with big change" '
-		git $cmd $args --stat-width=40 >output
+		git $cmd $args --stat-width=40 >output &&
 		grep " | " output >actual &&
 		test_cmp expect actual
 	'
 
 	test_expect_success "$cmd --stat-graph-width with big change" '
-		git $cmd $args --stat-graph-width=26 >output
+		git $cmd $args --stat-graph-width=26 >output &&
 		grep " | " output >actual &&
 		test_cmp expect actual
 	'
@@ -199,13 +199,13 @@ do
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success "$cmd --stat-width=width --graph with big change" '
-		git $cmd $args --stat-width=40 --graph >output
+		git $cmd $args --stat-width=40 --graph >output &&
 		grep " | " output >actual &&
 		test_cmp expect-graph actual
 	'
 
 	test_expect_success "$cmd --stat-graph-width --graph with big change" '
-		git $cmd $args --stat-graph-width=26 --graph >output
+		git $cmd $args --stat-graph-width=26 --graph >output &&
 		grep " | " output >actual &&
 		test_cmp expect-graph actual
 	'
@@ -265,7 +265,7 @@ EOF
 while read verb expect cmd args
 do
 	test_expect_success "$cmd $verb COLUMNS (long filename)" '
-		COLUMNS=200 git $cmd $args >output
+		COLUMNS=200 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
 	'
@@ -273,7 +273,7 @@ do
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
-		COLUMNS=200 git $cmd $args --graph >output
+		COLUMNS=200 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
 	'
@@ -294,7 +294,7 @@ while read verb expect cmd args
 do
 	test_expect_success COLUMNS_CAN_BE_1 \
 		"$cmd $verb prefix greater than COLUMNS (big change)" '
-		COLUMNS=1 git $cmd $args >output
+		COLUMNS=1 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
 	'
@@ -303,7 +303,7 @@ do
 
 	test_expect_success COLUMNS_CAN_BE_1 \
 		"$cmd --graph $verb prefix greater than COLUMNS (big change)" '
-		COLUMNS=1 git $cmd $args --graph >output
+		COLUMNS=1 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
 	'
@@ -320,7 +320,7 @@ EOF
 test_expect_success 'merge --stat respects COLUMNS (big change)' '
 	git checkout -b branch HEAD^^ &&
 	COLUMNS=100 git merge --stat --no-ff master^ >output &&
-	grep " | " output >actual
+	grep " | " output >actual &&
 	test_cmp expect actual
 '
 
@@ -329,7 +329,7 @@ cat >expect <<'EOF'
 EOF
 test_expect_success 'merge --stat respects COLUMNS (long filename)' '
 	COLUMNS=100 git merge --stat --no-ff master >output &&
-	grep " | " output >actual
+	grep " | " output >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 2ab3c48734..075ece6db1 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -17,8 +17,8 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'git diff --no-index directories' '
-	git diff --no-index a b >cnt
-	test $? = 1 && test_line_count = 14 cnt
+	test_expect_code 1 git diff --no-index a b >cnt &&
+	test_line_count = 14 cnt
 '
 
 test_expect_success 'git diff --no-index relative path outside repo' '
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
index 0843c87890..1d6efab3c5 100755
--- a/t/t4054-diff-bogus-tree.sh
+++ b/t/t4054-diff-bogus-tree.sh
@@ -16,7 +16,7 @@ test_expect_success 'create bogus tree' '
 test_expect_success 'create tree with matching file' '
 	echo bar >foo &&
 	git add foo &&
-	good_tree=$(git write-tree)
+	good_tree=$(git write-tree) &&
 	blob=$(git rev-parse :foo)
 '
 
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index c617c2a33d..32e3b0ee0b 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -18,7 +18,7 @@ test_expect_success setup '
 	cat victim >original &&
 	git update-index --add victim &&
 
-	: add to the head
+	# add to the head
 	for i in a b '"$L"' y
 	do
 		echo $i
@@ -27,7 +27,7 @@ test_expect_success setup '
 	git diff victim >add-a-patch.with &&
 	git diff --unified=0 >add-a-patch.without &&
 
-	: insert at line two
+	# insert at line two
 	for i in b a '"$L"' y
 	do
 		echo $i
@@ -36,7 +36,7 @@ test_expect_success setup '
 	git diff victim >insert-a-patch.with &&
 	git diff --unified=0 >insert-a-patch.without &&
 
-	: modify at the head
+	# modify at the head
 	for i in a '"$L"' y
 	do
 		echo $i
@@ -45,16 +45,16 @@ test_expect_success setup '
 	git diff victim >mod-a-patch.with &&
 	git diff --unified=0 >mod-a-patch.without &&
 
-	: remove from the head
+	# remove from the head
 	for i in '"$L"' y
 	do
 		echo $i
 	done >victim &&
 	cat victim >del-a-expect &&
-	git diff victim >del-a-patch.with
+	git diff victim >del-a-patch.with &&
 	git diff --unified=0 >del-a-patch.without &&
 
-	: add to the tail
+	# add to the tail
 	for i in b '"$L"' y z
 	do
 		echo $i
@@ -63,7 +63,7 @@ test_expect_success setup '
 	git diff victim >add-z-patch.with &&
 	git diff --unified=0 >add-z-patch.without &&
 
-	: modify at the tail
+	# modify at the tail
 	for i in b '"$L"' z
 	do
 		echo $i
@@ -72,16 +72,16 @@ test_expect_success setup '
 	git diff victim >mod-z-patch.with &&
 	git diff --unified=0 >mod-z-patch.without &&
 
-	: remove from the tail
+	# remove from the tail
 	for i in b '"$L"'
 	do
 		echo $i
 	done >victim &&
 	cat victim >del-z-expect &&
-	git diff victim >del-z-patch.with
-	git diff --unified=0 >del-z-patch.without &&
+	git diff victim >del-z-patch.with &&
+	git diff --unified=0 >del-z-patch.without
 
-	: done
+	# done
 '
 
 for with in with without
@@ -95,10 +95,7 @@ do
 		test_expect_success "apply $kind-patch $with context" '
 			cat original >victim &&
 			git update-index victim &&
-			git apply --index '"$u$kind-patch.$with"' || {
-				cat '"$kind-patch.$with"'
-				(exit 1)
-			} &&
+			git apply --index '"$u$kind-patch.$with"' &&
 			test_cmp '"$kind"'-expect victim
 		'
 	done
@@ -113,10 +110,7 @@ do
 	test_expect_success "apply non-git $kind-patch without context" '
 		cat original >victim &&
 		git update-index victim &&
-		git apply --unidiff-zero --index '"$kind-ng.without"' || {
-			cat '"$kind-ng.without"'
-			(exit 1)
-		} &&
+		git apply --unidiff-zero --index '"$kind-ng.without"' &&
 		test_cmp '"$kind"'-expect victim
 	'
 done
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index 8e15ecbdfd..d80187de94 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -56,23 +56,13 @@ test_expect_success 'apply --reject is incompatible with --3way' '
 
 test_expect_success 'apply without --reject should fail' '
 
-	if git apply patch.1
-	then
-		echo "Eh? Why?"
-		exit 1
-	fi
-
+	test_must_fail git apply patch.1 &&
 	test_cmp file1 saved.file1
 '
 
 test_expect_success 'apply without --reject should fail' '
 
-	if git apply --verbose patch.1
-	then
-		echo "Eh? Why?"
-		exit 1
-	fi
-
+	test_must_fail git apply --verbose patch.1 &&
 	test_cmp file1 saved.file1
 '
 
@@ -81,21 +71,11 @@ test_expect_success 'apply with --reject should fail but update the file' '
 	cat saved.file1 >file1 &&
 	rm -f file1.rej file2.rej &&
 
-	if git apply --reject patch.1
-	then
-		echo "succeeds with --reject?"
-		exit 1
-	fi
-
+	test_must_fail git apply --reject patch.1 &&
 	test_cmp file1 expected &&
 
 	cat file1.rej &&
-
-	if test -f file2.rej
-	then
-		echo "file2 should not have been touched"
-		exit 1
-	fi
+	test_path_is_missing file2.rej
 '
 
 test_expect_success 'apply with --reject should fail but update the file' '
@@ -103,25 +83,12 @@ test_expect_success 'apply with --reject should fail but update the file' '
 	cat saved.file1 >file1 &&
 	rm -f file1.rej file2.rej file2 &&
 
-	if git apply --reject patch.2 >rejects
-	then
-		echo "succeeds with --reject?"
-		exit 1
-	fi
-
-	test -f file1 && {
-		echo "file1 still exists?"
-		exit 1
-	}
+	test_must_fail git apply --reject patch.2 >rejects &&
+	test_path_is_missing file1 &&
 	test_cmp file2 expected &&
 
 	cat file2.rej &&
-
-	if test -f file1.rej
-	then
-		echo "file2 should not have been touched"
-		exit 1
-	fi
+	test_path_is_missing file1.rej
 
 '
 
@@ -130,25 +97,12 @@ test_expect_success 'the same test with --verbose' '
 	cat saved.file1 >file1 &&
 	rm -f file1.rej file2.rej file2 &&
 
-	if git apply --reject --verbose patch.2 >rejects
-	then
-		echo "succeeds with --reject?"
-		exit 1
-	fi
-
-	test -f file1 && {
-		echo "file1 still exists?"
-		exit 1
-	}
+	test_must_fail git apply --reject --verbose patch.2 >rejects &&
+	test_path_is_missing file1 &&
 	test_cmp file2 expected &&
 
 	cat file2.rej &&
-
-	if test -f file1.rej
-	then
-		echo "file2 should not have been touched"
-		exit 1
-	fi
+	test_path_is_missing file1.rej
 
 '
 
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index c6474de4c8..d350065f25 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -99,9 +99,8 @@ test_expect_success 'whitespace=warn, default rule' '
 
 test_expect_success 'whitespace=error-all, default rule' '
 
-	apply_patch --whitespace=error-all && return 1
-	test -s target && return 1
-	: happy
+	test_must_fail apply_patch --whitespace=error-all &&
+	! test -s target
 
 '
 
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 1176bcccf3..8d90634ab8 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -20,7 +20,7 @@ test_expect_success setup '
 		echo $i >otherfile-$i &&
 		git add otherfile-$i &&
 		test_tick &&
-		git commit -a -m $i || break
+		git commit -a -m $i || return 1
 	done &&
 	git format-patch --no-numbered initial &&
 	git checkout -b side initial &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index f111705e70..1b2e981a00 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -113,11 +113,7 @@ test_expect_success 'diff-filter=M' '
 
 	actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
 	expect=$(echo second) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	verbose test "$actual" = "$expect"
 
 '
 
@@ -125,11 +121,7 @@ test_expect_success 'diff-filter=D' '
 
 	actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
 	expect=$(echo sixth ; echo third) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	verbose test "$actual" = "$expect"
 
 '
 
@@ -137,11 +129,7 @@ test_expect_success 'diff-filter=R' '
 
 	actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
 	expect=$(echo third) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	verbose test "$actual" = "$expect"
 
 '
 
@@ -149,11 +137,7 @@ test_expect_success 'diff-filter=C' '
 
 	actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
 	expect=$(echo fourth) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	verbose test "$actual" = "$expect"
 
 '
 
@@ -161,11 +145,7 @@ test_expect_success 'git log --follow' '
 
 	actual=$(git log --follow --pretty="format:%s" ichi) &&
 	expect=$(echo third ; echo second ; echo initial) &&
-	test "$actual" = "$expect" || {
-		echo Oops
-		echo "Actual: $actual"
-		false
-	}
+	verbose test "$actual" = "$expect"
 
 '
 
@@ -481,7 +461,7 @@ test_expect_success 'log.decorate configuration' '
 	git log --oneline --no-decorate >actual &&
 	test_cmp expect.none actual &&
 	git log --oneline --decorate >actual &&
-	test_cmp expect.short actual
+	test_cmp expect.short actual &&
 
 	test_unconfig log.decorate &&
 	git log --pretty=raw >expect.raw &&
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 67bd8ec020..22aa8b7c0e 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -61,7 +61,7 @@ test_expect_success 'unparsable dates produce sentinel value' '
 test_expect_success 'unparsable dates produce sentinel value (%ad)' '
 	commit=$(munge_author_date HEAD totally_bogus) &&
 	echo >expect &&
-	git log -1 --format=%ad $commit >actual
+	git log -1 --format=%ad $commit >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index 305bcac6b7..654addaae3 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -66,8 +66,10 @@ test_expect_success UNZIP 'zip archive of empty tree is empty' '
 	# handle the empty repo at all, making our later check of its exit code
 	# a no-op). But we cannot do anything reasonable except skip the test
 	# on such platforms anyway, and this is the moral equivalent.
-	"$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/empty.zip
-	expect_code=$?
+	{
+		"$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/empty.zip
+		expect_code=$?
+	} &&
 
 	git archive --format=zip HEAD >empty.zip &&
 	make_dir extract &&
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index 60df10f46a..e97cfb2ab8 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -34,7 +34,7 @@ do
 		if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
 		then
 			check_mailinfo $mail --no-inbody-headers
-		fi
+		fi &&
 		if test -f "$TEST_DIRECTORY"/t5100/msg$mail--message-id
 		then
 			check_mailinfo $mail --message-id
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index bd37f040b6..692d71738b 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -414,7 +414,7 @@ test_expect_success 'setup tests for the --stdin parameter' '
 	do
 		git tag $head $head
 	done &&
-	cat >input <<-\EOF
+	cat >input <<-\EOF &&
 	refs/heads/C
 	refs/heads/A
 	refs/heads/D
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index f30c03885c..4ca48f0276 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -139,8 +139,8 @@ EOF
 '
 
 test_expect_success 'new clone fetch master and tags' '
-	git branch -D cat
-	rm -f $U
+	test_might_fail git branch -D cat &&
+	rm -f $U &&
 	(
 		mkdir clone2 &&
 		cd clone2 &&
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 17c6330f98..7a8499ce66 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -579,7 +579,7 @@ test_expect_success 'update with arguments' '
 		cd one &&
 		for b in $(git branch -r)
 		do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 		done &&
 		git remote add manduca ../mirror &&
 		git remote add megaloprepus ../mirror &&
@@ -622,7 +622,7 @@ test_expect_success 'update default' '
 		cd one &&
 		for b in $(git branch -r)
 		do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 		done &&
 		git config remote.drosophila.skipDefaultUpdate true &&
 		git remote update default &&
@@ -642,7 +642,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
 		cd one &&
 		for b in $(git branch -r)
 		do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 		done &&
 		git config remotes.default "$(printf "\t drosophila  \n")" &&
 		git remote update default &&
@@ -656,7 +656,7 @@ test_expect_success 'update (with remotes.default defined)' '
 		cd one &&
 		for b in $(git branch -r)
 		do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 		done &&
 		git config remotes.default "drosophila" &&
 		git remote update &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index d78f3201f4..0ba9db0884 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -124,7 +124,7 @@ test_expect_success 'fetch --prune handles overlapping refspecs' '
 	git rev-parse origin/master &&
 	git rev-parse origin/pr/42 &&
 
-	git config --unset-all remote.origin.fetch
+	git config --unset-all remote.origin.fetch &&
 	git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
 	git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
 
@@ -596,7 +596,7 @@ test_configured_prune () {
 			test_unconfig remote.origin.prune &&
 			git fetch &&
 			git rev-parse --verify refs/remotes/origin/newbranch
-		)
+		) &&
 
 		# now remove it
 		git branch -d newbranch &&
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 321c3e5234..3bd9759e0f 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -103,8 +103,10 @@ test_expect_success 'confuses pattern as remote when no remote specified' '
 '
 
 test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
-	git ls-remote --exit-code ./no-such-repository ;# not &&
-	status=$? &&
+	{
+		git ls-remote --exit-code ./no-such-repository
+		status=$?
+	} &&
 	test $status != 2 && test $status != 0
 '
 
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 0f8140957f..4b4b6673b8 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -120,7 +120,7 @@ test_expect_success 'git fetch --all (skipFetchAll)' '
 	(cd test4 &&
 	 for b in $(git branch -r)
 	 do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 	 done &&
 	 git remote add three ../three &&
 	 git config remote.three.skipFetchAll true &&
@@ -144,7 +144,7 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
 	(cd test4 &&
 	 for b in $(git branch -r)
 	 do
-		git branch -r -d $b || break
+		git branch -r -d $b || exit 1
 	 done &&
 	 git fetch --multiple one two three &&
 	 git branch -r > output &&
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index ca5b027c55..a4532b00d6 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -216,7 +216,7 @@ test_expect_success "Recursion stops when no new submodule commits are fetched"
 	head2=$(git rev-parse --short HEAD) &&
 	echo "Fetching submodule submodule" > expect.out.sub &&
 	echo "From $pwd/." > expect.err.sub &&
-	echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub
+	echo "   $head1..$head2  master     -> origin/master" >>expect.err.sub &&
 	head -2 expect.err >> expect.err.sub &&
 	(
 		cd downstream &&
@@ -315,7 +315,7 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
 		) &&
 		head1=$(git rev-parse --short HEAD^) &&
 		git add subdir/deepsubmodule &&
-		git commit -m "new deepsubmodule"
+		git commit -m "new deepsubmodule" &&
 		head2=$(git rev-parse --short HEAD) &&
 		echo "From $pwd/submodule" > ../expect.err.sub &&
 		echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
@@ -337,7 +337,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
 	head2=$(git rev-parse --short HEAD) &&
 	tail -2 expect.err > expect.err.deepsub &&
 	echo "From $pwd/." > expect.err &&
-	echo "   $head1..$head2  master     -> origin/master" >> expect.err
+	echo "   $head1..$head2  master     -> origin/master" >>expect.err &&
 	cat expect.err.sub >> expect.err &&
 	cat expect.err.deepsub >> expect.err &&
 	(
@@ -387,7 +387,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
 	git commit -m "new submodule" &&
 	head2=$(git rev-parse --short HEAD) &&
 	echo "From $pwd/." > expect.err.2 &&
-	echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+	echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
 	head -2 expect.err >> expect.err.2 &&
 	(
 		cd downstream &&
@@ -415,7 +415,7 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
 	git commit -m "new submodule" &&
 	head2=$(git rev-parse --short HEAD) &&
 	echo "From $pwd/." > expect.err.2 &&
-	echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+	echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
 	head -2 expect.err >> expect.err.2 &&
 	(
 		cd downstream &&
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 445bb5fe26..6507487c1a 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -104,7 +104,7 @@ test_expect_success 'push fails when commit on multiple branches if one branch h
 '
 
 test_expect_success 'push succeeds if submodule has no remote and is on the first superproject commit' '
-	git init --bare a
+	git init --bare a &&
 	git clone a a1 &&
 	(
 		cd a1 &&
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
index ba20d83333..c402d8d3d7 100755
--- a/t/t5533-push-cas.sh
+++ b/t/t5533-push-cas.sh
@@ -14,7 +14,7 @@ setup_srcdst_basic () {
 }
 
 test_expect_success setup '
-	: create template repository
+	# create template repository
 	test_commit A &&
 	test_commit B &&
 	test_commit C
@@ -159,7 +159,7 @@ test_expect_success 'cover everything with default force-with-lease (protected)'
 	(
 		cd src &&
 		git branch naster master^
-	)
+	) &&
 	git ls-remote src refs/heads/\* >expect &&
 	(
 		cd dst &&
@@ -174,7 +174,7 @@ test_expect_success 'cover everything with default force-with-lease (allowed)' '
 	(
 		cd src &&
 		git branch naster master^
-	)
+	) &&
 	(
 		cd dst &&
 		git fetch &&
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
index 8d7b3c57e3..88ff5a49e4 100755
--- a/t/t5540-http-push-webdav.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -169,7 +169,7 @@ test_expect_failure 'push to password-protected repository (no user in URL)' '
 	test_commit pw-nouser &&
 	set_askpass user@host pass@host &&
 	git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
-	expect_askpass both user@host
+	expect_askpass both user@host &&
 	git rev-parse --verify HEAD >expect &&
 	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
 		rev-parse --verify HEAD >actual &&
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 1ecb5881ac..9cf27e8c99 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -158,7 +158,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
 	# create a dissimilarly-named remote ref so that git is unable to match the
 	# two refs (viz. local, remote) unless an explicit refspec is provided.
-	git push origin master:retsam
+	git push origin master:retsam &&
 
 	echo "change changed" > path2 &&
 	git commit -a -m path2 --amend &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 2731ad4cea..3d11b7a6bb 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -15,7 +15,7 @@ test_expect_success 'setup repository' '
 	git config push.default matching &&
 	echo content1 >file &&
 	git add file &&
-	git commit -m one
+	git commit -m one &&
 	echo content2 >file &&
 	git add file &&
 	git commit -m two
@@ -184,8 +184,8 @@ test_expect_success 'fetch can handle previously-fetched .idx files' '
 '
 
 test_expect_success 'did not use upload-pack service' '
-	grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
-	: >exp
+	test_might_fail grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act &&
+	: >exp &&
 	test_cmp exp act
 '
 
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index df47851752..66439e58fc 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -83,7 +83,7 @@ test_expect_success 'clone http repository' '
 test_expect_success 'fetch changes via http' '
 	echo content >>file &&
 	git commit -a -m two &&
-	git push public
+	git push public &&
 	(cd clone && git pull) &&
 	test_cmp file clone/file
 '
@@ -169,7 +169,7 @@ test_expect_success 'GIT_SMART_HTTP can disable smart http' '
 '
 
 test_expect_success 'invalid Content-Type rejected' '
-	test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual
+	test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
 	grep "not valid:" actual
 '
 
@@ -209,7 +209,7 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set
 	git config http.cookiefile cookies.txt &&
 	git config http.savecookies true &&
 	git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
-	tail -3 cookies.txt > cookies_tail.txt
+	tail -3 cookies.txt >cookies_tail.txt &&
 	test_cmp expect_cookies.txt cookies_tail.txt
 '
 
diff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh
index 6f1ea984d4..7ace2535c8 100755
--- a/t/t5709-clone-refspec.sh
+++ b/t/t5709-clone-refspec.sh
@@ -147,7 +147,7 @@ test_expect_success '--single-branch with detached' '
 		git for-each-ref refs/remotes/origin |
 		sed -e "/HEAD$/d" \
 		    -e "s|/remotes/origin/|/heads/|" >../actual
-	)
+	) &&
 	# nothing
 	>expect &&
 	test_cmp expect actual
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 1f58b46e1f..3e752ce032 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -42,7 +42,7 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' '
 	test_tick &&
 	git commit -m that &&
 
-	ONE=$(git rev-parse HEAD:one)
+	ONE=$(git rev-parse HEAD:one) &&
 	git rev-list --objects HEAD two >output &&
 	grep "$ONE two/three" output &&
 	! grep one output
@@ -85,7 +85,7 @@ test_expect_success 'rev-list can show index objects' '
 	#   - we do not show the root tree; since we updated the index, it
 	#     does not have a valid cache tree
 	#
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	8e4020bb5a8d8c873b25de15933e75cc0fc275df one
 	d9d3a7417b9605cfd88ee6306b28dadc29e6ab08 only-in-index
 	9200b628cf9dc883a85a7abc8d6e6730baee589c two
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index a02a45afd2..b77d4c97c1 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -45,7 +45,7 @@ test_expect_success 'setup' '
 	head2=$(git rev-parse --verify HEAD) &&
 	head2_short=$(git rev-parse --verify --short $head2) &&
 	tree2=$(git rev-parse --verify HEAD:) &&
-	tree2_short=$(git rev-parse --verify --short $tree2)
+	tree2_short=$(git rev-parse --verify --short $tree2) &&
 	git config --unset i18n.commitEncoding
 '
 
@@ -358,10 +358,7 @@ test_expect_success 'empty email' '
 	test_tick &&
 	C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
 	A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
-	test "$A" = "A U Thor,,Thu Apr 7 15:14:13 2005 -0700" || {
-		echo "Eh? $A" >failure
-		false
-	}
+	verbose test "$A" = "A U Thor,,Thu Apr 7 15:14:13 2005 -0700"
 '
 
 test_expect_success 'del LF before empty (1)' '
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index fde5e712eb..b89cd6b07a 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -95,10 +95,7 @@ check_outcome () {
 		git log --pretty="$FMT" --parents $param |
 		unnote >actual &&
 		sed -e "s/^.*	\([^ ]*\) .*/\1/" >check <actual &&
-		test_cmp expect check || {
-			cat actual
-			false
-		}
+		test_cmp expect check
 	'
 }
 
@@ -121,10 +118,7 @@ test_expect_success 'full history simplification without parent' '
 	git log --pretty="$FMT" --full-history E -- lost |
 	unnote >actual &&
 	sed -e "s/^.*	\([^ ]*\) .*/\1/" >check <actual &&
-	test_cmp expect check || {
-		cat actual
-		false
-	}
+	test_cmp expect check
 '
 
 test_expect_success '--full-diff is not affected by --parents' '
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index a89dfbef08..05ebba7afa 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -685,7 +685,7 @@ test_expect_success 'setup avoid unnecessary update, dir->(file,nothing)' '
 	git add -A &&
 	git commit -mA &&
 
-	git checkout -b side
+	git checkout -b side &&
 	git rm -rf df &&
 	git commit -mB &&
 
@@ -716,7 +716,7 @@ test_expect_success 'setup avoid unnecessary update, modify/delete' '
 	git add -A &&
 	git commit -mA &&
 
-	git checkout -b side
+	git checkout -b side &&
 	git rm -f file &&
 	git commit -m "Delete file" &&
 
@@ -745,7 +745,7 @@ test_expect_success 'setup avoid unnecessary update, rename/add-dest' '
 	git add -A &&
 	git commit -mA &&
 
-	git checkout -b side
+	git checkout -b side &&
 	cp file newfile &&
 	git add -A &&
 	git commit -m "Add file copy" &&
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 5e439972be..3c21938a68 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -11,7 +11,7 @@ test_expect_success setup '
 
 	for f in text binary union
 	do
-		echo Initial >$f && git add $f || break
+		echo Initial >$f && git add $f || return 1
 	done &&
 	test_tick &&
 	git commit -m Initial &&
@@ -19,7 +19,7 @@ test_expect_success setup '
 	git branch side &&
 	for f in text binary union
 	do
-		echo Master >>$f && git add $f || break
+		echo Master >>$f && git add $f || return 1
 	done &&
 	test_tick &&
 	git commit -m Master &&
@@ -27,7 +27,7 @@ test_expect_success setup '
 	git checkout side &&
 	for f in text binary union
 	do
-		echo Side >>$f && git add $f || break
+		echo Side >>$f && git add $f || return 1
 	done &&
 	test_tick &&
 	git commit -m Side &&
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index c518e9c30c..7763c1ba98 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -83,7 +83,7 @@ test_expect_success 'merge fast-forward octopus' '
 
 	git reset --hard c0 &&
 	test_tick &&
-	git merge c1 c2
+	git merge c1 c2 &&
 	expect=$(git rev-parse c2) &&
 	current=$(git rev-parse HEAD) &&
 	test "$expect" = "$current"
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index e6abe65d5c..06b4868109 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -52,15 +52,8 @@ test_expect_success 'bisect starts with only one bad' '
 test_expect_success 'bisect does not start with only one good' '
 	git bisect reset &&
 	git bisect start &&
-	git bisect good $HASH1 || return 1
-
-	if git bisect next
-	then
-		echo Oops, should have failed.
-		false
-	else
-		:
-	fi
+	git bisect good $HASH1 &&
+	test_must_fail git bisect next
 '
 
 test_expect_success 'bisect start with one bad and good' '
@@ -191,34 +184,27 @@ test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' '
 # but $HASH2 is bad,
 # so we should find $HASH2 as the first bad commit
 test_expect_success 'bisect skip: successful result' '
+	test_when_finished git bisect reset &&
 	git bisect reset &&
 	git bisect start $HASH4 $HASH1 &&
 	git bisect skip &&
 	git bisect bad > my_bisect_log.txt &&
-	grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
-	git bisect reset
+	grep "$HASH2 is the first bad commit" my_bisect_log.txt
 '
 
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
 # so we should not be able to tell the first bad commit
 # among $HASH2, $HASH3 and $HASH4
 test_expect_success 'bisect skip: cannot tell between 3 commits' '
+	test_when_finished git bisect reset &&
 	git bisect start $HASH4 $HASH1 &&
-	git bisect skip || return 1
-
-	if git bisect skip > my_bisect_log.txt
-	then
-		echo Oops, should have failed.
-		false
-	else
-		test $? -eq 2 &&
-		grep "first bad commit could be any of" my_bisect_log.txt &&
-		! grep $HASH1 my_bisect_log.txt &&
-		grep $HASH2 my_bisect_log.txt &&
-		grep $HASH3 my_bisect_log.txt &&
-		grep $HASH4 my_bisect_log.txt &&
-		git bisect reset
-	fi
+	git bisect skip &&
+	test_expect_code 2 git bisect skip >my_bisect_log.txt &&
+	grep "first bad commit could be any of" my_bisect_log.txt &&
+	! grep $HASH1 my_bisect_log.txt &&
+	grep $HASH2 my_bisect_log.txt &&
+	grep $HASH3 my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt
 '
 
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3
@@ -226,22 +212,15 @@ test_expect_success 'bisect skip: cannot tell between 3 commits' '
 # so we should not be able to tell the first bad commit
 # among $HASH3 and $HASH4
 test_expect_success 'bisect skip: cannot tell between 2 commits' '
+	test_when_finished git bisect reset &&
 	git bisect start $HASH4 $HASH1 &&
-	git bisect skip || return 1
-
-	if git bisect good > my_bisect_log.txt
-	then
-		echo Oops, should have failed.
-		false
-	else
-		test $? -eq 2 &&
-		grep "first bad commit could be any of" my_bisect_log.txt &&
-		! grep $HASH1 my_bisect_log.txt &&
-		! grep $HASH2 my_bisect_log.txt &&
-		grep $HASH3 my_bisect_log.txt &&
-		grep $HASH4 my_bisect_log.txt &&
-		git bisect reset
-	fi
+	git bisect skip &&
+	test_expect_code 2 git bisect good >my_bisect_log.txt &&
+	grep "first bad commit could be any of" my_bisect_log.txt &&
+	! grep $HASH1 my_bisect_log.txt &&
+	! grep $HASH2 my_bisect_log.txt &&
+	grep $HASH3 my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt
 '
 
 # $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3
@@ -249,24 +228,18 @@ test_expect_success 'bisect skip: cannot tell between 2 commits' '
 # so we should not be able to tell the first bad commit
 # among $HASH3 and $HASH4
 test_expect_success 'bisect skip: with commit both bad and skipped' '
+	test_when_finished git bisect reset &&
 	git bisect start &&
 	git bisect skip &&
 	git bisect bad &&
 	git bisect good $HASH1 &&
 	git bisect skip &&
-	if git bisect good > my_bisect_log.txt
-	then
-		echo Oops, should have failed.
-		false
-	else
-		test $? -eq 2 &&
-		grep "first bad commit could be any of" my_bisect_log.txt &&
-		! grep $HASH1 my_bisect_log.txt &&
-		! grep $HASH2 my_bisect_log.txt &&
-		grep $HASH3 my_bisect_log.txt &&
-		grep $HASH4 my_bisect_log.txt &&
-		git bisect reset
-	fi
+	test_expect_code 2 git bisect good >my_bisect_log.txt &&
+	grep "first bad commit could be any of" my_bisect_log.txt &&
+	! grep $HASH1 my_bisect_log.txt &&
+	! grep $HASH2 my_bisect_log.txt &&
+	grep $HASH3 my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt
 '
 
 # We want to automatically find the commit that
@@ -601,8 +574,7 @@ test_expect_success 'test bisection on bare repo - --no-checkout specified' '
 		git bisect bad $HASH4 &&
 		git bisect run eval \
 			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
-			>../nocheckout.log &&
-		git bisect reset
+			>../nocheckout.log
 	) &&
 	grep "$HASH3 is the first bad commit" nocheckout.log
 '
@@ -617,8 +589,7 @@ test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
 		git bisect bad $HASH4 &&
 		git bisect run eval \
 			"test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
-			>../defaulted.log &&
-		git bisect reset
+			>../defaulted.log
 	) &&
 	grep "$HASH3 is the first bad commit" defaulted.log
 '
@@ -642,14 +613,14 @@ test_expect_success 'broken branch creation' '
 	mkdir missing &&
 	:> missing/MISSING &&
 	git add missing/MISSING &&
-	git commit -m "6(broken): Added file that will be deleted"
+	git commit -m "6(broken): Added file that will be deleted" &&
 	git tag BROKEN_HASH6 &&
 	add_line_into_file "7(broken): second line on a broken branch" hello2 &&
 	git tag BROKEN_HASH7 &&
 	add_line_into_file "8(broken): third line on a broken branch" hello2 &&
 	git tag BROKEN_HASH8 &&
 	git rm missing/MISSING &&
-	git commit -m "9(broken): Remove missing file"
+	git commit -m "9(broken): Remove missing file" &&
 	git tag BROKEN_HASH9 &&
 	rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
 '
diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh
index 15beecc3c6..0f79268917 100755
--- a/t/t6032-merge-large-rename.sh
+++ b/t/t6032-merge-large-rename.sh
@@ -28,10 +28,10 @@ make_text() {
 
 test_rename() {
 	test_expect_success "rename ($1, $2)" '
-	n='$1'
-	expect='$2'
+	n='$1' &&
+	expect='$2' &&
 	git checkout -f master &&
-	git branch -D test$n || true &&
+	test_might_fail git branch -D test$n &&
 	git reset --hard initial &&
 	for i in $(count $n); do
 		make_text $i initial initial >$i
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
index 65be95fbaa..89871aa5b0 100755
--- a/t/t6034-merge-rename-nocruft.sh
+++ b/t/t6034-merge-rename-nocruft.sh
@@ -73,33 +73,12 @@ test_expect_success 'merge white into red (A->B,M->N)' \
 '
 	git checkout -b red-white red &&
 	git merge white &&
-	git write-tree >/dev/null || {
-		echo "BAD: merge did not complete"
-		return 1
-	}
-
-	test -f B || {
-		echo "BAD: B does not exist in working directory"
-		return 1
-	}
-	test -f N || {
-		echo "BAD: N does not exist in working directory"
-		return 1
-	}
-	test -f R || {
-		echo "BAD: R does not exist in working directory"
-		return 1
-	}
-
-	test -f A && {
-		echo "BAD: A still exists in working directory"
-		return 1
-	}
-	test -f M && {
-		echo "BAD: M still exists in working directory"
-		return 1
-	}
-	return 0
+	git write-tree &&
+	test_path_is_file B &&
+	test_path_is_file N &&
+	test_path_is_file R &&
+	test_path_is_missing A &&
+	test_path_is_missing M
 '
 
 # This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
@@ -108,32 +87,13 @@ test_expect_success 'merge blue into white (A->B, mod A, A untracked)' \
 	git checkout -b white-blue white &&
 	echo dirty >A &&
 	git merge blue &&
-	git write-tree >/dev/null || {
-		echo "BAD: merge did not complete"
-		return 1
-	}
-
-	test -f A || {
-		echo "BAD: A does not exist in working directory"
-		return 1
-	}
-	test `cat A` = dirty || {
-		echo "BAD: A content is wrong"
-		return 1
-	}
-	test -f B || {
-		echo "BAD: B does not exist in working directory"
-		return 1
-	}
-	test -f N || {
-		echo "BAD: N does not exist in working directory"
-		return 1
-	}
-	test -f M && {
-		echo "BAD: M still exists in working directory"
-		return 1
-	}
-	return 0
+	git write-tree &&
+	test_path_is_file A &&
+	echo dirty >expect &&
+	test_cmp expect A &&
+	test_path_is_file B &&
+	test_path_is_file N &&
+	test_path_is_missing M
 '
 
 test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index a86087be95..9d6621c056 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -86,7 +86,7 @@ test_expect_success 'setup criss-cross + rename merges with basic modification'
 	rm -rf .git &&
 	git init &&
 
-	ten="0 1 2 3 4 5 6 7 8 9"
+	ten="0 1 2 3 4 5 6 7 8 9" &&
 	for i in $ten
 	do
 		echo line $i in a sample file
@@ -195,12 +195,7 @@ test_expect_success 'git detects differently handled merges conflict' '
 	git reset --hard &&
 	git checkout D^0 &&
 
-	git merge -s recursive E^0 && {
-		echo "BAD: should have conflicted"
-		test "Incorrectly merged content" = "$(cat new_a)" &&
-			echo "BAD: Silently accepted wrong content"
-		return 1
-	}
+	test_must_fail git merge -s recursive E^0 &&
 
 	test 3 = $(git ls-files -s | wc -l) &&
 	test 3 = $(git ls-files -u | wc -l) &&
@@ -533,7 +528,7 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
 
 	test $(git rev-parse :3:a) = $(git rev-parse B:a) &&
 	test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) &&
-	test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file)
+	test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
 	test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
 
 	test -f a~D^0
diff --git a/t/t6039-merge-ignorecase.sh b/t/t6039-merge-ignorecase.sh
index a977653147..531850d834 100755
--- a/t/t6039-merge-ignorecase.sh
+++ b/t/t6039-merge-ignorecase.sh
@@ -15,7 +15,7 @@ test_expect_success 'merge with case-changing rename' '
 	>TestCase &&
 	git add TestCase &&
 	git commit -m "add TestCase" &&
-	git tag baseline
+	git tag baseline &&
 	git checkout -b with-camel &&
 	>foo &&
 	git add foo &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 7ac8fd06c3..3d5c238c81 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -12,10 +12,9 @@ advance () {
 }
 
 test_expect_success setup '
-	for i in a b c;
-	do
-		advance $i || break
-	done &&
+	advance a &&
+	advance b &&
+	advance c &&
 	git clone . test &&
 	(
 		cd test &&
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
index 88b84dfa73..32474c23d3 100755
--- a/t/t6111-rev-list-treesame.sh
+++ b/t/t6111-rev-list-treesame.sh
@@ -58,7 +58,7 @@ test_expect_success setup '
 
 	git checkout master &&
 	test_tick && git merge --no-ff fiddler-branch &&
-	note K
+	note K &&
 
 	test_commit "file=Part 1" file "Part 1" L &&
 
@@ -92,10 +92,7 @@ check_outcome () {
 		git log --format="$FMT" $param |
 		unnote >actual &&
 		sed -e "$munge_actual" <actual >check &&
-		test_cmp expect check || {
-			cat actual
-			false
-		}
+		test_cmp expect check
 	'
 }
 
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
index 62049be0c7..e1e1b1fa38 100755
--- a/t/t6132-pathspec-exclude.sh
+++ b/t/t6132-pathspec-exclude.sh
@@ -30,7 +30,7 @@ test_expect_success 'exclude only should error out' '
 '
 
 test_expect_success 't_e_i() exclude sub' '
-	git log --oneline --format=%s -- . ":(exclude)sub" >actual
+	git log --oneline --format=%s -- . ":(exclude)sub" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 file
@@ -39,7 +39,7 @@ EOF
 '
 
 test_expect_success 't_e_i() exclude sub/sub/file' '
-	git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual
+	git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 sub/sub/sub/file
@@ -51,7 +51,7 @@ EOF
 '
 
 test_expect_success 't_e_i() exclude sub using mnemonic' '
-	git log --oneline --format=%s -- . ":!sub" >actual
+	git log --oneline --format=%s -- . ":!sub" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 file
@@ -60,7 +60,7 @@ EOF
 '
 
 test_expect_success 't_e_i() exclude :(icase)SUB' '
-	git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual
+	git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 file
@@ -71,7 +71,7 @@ EOF
 test_expect_success 't_e_i() exclude sub2 from sub' '
 	(
 	cd sub &&
-	git log --oneline --format=%s -- :/ ":/!sub2" >actual
+	git log --oneline --format=%s -- :/ ":/!sub2" >actual &&
 	cat <<EOF >expect &&
 sub/sub/sub/file
 sub/file2
@@ -84,7 +84,7 @@ EOF
 '
 
 test_expect_success 't_e_i() exclude sub/*file' '
-	git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual
+	git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 sub/file2
@@ -94,7 +94,7 @@ EOF
 '
 
 test_expect_success 't_e_i() exclude :(glob)sub/*/file' '
-	git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual
+	git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual &&
 	cat <<EOF >expect &&
 sub2/file
 sub/sub/sub/file
@@ -106,7 +106,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude sub' '
-	git ls-files -- . ":(exclude)sub" >actual
+	git ls-files -- . ":(exclude)sub" >actual &&
 	cat <<EOF >expect &&
 file
 sub2/file
@@ -115,7 +115,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude sub/sub/file' '
-	git ls-files -- . ":(exclude)sub/sub/file" >actual
+	git ls-files -- . ":(exclude)sub/sub/file" >actual &&
 	cat <<EOF >expect &&
 file
 sub/file
@@ -127,7 +127,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude sub using mnemonic' '
-	git ls-files -- . ":!sub" >actual
+	git ls-files -- . ":!sub" >actual &&
 	cat <<EOF >expect &&
 file
 sub2/file
@@ -136,7 +136,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude :(icase)SUB' '
-	git ls-files -- . ":(exclude,icase)SUB" >actual
+	git ls-files -- . ":(exclude,icase)SUB" >actual &&
 	cat <<EOF >expect &&
 file
 sub2/file
@@ -147,7 +147,7 @@ EOF
 test_expect_success 'm_p_d() exclude sub2 from sub' '
 	(
 	cd sub &&
-	git ls-files -- :/ ":/!sub2" >actual
+	git ls-files -- :/ ":/!sub2" >actual &&
 	cat <<EOF >expect &&
 ../file
 file
@@ -160,7 +160,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude sub/*file' '
-	git ls-files -- . ":(exclude)sub/*file" >actual
+	git ls-files -- . ":(exclude)sub/*file" >actual &&
 	cat <<EOF >expect &&
 file
 sub/file2
@@ -170,7 +170,7 @@ EOF
 '
 
 test_expect_success 'm_p_d() exclude :(glob)sub/*/file' '
-	git ls-files -- . ":(exclude,glob)sub/*/file" >actual
+	git ls-files -- . ":(exclude,glob)sub/*/file" >actual &&
 	cat <<EOF >expect &&
 file
 sub/file
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 54b5744cc5..2e2fb0e957 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -194,7 +194,7 @@ test_expect_success '--log=5 with custom comment character' '
 '
 
 test_expect_success 'merge.log=0 disables shortlog' '
-	echo "Merge branch ${apos}left${apos}" >expected
+	echo "Merge branch ${apos}left${apos}" >expected &&
 	git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
 	test_cmp expected actual
 '
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 69f11bd40d..7b56081137 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -161,10 +161,7 @@ test_expect_success "Michael Cassar's test case" '
 	git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
 
 	T=`git write-tree` &&
-	git ls-tree -r $T | grep partA/outline.txt || {
-		git ls-tree -r $T
-		(exit 1)
-	}
+	git ls-tree -r $T | verbose grep partA/outline.txt
 '
 
 rm -fr papers partA path?
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 8bfeef9b0b..fa207f3b8c 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1180,8 +1180,8 @@ test_expect_success 'message in editor has initial comment: first line' '
 test_expect_success \
 	'message in editor has initial comment: remainder' '
 	# remove commented lines from the remainder -- should be empty
-	>rest.expect
-	sed -e 1d -e '/^#/d' <actual >rest.actual &&
+	>rest.expect &&
+	sed -e 1d -e "/^#/d" <actual >rest.actual &&
 	test_cmp rest.expect rest.actual
 '
 
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index da958a8b56..947b690fd7 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -396,7 +396,7 @@ test_expect_success TTY 'command-specific pager overrides core.pager' '
 	sane_unset PAGER GIT_PAGER &&
 	echo "foo:initial" >expect &&
 	>actual &&
-	test_config core.pager "exit 1"
+	test_config core.pager "exit 1" &&
 	test_config pager.log "sed s/^/foo:/ >actual" &&
 	test_terminal git log --format=%s -1 &&
 	test_cmp expect actual
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
index a997f7ac3a..c27f90f285 100755
--- a/t/t7009-filter-branch-null-sha1.sh
+++ b/t/t7009-filter-branch-null-sha1.sh
@@ -13,7 +13,7 @@ test_expect_success 'setup: a commit with a bogus null sha1 in the tree' '
 	{
 		git ls-tree HEAD &&
 		printf "160000 commit $_z40\\tbroken\\n"
-	} >broken-tree
+	} >broken-tree &&
 	echo "add broken entry" >msg &&
 
 	tree=$(git mktree <broken-tree) &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index eae9e5a937..885923610a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -88,14 +88,10 @@ test_expect_success "checkout with unrelated dirty tree without -m" '
 
 	git checkout -f master &&
 	fill 0 1 2 3 4 5 6 7 8 >same &&
-	cp same kept
+	cp same kept &&
 	git checkout side >messages &&
-	test_cmp same kept
-	(cat > messages.expect <<EOF
-M	same
-EOF
-) &&
-	touch messages.expect &&
+	test_cmp same kept &&
+	printf "M\t%s\n" same >messages.expect &&
 	test_cmp messages.expect messages
 '
 
@@ -109,10 +105,7 @@ test_expect_success "checkout -m with dirty tree" '
 
 	test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
 
-	(cat >expect.messages <<EOF
-M	one
-EOF
-) &&
+	printf "M\t%s\n" one >expect.messages &&
 	test_cmp expect.messages messages &&
 
 	fill "M	one" "A	three" "D	two" >expect.master &&
@@ -409,12 +402,12 @@ test_expect_success \
 
 test_expect_success \
     'checkout w/autosetupmerge=always sets up tracking' '
+    test_when_finished git config branch.autosetupmerge false &&
     git config branch.autosetupmerge always &&
     git checkout master &&
     git checkout -b track2 &&
     test "$(git config branch.track2.remote)" &&
-    test "$(git config branch.track2.merge)"
-    git config branch.autosetupmerge false'
+    test "$(git config branch.track2.merge)"'
 
 test_expect_success 'checkout w/--track from non-branch HEAD fails' '
     git checkout master^0 &&
@@ -591,7 +584,7 @@ test_expect_success 'checkout --conflict=merge, overriding config' '
 '
 
 test_expect_success 'checkout --conflict=diff3' '
-	git config --unset merge.conflictstyle
+	test_unconfig merge.conflictstyle &&
 	setup_conflicting_index &&
 	echo "none of the above" >sample &&
 	echo ourside >expect &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 04118ad75b..99be5d95d0 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -119,10 +119,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
 		git clean -n ../src |
 		sed -n -e "s|^Would remove ||p"
 	) &&
-	test "$would_clean" = ../src/part3.c || {
-		echo "OOps <$would_clean>"
-		false
-	}
+	verbose test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
@@ -134,10 +131,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
 		git clean -n "$(pwd)/../src" |
 		sed -n -e "s|^Would remove ||p"
 	) &&
-	test "$would_clean" = ../src/part3.c || {
-		echo "OOps <$would_clean>"
-		false
-	}
+	verbose test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success 'git clean with out of work tree relative path' '
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 5811a982f4..540771ca41 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -766,7 +766,7 @@ test_expect_success 'moving the superproject does not break submodules' '
 	(
 		cd addtest &&
 		git submodule status >expect
-	)
+	) &&
 	mv addtest addtest2 &&
 	(
 		cd addtest2 &&
@@ -987,7 +987,7 @@ test_expect_success 'submodule with UTF-8 name' '
 
 test_expect_success 'submodule add clone shallow submodule' '
 	mkdir super &&
-	pwd=$(pwd)
+	pwd=$(pwd) &&
 	(
 		cd super &&
 		git init &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 29d3d2cca0..dda3929d99 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -754,7 +754,7 @@ test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd'
 
 test_expect_success 'submodule update clone shallow submodule' '
 	git clone cloned super3 &&
-	pwd=$(pwd)
+	pwd=$(pwd) &&
 	(cd super3 &&
 	 sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
 	 mv -f .gitmodules.tmp .gitmodules &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 6b16bcb640..c3ed7cb51c 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -431,7 +431,7 @@ test_expect_success 'status -s -uno' '
 '
 
 test_expect_success 'status -s (status.showUntrackedFiles no)' '
-	git config status.showuntrackedfiles no
+	git config status.showuntrackedfiles no &&
 	git status -s >output &&
 	test_cmp expect output
 '
@@ -465,7 +465,7 @@ EOF
 '
 
 test_expect_success 'status (status.showUntrackedFiles normal)' '
-	test_config status.showuntrackedfiles normal
+	test_config status.showuntrackedfiles normal &&
 	git status >output &&
 	test_i18ncmp expect output
 '
@@ -485,7 +485,7 @@ test_expect_success 'status -s -unormal' '
 '
 
 test_expect_success 'status -s (status.showUntrackedFiles normal)' '
-	git config status.showuntrackedfiles normal
+	git config status.showuntrackedfiles normal &&
 	git status -s >output &&
 	test_cmp expect output
 '
@@ -520,7 +520,7 @@ EOF
 '
 
 test_expect_success 'status (status.showUntrackedFiles all)' '
-	test_config status.showuntrackedfiles all
+	test_config status.showuntrackedfiles all &&
 	git status >output &&
 	test_i18ncmp expect output
 '
@@ -538,7 +538,7 @@ A  dir2/added
 ?? untracked
 EOF
 test_expect_success 'status -s -uall' '
-	git config --unset status.showuntrackedfiles
+	test_unconfig status.showuntrackedfiles &&
 	git status -s -uall >output &&
 	test_cmp expect output
 '
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 3cef18cfda..13331e533b 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -42,7 +42,7 @@ test_expect_success GPG 'create signed commits' '
 	git tag seventh-unsigned &&
 
 	test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
-	git tag seventh-signed
+	git tag seventh-signed &&
 
 	echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
 	git tag eighth-signed-alt
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index b16462132f..75c50eea15 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -133,7 +133,7 @@ test_expect_success 'setup' '
 	test_tick &&
 	git commit -m "commit 3" &&
 	git tag c3 &&
-	c3=$(git rev-parse HEAD)
+	c3=$(git rev-parse HEAD) &&
 	git reset --hard "$c0" &&
 	create_merge_msgs
 '
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index 21a0bf8fb8..8ae69a61c3 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -29,7 +29,7 @@ test_expect_success GPG 'create signed commits' '
 
 	git checkout -b side-untrusted &&
 	echo 3 >baz && git add baz &&
-	test_tick && git commit -SB7227189 -m "untrusted on side"
+	test_tick && git commit -SB7227189 -m "untrusted on side" &&
 
 	git checkout master
 '
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index aad8a9c64d..b66e383866 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -57,7 +57,7 @@ compare_mtimes ()
 {
 	read tref rest &&
 	while read t rest; do
-		test "$tref" = "$t" || break
+		test "$tref" = "$t" || return 1
 	done
 }
 
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 2a3469bcbe..32895e5acb 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -26,7 +26,7 @@ test_expect_success setup '
 	cat one >uno &&
 	mv two dos &&
 	cat one >>tres &&
-	echo DEF >>mouse
+	echo DEF >>mouse &&
 	git add uno dos tres mouse &&
 	test_tick &&
 	GIT_AUTHOR_NAME=Second git commit -a -m Second &&
@@ -153,15 +153,15 @@ test_expect_success 'blame path that used to be a directory' '
 '
 
 test_expect_success 'blame to a commit with no author name' '
-  TREE=`git rev-parse HEAD:`
-  cat >badcommit <<EOF
+  TREE=`git rev-parse HEAD:` &&
+  cat >badcommit <<EOF &&
 tree $TREE
 author <noname> 1234567890 +0000
 committer David Reiss <dreiss@facebook.com> 1234567890 +0000
 
 some message
 EOF
-  COMMIT=`git hash-object -t commit -w badcommit`
+  COMMIT=`git hash-object -t commit -w badcommit` &&
   git --no-pager blame $COMMIT -- uno >/dev/null
 '
 
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
index d15f8b3d47..29f84a6dd1 100755
--- a/t/t8008-blame-formats.sh
+++ b/t/t8008-blame-formats.sh
@@ -5,7 +5,7 @@ test_description='blame output in various formats on a simple case'
 
 test_expect_success 'setup' '
 	echo a >file &&
-	git add file
+	git add file &&
 	test_tick &&
 	git commit -m one &&
 	echo b >>file &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 051305545c..7be14a4e37 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -818,25 +818,19 @@ test_expect_success $PREREQ '--confirm=compose' '
 '
 
 test_expect_success $PREREQ 'confirm by default (due to cc)' '
-	CONFIRM=$(git config --get sendemail.confirm) &&
+	test_when_finished git config sendemail.confirm never &&
 	git config --unset sendemail.confirm &&
 	test_confirm
-	ret="$?"
-	git config sendemail.confirm ${CONFIRM:-never}
-	test $ret = "0"
 '
 
 test_expect_success $PREREQ 'confirm by default (due to --compose)' '
-	CONFIRM=$(git config --get sendemail.confirm) &&
+	test_when_finished git config sendemail.confirm never &&
 	git config --unset sendemail.confirm &&
 	test_confirm --suppress-cc=all --compose
-	ret="$?"
-	git config sendemail.confirm ${CONFIRM:-never}
-	test $ret = "0"
 '
 
 test_expect_success $PREREQ 'confirm detects EOF (inform assumes y)' '
-	CONFIRM=$(git config --get sendemail.confirm) &&
+	test_when_finished git config sendemail.confirm never &&
 	git config --unset sendemail.confirm &&
 	rm -fr outdir &&
 	git format-patch -2 -o outdir &&
@@ -846,13 +840,10 @@ test_expect_success $PREREQ 'confirm detects EOF (inform assumes y)' '
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/fake.sendmail" \
 			outdir/*.patch </dev/null
-	ret="$?"
-	git config sendemail.confirm ${CONFIRM:-never}
-	test $ret = "0"
 '
 
 test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' '
-	CONFIRM=$(git config --get sendemail.confirm) &&
+	test_when_finished git config sendemail.confirm never &&
 	git config sendemail.confirm auto &&
 	GIT_SEND_EMAIL_NOTTY=1 &&
 	export GIT_SEND_EMAIL_NOTTY &&
@@ -861,13 +852,10 @@ test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' '
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/fake.sendmail" \
 			$patches </dev/null
-	ret="$?"
-	git config sendemail.confirm ${CONFIRM:-never}
-	test $ret = "0"
 '
 
 test_expect_success $PREREQ 'confirm does not loop forever' '
-	CONFIRM=$(git config --get sendemail.confirm) &&
+	test_when_finished git config sendemail.confirm never &&
 	git config sendemail.confirm auto &&
 	GIT_SEND_EMAIL_NOTTY=1 &&
 	export GIT_SEND_EMAIL_NOTTY &&
@@ -876,9 +864,6 @@ test_expect_success $PREREQ 'confirm does not loop forever' '
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/fake.sendmail" \
 			$patches
-	ret="$?"
-	git config sendemail.confirm ${CONFIRM:-never}
-	test $ret = "0"
 '
 
 test_expect_success $PREREQ 'utf8 Cc is rfc2047 encoded' '
@@ -1325,7 +1310,7 @@ test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data'
 
 test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
 	clean_fake_sendmail &&
-	git config sendemail.transferEncoding 8bit
+	git config sendemail.transferEncoding 8bit &&
 	test_must_fail git send-email \
 		--transfer-encoding=7bit \
 		--smtp-server="$(pwd)/fake.sendmail" \
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 13b179e721..83f17e13e8 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -72,16 +72,18 @@ test_expect_success 'follow larger parent' '
         svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
         svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
         git svn init --minimize-url -i larger \
-          "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+	  "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
         git svn fetch -i larger &&
+	git svn init --minimize-url -i larger-parent \
+	  "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+	git svn fetch -i larger-parent &&
         git rev-parse --verify refs/remotes/larger &&
         git rev-parse --verify \
-           refs/remotes/larger-parent/trunk/thunk/bump/thud &&
+	   refs/remotes/larger-parent &&
         test "`git merge-base \
-                 refs/remotes/larger-parent/trunk/thunk/bump/thud \
+		 refs/remotes/larger-parent \
                  refs/remotes/larger`" = \
              "`git rev-parse refs/remotes/larger`"
-        true
         '
 
 test_expect_success 'follow higher-level parent' '
diff --git a/t/t9158-git-svn-mergeinfo.sh b/t/t9158-git-svn-mergeinfo.sh
index 8c9539e1b4..13f78f2682 100755
--- a/t/t9158-git-svn-mergeinfo.sh
+++ b/t/t9158-git-svn-mergeinfo.sh
@@ -34,7 +34,7 @@ test_expect_success 'change svn:mergeinfo' '
 '
 
 test_expect_success 'verify svn:mergeinfo' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk) &&
 	test "$mergeinfo" = "/branches/foo:1-10"
 '
 
@@ -46,7 +46,7 @@ test_expect_success 'change svn:mergeinfo multiline' '
 '
 
 test_expect_success 'verify svn:mergeinfo multiline' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk) &&
 	test "$mergeinfo" = "/branches/bar:1-10
 /branches/other:3-5,8,10-11"
 '
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
index 6cb0909afe..f113acaa6c 100755
--- a/t/t9161-git-svn-mergeinfo-push.sh
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -24,7 +24,7 @@ test_expect_success 'propagate merge information' '
 	'
 
 test_expect_success 'check svn:mergeinfo' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
 	test "$mergeinfo" = "/branches/svnb2:3,8"
 	'
 
@@ -34,7 +34,7 @@ test_expect_success 'merge another branch' '
 	'
 
 test_expect_success 'check primary parent mergeinfo respected' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
 	test "$mergeinfo" = "/branches/svnb2:3,8
 /branches/svnb3:4,9"
 	'
@@ -45,7 +45,7 @@ test_expect_success 'merge existing merge' '
 	'
 
 test_expect_success "check both parents' mergeinfo respected" '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
 	test "$mergeinfo" = "/branches/svnb2:3,8
 /branches/svnb3:4,9
 /branches/svnb4:5-6,10-12
@@ -70,7 +70,7 @@ test_expect_success 'second forward merge' '
 	'
 
 test_expect_success 'check new mergeinfo added' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1) &&
 	test "$mergeinfo" = "/branches/svnb2:3,8,16-17
 /branches/svnb3:4,9
 /branches/svnb4:5-6,10-12
@@ -84,7 +84,7 @@ test_expect_success 'reintegration merge' '
 	'
 
 test_expect_success 'check reintegration mergeinfo' '
-	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb4)
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb4) &&
 	test "$mergeinfo" = "/branches/svnb1:2-4,7-9,13-18
 /branches/svnb2:3,8,16-17
 /branches/svnb3:4,9
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index c538e0a4e9..aac126fd57 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -1132,7 +1132,7 @@ test_expect_success \
 	 compare_diff_raw expect actual'
 
 test_expect_success PIPE 'N: read and copy directory' '
-	cat >expect <<-\EOF
+	cat >expect <<-\EOF &&
 	:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100	file2/newf	file3/newf
 	:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100	file2/oldf	file3/oldf
 	EOF
@@ -2228,7 +2228,7 @@ test_expect_success 'R: feature import-marks-if-exists' '
 	>expect &&
 
 	git fast-import --import-marks-if-exists=not_io.marks \
-			--export-marks=io.marks <<-\EOF
+			--export-marks=io.marks <<-\EOF &&
 	feature import-marks-if-exists=io.marks
 	EOF
 	test_cmp expect io.marks
@@ -2853,8 +2853,8 @@ test_expect_success 'S: notemodify with garbage after mark commit-ish must fail'
 # from
 #
 test_expect_success 'S: from with garbage after mark must fail' '
-	# no &&
-	git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err
+	test_must_fail \
+	git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err &&
 	commit refs/heads/S2
 	mark :303
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -2865,9 +2865,6 @@ test_expect_success 'S: from with garbage after mark must fail' '
 	M 100644 :403 hello.c
 	EOF
 
-	ret=$? &&
-	echo returned $ret &&
-	test $ret -ne 0 && # failed, but it created the commit
 
 	# go create the commit, need it for merge test
 	git fast-import --import-marks=marks --export-marks=marks <<-EOF &&
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index e74b9ab1e1..e94b2f147a 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -297,7 +297,7 @@ test_expect_success 'setup incomplete lines' '
 	echo "Dominus regit me," >file &&
 	echo "incomplete line" | tr -d "\\012" >>file &&
 	git commit -a -m "Change incomplete line" &&
-	git tag incomplete_lines_chg
+	git tag incomplete_lines_chg &&
 	echo "Dominus regit me," >file &&
 	git commit -a -m "Remove incomplete line" &&
 	git tag incomplete_lines_rem
@@ -779,7 +779,10 @@ test_expect_success \
 
 test_expect_success \
 	'unborn HEAD: "summary" page (with "heads" subview)' \
-	'git checkout orphan_branch || git checkout --orphan orphan_branch &&
+	'{
+		git checkout orphan_branch ||
+		git checkout --orphan orphan_branch
+	 } &&
 	 test_when_finished "git checkout master" &&
 	 gitweb_run "p=.git;a=summary"'
 
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index 86dfee2e4f..0796a438bc 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -145,9 +145,11 @@ test_expect_success 'forks: not skipped unless "forks" feature enabled' '
 	grep -q ">fork of .*<"           gitweb.body
 '
 
-cat >>gitweb_config.perl <<\EOF &&
-$feature{'forks'}{'default'} = [1];
-EOF
+test_expect_success 'enable forks feature' '
+	cat >>gitweb_config.perl <<-\EOF
+	$feature{"forks"}{"default"} = [1];
+	EOF
+'
 
 test_expect_success 'forks: forks skipped if "forks" feature enabled' '
 	gitweb_run "a=project_list" &&
@@ -173,7 +175,7 @@ test_expect_success 'forks: can access forked repository' '
 '
 
 test_expect_success 'forks: project_index lists all projects (incl. forks)' '
-	cat >expected <<-\EOF
+	cat >expected <<-\EOF &&
 	.git
 	foo.bar.git
 	foo.git
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 7a883d1a67..4a14a5892e 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -568,7 +568,7 @@ test_expect_success 'complete files' '
 	test_completion "git commit " "modified" &&
 
 	: TODO .gitignore should not be here &&
-	test_completion "git ls-files " <<-\EOF
+	test_completion "git ls-files " <<-\EOF &&
 	.gitignore
 	dir
 	modified
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 46d7d37a51..49d58e6726 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -166,7 +166,7 @@ test_expect_success 'prompt - inside bare repository' '
 '
 
 test_expect_success 'prompt - interactive rebase' '
-	printf " (b1|REBASE-i 2/3)" >expected
+	printf " (b1|REBASE-i 2/3)" >expected &&
 	write_script fake_editor.sh <<-\EOF &&
 		echo "exec echo" >"$1"
 		echo "edit $(git log -1 --format="%h")" >>"$1"
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 9914d3e1cf..4ea99a209d 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -229,6 +229,12 @@ do
 	--root=*)
 		root=$(expr "z$1" : 'z[^=]*=\(.*\)')
 		shift ;;
+	--chain-lint)
+		GIT_TEST_CHAIN_LINT=1
+		shift ;;
+	--no-chain-lint)
+		GIT_TEST_CHAIN_LINT=0
+		shift ;;
 	-x)
 		trace=t
 		verbose=t
@@ -522,6 +528,16 @@ test_eval_ () {
 test_run_ () {
 	test_cleanup=:
 	expecting_failure=$2
+
+	if test "${GIT_TEST_CHAIN_LINT:-0}" != 0; then
+		# 117 is magic because it is unlikely to match the exit
+		# code of other programs
+		test_eval_ "(exit 117) && $1"
+		if test "$?" != 117; then
+			error "bug in the test script: broken &&-chain: $1"
+		fi
+	fi
+
 	setup_malloc_check
 	test_eval_ "$1"
 	eval_ret=$?