{"thread":{"id":"66272","subject":"[PATCH 0/2] Update tests to use semantic functions","startedAt":"2026-09-04T20:35:59Z","lastAt":"2026-09-07T12:10:18Z","messageCount":5,"participants":["Mark C. Chu-Carroll via B4 Relay","Junio C Hamano","Patrick Steinhardt"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"551998","messageId":"20260904-file-tests-use-shell-functions-v1-0-b66f9cb4adbe@fastmail.com","threadId":"66272","inReplyTo":null,"subject":"[PATCH 0/2] Update tests to use semantic functions","fromName":"Mark C. Chu-Carroll via B4 Relay","fromEmail":"devnull+markchucarroll.fastmail.com@kernel.org","sentAt":"2026-09-04T20:35:51Z","receivedAt":"2026-09-04T20:35:59Z","isPatch":true,"body":"Update tests to use semantic functions like \"test_path_is_file\"\ninstead of the older \"test -f\". All of the tests that make simple\nusages are updated; tests that do more complicated things like\nstarting subshells are left unchanged.\n\nSigned-off-by: Mark C. Chu-Carroll <markchucarroll@fastmail.com>\n---\nMark C. Chu-Carroll (2):\n      Update shell tests to use semantic functions\n      Second batch of shell test migrations.\n\n t/perf/p5302-pack-index.sh              |  2 +-\n t/t0007-git-var.sh                      |  2 +-\n t/t0031-lockfile-pid.sh                 |  2 +-\n t/t0081-find-pack.sh                    |  2 +-\n t/t0200-gettext-basic.sh                |  2 +-\n t/t1007-hash-object.sh                  |  6 +++---\n t/t1700-split-index.sh                  |  2 +-\n t/t2005-checkout-index-symlinks.sh      |  2 +-\n t/t2030-unresolve-info.sh               |  8 ++++----\n t/t2201-add-update-typechange.sh        |  2 +-\n t/t3300-funny-names.sh                  |  2 +-\n t/t3306-notes-prune.sh                  |  2 +-\n t/t3311-notes-merge-fanout.sh           |  2 +-\n t/t3434-rebase-i18n.sh                  |  2 +-\n t/t3902-quoted.sh                       |  2 +-\n t/t4013-diff-various.sh                 |  2 +-\n t/t4014-format-patch.sh                 |  6 +++---\n t/t4016-diff-quote.sh                   |  2 +-\n t/t4032-diff-inter-hunk-context.sh      |  4 ++--\n t/t4102-apply-rename.sh                 |  2 +-\n t/t4131-apply-fake-ancestor.sh          |  2 +-\n t/t4132-apply-removal.sh                |  4 ++--\n t/t4252-am-options.sh                   |  2 +-\n t/t5000-tar-tree.sh                     |  2 +-\n t/t5100-mailinfo.sh                     |  6 +++---\n t/t5300-pack-object.sh                  | 10 +++++-----\n t/t5301-sliding-window.sh               |  4 ++--\n t/t5302-pack-index.sh                   |  8 ++++----\n t/t5334-incremental-multi-pack-index.sh |  2 +-\n t/t5402-post-merge-hook.sh              |  2 +-\n t/t5500-fetch-pack.sh                   |  2 +-\n t/t5502-quickfetch.sh                   |  2 +-\n t/t5510-fetch.sh                        |  8 ++++----\n t/t5515-fetch-merge-logic.sh            |  4 ++--\n t/t5516-fetch-push.sh                   |  2 +-\n t/t5526-fetch-submodules.sh             |  8 ++++----\n t/t5534-push-signed.sh                  |  6 +++---\n t/t5550-http-fetch-dumb.sh              |  2 +-\n t/t5601-clone.sh                        | 16 ++++++++--------\n t/t5604-clone-reference.sh              |  2 +-\n t/t5702-protocol-v2.sh                  |  4 ++--\n t/t5710-promisor-remote-capability.sh   |  2 +-\n t/t6030-bisect-porcelain.sh             |  2 +-\n t/t6500-gc.sh                           |  2 +-\n t/t7004-tag.sh                          |  4 ++--\n t/t7011-skip-worktree-reading.sh        |  2 +-\n t/t7012-skip-worktree-writing.sh        |  2 +-\n t/t7102-reset.sh                        |  2 +-\n t/t7104-reset-hard.sh                   |  2 +-\n t/t7113-post-index-change-hook.sh       | 12 ++++++------\n t/t7201-co.sh                           |  6 +++---\n t/t7400-submodule-basic.sh              | 10 +++++-----\n t/t7407-submodule-foreach.sh            |  6 +++---\n t/t7409-submodule-detached-work-tree.sh |  2 +-\n t/t7412-submodule-absorbgitdirs.sh      |  8 ++++----\n t/t7450-bad-git-dotfiles.sh             |  6 +++---\n t/t7602-merge-octopus-many.sh           |  2 +-\n t/t7606-merge-custom.sh                 |  8 ++++----\n t/t7610-mergetool.sh                    |  6 +++---\n t/t9400-git-cvsserver-server.sh         |  6 +++---\n t/t9804-git-p4-label.sh                 |  8 ++++----\n 61 files changed, 126 insertions(+), 126 deletions(-)\n\n\n---\nbase-commit: 2c3adbb2c475981e340c79fdc5e7f4f9b5d9054e\nchange-id: 20260904-file-tests-use-shell-functions-e0d6c545d56c\n\n\n"},{"id":"551999","messageId":"20260904-file-tests-use-shell-functions-v1-2-b66f9cb4adbe@fastmail.com","threadId":"66272","inReplyTo":"20260904-file-tests-use-shell-functions-v1-0-b66f9cb4adbe@fastmail.com","subject":"[PATCH 2/2] Second batch of shell test migrations.","fromName":"Mark C. Chu-Carroll via B4 Relay","fromEmail":"devnull+markchucarroll.fastmail.com@kernel.org","sentAt":"2026-09-04T20:35:53Z","receivedAt":"2026-09-04T20:35:59Z","isPatch":true,"body":"From: \"Mark C. Chu-Carroll\" <markchucarroll@fastmail.com>\n\n---\n t/perf/p5302-pack-index.sh              |  2 +-\n t/t0007-git-var.sh                      |  2 +-\n t/t0081-find-pack.sh                    |  2 +-\n t/t1700-split-index.sh                  |  2 +-\n t/t2005-checkout-index-symlinks.sh      |  2 +-\n t/t3434-rebase-i18n.sh                  |  2 +-\n t/t3902-quoted.sh                       |  2 +-\n t/t4013-diff-various.sh                 |  2 +-\n t/t4016-diff-quote.sh                   |  2 +-\n t/t4252-am-options.sh                   |  2 +-\n t/t5000-tar-tree.sh                     |  2 +-\n t/t5100-mailinfo.sh                     |  6 +++---\n t/t5334-incremental-multi-pack-index.sh |  2 +-\n t/t5402-post-merge-hook.sh              |  2 +-\n t/t5500-fetch-pack.sh                   |  2 +-\n t/t5515-fetch-merge-logic.sh            |  4 ++--\n t/t5526-fetch-submodules.sh             |  8 ++++----\n t/t5601-clone.sh                        | 16 ++++++++--------\n t/t5702-protocol-v2.sh                  |  4 ++--\n t/t5710-promisor-remote-capability.sh   |  2 +-\n t/t6030-bisect-porcelain.sh             |  2 +-\n t/t7004-tag.sh                          |  4 ++--\n t/t7011-skip-worktree-reading.sh        |  2 +-\n t/t7409-submodule-detached-work-tree.sh |  2 +-\n t/t7450-bad-git-dotfiles.sh             |  6 +++---\n t/t7606-merge-custom.sh                 |  8 ++++----\n t/t7610-mergetool.sh                    |  6 +++---\n t/t9001-send-email.sh                   |  6 +++---\n t/t9804-git-p4-label.sh                 |  8 ++++----\n 29 files changed, 56 insertions(+), 56 deletions(-)\n\ndiff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh\nindex 14c601bbf8..d3a3ea360f 100755\n--- a/t/perf/p5302-pack-index.sh\n+++ b/t/perf/p5302-pack-index.sh\n@@ -9,7 +9,7 @@ test_perf_large_repo\n test_expect_success 'repack' '\n \tgit repack -ad &&\n \tPACK=$(ls .git/objects/pack/*.pack | head -n1) &&\n-\ttest -f \"$PACK\" &&\n+\ttest_path_is_file \"$PACK\" &&\n \texport PACK\n '\n \ndiff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh\nindex 2b60317758..6a8fe69c08 100755\n--- a/t/t0007-git-var.sh\n+++ b/t/t0007-git-var.sh\n@@ -156,7 +156,7 @@ test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '\n test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '\n \tshellpath=$(git var GIT_SHELL_PATH) &&\n \tcase \"$shellpath\" in\n-\t[A-Z]:/*/sh.exe) test -f \"$shellpath\";;\n+\t[A-Z]:/*/sh.exe) test_path_is_file \"$shellpath\";;\n \t*) return 1;;\n \tesac\n '\ndiff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh\nindex ff9d56fbf1..7d5082c487 100755\n--- a/t/t0081-find-pack.sh\n+++ b/t/t0081-find-pack.sh\n@@ -32,7 +32,7 @@ test_expect_success 'repack everything into a single packfile' '\n \t\t\".git/objects/pack/pack-\"*\".pack\") true ;;\n \t\t*) false ;;\n \tesac &&\n-\ttest -f \"$head_commit_pack\" &&\n+\ttest_path_is_file \"$head_commit_pack\" &&\n \n \t# Everything is in the same pack\n \ttest \"$head_commit_pack\" = \"$head_tree_pack\" &&\ndiff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh\nindex 887e72a5fa..8e973838d0 100755\n--- a/t/t1700-split-index.sh\n+++ b/t/t1700-split-index.sh\n@@ -460,7 +460,7 @@ test_expect_success POSIXPERM,SANITY 'graceful handling when splitting index is\n \t\tcd ro &&\n \t\ttest_commit initial &&\n \t\tgit update-index --split-index &&\n-\t\ttest -f .git/sharedindex.*\n+\t\ttest_path_is_file .git/sharedindex.*\n \t) &&\n \tcp ro/.git/index new-index &&\n \ttest_when_finished \"chmod u+w ro/.git\" &&\ndiff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh\nindex 91b08e0371..3ec071125f 100755\n--- a/t/t2005-checkout-index-symlinks.sh\n+++ b/t/t2005-checkout-index-symlinks.sh\n@@ -19,7 +19,7 @@ echo \"120000 $l\tsymlink\" | git update-index --index-info'\n test_expect_success \\\n 'the checked-out symlink must be a file' '\n git checkout-index symlink &&\n-test -f symlink'\n+test_path_is_file symlink'\n \n test_expect_success 'the file must be the blob we added during the setup' '\n \techo \"$l\" >expect &&\ndiff --git a/t/t3434-rebase-i18n.sh b/t/t3434-rebase-i18n.sh\nindex 0f93a239f8..8b217380f0 100755\n--- a/t/t3434-rebase-i18n.sh\n+++ b/t/t3434-rebase-i18n.sh\n@@ -75,7 +75,7 @@ test_rebase_continue_update_encode () {\n \t\tgit commit -F \"$TEST_DIRECTORY/t3434/$msgfile\" &&\n \t\tgit config i18n.commitencoding $new &&\n \t\ttest_must_fail git rebase -m main &&\n-\t\ttest -f .git/rebase-merge/message &&\n+\t\ttest_path_is_file .git/rebase-merge/message &&\n \t\tgit stripspace -s <.git/rebase-merge/message >two.t &&\n \t\tgit add two.t &&\n \t\tgit rebase --continue &&\ndiff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh\nindex 8660ec5cb0..ee0bfb6432 100755\n--- a/t/t3902-quoted.sh\n+++ b/t/t3902-quoted.sh\n@@ -14,7 +14,7 @@ DQ='\"'\n \n test_have_prereq MINGW ||\n echo foo 2>/dev/null > \"Name and an${HT}HT\"\n-if ! test -f \"Name and an${HT}HT\"\n+if test_path_is_missing \"Name and an${HT}HT\"\n then\n \t# FAT/NTFS does not allow tabs in filenames\n \tskip_all='Your filesystem does not allow tabs in filenames'\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex b7a382c881..e3a1eb89c5 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -235,7 +235,7 @@ do\n \t\t\t    -e \"s/^\\\\(.*mixed; boundary=\\\"-*\\\\)$V\\\\(-*\\\\)\\\"\\$/\\\\1g-i-t--v-e-r-s-i-o-n\\2\\\"/\"\n \t\t\techo \"\\$\"\n \t\t} >\"$actual\" &&\n-\t\tif test -f \"$expect\"\n+\t\tif test_path_is_file \"$expect\"\n \t\tthen\n \t\t\tprocess_diffs \"$actual\" >actual &&\n \t\t\tprocess_diffs \"$expect\" >expect &&\ndiff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh\nindex 876271d682..1226da6703 100755\n--- a/t/t4016-diff-quote.sh\n+++ b/t/t4016-diff-quote.sh\n@@ -14,7 +14,7 @@ P2='pathname with SP'\n P3='pathname\n with LF'\n test_have_prereq !MINGW &&\n-echo 2>/dev/null >\"$P1\" && test -f \"$P1\" && rm -f \"$P1\" || {\n+echo 2>/dev/null >\"$P1\" && test_path_is_file \"$P1\" && rm -f \"$P1\" || {\n \tskip_all='Your filesystem does not allow tabs in filenames'\n \ttest_done\n }\ndiff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh\nindex c36c7e9017..617b850f55 100755\n--- a/t/t4252-am-options.sh\n+++ b/t/t4252-am-options.sh\n@@ -64,7 +64,7 @@ test_expect_success 'apply to a funny path' '\n \trm -fr .git/rebase-apply &&\n \tgit reset --hard initial &&\n \tgit am --directory=\"$with_sq\" \"$tm\"/am-test-5-2 &&\n-\ttest -f \"$with_sq/file-5\"\n+\ttest_path_is_file \"$with_sq/file-5\"\n '\n \n test_expect_success 'am --reject' '\ndiff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh\nindex 3ad600c793..c99f90a212 100755\n--- a/t/t5000-tar-tree.sh\n+++ b/t/t5000-tar-tree.sh\n@@ -34,7 +34,7 @@ test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '\n \t\tmkdir pax &&\n \t\tcd pax &&\n \t\t\"$TAR\" xf \"$TEST_DIRECTORY\"/t5000/pax.tar &&\n-\t\ttest -f PaxHeaders.1791/file\n+\t\ttest_path_is_file PaxHeaders.1791/file\n \t)\n '\n \ndiff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh\nindex e01078abe7..68d6c23175 100755\n--- a/t/t5100-mailinfo.sh\n+++ b/t/t5100-mailinfo.sh\n@@ -34,15 +34,15 @@ do\n \n \ttest_expect_success $prereq \"mailinfo $mail\" '\n \t\tcheck_mailinfo \"$mail\" \"\" &&\n-\t\tif test -f \"$DATA/msg$mail--scissors\"\n+\t\tif test_path_is_file \"$DATA/msg$mail--scissors\"\n \t\tthen\n \t\t\tcheck_mailinfo \"$mail\" --scissors\n \t\tfi &&\n-\t\tif test -f \"$DATA/msg$mail--no-inbody-headers\"\n+\t\tif test_path_is_file \"$DATA/msg$mail--no-inbody-headers\"\n \t\tthen\n \t\t\tcheck_mailinfo \"$mail\" --no-inbody-headers\n \t\tfi &&\n-\t\tif test -f \"$DATA/msg$mail--message-id\"\n+\t\tif test_path_is_file \"$DATA/msg$mail--message-id\"\n \t\tthen\n \t\t\tcheck_mailinfo \"$mail\" --message-id\n \t\tfi\ndiff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh\nindex f0b82b5f65..d9bad96f78 100755\n--- a/t/t5334-incremental-multi-pack-index.sh\n+++ b/t/t5334-incremental-multi-pack-index.sh\n@@ -47,7 +47,7 @@ compare_results_with_midx 'non-incremental MIDX conversion'\n \n write_midx_layer () {\n \tn=1\n-\tif test -f $midx_chain\n+\tif test_path_is_file $midx_chain\n \tthen\n \t\tn=\"$(($(wc -l <$midx_chain) + 1))\"\n \tfi\ndiff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh\nindex c77aa56421..e43b19ac43 100755\n--- a/t/t5402-post-merge-hook.sh\n+++ b/t/t5402-post-merge-hook.sh\n@@ -37,7 +37,7 @@ test_expect_success 'setup clone hooks' '\n \n test_expect_success 'post-merge does not run for up-to-date ' '\n \tGIT_DIR=clone1/.git git merge $commit0 &&\n-\t! test -f clone1/.git/post-merge.args\n+\ttest_path_is_missing clone1/.git/post-merge.args\n '\n \n test_expect_success 'post-merge runs as expected ' '\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 6c1edf2117..5916e43565 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -317,7 +317,7 @@ test_expect_success 'turn shallow to complete repository' '\n \t\tcd shallow &&\n \t\tGIT_TRACE2_EVENT=\"$(pwd)/trace2_event\" \\\n \t\t\tgit fetch --unshallow &&\n-\t\t! test -f .git/shallow &&\n+\t\ttest_path_is_missing .git/shallow &&\n \t\tgit fsck --full &&\n \t\ttest_grep \\\"fetch-info\\\".*\\\"shallows\\\":2 trace2_event &&\n \t\ttest_grep \\\"fetch-info\\\".*\\\"depth\\\":2147483647 trace2_event\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 8ac04d742c..a7d57b0e07 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -208,7 +208,7 @@ do\n \t\t\tcat .git/FETCH_HEAD\n \t\t} >\"$actual_f\" &&\n \t\tgit show-ref >\"$actual_r\" &&\n-\t\tif test -f \"expect_f\"\n+\t\tif test_path_is_file \"expect_f\"\n \t\tthen\n \t\t\ttest_cmp \"expect_f\" \"$actual_f\" &&\n \t\t\trm -f \"$actual_f\"\n@@ -217,7 +217,7 @@ do\n \t\t\tcp \"$actual_f\" \"$expect_f\"\n \t\t\tfalse\n \t\tfi &&\n-\t\tif test -f \"expect_r\"\n+\t\tif test_path_is_file \"expect_r\"\n \t\tthen\n \t\t\ttest_cmp \"expect_r\" \"$actual_r\" &&\n \t\t\trm -f \"$actual_r\"\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 7b3b7359da..a5b2fc8767 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -106,19 +106,19 @@ add_superproject_commits () {\n verify_fetch_result () {\n \tACTUAL_ERR=$1 &&\n \trm -f expect.err.combined &&\n-\tif test -f expect.err.super\n+\tif test_path_is_file expect.err.super\n \tthen\n \t\tcat expect.err.super >>expect.err.combined\n \tfi &&\n-\tif test -f expect.err.sub\n+\tif test_path_is_file expect.err.sub\n \tthen\n \t\tcat expect.err.sub >>expect.err.combined\n \tfi &&\n-\tif test -f expect.err.deep\n+\tif test_path_is_file expect.err.deep\n \tthen\n \t\tcat expect.err.deep >>expect.err.combined\n \tfi &&\n-\tif test -f expect.err.sub2\n+\tif test_path_is_file expect.err.sub2\n \tthen\n \t\tcat expect.err.sub2 >>expect.err.combined\n \tfi &&\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex b6167582a1..8387bca417 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -57,7 +57,7 @@ test_expect_success 'clone does not keep pack' '\n \n \trm -fr dst &&\n \tgit clone -n \"file://$(pwd)/src\" dst &&\n-\t! test -f dst/file &&\n+\ttest_path_is_missing dst/file &&\n \t! (echo dst/.git/objects/pack/pack-* | grep \"\\.keep\")\n \n '\n@@ -66,15 +66,15 @@ test_expect_success 'clone checks out files' '\n \n \trm -fr dst &&\n \tgit clone src dst &&\n-\ttest -f dst/file\n+\ttest_path_is_file dst/file\n \n '\n \n test_expect_success 'clone respects GIT_WORK_TREE' '\n \n \tGIT_WORK_TREE=worktree git clone src bare &&\n-\ttest -f bare/config &&\n-\ttest -f worktree/file\n+\ttest_path_is_file bare/config &&\n+\ttest_path_is_file worktree/file\n \n '\n \n@@ -102,22 +102,22 @@ test_expect_success 'clone from hooks' '\n test_expect_success 'clone creates intermediate directories' '\n \n \tgit clone src long/path/to/dst &&\n-\ttest -f long/path/to/dst/file\n+\ttest_path_is_file long/path/to/dst/file\n \n '\n \n test_expect_success 'clone creates intermediate directories for bare repo' '\n \n \tgit clone --bare src long/path/to/bare/dst &&\n-\ttest -f long/path/to/bare/dst/config\n+\ttest_path_is_file long/path/to/bare/dst/config\n \n '\n \n test_expect_success 'clone --mirror' '\n \n \tgit clone --mirror src mirror &&\n-\ttest -f mirror/HEAD &&\n-\ttest ! -f mirror/file &&\n+\ttest_path_is_file mirror/HEAD &&\n+\ttest_path_is_missing mirror/file &&\n \tFETCH=\"$(cd mirror && git config remote.origin.fetch)\" &&\n \ttest \"+refs/*:refs/*\" = \"$FETCH\" &&\n \tMIRROR=\"$(cd mirror && git config --bool remote.origin.mirror)\" &&\ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex 0f05286de8..68fa8ebcf5 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -1261,8 +1261,8 @@ test_expect_success 'part of packfile response provided as URI' '\n \t\t\tfi\n \t\tfi || return 1\n \tdone &&\n-\ttest -f hfound &&\n-\ttest -f h2found &&\n+\ttest_path_is_file hfound &&\n+\ttest_path_is_file h2found &&\n \n \t# Ensure that there are exactly 3 packfiles with associated .idx\n \tls http_child/.git/objects/pack/*.pack \\\ndiff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh\nindex 549acff23f..fa2455cf31 100755\n--- a/t/t5710-promisor-remote-capability.sh\n+++ b/t/t5710-promisor-remote-capability.sh\n@@ -41,7 +41,7 @@ check_missing_objects () {\n \tthen\n \t\ttest \"$3\" = \"$(cat missing.txt)\"\n \telse\n-\t\ttest -f \"$3\" &&\n+\t\ttest_path_is_file \"$3\" &&\n \t\tsort <\"$3\" >expected_sorted &&\n \t\tsort <missing.txt >actual_sorted &&\n \t\ttest_cmp expected_sorted actual_sorted\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex a7588222a8..c44daa7856 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -851,7 +851,7 @@ test_expect_success 'optimized merge base checks' '\n \ttest_grep \"merge base must be tested\" my_bisect_log.txt &&\n \ttest_grep \"$HASH4\" my_bisect_log.txt &&\n \tgit bisect good > my_bisect_log2.txt &&\n-\ttest -f \".git/BISECT_ANCESTORS_OK\" &&\n+\ttest_path_is_file \".git/BISECT_ANCESTORS_OK\" &&\n \ttest \"$HASH6\" = $(git rev-parse --verify HEAD) &&\n \tgit bisect bad &&\n \tgit bisect good \"$A_HASH\" > my_bisect_log4.txt &&\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 8c795d7218..09a9559c74 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -538,7 +538,7 @@ test_expect_success 'creating an annotated tag with -F - should succeed' '\n '\n \n test_expect_success 'trying to create a tag with a non-existing -F file should fail' '\n-\t! test -f nonexistingfile &&\n+\ttest_path_is_missing nonexistingfile &&\n \t! tag_exists notag &&\n \ttest_must_fail git tag -F nonexistingfile notag &&\n \t! tag_exists notag\n@@ -1099,7 +1099,7 @@ test_expect_success GPG 'git tag --no-sign configured tag.gpgsign skip GPG sign'\n '\n \n test_expect_success GPG 'trying to create a signed tag with non-existing -F file should fail' '\n-\t! test -f nonexistingfile &&\n+\ttest_path_is_missing nonexistingfile &&\n \t! tag_exists nosigtag &&\n \ttest_must_fail git tag -s -F nonexistingfile nosigtag &&\n \t! tag_exists nosigtag\ndiff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh\nindex 1ff2714cb4..4301960015 100755\n--- a/t/t7011-skip-worktree-reading.sh\n+++ b/t/t7011-skip-worktree-reading.sh\n@@ -24,7 +24,7 @@ H sub/2\n EOF\n \n setup_absent() {\n-\ttest -f 1 && rm 1\n+\ttest_path_is_file 1 && rm 1\n \tgit update-index --remove 1 &&\n \tgit update-index --add --cacheinfo 100644 $EMPTY_BLOB 1 &&\n \tgit update-index --skip-worktree 1\ndiff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh\nindex 374ed481e9..2b295bed6e 100755\n--- a/t/t7409-submodule-detached-work-tree.sh\n+++ b/t/t7409-submodule-detached-work-tree.sh\n@@ -84,7 +84,7 @@ test_expect_success 'submodule on detached working pointed by core.worktree' '\n \t\tgit config core.worktree .. &&\n \t\tgit pull &&\n \t\tgit submodule update --init &&\n-\t\ttest -f .vim/bundle/dupe/shoot.t\n+\t\ttest_path_is_file .vim/bundle/dupe/shoot.t\n \t)\n '\n \ndiff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh\nindex 72c7f6f73b..f8a7b262f2 100755\n--- a/t/t7450-bad-git-dotfiles.sh\n+++ b/t/t7450-bad-git-dotfiles.sh\n@@ -320,7 +320,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '\n \t\t# git~2 is an 8.3 short name, present only when 8.3 name\n \t\t# generation is enabled. The \"directory not empty\" check\n \t\t# above is the primary assertion.\n-\t\tif test -f squatting-clone/d/a/git~2\n+\t\tif test_path_is_file squatting-clone/d/a/git~2\n \t\tthen\n \t\t\ttest_grep ! gitdir squatting-clone/d/a/git~2\n \t\tfi\n@@ -409,8 +409,8 @@ test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into d\n \tgit -C repo commit -m submodule &&\n \n \tgit -c protocol.file.allow=always clone --recurse-submodules repo bad-clone &&\n-\t! test -f \"$PWD/bad-clone/sub/foo\" &&\n-\ttest -f $(printf \"bad-clone/sub\\r/post-checkout\")\n+\ttest_path_is_missing \"$PWD/bad-clone/sub/foo\" &&\n+\ttest_path_is_file $(printf \"bad-clone/sub\\r/post-checkout\")\n '\n \n test_done\ndiff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh\nindex 0ae2f59b1d..67b01649e0 100755\n--- a/t/t7606-merge-custom.sh\n+++ b/t/t7606-merge-custom.sh\n@@ -59,9 +59,9 @@ test_expect_success 'merge c2 with a custom strategy' '\n \ttest_cmp head.old first-parent &&\n \ttest_cmp second-parent.expected second-parent &&\n \ttest_cmp tree.expected tree &&\n-\ttest -f c0.c &&\n+\ttest_path_is_file c0.c &&\n \ttest_grep c1c1 c1.c &&\n-\ttest -f c2.c\n+\ttest_path_is_file c2.c\n '\n \n test_expect_success 'trivial merge with custom strategy' '\n@@ -85,9 +85,9 @@ test_expect_success 'trivial merge with custom strategy' '\n \ttest_cmp head.old first-parent &&\n \ttest_cmp second-parent.expected second-parent &&\n \ttest_cmp tree.expected tree &&\n-\ttest -f c0.c &&\n+\ttest_path_is_file c0.c &&\n \t! test -e c1.c &&\n-\ttest -f c3.c\n+\ttest_path_is_file c3.c\n '\n \n test_done\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex 0128b14452..2cc3ea61c1 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -382,15 +382,15 @@ test_expect_success 'mergetool delete/delete conflict' '\n \tgit checkout -b test$test_count move-to-c &&\n \ttest_must_fail git merge move-to-b &&\n \techo d | git mergetool a/a/file.txt &&\n-\t! test -f a/a/file.txt &&\n+\ttest_path_is_missing a/a/file.txt &&\n \tgit reset --hard &&\n \ttest_must_fail git merge move-to-b &&\n \techo m | git mergetool a/a/file.txt &&\n-\ttest -f b/b/file.txt &&\n+\ttest_path_is_file b/b/file.txt &&\n \tgit reset --hard &&\n \ttest_must_fail git merge move-to-b &&\n \t! echo a | git mergetool a/a/file.txt &&\n-\t! test -f a/a/file.txt\n+\ttest_path_is_missing a/a/file.txt\n '\n \n test_expect_success 'mergetool produces no errors when keepBackup is used' '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 78db65189e..d1393ef197 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -26,7 +26,7 @@ test_expect_success $PREREQ 'Setup helper tool' '\n \twrite_script fake.sendmail <<-\\EOF &&\n \tshift\n \toutput=1\n-\twhile test_path_is_file commandline$output\n+\twhile test -f commandline$output\n \tdo\n \t\toutput=$(($output+1))\n \tdone\n@@ -66,7 +66,7 @@ test_no_confirm () {\n \n # Exit immediately to prevent hang if a no-confirm test fails\n check_no_confirm () {\n-\tif test_path_is_missing no_confirm_okay\n+\tif ! test -f no_confirm_okay\n \tthen\n \t\tsay 'confirm test failed; skipping remaining tests to prevent hanging'\n \t\tPREREQ=\"$PREREQ,CHECK_NO_CONFIRM\"\n@@ -2722,7 +2722,7 @@ test_expect_success $PREREQ 'invoke hook' '\n \t\tfalse\n \t\t;;\n \tesac &&\n-\ttest_path_is_file 0001-add-main.patch &&\n+\ttest -f 0001-add-main.patch &&\n \tgrep \"add main\" \"$1\"\n \tEOF\n \ndiff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh\nindex 3236457106..f64a11f85f 100755\n--- a/t/t9804-git-p4-label.sh\n+++ b/t/t9804-git-p4-label.sh\n@@ -59,9 +59,9 @@ test_expect_success 'basic p4 labels' '\n \n \t\tcd main &&\n \t\tgit checkout tag_tag_f1_only &&\n-\t\t! test -f f2 &&\n+\t\ttest_path_is_missing f2 &&\n \t\tgit checkout tag_tag_with\\$_shell_char &&\n-\t\ttest -f f1 && test -f f2 && test -f file_with_\\$metachar &&\n+\t\ttest_path_is_file f1 && test_path_is_file f2 && test_path_is_file file_with_\\$metachar &&\n \n \t\tgit show tag_long_label | grep -q \"A Label second line\"\n \t)\n@@ -100,11 +100,11 @@ test_expect_failure 'two labels on the same changelist' '\n \n \t\tgit checkout tag_tag_f1_1 &&\n \t\tls &&\n-\t\ttest -f f1 &&\n+\t\ttest_path_is_file f1 &&\n \n \t\tgit checkout tag_tag_f1_2 &&\n \t\tls &&\n-\t\ttest -f f1\n+\t\ttest_path_is_file f1\n \t)\n '\n \n\n-- \n2.53.0\n\n\n"},{"id":"552000","messageId":"20260904-file-tests-use-shell-functions-v1-1-b66f9cb4adbe@fastmail.com","threadId":"66272","inReplyTo":"20260904-file-tests-use-shell-functions-v1-0-b66f9cb4adbe@fastmail.com","subject":"[PATCH 1/2] Update shell tests to use semantic functions","fromName":"Mark C. Chu-Carroll via B4 Relay","fromEmail":"devnull+markchucarroll.fastmail.com@kernel.org","sentAt":"2026-09-04T20:35:52Z","receivedAt":"2026-09-04T20:35:59Z","isPatch":true,"body":"From: \"Mark C. Chu-Carroll\" <markchucarroll@fastmail.com>\n\nThis updates an initial bash of shell tests to replace uses\nof \"test -f\" and \"! test -f\" with \"test_path_is_file\" and\n\"test_path_is_missing\".\n---\n t/t0031-lockfile-pid.sh            |  2 +-\n t/t0200-gettext-basic.sh           |  2 +-\n t/t1007-hash-object.sh             |  6 +++---\n t/t2030-unresolve-info.sh          |  8 ++++----\n t/t2201-add-update-typechange.sh   |  2 +-\n t/t3300-funny-names.sh             |  2 +-\n t/t3306-notes-prune.sh             |  2 +-\n t/t3311-notes-merge-fanout.sh      |  2 +-\n t/t4014-format-patch.sh            |  6 +++---\n t/t4032-diff-inter-hunk-context.sh |  4 ++--\n t/t4102-apply-rename.sh            |  2 +-\n t/t4131-apply-fake-ancestor.sh     |  2 +-\n t/t4132-apply-removal.sh           |  4 ++--\n t/t5300-pack-object.sh             | 10 +++++-----\n t/t5301-sliding-window.sh          |  4 ++--\n t/t5302-pack-index.sh              |  8 ++++----\n t/t5502-quickfetch.sh              |  2 +-\n t/t5510-fetch.sh                   |  8 ++++----\n t/t5516-fetch-push.sh              |  2 +-\n t/t5534-push-signed.sh             |  6 +++---\n t/t5550-http-fetch-dumb.sh         |  2 +-\n t/t5604-clone-reference.sh         |  2 +-\n t/t6500-gc.sh                      |  2 +-\n t/t7012-skip-worktree-writing.sh   |  2 +-\n t/t7102-reset.sh                   |  2 +-\n t/t7104-reset-hard.sh              |  2 +-\n t/t7113-post-index-change-hook.sh  | 12 ++++++------\n t/t7201-co.sh                      |  6 +++---\n t/t7400-submodule-basic.sh         | 10 +++++-----\n t/t7407-submodule-foreach.sh       |  6 +++---\n t/t7412-submodule-absorbgitdirs.sh |  8 ++++----\n t/t7602-merge-octopus-many.sh      |  2 +-\n t/t9001-send-email.sh              |  6 +++---\n t/t9400-git-cvsserver-server.sh    |  6 +++---\n 34 files changed, 76 insertions(+), 76 deletions(-)\n\ndiff --git a/t/t0031-lockfile-pid.sh b/t/t0031-lockfile-pid.sh\nindex e9e2f04049..9cca369e32 100755\n--- a/t/t0031-lockfile-pid.sh\n+++ b/t/t0031-lockfile-pid.sh\n@@ -40,7 +40,7 @@ test_expect_success 'running process detected when PID is alive' '\n \t\techo content >file &&\n \t\t# Get the correct PID for this platform\n \t\tshell_pid=$$ &&\n-\t\tif test_have_prereq MINGW && test -f /proc/$shell_pid/winpid\n+\t\tif test_have_prereq MINGW && test_path_is_file /proc/$shell_pid/winpid\n \t\tthen\n \t\t\t# In Git for Windows, Bash uses MSYS2 PIDs but git.exe\n \t\t\t# uses Windows PIDs. Use the Windows PID.\ndiff --git a/t/t0200-gettext-basic.sh b/t/t0200-gettext-basic.sh\nindex 8db26c1ada..8305291ce9 100755\n--- a/t/t0200-gettext-basic.sh\n+++ b/t/t0200-gettext-basic.sh\n@@ -36,7 +36,7 @@ test_expect_success GETTEXT 'sanity: $TEXTDOMAINDIR exists without NO_GETTEXT=Ye\n '\n \n test_expect_success GETTEXT 'sanity: Icelandic locale was compiled' '\n-    test -f \"$TEXTDOMAINDIR/is/LC_MESSAGES/git.mo\"\n+     test_path_is_file \"$TEXTDOMAINDIR/is/LC_MESSAGES/git.mo\"\n '\n \n # TODO: When we have more locales, generalize this to test them\ndiff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\nindex 463b38f990..e6d1fe3e13 100755\n--- a/t/t1007-hash-object.sh\n+++ b/t/t1007-hash-object.sh\n@@ -271,7 +271,7 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n \n test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n \t\t'files over 4GB hash correctly via --stdin' '\n-\t{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n+\t{ test_path_is_file big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n \ttest_oid large5GB >expect &&\n \tgit hash-object --stdin <big >actual &&\n \ttest_cmp expect actual\n@@ -279,7 +279,7 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n \n test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n \t\t'files over 4GB hash correctly' '\n-\t{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n+\t{ test_path_is_file big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n \ttest_oid large5GB >expect &&\n \tgit hash-object -- big >actual &&\n \ttest_cmp expect actual\n@@ -289,7 +289,7 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n # We ensure that cleaning doesn't mangle large files on 64-bit Windows.\n test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n \t\t'hash filtered files over 4GB correctly' '\n-\t{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n+\t{ test_path_is_file big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n \ttest_oid large5GB >expect &&\n \ttest_config filter.null-filter.clean \"cat\" &&\n \techo \"big filter=null-filter\" >.gitattributes &&\ndiff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh\nindex 9af24b1204..f4ff7591fc 100755\n--- a/t/t2030-unresolve-info.sh\n+++ b/t/t2030-unresolve-info.sh\n@@ -170,7 +170,7 @@ test_expect_success 'rerere and rerere forget' '\n \tgit rerere &&\n \trerere_id=$(cd .git/rr-cache && echo */postimage) &&\n \trerere_id=${rerere_id%/postimage} &&\n-\ttest -f .git/rr-cache/$rerere_id/postimage &&\n+\ttest_path_is_file .git/rr-cache/$rerere_id/postimage &&\n \tgit checkout -m fi/le &&\n \techo resurrect the conflict &&\n \ttest_grep \"^=======\" fi/le &&\n@@ -182,7 +182,7 @@ test_expect_success 'rerere and rerere forget' '\n \tcheck_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&\n \ttest -z \"$(git ls-files -u)\" &&\n \tgit rerere forget fi/le &&\n-\t! test -f .git/rr-cache/$rerere_id/postimage &&\n+\t! test_path_is_file .git/rr-cache/$rerere_id/postimage &&\n \ttr \"\\0\" \"\\n\" <.git/MERGE_RR >actual &&\n \techo \"$rerere_id\tfi/le\" >expect &&\n \ttest_cmp expect actual\n@@ -196,7 +196,7 @@ test_expect_success 'rerere and rerere forget (subdirectory)' '\n \t(cd fi && git rerere) &&\n \trerere_id=$(cd .git/rr-cache && echo */postimage) &&\n \trerere_id=${rerere_id%/postimage} &&\n-\ttest -f .git/rr-cache/$rerere_id/postimage &&\n+\ttest_path_is_file .git/rr-cache/$rerere_id/postimage &&\n \t(cd fi && git checkout -m le) &&\n \techo resurrect the conflict &&\n \ttest_grep \"^=======\" fi/le &&\n@@ -208,7 +208,7 @@ test_expect_success 'rerere and rerere forget (subdirectory)' '\n \tcheck_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&\n \ttest -z \"$(git ls-files -u)\" &&\n \t(cd fi && git rerere forget le) &&\n-\t! test -f .git/rr-cache/$rerere_id/postimage &&\n+\ttest_path_is_missing .git/rr-cache/$rerere_id/postimage &&\n \ttr \"\\0\" \"\\n\" <.git/MERGE_RR >actual &&\n \techo \"$rerere_id\tfi/le\" >expect &&\n \ttest_cmp expect actual\ndiff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh\nindex 687be974d4..f46ddc0100 100755\n--- a/t/t2201-add-update-typechange.sh\n+++ b/t/t2201-add-update-typechange.sh\n@@ -131,7 +131,7 @@ test_expect_success 'add -u' '\n '\n \n test_expect_success 'commit -a' '\n-\tif test -f \".git/saved-index\"\n+\tif test_path_is_file \".git/saved-index\"\n \tthen\n \t\trm -f \".git/index\" &&\n \t\tmv \".git/saved-index\" \".git/index\"\ndiff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh\nindex dd0586b007..d73049922a 100755\n--- a/t/t3300-funny-names.sh\n+++ b/t/t3300-funny-names.sh\n@@ -15,7 +15,7 @@ HT='\t'\n \n test_have_prereq MINGW ||\n echo 2>/dev/null > \"Name with an${HT}HT\"\n-if ! test -f \"Name with an${HT}HT\"\n+if test_path_is_missing \"Name with an${HT}HT\"\n then\n \t# since FAT/NTFS does not allow tabs in filenames, skip this test\n \tskip_all='Your filesystem does not allow tabs in filenames'\ndiff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh\nindex 8f4102ff9e..40c1eac56b 100755\n--- a/t/t3306-notes-prune.sh\n+++ b/t/t3306-notes-prune.sh\n@@ -24,7 +24,7 @@ test_expect_success 'setup: create a few commits with notes' '\n \tgit commit -m 3rd &&\n \tthird=$(git rev-parse HEAD) &&\n \tCOMMIT_FILE=$(echo $third | sed \"s!^..!.git/objects/&/!\") &&\n-\ttest -f $COMMIT_FILE &&\n+\ttest_path_is_file $COMMIT_FILE &&\n \ttest-tool chmtime =+0 $COMMIT_FILE &&\n \tgit notes add -m \"Note #3\"\n '\ndiff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh\nindex 5b675417e9..a90999d6b4 100755\n--- a/t/t3311-notes-merge-fanout.sh\n+++ b/t/t3311-notes-merge-fanout.sh\n@@ -10,7 +10,7 @@ test_description='Test notes merging at various fanout levels'\n verify_notes () {\n \tnotes_ref=\"$1\"\n \tcommit=\"$2\"\n-\tif test -f \"expect_notes_$notes_ref\"\n+\tif test_path_is_file \"expect_notes_$notes_ref\"\n \tthen\n \t\tgit -c core.notesRef=\"refs/notes/$notes_ref\" notes |\n \t\t\tsort >\"output_notes_$notes_ref\" &&\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 4afcd85779..53257e7e0a 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -888,7 +888,7 @@ test_expect_success 'format-patch from a subdirectory (1)' '\n \t\tfalse\n \t\t;;\n \tesac &&\n-\ttest -f \"$filename\"\n+\ttest_path_is_file \"$filename\"\n '\n \n test_expect_success 'format-patch from a subdirectory (2)' '\n@@ -907,7 +907,7 @@ test_expect_success 'format-patch from a subdirectory (2)' '\n \t\t;;\n \tesac &&\n \tbasename=$(expr \"$filename\" : \".*/\\(.*\\)\") &&\n-\ttest -f \"sub/$basename\"\n+\ttest_path_is_file \"sub/$basename\"\n '\n \n test_expect_success 'format-patch from a subdirectory (3)' '\n@@ -919,7 +919,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '\n \t\tgit format-patch -1 -o \"$TRASH_DIRECTORY\"\n \t) &&\n \tbasename=$(expr \"$filename\" : \".*/\\(.*\\)\") &&\n-\ttest -f \"$basename\"\n+\ttest_path_is_file \"$basename\"\n '\n \n test_expect_success 'format-patch --in-reply-to' '\ndiff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh\nindex 7d443968e3..cc213c04a1 100755\n--- a/t/t4032-diff-inter-hunk-context.sh\n+++ b/t/t4032-diff-inter-hunk-context.sh\n@@ -28,7 +28,7 @@ t() {\n \tfile=f$1\n \texpected=expected.$file.$3.$hunks\n \n-\tif ! test -f $file\n+\tif test_path_is_missing $file\n \tthen\n \t\tf A $1 B >$file\n \t\tgit add $file\n@@ -40,7 +40,7 @@ t() {\n \t\ttest $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks\n \t\"\n \n-\tif test -f $expected\n+\tif test_path_is_file $expected\n \tthen\n \t\ttest_expect_success \"$label: check output\" \"\n \t\t\tgit $cmd $file | grep -v '^index ' >actual &&\ndiff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh\nindex e42a31c917..6910289e88 100755\n--- a/t/t4102-apply-rename.sh\n+++ b/t/t4102-apply-rename.sh\n@@ -33,7 +33,7 @@ test_expect_success apply \\\n     'git apply --index --stat --summary --apply test-patch'\n \n test_expect_success FILEMODE validate \\\n-\t    'test -f bar && ls -l bar | grep \"^-..x......\"'\n+\t    'test_path_is_file bar && ls -l bar | grep \"^-..x......\"'\n \n test_expect_success 'apply reverse' \\\n     'git apply -R --index --stat --summary --apply test-patch &&\ndiff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh\nindex b1361ce546..648f928f7a 100755\n--- a/t/t4131-apply-fake-ancestor.sh\n+++ b/t/t4131-apply-fake-ancestor.sh\n@@ -33,7 +33,7 @@ test_expect_success 'apply --build-fake-ancestor in a subdirectory' '\n \t(\n \t\tcd sub &&\n \t\tgit apply --build-fake-ancestor 3.ancestor ../3.patch &&\n-\t\ttest -f 3.ancestor\n+\t\ttest_path_is_file 3.ancestor\n \t) &&\n \tgit apply --build-fake-ancestor 3.ancestor 3.patch &&\n \ttest_cmp sub/3.ancestor 3.ancestor\ndiff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh\nindex ab1628d27d..55fc9d1700 100755\n--- a/t/t4132-apply-removal.sh\n+++ b/t/t4132-apply-removal.sh\n@@ -80,7 +80,7 @@ do\n \t\t\tcat something >file &&\n \t\t\tgit add file &&\n \t\t\tgit apply --index $patch &&\n-\t\t\ttest -f file &&\n+\t\t\ttest_path_is_file file &&\n \t\t\ttest_must_be_empty file\n \t\t\t;;\n \t\tremove*)\n@@ -88,7 +88,7 @@ do\n \t\t\tcat something >file &&\n \t\t\tgit add file &&\n \t\t\tgit apply --index $patch &&\n-\t\t\t! test -f file\n+\t\t\ttest_path_is_missing file\n \t\t\t;;\n \t\tesac\n \t'\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex 9dabb3615a..7e244a76df 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -259,11 +259,11 @@ test_expect_success 'survive missing objects/pack directory' '\n \t\tGOP=.git/objects/pack &&\n \t\trm -fr $GOP &&\n \t\tgit index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&\n-\t\ttest -f $GOP/pack-${packname_3}.pack &&\n+\t\ttest_path_is_file $GOP/pack-${packname_3}.pack &&\n \t\tcmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack &&\n-\t\ttest -f $GOP/pack-${packname_3}.idx &&\n+\t\ttest_path_is_file $GOP/pack-${packname_3}.idx &&\n \t\tcmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx &&\n-\t\ttest -f $GOP/pack-${packname_3}.keep\n+\t\ttest_path_is_file $GOP/pack-${packname_3}.keep\n \t)\n '\n \n@@ -352,7 +352,7 @@ test_expect_success 'build pack index for an existing pack' '\n \trm -f test-4.keep &&\n \tgit index-pack --keep=why test-4.pack &&\n \tcmp test-1-${packname_1}.idx test-4.idx &&\n-\ttest -f test-4.keep &&\n+\ttest_path_is_file test-4.keep &&\n \n \t:\n '\n@@ -592,7 +592,7 @@ test_expect_success 'setup: fake a SHA1 hash collision' '\n \t\tcd corrupt &&\n \t\tlong_a=$(git hash-object -w ../a | sed -e \"s!^..!&/!\") &&\n \t\tlong_b=$(git hash-object -w ../b | sed -e \"s!^..!&/!\") &&\n-\t\ttest -f\t.git/objects/$long_b &&\n+\t\ttest_path_is_file\t.git/objects/$long_b &&\n \t\tcp -f\t.git/objects/$long_a \\\n \t\t\t.git/objects/$long_b\n \t)\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 3c3666b278..b6868229bf 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -22,7 +22,7 @@ test_expect_success 'setup' '\n \tgit repack -a -d &&\n \ttest \"$(git count-objects)\" = \"0 objects, 0 kilobytes\" &&\n \tpack1=$(ls .git/objects/pack/*.pack) &&\n-\ttest -f \"$pack1\"\n+\ttest_path_is_file \"$pack1\"\n '\n \n test_expect_success 'verify-pack -v, defaults' '\n@@ -48,7 +48,7 @@ test_expect_success 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' '\n \tgit repack -a -d &&\n \ttest \"$(git count-objects)\" = \"0 objects, 0 kilobytes\" &&\n \tpack2=$(ls .git/objects/pack/*.pack) &&\n-\ttest -f \"$pack2\" &&\n+\ttest_path_is_file \"$pack2\" &&\n \ttest \"$pack1\" \\!= \"$pack2\"\n '\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 735de1023e..3182ff2023 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -169,7 +169,7 @@ test_expect_success \\\n \n test_expect_success \\\n \t'[index v1] 3) corrupted delta happily returned wrong data' '\n-\ttest -f file_101_foo1 && ! cmp file_101 file_101_foo1\n+\ttest_path_is_file file_101_foo1 && ! cmp file_101 file_101_foo1\n '\n \n test_expect_success \\\n@@ -180,7 +180,7 @@ test_expect_success \\\n test_expect_success \\\n \t'[index v1] 5) pack-objects happily reuses corrupted data' '\n \tpack4=$(git pack-objects test-4 <obj-list) &&\n-\ttest -f \"test-4-${pack4}.pack\"\n+\ttest_path_is_file \"test-4-${pack4}.pack\"\n '\n \n test_expect_success '[index v1] 6) newly created pack is BAD !' '\n@@ -214,7 +214,7 @@ test_expect_success \\\n \n test_expect_success \\\n \t'[index v2] 3) corrupted delta happily returned wrong data' '\n-\ttest -f file_101_foo2 && ! cmp file_101 file_101_foo2\n+\ttest_path_is_file file_101_foo2 && ! cmp file_101 file_101_foo2\n '\n \n test_expect_success \\\n@@ -251,7 +251,7 @@ test_expect_success 'running index-pack in the object store' '\n \t\tcd .git/objects/pack &&\n \t\tgit index-pack pack-${pack1}.pack\n \t) &&\n-\ttest -f .git/objects/pack/pack-${pack1}.idx\n+\ttest_path_is_file .git/objects/pack/pack-${pack1}.idx\n '\n \n test_expect_success 'index-pack --strict warns upon missing tagger in tag' '\ndiff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh\nindex b160f8b7fb..62d2284752 100755\n--- a/t/t5502-quickfetch.sh\n+++ b/t/t5502-quickfetch.sh\n@@ -63,7 +63,7 @@ test_expect_success 'copy commit and tree but not blob by hand' '\n \ttest $cnt -eq 6 &&\n \n \tblob=$(git rev-parse HEAD:file | sed -e \"s|..|&/|\") &&\n-\ttest -f \"cloned/.git/objects/$blob\" &&\n+\ttest_path_is_file \"cloned/.git/objects/$blob\" &&\n \trm -f \"cloned/.git/objects/$blob\" &&\n \n \tcnt=$( (\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex a8d38d9176..c4b0770d0d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -1009,21 +1009,21 @@ test_expect_success 'fetch into the current branch with --update-head-ok' '\n test_expect_success 'fetch --dry-run does not touch FETCH_HEAD, but still prints what would be written' '\n \trm -f .git/FETCH_HEAD err &&\n \tgit fetch --dry-run . 2>err &&\n-\t! test -f .git/FETCH_HEAD &&\n+\ttest_path_is_missing .git/FETCH_HEAD &&\n \ttest_grep FETCH_HEAD err\n '\n \n test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD, and does not print what would be written' '\n \trm -f .git/FETCH_HEAD err &&\n \tgit fetch --no-write-fetch-head . 2>err &&\n-\t! test -f .git/FETCH_HEAD &&\n+\ttest_path_is_missing .git/FETCH_HEAD &&\n \ttest_grep ! FETCH_HEAD err\n '\n \n test_expect_success '--write-fetch-head gets defeated by --dry-run' '\n \trm -f .git/FETCH_HEAD &&\n \tgit fetch --dry-run --write-fetch-head . &&\n-\t! test -f .git/FETCH_HEAD\n+\ttest_path_is_missing .git/FETCH_HEAD\n '\n \n test_expect_success \"should be able to fetch with duplicate refspecs\" '\n@@ -2026,7 +2026,7 @@ test_expect_success REFFILES \"HEAD is updated even with conflicts\" '\n \t\tmkdir -p refs/remotes/origin &&\n \t\t>refs/remotes/origin/branch.lock &&\n \t\ttest_must_fail git fetch origin &&\n-\t\ttest -f refs/remotes/origin/HEAD\n+\t\ttest_path_is_file refs/remotes/origin/HEAD\n \t)\n '\n \ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex f3b3efc47f..8efb6b185d 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -688,7 +688,7 @@ test_expect_success 'push preserves up-to-date packed refs' '\n \t(\n \t\tcd child &&\n \t\tgit push &&\n-\t\t! test -f .git/refs/remotes/origin/main\n+\t\ttest_path_is_missing .git/refs/remotes/origin/main\n \t)\n '\n \ndiff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh\nindex 21f0262cbd..d60f5fe69c 100755\n--- a/t/t5534-push-signed.sh\n+++ b/t/t5534-push-signed.sh\n@@ -46,7 +46,7 @@ test_expect_success 'unsigned push does not send push certificate' '\n \tEOF\n \n \tgit push dst noop ff +noff &&\n-\t! test -f dst/push-cert\n+\ttest_path_is_missing dst/push-cert\n '\n \n test_expect_success 'talking with a receiver without push certificate support' '\n@@ -62,7 +62,7 @@ test_expect_success 'talking with a receiver without push certificate support' '\n \tEOF\n \n \tgit push dst noop ff +noff &&\n-\t! test -f dst/push-cert\n+\ttest_path_is_missing dst/push-cert\n '\n \n test_expect_success 'push --signed fails with a receiver without push certificate support' '\n@@ -86,7 +86,7 @@ test_expect_success GPG 'no certificate for a signed push with no update' '\n \tfi\n \tEOF\n \tgit push dst noop &&\n-\t! test -f dst/push-cert\n+\ttest_path_is_missing dst/push-cert\n '\n \n test_expect_success GPG 'signed push sends push certificate' '\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex b5758f1c9c..449d75b7c5 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -334,7 +334,7 @@ test_expect_success 'http-fetch --packfile permits unlink while indexing' '\n \t\tls objects/pack/pack-*.pack) &&\n \ttmpfile=\"packfileclient-unlink/.git/objects/pack/pack-$ARBITRARY.pack.temp\" &&\n \twrite_script git-unlink-index-pack <<-\\EOF &&\n-\ttest -f \"$GIT_TEST_PACK_TEMP\" || exit 1\n+\ttest_path_is_file \"$GIT_TEST_PACK_TEMP\" || exit 1\n \trm \"$GIT_TEST_PACK_TEMP\" || exit 1\n \texec git index-pack \"$@\"\n \tEOF\ndiff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh\nindex 39a0c318df..14e93c4352 100755\n--- a/t/t5604-clone-reference.sh\n+++ b/t/t5604-clone-reference.sh\n@@ -220,7 +220,7 @@ test_expect_success 'clone, dissociate from alternates' '\n \tgit clone --reference=A A B &&\n \ttest_line_count = 1 B/.git/objects/info/alternates &&\n \tgit clone --local --dissociate B C &&\n-\t! test -f C/.git/objects/info/alternates &&\n+\ttest_path_is_missing C/.git/objects/info/alternates &&\n \t( cd C && git fsck )\n '\n \ndiff --git a/t/t6500-gc.sh b/t/t6500-gc.sh\nindex a7142ab815..1ae9192cd2 100755\n--- a/t/t6500-gc.sh\n+++ b/t/t6500-gc.sh\n@@ -415,7 +415,7 @@ test_expect_success 'background auto gc respects lock for all operations' '\n \t# shell pid so that it looks valid.\n \thostname=$(hostname || echo unknown) &&\n \tshell_pid=$$ &&\n-\tif test_have_prereq MINGW && test -f /proc/$shell_pid/winpid\n+\tif test_have_prereq MINGW && test_path_is_file /proc/$shell_pid/winpid\n \tthen\n \t\t# In Git for Windows, Bash (actually, the MSYS2 runtime) has a\n \t\t# different idea of PIDs than git.exe (actually Windows). Use\ndiff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh\nindex 526cc4ec83..4073d87753 100755\n--- a/t/t7012-skip-worktree-writing.sh\n+++ b/t/t7012-skip-worktree-writing.sh\n@@ -54,7 +54,7 @@ test_expect_success 'read-tree removes worktree, dirty case' '\n '\n \n setup_absent() {\n-\ttest -f 1 && rm 1\n+\ttest_path_is_file 1 && rm 1\n \tgit update-index --remove 1 &&\n \tgit update-index --add --cacheinfo 100644 $EMPTY_BLOB 1 &&\n \tgit update-index --skip-worktree 1\ndiff --git a/t/t7102-reset.sh b/t/t7102-reset.sh\nindex 298272cb13..70fac59847 100755\n--- a/t/t7102-reset.sh\n+++ b/t/t7102-reset.sh\n@@ -558,7 +558,7 @@ test_expect_success 'disambiguation (1)' '\n \tgit reset secondfile &&\n \ttest_must_fail git diff --quiet -- secondfile &&\n \ttest -z \"$(git diff --cached --name-only)\" &&\n-\ttest -f secondfile &&\n+\ttest_path_is_file secondfile &&\n \ttest_must_be_empty secondfile\n '\n \ndiff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh\nindex c23d6e3f52..acf145e69b 100755\n--- a/t/t7104-reset-hard.sh\n+++ b/t/t7104-reset-hard.sh\n@@ -29,7 +29,7 @@ test_expect_success 'reset --hard should restore unmerged ones' '\n \n \tgit reset --hard &&\n \tgit ls-files --error-unmatch before/1 before/2 hello later/3 &&\n-\ttest -f hello\n+\ttest_path_is_file hello\n \n '\n \ndiff --git a/t/t7113-post-index-change-hook.sh b/t/t7113-post-index-change-hook.sh\nindex c10d94fe3d..0d97ab5fd3 100755\n--- a/t/t7113-post-index-change-hook.sh\n+++ b/t/t7113-post-index-change-hook.sh\n@@ -25,11 +25,11 @@ test_expect_success 'test status, add, commit, others trigger hook without flags\n \t\t\techo \"Invalid combination of flags passed to hook; updated_skipworktree is set.\" >testfailure\n \t\t\texit 1\n \t\tfi\n-\t\tif test -f \".git/index.lock\"; then\n+\t\tif test_path_is_file \".git/index.lock\"; then\n \t\t\techo \".git/index.lock exists\" >testfailure\n \t\t\texit 3\n \t\tfi\n-\t\tif ! test -f \".git/index\"; then\n+\t\tif test_path_is_missing \".git/index\"; then\n \t\t\techo \".git/index does not exist\" >testfailure\n \t\t\texit 3\n \t\tfi\n@@ -71,11 +71,11 @@ test_expect_success 'test checkout and reset trigger the hook' '\n \t\t\texit 2\n \t\tfi\n \t\tif test \"$1\" -eq 1; then\n-\t\t\tif test -f \".git/index.lock\"; then\n+\t\t\tif test_path_is_file \".git/index.lock\"; then\n \t\t\t\techo \"updated_workdir set but .git/index.lock exists\" >testfailure\n \t\t\t\texit 3\n \t\t\tfi\n-\t\t\tif ! test -f \".git/index\"; then\n+\t\t\tif test_path_is_missing \".git/index\"; then\n \t\t\t\techo \"updated_workdir set but .git/index does not exist\" >testfailure\n \t\t\t\texit 3\n \t\t\tfi\n@@ -114,11 +114,11 @@ test_expect_success 'test reset --mixed and update-index triggers the hook' '\n \t\t\texit 2\n \t\tfi\n \t\tif test \"$2\" -eq 1; then\n-\t\t\tif test -f \".git/index.lock\"; then\n+\t\t\tif test_path_is_file \".git/index.lock\"; then\n \t\t\t\techo \"updated_skipworktree set but .git/index.lock exists\" >testfailure\n \t\t\t\texit 3\n \t\t\tfi\n-\t\t\tif ! test -f \".git/index\"; then\n+\t\t\tif test_path_is_missing \".git/index\"; then\n \t\t\t\techo \"updated_skipworktree set but .git/index does not exist\" >testfailure\n \t\t\t\texit 3\n \t\t\tfi\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 0ddd1ad7aa..835dd5eb4d 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -135,7 +135,7 @@ test_expect_success 'checkout -m with dirty tree, renamed' '\n \tgit checkout -m renamer &&\n \tfill 1 3 4 5 7 8 >expect &&\n \ttest_cmp expect uno &&\n-\t! test -f one &&\n+\ttest_path_is_missing one &&\n \tgit diff --cached >current &&\n \ttest_must_be_empty current\n '\n@@ -422,7 +422,7 @@ test_expect_success 'switch branches while in subdirectory' '\n \n \tmkdir subs &&\n \tgit -C subs checkout side &&\n-\t! test -f subs/one &&\n+\ttest_path_is_missing subs/one &&\n \trm -fr subs\n '\n \n@@ -437,7 +437,7 @@ test_expect_success 'checkout specific path while in subdirectory' '\n \tgit checkout main &&\n \tmkdir -p subs &&\n \tgit -C subs checkout side -- bero &&\n-\ttest -f subs/bero\n+\ttest_path_is_file subs/bero\n '\n \n test_expect_success 'checkout w/--track sets up tracking' '\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex eefdecb0bd..de291a03b0 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -1147,7 +1147,7 @@ test_expect_success 'submodule deinit should remove the whole submodule section\n \tgit submodule deinit init &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest -n \"$(git config --get-regexp \"submodule\\.example2\\.\")\" &&\n-\ttest -f example2/.git &&\n+\ttest_path_is_file example2/.git &&\n \trmdir init\n '\n \n@@ -1167,7 +1167,7 @@ test_expect_success 'submodule deinit from subdirectory' '\n \ttest_grep \"\\\\.\\\\./init\" output &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest -n \"$(git config --get-regexp \"submodule\\.example2\\.\")\" &&\n-\ttest -f example2/.git &&\n+\ttest_path_is_file example2/.git &&\n \trmdir init\n '\n \n@@ -1213,7 +1213,7 @@ test_expect_success 'submodule deinit fails when the submodule contains modifica\n \techo X >>init/s &&\n \ttest_must_fail git submodule deinit init &&\n \ttest -n \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n-\ttest -f example2/.git &&\n+\ttest_path_is_file example2/.git &&\n \tgit submodule deinit -f init >actual &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest_grep \"Cleared directory .init\" actual &&\n@@ -1225,7 +1225,7 @@ test_expect_success 'submodule deinit fails when the submodule contains untracke\n \techo X >>init/untracked &&\n \ttest_must_fail git submodule deinit init &&\n \ttest -n \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n-\ttest -f example2/.git &&\n+\ttest_path_is_file example2/.git &&\n \tgit submodule deinit -f init >actual &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest_grep \"Cleared directory .init\" actual &&\n@@ -1240,7 +1240,7 @@ test_expect_success 'submodule deinit fails when the submodule HEAD does not mat\n \t) &&\n \ttest_must_fail git submodule deinit init &&\n \ttest -n \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n-\ttest -f example2/.git &&\n+\ttest_path_is_file example2/.git &&\n \tgit submodule deinit -f init >actual &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest_grep \"Cleared directory .init\" actual &&\ndiff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh\nindex 77b6d0040e..fcdf298055 100755\n--- a/t/t7407-submodule-foreach.sh\n+++ b/t/t7407-submodule-foreach.sh\n@@ -368,9 +368,9 @@ test_expect_success 'test \"update --recursive\" with a flag with spaces' '\n \t\tgit rev-parse --resolve-git-dir nested1/.git &&\n \t\tgit rev-parse --resolve-git-dir nested1/nested2/.git &&\n \t\tgit rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&\n-\t\ttest -f .git/modules/nested1/objects/info/alternates &&\n-\t\ttest -f .git/modules/nested1/modules/nested2/objects/info/alternates &&\n-\t\ttest -f .git/modules/nested1/modules/nested2/modules/nested3/objects/info/alternates\n+\t\ttest_path_is_file .git/modules/nested1/objects/info/alternates &&\n+\t\ttest_path_is_file .git/modules/nested1/modules/nested2/objects/info/alternates &&\n+\t\ttest_path_is_file .git/modules/nested1/modules/nested2/modules/nested3/objects/info/alternates\n \t)\n '\n \ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 0490499573..70df34ac28 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -34,7 +34,7 @@ test_expect_success 'absorb the git dir' '\n \tgit submodule absorbgitdirs 2>actual &&\n \ttest_cmp expect actual &&\n \tgit fsck &&\n-\ttest -f sub1/.git &&\n+\ttest_path_is_file sub1/.git &&\n \ttest -d .git/modules/sub1 &&\n \tgit status >actual.1 &&\n \tgit -C sub1 rev-parse HEAD >actual.2 &&\n@@ -72,7 +72,7 @@ test_expect_success 'absorb the git dir in a nested submodule' '\n \tEOF\n \tgit submodule absorbgitdirs 2>actual &&\n \ttest_cmp expect actual &&\n-\ttest -f sub1/nested/.git &&\n+\ttest_path_is_file sub1/nested/.git &&\n \ttest -d .git/modules/sub1/modules/nested &&\n \tgit status >actual.1 &&\n \tgit -C sub1/nested rev-parse HEAD >actual.2 &&\n@@ -109,8 +109,8 @@ test_expect_success 'absorb the git dir in a nested submodule' '\n \tEOF\n \tgit submodule absorbgitdirs 2>actual &&\n \ttest_cmp expect actual &&\n-\ttest -f sub1/.git &&\n-\ttest -f sub1/nested/.git &&\n+\ttest_path_is_file sub1/.git &&\n+\ttest_path_is_file sub1/nested/.git &&\n \ttest -d .git/modules/sub1/modules/nested &&\n \tgit status >actual.1 &&\n \tgit -C sub1/nested rev-parse HEAD >actual.2 &&\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex 42f675b739..20bc57ef30 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -44,7 +44,7 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n \ti=1 &&\n \twhile test $i -le 30\n \tdo\n-\t\ttest -f c$i.c &&\n+\t\ttest_path_is_file  c$i.c &&\n \t\ti=$(expr $i + 1) || return 1\n \tdone\n '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex d1393ef197..78db65189e 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -26,7 +26,7 @@ test_expect_success $PREREQ 'Setup helper tool' '\n \twrite_script fake.sendmail <<-\\EOF &&\n \tshift\n \toutput=1\n-\twhile test -f commandline$output\n+\twhile test_path_is_file commandline$output\n \tdo\n \t\toutput=$(($output+1))\n \tdone\n@@ -66,7 +66,7 @@ test_no_confirm () {\n \n # Exit immediately to prevent hang if a no-confirm test fails\n check_no_confirm () {\n-\tif ! test -f no_confirm_okay\n+\tif test_path_is_missing no_confirm_okay\n \tthen\n \t\tsay 'confirm test failed; skipping remaining tests to prevent hanging'\n \t\tPREREQ=\"$PREREQ,CHECK_NO_CONFIRM\"\n@@ -2722,7 +2722,7 @@ test_expect_success $PREREQ 'invoke hook' '\n \t\tfalse\n \t\t;;\n \tesac &&\n-\ttest -f 0001-add-main.patch &&\n+\ttest_path_is_file 0001-add-main.patch &&\n \tgrep \"add main\" \"$1\"\n \tEOF\n \ndiff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\nindex b2379e4671..c9f9fb6975 100755\n--- a/t/t9400-git-cvsserver-server.sh\n+++ b/t/t9400-git-cvsserver-server.sh\n@@ -286,7 +286,7 @@ test_expect_success 'gitcvs.dbname' '\n \tGIT_DIR=\"$SERVERDIR\" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&\n \tGIT_CONFIG=\"$git_config\" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&\n \ttest_cmp cvswork cvswork2 &&\n-\ttest -f \"$SERVERDIR/gitcvs.ext.main.sqlite\" &&\n+\ttest_path_is_file \"$SERVERDIR/gitcvs.ext.main.sqlite\" &&\n \tcmp \"$SERVERDIR/gitcvs.main.sqlite\" \"$SERVERDIR/gitcvs.ext.main.sqlite\"\n '\n \n@@ -297,8 +297,8 @@ test_expect_success 'gitcvs.ext.dbname' '\n \tGIT_DIR=\"$SERVERDIR\" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n \tGIT_CONFIG=\"$git_config\" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&\n \ttest_cmp cvswork cvswork2 &&\n-\ttest -f \"$SERVERDIR/gitcvs1.ext.main.sqlite\" &&\n-\ttest ! -f \"$SERVERDIR/gitcvs2.ext.main.sqlite\" &&\n+\ttest_path_is_file \"$SERVERDIR/gitcvs1.ext.main.sqlite\" &&\n+\ttest_path_is_missing \"$SERVERDIR/gitcvs2.ext.main.sqlite\" &&\n \tcmp \"$SERVERDIR/gitcvs.main.sqlite\" \"$SERVERDIR/gitcvs1.ext.main.sqlite\"\n '\n \n\n-- \n2.53.0\n\n\n"},{"id":"552019","messageId":"xmqq1pb830ih.fsf@gitster.g","threadId":"66272","inReplyTo":"20260904-file-tests-use-shell-functions-v1-1-b66f9cb4adbe@fastmail.com","subject":"Re: [PATCH 1/2] Update shell tests to use semantic functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-05T02:17:10Z","receivedAt":"2026-09-05T02:17:13Z","isPatch":true,"body":"\"Mark C. Chu-Carroll via B4 Relay\"\n<devnull+markchucarroll.fastmail.com@kernel.org> writes:\n\n> -\t\tif test_have_prereq MINGW && test -f /proc/$shell_pid/winpid\n> +\t\tif test_have_prereq MINGW && test_path_is_file /proc/$shell_pid/winpid\n\nThis is Wrong.\n\nFirst think what the point of \"test_path_is_file\" and other helpers\nis.  Once you understood them, you would never write them as part of\nconditional expressions like the above one.\n\nA conditional statement like this \"if\" does *not* want to shout when\nthe test condition it used to switch between its \"then\" (and \"else\"\nif it has it) is not satisfied.  If this \"test -f\" does not find the\nnamed /proc/$shell_pid/winpid file, it does not mean that we\ndetected a breakage in the system that is being tested.\n\nAfter looking at a handful of hunks from the top of this patch, it\nseems there are many more similarly wrong conversions among them\nthan a few that are good, and I won't look at the remainder of the\n[1/2] or [2/2].  Sifting the two large patches that lack sign-off\nthat span across many files and finding salvageable bits is not\nsomething I would spend my weekend on.\n\nThis kind of conversion first needs to understand what each\ninvocation of \"test -X\" is really trying to check and why.\nBrain-less mechanical conversion is not sufficient and will produce\na wrong conversion like the above one.\n\n\n\n"},{"id":"552136","messageId":"ap6ppBwdg0Tpb8TM@pks.im","threadId":"66272","inReplyTo":"20260904-file-tests-use-shell-functions-v1-1-b66f9cb4adbe@fastmail.com","subject":"Re: [PATCH 1/2] Update shell tests to use semantic functions","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-07T12:10:12Z","receivedAt":"2026-09-07T12:10:18Z","isPatch":true,"body":"On Fri, Sep 04, 2026 at 04:35:52PM -0400, Mark C. Chu-Carroll via B4 Relay wrote:\n> From: \"Mark C. Chu-Carroll\" <markchucarroll@fastmail.com>\n> \n> This updates an initial bash of shell tests to replace uses\n> of \"test -f\" and \"! test -f\" with \"test_path_is_file\" and\n> \"test_path_is_missing\".\n\nThe commit message doesn't quite match our conventions:\n\n  - We typically write the messages in imperative style, as if telling\n    the code to change.\n\n  - The subject should typically start with the subsystem that you're\n    changing, followed by a lower-case letter. So in your case, \"t:\"\n    would be a good prefix.\n\n  - The message should also briefly explain what the benefit of this\n    conversion is.\n\n  - You're missing the Signed-off-by line.\n\n>  t/t0031-lockfile-pid.sh            |  2 +-\n>  t/t0200-gettext-basic.sh           |  2 +-\n>  t/t1007-hash-object.sh             |  6 +++---\n>  t/t2030-unresolve-info.sh          |  8 ++++----\n>  t/t2201-add-update-typechange.sh   |  2 +-\n>  t/t3300-funny-names.sh             |  2 +-\n>  t/t3306-notes-prune.sh             |  2 +-\n>  t/t3311-notes-merge-fanout.sh      |  2 +-\n>  t/t4014-format-patch.sh            |  6 +++---\n>  t/t4032-diff-inter-hunk-context.sh |  4 ++--\n>  t/t4102-apply-rename.sh            |  2 +-\n>  t/t4131-apply-fake-ancestor.sh     |  2 +-\n>  t/t4132-apply-removal.sh           |  4 ++--\n>  t/t5300-pack-object.sh             | 10 +++++-----\n>  t/t5301-sliding-window.sh          |  4 ++--\n>  t/t5302-pack-index.sh              |  8 ++++----\n>  t/t5502-quickfetch.sh              |  2 +-\n>  t/t5510-fetch.sh                   |  8 ++++----\n>  t/t5516-fetch-push.sh              |  2 +-\n>  t/t5534-push-signed.sh             |  6 +++---\n>  t/t5550-http-fetch-dumb.sh         |  2 +-\n>  t/t5604-clone-reference.sh         |  2 +-\n>  t/t6500-gc.sh                      |  2 +-\n>  t/t7012-skip-worktree-writing.sh   |  2 +-\n>  t/t7102-reset.sh                   |  2 +-\n>  t/t7104-reset-hard.sh              |  2 +-\n>  t/t7113-post-index-change-hook.sh  | 12 ++++++------\n>  t/t7201-co.sh                      |  6 +++---\n>  t/t7400-submodule-basic.sh         | 10 +++++-----\n>  t/t7407-submodule-foreach.sh       |  6 +++---\n>  t/t7412-submodule-absorbgitdirs.sh |  8 ++++----\n>  t/t7602-merge-octopus-many.sh      |  2 +-\n>  t/t9001-send-email.sh              |  6 +++---\n>  t/t9400-git-cvsserver-server.sh    |  6 +++---\n>  34 files changed, 76 insertions(+), 76 deletions(-)\n\nI'd recommend significantly shrinking the number of files you convert to\nat most a handful in this series. The conversion to use the\n`test_path_*()` helpers is something that we mostly hand out to\nnewcomers as the usefulness of it is really rather in the educational\npart rather than it bringing a lot of value to the Git project.\n\n> diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\n> index 463b38f990..e6d1fe3e13 100755\n> --- a/t/t1007-hash-object.sh\n> +++ b/t/t1007-hash-object.sh\n> @@ -271,7 +271,7 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n>  \n>  test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \\\n>  \t\t'files over 4GB hash correctly via --stdin' '\n> -\t{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n> +\t{ test_path_is_file big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&\n>  \ttest_oid large5GB >expect &&\n>  \tgit hash-object --stdin <big >actual &&\n>  \ttest_cmp expect actual\n\nThis is wrong. The intent is that we only generate the file if we didn't\nalready do it beforehand, so it's a form of lazy creation. So it is\nexpected that the file may not exist, but with `test_path_is_file` we'd\nnow generate an error message if so.\n\nLikewise for the subsequent changes in this fiel.\n\n> diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh\n> index 7d443968e3..cc213c04a1 100755\n> --- a/t/t4032-diff-inter-hunk-context.sh\n> +++ b/t/t4032-diff-inter-hunk-context.sh\n> @@ -28,7 +28,7 @@ t() {\n>  \tfile=f$1\n>  \texpected=expected.$file.$3.$hunks\n>  \n> -\tif ! test -f $file\n> +\tif test_path_is_missing $file\n>  \tthen\n>  \t\tf A $1 B >$file\n>  \t\tgit add $file\n> @@ -40,7 +40,7 @@ t() {\n>  \t\ttest $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks\n>  \t\"\n>  \n> -\tif test -f $expected\n> +\tif test_path_is_file $expected\n>  \tthen\n>  \t\ttest_expect_success \"$label: check output\" \"\n>  \t\t\tgit $cmd $file | grep -v '^index ' >actual &&\n\nLikewise, these here are expected cases where the file may be missing.\nWe shouldn't print an error message in such cases. There's also a couple\nmore such cases.\n\nThanks!\n\nPatrick\n"}]}