{"thread":{"id":"57057","subject":"[PATCH 00/19] tests: fix broken &&-chains & abort loops on error","startedAt":"2021-12-09T05:12:11Z","lastAt":"2021-12-11T10:00:00Z","messageCount":47,"participants":["Eric Sunshine","Elijah Newren","Jeff King","Fabian Stelzer"],"isPatch":true,"patchVersion":1,"patchTotal":19},"messages":[{"id":"443582","messageId":"20211209051115.52629-1-sunshine@sunshineco.com","threadId":"57057","inReplyTo":null,"subject":"[PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:10:56Z","receivedAt":"2021-12-09T05:12:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"A while back, Peff successfully nerd-sniped[1] me into tackling a\nlong-brewing idea I had about (possibly) improving `chainlint`\nperformance by linting all tests in all scripts with a single command\ninvocation instead of running `sed` 25300+ times (once for each test).\n\nBeside the obvious improvement of checking all tests in all scripts at\nonce, the new linter is also a good deal smarter than chainlint.sed and\nunderstands not just shell syntax but also some semantics of test\nconstruction, unlike chainlint.sed which is merely heuristics-based.\nFor instance, the new linter recognizes cases when a broken &&-chain is\nlegitimate, such as when `$?` is handled explicitly or when a failure is\nsignaled directly with `false`, in which case the &&-chain leading up to\nthe `false` is immaterial, as well as other cases. Unlike chainlint.sed,\nit recognizes that a semicolon after the last command in a compound\nstatement is harmless, thus won't interpret the semicolon as breaking\nthe &&-chain.\n\nThe new linter also provides considerably better coverage for broken\n&&-chains. The &&-chain checker built into t/test-lib.sh, which causes\ntests to magically exit with code 117 if the &&-chain is broken, only\nworks for top-level command invocations. The magic doesn't work within\n`{...}` groups, `(...)` subshells, `$(...)` substitutions, or within\nbodies of compound statements, such as `if`, `for`, `while`, `case`,\netc. chainlint.sed partly fills the gap by catching broken &&-chains in\n`(...)` subshells one level deep, but bugs can still lurk behind broken\n&&-chains in the other cases. The new linter catches broken &&-chains\nwithin all those constructs to any depth.\n\nAnother important improvement is that the new linter understands that\nshell loops do not terminate automatically when a command in the loop\nbody fails, and that the condition needs to be handled explicitly by the\ntest author by using `|| return 1` (or `|| exit 1` in a subshell) within\nthe loop body to flag the failure. Consequently, the new linter will\ncomplain when a loop is lacking `|| return 1` (or `|| exit 1`). There\nare a number of other improvements, as well, but the above are some of\nthe more important ones.\n\nAlthough the new chainlint implementation has been complete for several\nmonths, I'm still working out how to organize its patch series. In the\nmeantime, _this_ patch series fixes problems discovered by the new\nlinter due to its improved coverage and extra semantic knowledge about\nGit tests. As much as possible, I resisted the temptation to make\nancillary cleanups (including indentation fixes) to tests even when such\ncleanups would be obvious improvements. Avoiding such unrelated cleanups\nshould make the long patches in this series, which touch a lot of tests,\neasier to review (--color-words helps a lot here).\n\nThis series merges cleanly with 'next' but conflicts with a couple topics\nin 'seen':\n\n* jh/builtin-fsmonitor-part2\n\n  t/perf/p7519-fsmonitor.sh\n    simple resolution: keep all changes from jh/builtin-fsmonitor-part2\n    (it obviates the need for the fixes made by this series)\n\n* ms/customizable-ident-expansion\n\n  t/t0021-conversion.sh\n    this is a messy conflict but resolution is simple enough: keep all\n    the changes made by ms/customizable-ident-expansion and throw away\n    the changes by this series; this will leave a few broken &&-chains\n    in t0021-conversion.sh but there are a few other topics in 'seen'\n    with such problems already, so it has company; anyhow, \"What's\n    Cooking\" indicates that ms/customizable-ident-expansion is going to\n    be discarded, so it may not be worth worrying about it\n\n[1]: https://lore.kernel.org/git/YJzGcZpZ+E9R0gYd@coredump.intra.peff.net/\n\nEric Sunshine (19):\n  t/lib-pager: use sane_unset() to avoid breaking &&-chain\n  t1010: fix unnoticed failure on Windows\n  t1020: avoid aborting entire test script when one test fails\n  t4202: clarify intent by creating expected content less cleverly\n  t5516: drop unnecessary subshell and command invocation\n  t6300: make `%(raw:size) --shell` test more robust\n  t9107: use shell parameter expansion to avoid breaking &&-chain\n  tests: simplify construction of large blocks of text\n  tests: use test_write_lines() to generate line-oriented output\n  tests: fix broken &&-chains in compound statements\n  tests: fix broken &&-chains in `$(...)` command substitutions\n  tests: fix broken &&-chains in `{...}` groups\n  tests: apply modern idiom for signaling test failure\n  tests: apply modern idiom for exiting loop upon failure\n  tests: simplify by dropping unnecessary `for` loops\n  t0000-t3999: detect and signal failure within loop\n  t4000-t4999: detect and signal failure within loop\n  t5000-t5999: detect and signal failure within loop\n  t6000-t9999: detect and signal failure within loop\n\n .../mw-to-git/t/t9365-continuing-queries.sh   |   2 +-\n contrib/subtree/t/t7900-subtree.sh            |   2 +-\n t/annotate-tests.sh                           |   2 +-\n t/lib-pager.sh                                |   2 +-\n t/perf/p0005-status.sh                        |  12 +-\n t/perf/p0006-read-tree-checkout.sh            |  20 +-\n t/perf/p0007-write-cache.sh                   |   4 +-\n t/perf/p0100-globbing.sh                      |   4 +-\n t/perf/p1400-update-ref.sh                    |   4 +-\n t/perf/p1451-fsck-skip-list.sh                |   2 +-\n t/perf/p3400-rebase.sh                        |   2 +-\n t/perf/p5302-pack-index.sh                    |   4 +-\n t/perf/p5303-many-packs.sh                    |  10 +-\n t/perf/p7519-fsmonitor.sh                     |   8 +-\n t/t0005-signals.sh                            |   2 +-\n t/t0008-ignores.sh                            |   2 +-\n t/t0011-hashmap.sh                            |   4 +-\n t/t0020-crlf.sh                               |  28 +-\n t/t0021-conversion.sh                         |  60 ++--\n t/t0026-eol-config.sh                         |   4 +-\n t/t0060-path-utils.sh                         |   4 +-\n t/t0069-oidtree.sh                            |  12 +-\n t/t0095-bloom.sh                              |   4 +-\n t/t0410-partial-clone.sh                      |   2 +-\n t/t1006-cat-file.sh                           |  12 +-\n t/t1010-mktree.sh                             |   4 +-\n t/t1020-subdirectory.sh                       |  10 +-\n t/t1050-large.sh                              |  34 +-\n t/t1091-sparse-checkout-builtin.sh            |   2 +-\n t/t1300-config.sh                             |   6 +-\n t/t1400-update-ref.sh                         |   4 +-\n t/t1403-show-ref.sh                           |  12 +-\n t/t1410-reflog.sh                             |   4 +-\n t/t1512-rev-parse-disambiguation.sh           |  12 +-\n t/t1700-split-index.sh                        |   4 +-\n t/t2004-checkout-cache-temp.sh                |   4 +-\n t/t2012-checkout-last.sh                      |   4 +-\n t/t2102-update-index-symlinks.sh              |   2 +-\n t/t2103-update-index-ignore-missing.sh        |   2 +-\n t/t2200-add-update.sh                         |  18 +-\n t/t2201-add-update-typechange.sh              |  10 +-\n t/t2203-add-intent.sh                         |   2 +-\n t/t3005-ls-files-relative.sh                  |  10 +-\n t/t3070-wildmatch.sh                          |   2 +-\n t/t3202-show-branch.sh                        |   8 +-\n t/t3303-notes-subtrees.sh                     |   6 +-\n t/t3305-notes-fanout.sh                       |   4 +-\n t/t3402-rebase-merge.sh                       |   8 +-\n t/t3404-rebase-interactive.sh                 |   4 +-\n t/t3417-rebase-whitespace-fix.sh              |   4 +-\n t/t3501-revert-cherry-pick.sh                 |   2 +-\n t/t3508-cherry-pick-many-commits.sh           |   2 +-\n t/t3600-rm.sh                                 |   7 +-\n t/t3700-add.sh                                |   8 +-\n t/t3920-crlf-messages.sh                      |   4 +-\n t/t4001-diff-rename.sh                        |   2 +-\n t/t4012-diff-binary.sh                        |   2 +-\n t/t4013-diff-various.sh                       |  22 +-\n t/t4014-format-patch.sh                       |  32 +-\n t/t4015-diff-whitespace.sh                    |   4 +-\n t/t4018-diff-funcname.sh                      |   2 +-\n t/t4019-diff-wserror.sh                       |   4 +-\n t/t4023-diff-rename-typechange.sh             |   6 +-\n t/t4024-diff-optimize-common.sh               |   2 +-\n t/t4025-hunk-header.sh                        |  10 +-\n t/t4038-diff-combined.sh                      |   2 +-\n t/t4046-diff-unmerged.sh                      |  10 +-\n t/t4049-diff-stat-count.sh                    |   2 +-\n t/t4052-stat-output.sh                        |   2 +-\n t/t4057-diff-combined-paths.sh                |  16 +-\n t/t4105-apply-fuzz.sh                         |  10 +-\n t/t4106-apply-stdin.sh                        |   5 +-\n t/t4116-apply-reverse.sh                      |   4 +-\n t/t4117-apply-reject.sh                       |  20 +-\n t/t4118-apply-empty-context.sh                |   6 +-\n t/t4123-apply-shrink.sh                       |   4 +-\n t/t4124-apply-ws-rule.sh                      |  58 ++--\n t/t4125-apply-ws-fuzz.sh                      |   5 +-\n t/t4126-apply-empty.sh                        |   5 +-\n t/t4127-apply-same-fn.sh                      |   5 +-\n t/t4138-apply-ws-expansion.sh                 |  36 +-\n t/t4150-am.sh                                 |   2 +-\n t/t4151-am-abort.sh                           |  10 +-\n t/t4202-log.sh                                |  42 +--\n t/t4205-log-pretty-formats.sh                 |   2 +-\n t/t4211-line-log.sh                           |   2 +-\n t/t4212-log-corrupt.sh                        |   8 +-\n t/t4216-log-bloom.sh                          |   4 +-\n t/t5000-tar-tree.sh                           |   4 +-\n t/t5003-archive-zip.sh                        |   2 +-\n t/t5004-archive-corner-cases.sh               |   6 +-\n t/t5100-mailinfo.sh                           |   2 +-\n t/t5300-pack-object.sh                        |  18 +-\n t/t5302-pack-index.sh                         |   2 +-\n t/t5306-pack-nobase.sh                        |   2 +-\n t/t5307-pack-missing-commit.sh                |   2 +-\n t/t5310-pack-bitmaps.sh                       |   2 +-\n t/t5316-pack-delta-depth.sh                   |   7 +-\n t/t5317-pack-objects-filter-objects.sh        |  30 +-\n t/t5318-commit-graph.sh                       |   6 +-\n t/t5319-multi-pack-index.sh                   |  10 +-\n t/t5322-pack-objects-sparse.sh                |   4 +-\n t/t5325-reverse-index.sh                      |   2 +-\n t/t5500-fetch-pack.sh                         |   8 +-\n t/t5502-quickfetch.sh                         |   2 +-\n t/t5505-remote.sh                             |   6 +-\n t/t5510-fetch.sh                              |  14 +-\n t/t5515-fetch-merge-logic.sh                  |  38 +--\n t/t5516-fetch-push.sh                         |   5 +-\n t/t5552-skipping-fetch-negotiator.sh          |  10 +-\n t/t5562-http-backend-content-length.sh        |   2 +-\n t/t5570-git-daemon.sh                         |   2 +-\n t/t5571-pre-push-hook.sh                      |   6 +-\n t/t5611-clone-config.sh                       |   2 +-\n t/t5616-partial-clone.sh                      |  30 +-\n t/t5702-protocol-v2.sh                        |   4 +-\n t/t6005-rev-list-count.sh                     |   8 +-\n t/t6009-rev-list-parent.sh                    |   6 +-\n t/t6019-rev-list-ancestry-path.sh             |  10 +-\n t/t6060-merge-index.sh                        |   4 +-\n t/t6101-rev-parse-parents.sh                  |   2 +-\n t/t6112-rev-list-filters-objects.sh           |  22 +-\n t/t6120-describe.sh                           |  13 +-\n t/t6132-pathspec-exclude.sh                   |   2 +-\n t/t6200-fmt-merge-msg.sh                      |   2 +-\n t/t6300-for-each-ref.sh                       |   7 +-\n t/t6406-merge-attr.sh                         |   8 +-\n t/t6407-merge-binary.sh                       |   4 +-\n t/t6409-merge-subtree.sh                      |   6 +-\n t/t6411-merge-filemode.sh                     |   8 +-\n t/t6412-merge-large-rename.sh                 |  10 +-\n t/t6416-recursive-corner-cases.sh             |  30 +-\n t/t6417-merge-ours-theirs.sh                  |   5 +-\n t/t6430-merge-recursive.sh                    |   2 +-\n t/t6600-test-reach.sh                         |   4 +-\n t/t7004-tag.sh                                |   9 +-\n t/t7010-setup.sh                              |   2 +-\n t/t7110-reset-merge.sh                        |   2 +-\n t/t7501-commit-basic-functionality.sh         |   5 +-\n t/t7505-prepare-commit-msg-hook.sh            |   2 +-\n t/t7513-interpret-trailers.sh                 |   2 +-\n t/t7519-status-fsmonitor.sh                   |   2 +-\n t/t7600-merge.sh                              |   2 +-\n t/t7602-merge-octopus-many.sh                 |   4 +-\n t/t7603-merge-reduce-heads.sh                 |   4 +-\n t/t7700-repack.sh                             |   2 +-\n t/t7810-grep.sh                               | 310 +++++++++---------\n t/t8002-blame.sh                              |   2 +-\n t/t8003-blame-corner-cases.sh                 |  10 +-\n t/t8014-blame-ignore-fuzzy.sh                 |   4 +-\n t/t9104-git-svn-follow-parent.sh              |   4 +-\n t/t9107-git-svn-migrate.sh                    |   8 +-\n t/t9130-git-svn-authors-file.sh               |   6 +-\n t/t9134-git-svn-ignore-paths.sh               |  16 +-\n t/t9138-git-svn-authors-prog.sh               |   2 +-\n t/t9146-git-svn-empty-dirs.sh                 |   4 +-\n t/t9147-git-svn-include-paths.sh              |  16 +-\n t/t9152-svn-empty-dirs-after-gc.sh            |   2 +-\n t/t9304-fast-import-marks.sh                  |   2 +-\n t/t9400-git-cvsserver-server.sh               |  11 +-\n t/t9800-git-p4-basic.sh                       |   2 +-\n t/t9818-git-p4-block.sh                       |   6 +-\n t/t9902-completion.sh                         |   4 +-\n 163 files changed, 740 insertions(+), 847 deletions(-)\n\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443583","messageId":"20211209051115.52629-2-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 01/19] t/lib-pager: use sane_unset() to avoid breaking &&-chain","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:10:57Z","receivedAt":"2021-12-09T05:12:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"This test intentionally breaks the &&-chain following `unset` since it\ndoesn't know if `unset` will succeed or fail and doesn't want a local\n`unset` failure to abort the test overall. We can do better by using\nsane_unset() which can be linked into the &&-chain as usual.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/lib-pager.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/lib-pager.sh b/t/lib-pager.sh\nindex 3aa7a3ffd8..e5eb28df4e 100644\n--- a/t/lib-pager.sh\n+++ b/t/lib-pager.sh\n@@ -3,7 +3,7 @@\n test_expect_success 'determine default pager' '\n \ttest_might_fail git config --unset core.pager &&\n \tless=$(\n-\t\tunset PAGER GIT_PAGER;\n+\t\tsane_unset PAGER GIT_PAGER &&\n \t\tgit var GIT_PAGER\n \t) &&\n \ttest -n \"$less\"\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443584","messageId":"20211209051115.52629-3-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 02/19] t1010: fix unnoticed failure on Windows","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:10:58Z","receivedAt":"2021-12-09T05:12:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Microsoft Windows, a directory name should never end with a period.\nQuoting from Microsoft documentation[1]:\n\n    Do not end a file or directory name with a space or a period.\n    Although the underlying file system may support such names, the\n    Windows shell and user interface does not.\n\nNaming a directory with a trailing period is indeed perilous:\n\n    % git init foo\n    % cd foo\n    % mkdir a.\n    % git status\n    warning: could not open directory 'a./': No such file or directory\n\nThe t1010 \"setup\" test:\n\n    for d in a a. a0\n    do\n        mkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n        git add \"$d\"\n    done &&\n\nruns afoul of this Windows limitation, as can be observed when running\nthe test verbosely:\n\n    error: open(\"a./one\"): No such file or directory\n    error: unable to index file 'a./one'\n    fatal: adding files failed\n\nThe reason this problem has gone unnoticed for so long is twofold.\nFirst, the failed `git add` is swallowed silently because the loop is\nnot terminated explicitly by `|| return 1` to signal the failure.\nSecond, none of the tests in this script care about the actual directory\nnames or even the number of tree entries. They care only that the tree\nsynthesized in the index and created by `git write-tree` matches the\ntree created by the output of `git ls-tree` fed into `git mktree`, and\nthe failure of `git add \"a./one\"` doesn't change that outcome.\n\nSkipping these tests on Windows by, for instance, checking the\nFUNNYNAMES predicate would avoid the problem, however, the funny-looking\nname is not what is being tested here. Rather, the tests are about\nchecking that `git mktree` produces stable results for various input\nconditions, such as when the input order is not consistent or when an\nobject is missing.\n\nTherefore, resolve the problem simply by using a directory name which is\nlegal on Windows (i.e. \"a-\" rather than \"a.\"). While at it, add the\nmissing `|| return 1` to the loop body in order to catch this sort of\nproblem in the future.\n\n[1]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t1010-mktree.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh\nindex 48bfad07ab..3c08194526 100755\n--- a/t/t1010-mktree.sh\n+++ b/t/t1010-mktree.sh\n@@ -6,10 +6,10 @@ TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tfor d in a a. a0\n+\tfor d in a a- a0\n \tdo\n \t\tmkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n-\t\tgit add \"$d\"\n+\t\tgit add \"$d\" || return 1\n \tdone &&\n \techo zero >one &&\n \tgit update-index --add --info-only one &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443585","messageId":"20211209051115.52629-4-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 03/19] t1020: avoid aborting entire test script when one test fails","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:10:59Z","receivedAt":"2021-12-09T05:12:18Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Although `exit 1` is the proper way to signal a test failure from within\na subshell, its use outside any subshell should be avoided since it\naborts the entire script rather than aborting only the failed test.\nInstead, a simple `return 1` is the proper idiom for signaling failure\noutside a subshell since it aborts only the test in question, not the\nentire script.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t1020-subdirectory.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh\nindex c2df75e495..da19c06fb5 100755\n--- a/t/t1020-subdirectory.sh\n+++ b/t/t1020-subdirectory.sh\n@@ -22,7 +22,7 @@ test_expect_success 'update-index and ls-files' '\n \tgit update-index --add one &&\n \tcase \"$(git ls-files)\" in\n \tone) echo pass one ;;\n-\t*) echo bad one; exit 1 ;;\n+\t*) echo bad one; return 1 ;;\n \tesac &&\n \t(\n \t\tcd dir &&\n@@ -34,7 +34,7 @@ test_expect_success 'update-index and ls-files' '\n \t) &&\n \tcase \"$(git ls-files)\" in\n \tdir/two\"$LF\"one) echo pass both ;;\n-\t*) echo bad; exit 1 ;;\n+\t*) echo bad; return 1 ;;\n \tesac\n '\n \n@@ -57,7 +57,7 @@ test_expect_success 'diff-files' '\n \techo d >>dir/two &&\n \tcase \"$(git diff-files --name-only)\" in\n \tdir/two\"$LF\"one) echo pass top ;;\n-\t*) echo bad top; exit 1 ;;\n+\t*) echo bad top; return 1 ;;\n \tesac &&\n \t# diff should not omit leading paths\n \t(\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443586","messageId":"20211209051115.52629-7-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 06/19] t6300: make `%(raw:size) --shell` test more robust","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:02Z","receivedAt":"2021-12-09T05:12:19Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"This test populates its `expect` file solely by appending content but\nfails to ensure that the file starts out empty. The test succeeds only\nbecause no earlier test populated a file of the exact same name, however\nthis is an accident waiting to happen. Make the test more robust by\nensuring that it contains exactly the intended content.\n\nWhile at it, simplify the implementation via a straightforward `sed`\napplication and by avoiding dropping out of the single-quote context\nwithin the test body (thus eliminating a hard-to-digest combination of\napostrophes and backslashes).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNotes:\n    An alternative simple fix would be to capture the output of the\n    `while` loop itself rather than the output of `echo`:\n    \n        git for-each-ref ... | while read line\n        do\n            echo ...\n        done >expect &&\n    \n    however, I prefer the conciseness of the `sed` approach,\n    \n    This patch makes no attempt to address `git` being upstream in a\n    pipe. There are enough other such instances in this script that\n    fixing them warrants a separate patch (if someone wants to tackle\n    it).\n\n t/t6300-for-each-ref.sh | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 9f2c706c12..aae57908f8 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -955,10 +955,7 @@ test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n '\n \n test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | while read line\n-\tdo\n-\t\techo \"'\\''$line'\\''\" >>expect\n-\tdone &&\n+\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n \tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n \ttest_cmp expect actual\n '\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443587","messageId":"20211209051115.52629-5-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 04/19] t4202: clarify intent by creating expected content less cleverly","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:00Z","receivedAt":"2021-12-09T05:12:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Several tests assign the output of `$(...)` command substitution to an\n\"expect\" variable, taking advantage of the fact that `$(...)` folds out\nthe final line terminator while leaving internal line terminators\nintact. They do this because the \"actual\" string with which \"expect\"\nwill be compared is shaped the same way. However, this intent (having\ninternal line terminators, but no final line terminator) is not\nnecessarily obvious at first glance and may confuse casual readers. The\nintent can be made more obvious by using `printf` instead, with which\nline termination is stated clearly:\n\n    printf \"sixth\\nthird\"\n\nIn fact, many other tests in this script already use `printf` for\nprecisely this purpose, thus it is an established pattern. Therefore,\nconvert these tests to employ `printf`, as well.\n\nWhile at it, modernize the tests to use test_cmp() to compare the\nexpected and actual output rather than using the semi-deprecated\n`verbose test \"$x\" = \"$y\"`.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t4202-log.sh | 42 +++++++++++++++++++++---------------------\n 1 file changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex 7884e3d46b..c2cfbc69f7 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -120,48 +120,48 @@ test_expect_success 'diff-filter=A' '\n \n test_expect_success 'diff-filter=M' '\n \n-\tactual=$(git log --pretty=\"format:%s\" --diff-filter=M HEAD) &&\n-\texpect=$(echo second) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log --pretty=\"format:%s\" --diff-filter=M HEAD >actual &&\n+\tprintf \"second\" >expect &&\n+\ttest_cmp expect actual\n \n '\n \n test_expect_success 'diff-filter=D' '\n \n-\tactual=$(git log --no-renames --pretty=\"format:%s\" --diff-filter=D HEAD) &&\n-\texpect=$(echo sixth ; echo third) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log --no-renames --pretty=\"format:%s\" --diff-filter=D HEAD >actual &&\n+\tprintf \"sixth\\nthird\" >expect &&\n+\ttest_cmp expect actual\n \n '\n \n test_expect_success 'diff-filter=R' '\n \n-\tactual=$(git log -M --pretty=\"format:%s\" --diff-filter=R HEAD) &&\n-\texpect=$(echo third) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log -M --pretty=\"format:%s\" --diff-filter=R HEAD >actual &&\n+\tprintf \"third\" >expect &&\n+\ttest_cmp expect actual\n \n '\n \n test_expect_success 'diff-filter=C' '\n \n-\tactual=$(git log -C -C --pretty=\"format:%s\" --diff-filter=C HEAD) &&\n-\texpect=$(echo fourth) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log -C -C --pretty=\"format:%s\" --diff-filter=C HEAD >actual &&\n+\tprintf \"fourth\" >expect &&\n+\ttest_cmp expect actual\n \n '\n \n test_expect_success 'git log --follow' '\n \n-\tactual=$(git log --follow --pretty=\"format:%s\" ichi) &&\n-\texpect=$(echo third ; echo second ; echo initial) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log --follow --pretty=\"format:%s\" ichi >actual &&\n+\tprintf \"third\\nsecond\\ninitial\" >expect &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git config log.follow works like --follow' '\n \ttest_config log.follow true &&\n-\tactual=$(git log --pretty=\"format:%s\" ichi) &&\n-\texpect=$(echo third ; echo second ; echo initial) &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log --pretty=\"format:%s\" ichi >actual &&\n+\tprintf \"third\\nsecond\\ninitial\" >expect &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git config log.follow does not die with multiple paths' '\n@@ -176,9 +176,9 @@ test_expect_success 'git config log.follow does not die with no paths' '\n \n test_expect_success 'git config log.follow is overridden by --no-follow' '\n \ttest_config log.follow true &&\n-\tactual=$(git log --no-follow --pretty=\"format:%s\" ichi) &&\n-\texpect=\"third\" &&\n-\tverbose test \"$actual\" = \"$expect\"\n+\tgit log --no-follow --pretty=\"format:%s\" ichi >actual &&\n+\tprintf \"third\" >expect &&\n+\ttest_cmp expect actual\n '\n \n # Note that these commits are intentionally listed out of order.\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443588","messageId":"20211209051115.52629-6-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 05/19] t5516: drop unnecessary subshell and command invocation","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:01Z","receivedAt":"2021-12-09T05:12:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"To create its \"expect\" file, this test pipes into `sort` the output of\n`git for-each-ref` and a copy of that same output but with a minor\ntextual transformation applied. To do so, it employs a subshell and\ncommands `cat` and `sed` even though the same result can be accomplished\nby `sed` alone (without a subshell).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t5516-fetch-push.sh | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 8212ca56dc..d5e19b54af 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1316,10 +1316,7 @@ test_expect_success 'fetch follows tags by default' '\n \t\tgit pull ../testrepo main &&\n \t\tgit tag -m \"annotated\" tag &&\n \t\tgit for-each-ref >tmp1 &&\n-\t\t(\n-\t\t\tcat tmp1\n-\t\t\tsed -n \"s|refs/heads/main$|refs/remotes/origin/main|p\" tmp1\n-\t\t) |\n+\t\tsed -n \"p; s|refs/heads/main$|refs/remotes/origin/main|p\" tmp1 |\n \t\tsort -k 3 >../expect\n \t) &&\n \tgit init dst &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443589","messageId":"20211209051115.52629-8-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 07/19] t9107: use shell parameter expansion to avoid breaking &&-chain","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:03Z","receivedAt":"2021-12-09T05:12:22Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"This test intentionally breaks the &&-chain when using `expr` to parse\n\"[<path>]:<ref>\" since the pattern matching operation will return 1\n(failure) when <path> is empty even though an empty <path> is legitimate\nin this test and should not cause the test to fail. However, it is\npossible to parse the input without breaking the &&-chain by using shell\nparameter expansion (i.e. `${i%%...}`). Other ways to avoid the problem\nwould be `{ expr $i : ... ||:; }` or test_might_fail(), however,\nparameter expansion seems simplest.\n\nIMPLEMENTATION NOTE\n\nThe rewritten `if` expression:\n\n    if test \"$ref\" = \"${ref#refs/remotes/}\"`; then continue; fi\n\nis perhaps a bit subtle. At first glance, it looks like it will\n`continue` the loop if $ref starts with \"refs/remotes/\", but in fact\nit's the opposite: the loop will `continue` if $ref does not start with\n\"refs/remotes/\".\n\nIn the original, `expr` would only match if the ref started with\n\"refs/remotes/\", and $ref would end up empty if it didn't, so `test -z`\nwould `continue` the loop if the ref did not start with \"refs/remotes/\".\n\nWith parameter expansion, ${ref#refs/remotes/} attempts to strip\n\"refs/remotes/\" from $ref. If it fails, meaning that $ref does not start\nwith \"refs/remotes/\", then the expansion will just be $ref unchanged,\nand it will `continue` the loop. On the other hand, if stripping\nsucceeds, meaning that $ref begins with \"refs/remotes/\", then the\nexpansion will be the value of $ref with \"refs/remotes/\" removed, hence\n`continue` will not be taken.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t9107-git-svn-migrate.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh\nindex ceaa5bad10..aa908bbc2f 100755\n--- a/t/t9107-git-svn-migrate.sh\n+++ b/t/t9107-git-svn-migrate.sh\n@@ -98,10 +98,10 @@ test_expect_success 'migrate --minimize on old inited layout' '\n \trm -rf \"$GIT_DIR\"/svn &&\n \tfor i in $(cat fetch.out)\n \tdo\n-\t\tpath=$(expr $i : \"\\([^:]*\\):.*$\")\n-\t\tref=$(expr $i : \"[^:]*:\\(refs/remotes/.*\\)$\")\n-\t\tif test -z \"$ref\"; then continue; fi\n-\t\tif test -n \"$path\"; then path=\"/$path\"; fi\n+\t\tpath=${i%%:*} &&\n+\t\tref=${i#*:} &&\n+\t\tif test \"$ref\" = \"${ref#refs/remotes/}\"; then continue; fi &&\n+\t\tif test -n \"$path\"; then path=\"/$path\"; fi &&\n \t\tmkdir -p \"$GIT_DIR\"/svn/$ref/info/ &&\n \t\techo \"$svnrepo\"$path >\"$GIT_DIR\"/svn/$ref/info/url ||\n \t\treturn 1\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443590","messageId":"20211209051115.52629-9-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 08/19] tests: simplify construction of large blocks of text","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:04Z","receivedAt":"2021-12-09T05:12:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Take advantage of here-docs to create large blocks of text rather than\nusing a series of `echo` statements. Not only are here-docs a natural\nfit for such a task, but there is less opportunity for a broken\n&&-chain.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t0021-conversion.sh        |  44 ++---\n t/t4106-apply-stdin.sh       |   5 +-\n t/t5510-fetch.sh             |  10 +-\n t/t5515-fetch-merge-logic.sh |  22 +--\n t/t6406-merge-attr.sh        |   8 +-\n t/t6411-merge-filemode.sh    |   8 +-\n t/t7810-grep.sh              | 310 +++++++++++++++++------------------\n 7 files changed, 205 insertions(+), 202 deletions(-)\n\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex 33dfc9cd56..1a1a69ad92 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -118,17 +118,17 @@ test_expect_success check '\n # If an expanded ident ever gets into the repository, we want to make sure that\n # it is collapsed before being expanded again on checkout\n test_expect_success expanded_in_repo '\n-\t{\n-\t\techo \"File with expanded keywords\"\n-\t\techo \"\\$Id\\$\"\n-\t\techo \"\\$Id:\\$\"\n-\t\techo \"\\$Id: 0000000000000000000000000000000000000000 \\$\"\n-\t\techo \"\\$Id: NoSpaceAtEnd\\$\"\n-\t\techo \"\\$Id:NoSpaceAtFront \\$\"\n-\t\techo \"\\$Id:NoSpaceAtEitherEnd\\$\"\n-\t\techo \"\\$Id: NoTerminatingSymbol\"\n-\t\techo \"\\$Id: Foreign Commit With Spaces \\$\"\n-\t} >expanded-keywords.0 &&\n+\tcat >expanded-keywords.0 <<-\\EOF &&\n+\tFile with expanded keywords\n+\t$Id$\n+\t$Id:$\n+\t$Id: 0000000000000000000000000000000000000000 $\n+\t$Id: NoSpaceAtEnd$\n+\t$Id:NoSpaceAtFront $\n+\t$Id:NoSpaceAtEitherEnd$\n+\t$Id: NoTerminatingSymbol\n+\t$Id: Foreign Commit With Spaces $\n+\tEOF\n \n \t{\n \t\tcat expanded-keywords.0 &&\n@@ -139,17 +139,17 @@ test_expect_success expanded_in_repo '\n \tgit commit -m \"File with keywords expanded\" &&\n \tid=$(git rev-parse --verify :expanded-keywords) &&\n \n-\t{\n-\t\techo \"File with expanded keywords\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: $id \\$\"\n-\t\techo \"\\$Id: NoTerminatingSymbol\"\n-\t\techo \"\\$Id: Foreign Commit With Spaces \\$\"\n-\t} >expected-output.0 &&\n+\tcat >expected-output.0 <<-EOF &&\n+\tFile with expanded keywords\n+\t\\$Id: $id \\$\n+\t\\$Id: $id \\$\n+\t\\$Id: $id \\$\n+\t\\$Id: $id \\$\n+\t\\$Id: $id \\$\n+\t\\$Id: $id \\$\n+\t\\$Id: NoTerminatingSymbol\n+\t\\$Id: Foreign Commit With Spaces \\$\n+\tEOF\n \t{\n \t\tcat expected-output.0 &&\n \t\tprintf \"\\$Id: NoTerminatingSymbolAtEOF\"\ndiff --git a/t/t4106-apply-stdin.sh b/t/t4106-apply-stdin.sh\nindex 72467a1e8e..1e70810b9c 100755\n--- a/t/t4106-apply-stdin.sh\n+++ b/t/t4106-apply-stdin.sh\n@@ -18,7 +18,10 @@ test_expect_success 'git apply --numstat - < patch' '\n '\n \n test_expect_success 'git apply --numstat - < patch patch' '\n-\tfor i in 1 2; do echo \"1\t1\ttext\"; done >expect &&\n+\tcat >expect <<-\\EOF &&\n+\t1\t1\ttext\n+\t1\t1\ttext\n+\tEOF\n \tgit apply --numstat - < patch patch >actual &&\n \ttest_cmp expect actual\n '\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex a0faf0dd94..1892d6615a 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -40,11 +40,11 @@ test_expect_success \"clone and setup child repos\" '\n \t\tgit config branch.main.remote two &&\n \t\tgit config branch.main.merge refs/heads/one &&\n \t\tmkdir -p .git/remotes &&\n-\t\t{\n-\t\t\techo \"URL: ../two/.git/\"\n-\t\t\techo \"Pull: refs/heads/main:refs/heads/two\"\n-\t\t\techo \"Pull: refs/heads/one:refs/heads/one\"\n-\t\t} >.git/remotes/two\n+\t\tcat >.git/remotes/two <<-\\EOF\n+\t\tURL: ../two/.git/\n+\t\tPull: refs/heads/main:refs/heads/two\n+\t\tPull: refs/heads/one:refs/heads/one\n+\t\tEOF\n \t) &&\n \tgit clone . bundle &&\n \tgit clone . seven\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 50f14101c5..9d440e2821 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -105,19 +105,19 @@ test_expect_success setup '\n \tremotes=\"$remotes config-glob\" &&\n \n \tmkdir -p .git/remotes &&\n-\t{\n-\t\techo \"URL: ../.git/\"\n-\t\techo \"Pull: refs/heads/main:remotes/rem/main\"\n-\t\techo \"Pull: refs/heads/one:remotes/rem/one\"\n-\t\techo \"Pull: two:remotes/rem/two\"\n-\t\techo \"Pull: refs/heads/three:remotes/rem/three\"\n-\t} >.git/remotes/remote-explicit &&\n+\tcat >.git/remotes/remote-explicit <<-\\EOF &&\n+\tURL: ../.git/\n+\tPull: refs/heads/main:remotes/rem/main\n+\tPull: refs/heads/one:remotes/rem/one\n+\tPull: two:remotes/rem/two\n+\tPull: refs/heads/three:remotes/rem/three\n+\tEOF\n \tremotes=\"$remotes remote-explicit\" &&\n \n-\t{\n-\t\techo \"URL: ../.git/\"\n-\t\techo \"Pull: refs/heads/*:refs/remotes/rem/*\"\n-\t} >.git/remotes/remote-glob &&\n+\tcat >.git/remotes/remote-glob <<-\\EOF &&\n+\tURL: ../.git/\n+\tPull: refs/heads/*:refs/remotes/rem/*\n+\tEOF\n \tremotes=\"$remotes remote-glob\" &&\n \n \tmkdir -p .git/branches &&\ndiff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh\nindex 8494645837..57e6af5eaa 100755\n--- a/t/t6406-merge-attr.sh\n+++ b/t/t6406-merge-attr.sh\n@@ -62,10 +62,10 @@ test_expect_success setup '\n \n test_expect_success merge '\n \n-\t{\n-\t\techo \"binary -merge\"\n-\t\techo \"union merge=union\"\n-\t} >.gitattributes &&\n+\tcat >.gitattributes <<-\\EOF &&\n+\tbinary -merge\n+\tunion merge=union\n+\tEOF\n \n \tif git merge main\n \tthen\ndiff --git a/t/t6411-merge-filemode.sh b/t/t6411-merge-filemode.sh\nindex f54c915d6a..6ae2489286 100755\n--- a/t/t6411-merge-filemode.sh\n+++ b/t/t6411-merge-filemode.sh\n@@ -51,10 +51,10 @@ test_expect_success 'set up mode change in both branches' '\n \t: >file2 &&\n \tgit add file2 &&\n \tgit commit -m b2 &&\n-\t{\n-\t\techo \"100755 $H 2\tfile2\"\n-\t\techo \"100644 $H 3\tfile2\"\n-\t} >expect\n+\tcat >expect <<-EOF\n+\t100755 $H 2\tfile2\n+\t100644 $H 3\tfile2\n+\tEOF\n '\n \n do_both_modes () {\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 6b6423a07c..424c31c328 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -31,28 +31,28 @@ int main(int argc, const char **argv)\n EOF\n \n test_expect_success setup '\n-\t{\n-\t\techo foo mmap bar\n-\t\techo foo_mmap bar\n-\t\techo foo_mmap bar mmap\n-\t\techo foo mmap bar_mmap\n-\t\techo foo_mmap bar mmap baz\n-\t} >file &&\n-\t{\n-\t\techo Hello world\n-\t\techo HeLLo world\n-\t\techo Hello_world\n-\t\techo HeLLo_world\n-\t} >hello_world &&\n-\t{\n-\t\techo \"a+b*c\"\n-\t\techo \"a+bc\"\n-\t\techo \"abc\"\n-\t} >ab &&\n-\t{\n-\t\techo d &&\n-\t\techo 0\n-\t} >d0 &&\n+\tcat >file <<-\\EOF &&\n+\tfoo mmap bar\n+\tfoo_mmap bar\n+\tfoo_mmap bar mmap\n+\tfoo mmap bar_mmap\n+\tfoo_mmap bar mmap baz\n+\tEOF\n+\tcat >hello_world <<-\\EOF &&\n+\tHello world\n+\tHeLLo world\n+\tHello_world\n+\tHeLLo_world\n+\tEOF\n+\tcat >ab <<-\\EOF &&\n+\ta+b*c\n+\ta+bc\n+\tabc\n+\tEOF\n+\tcat >d0 <<-\\EOF &&\n+\td\n+\t0\n+\tEOF\n \techo vvv >v &&\n \techo ww w >w &&\n \techo x x xx x >x &&\n@@ -63,13 +63,13 @@ test_expect_success setup '\n \techo vvv >t/v &&\n \tmkdir t/a &&\n \techo vvv >t/a/v &&\n-\t{\n-\t\techo \"line without leading space1\"\n-\t\techo \" line with leading space1\"\n-\t\techo \" line with leading space2\"\n-\t\techo \" line with leading space3\"\n-\t\techo \"line without leading space2\"\n-\t} >space &&\n+\tqz_to_tab_space >space <<-\\EOF &&\n+\tline without leading space1\n+\tZline with leading space1\n+\tZline with leading space2\n+\tZline with leading space3\n+\tline without leading space2\n+\tEOF\n \tcat >hello.ps1 <<-\\EOF &&\n \t# No-op.\n \tfunction dummy() {}\n@@ -106,129 +106,129 @@ do\n \tesac\n \n \ttest_expect_success \"grep -w $L\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:foo mmap bar\n-\t\t\techo ${HC}file:3:foo_mmap bar mmap\n-\t\t\techo ${HC}file:4:foo mmap bar_mmap\n-\t\t\techo ${HC}file:5:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:foo mmap bar\n+\t\t${HC}file:3:foo_mmap bar mmap\n+\t\t${HC}file:4:foo mmap bar_mmap\n+\t\t${HC}file:5:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit -c grep.linenumber=false grep -n -w -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with --column)\" '\n-\t\t{\n-\t\t\techo ${HC}file:5:foo mmap bar\n-\t\t\techo ${HC}file:14:foo_mmap bar mmap\n-\t\t\techo ${HC}file:5:foo mmap bar_mmap\n-\t\t\techo ${HC}file:14:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:5:foo mmap bar\n+\t\t${HC}file:14:foo_mmap bar mmap\n+\t\t${HC}file:5:foo mmap bar_mmap\n+\t\t${HC}file:14:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep --column -w -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with --column, extended OR)\" '\n-\t\t{\n-\t\t\techo ${HC}file:14:foo_mmap bar mmap\n-\t\t\techo ${HC}file:19:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:14:foo_mmap bar mmap\n+\t\t${HC}file:19:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep --column -w -e mmap$ --or -e baz $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with --column, --invert-match)\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:foo mmap bar\n-\t\t\techo ${HC}file:1:foo_mmap bar\n-\t\t\techo ${HC}file:1:foo_mmap bar mmap\n-\t\t\techo ${HC}file:1:foo mmap bar_mmap\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:foo mmap bar\n+\t\t${HC}file:1:foo_mmap bar\n+\t\t${HC}file:1:foo_mmap bar mmap\n+\t\t${HC}file:1:foo mmap bar_mmap\n+\t\tEOF\n \t\tgit grep --column --invert-match -w -e baz $H -- file >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep $L (with --column, --invert-match, extended OR)\" '\n-\t\t{\n-\t\t\techo ${HC}hello_world:6:HeLLo_world\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}hello_world:6:HeLLo_world\n+\t\tEOF\n \t\tgit grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \\\n \t\t\t>actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep $L (with --column, --invert-match, extended AND)\" '\n-\t\t{\n-\t\t\techo ${HC}hello_world:3:Hello world\n-\t\t\techo ${HC}hello_world:3:Hello_world\n-\t\t\techo ${HC}hello_world:6:HeLLo_world\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}hello_world:3:Hello world\n+\t\t${HC}hello_world:3:Hello_world\n+\t\t${HC}hello_world:6:HeLLo_world\n+\t\tEOF\n \t\tgit grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \\\n \t\t\t>actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep $L (with --column, double-negation)\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep --column --not \\( --not -e foo --or --not -e baz \\) $H -- file \\\n \t\t\t>actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with --column, -C)\" '\n-\t\t{\n-\t\t\techo ${HC}file:5:foo mmap bar\n-\t\t\techo ${HC}file-foo_mmap bar\n-\t\t\techo ${HC}file:14:foo_mmap bar mmap\n-\t\t\techo ${HC}file:5:foo mmap bar_mmap\n-\t\t\techo ${HC}file:14:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:5:foo mmap bar\n+\t\t${HC}file-foo_mmap bar\n+\t\t${HC}file:14:foo_mmap bar mmap\n+\t\t${HC}file:5:foo mmap bar_mmap\n+\t\t${HC}file:14:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep --column -w -C1 -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with --line-number, --column)\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:5:foo mmap bar\n-\t\t\techo ${HC}file:3:14:foo_mmap bar mmap\n-\t\t\techo ${HC}file:4:5:foo mmap bar_mmap\n-\t\t\techo ${HC}file:5:14:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:5:foo mmap bar\n+\t\t${HC}file:3:14:foo_mmap bar mmap\n+\t\t${HC}file:4:5:foo mmap bar_mmap\n+\t\t${HC}file:5:14:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep -n --column -w -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (with non-extended patterns, --column)\" '\n-\t\t{\n-\t\t\techo ${HC}file:5:foo mmap bar\n-\t\t\techo ${HC}file:10:foo_mmap bar\n-\t\t\techo ${HC}file:10:foo_mmap bar mmap\n-\t\t\techo ${HC}file:5:foo mmap bar_mmap\n-\t\t\techo ${HC}file:10:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:5:foo mmap bar\n+\t\t${HC}file:10:foo_mmap bar\n+\t\t${HC}file:10:foo_mmap bar mmap\n+\t\t${HC}file:5:foo mmap bar_mmap\n+\t\t${HC}file:10:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit grep --column -w -e bar -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:foo mmap bar\n-\t\t\techo ${HC}file:3:foo_mmap bar mmap\n-\t\t\techo ${HC}file:4:foo mmap bar_mmap\n-\t\t\techo ${HC}file:5:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:foo mmap bar\n+\t\t${HC}file:3:foo_mmap bar mmap\n+\t\t${HC}file:4:foo mmap bar_mmap\n+\t\t${HC}file:5:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit -c grep.linenumber=true grep -w -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L\" '\n-\t\t{\n-\t\t\techo ${HC}file:foo mmap bar\n-\t\t\techo ${HC}file:foo_mmap bar mmap\n-\t\t\techo ${HC}file:foo mmap bar_mmap\n-\t\t\techo ${HC}file:foo_mmap bar mmap baz\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:foo mmap bar\n+\t\t${HC}file:foo_mmap bar mmap\n+\t\t${HC}file:foo mmap bar_mmap\n+\t\t${HC}file:foo_mmap bar mmap baz\n+\t\tEOF\n \t\tgit -c grep.linenumber=true grep --no-line-number -w -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n@@ -239,17 +239,17 @@ do\n \t'\n \n \ttest_expect_success \"grep -w $L (x)\" '\n-\t\t{\n-\t\t\techo ${HC}x:1:x x xx x\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}x:1:x x xx x\n+\t\tEOF\n \t\tgit grep -n -w -e \"x xx* x\" $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep -w $L (y-1)\" '\n-\t\t{\n-\t\t\techo ${HC}y:1:y yy\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}y:1:y yy\n+\t\tEOF\n \t\tgit grep -n -w -e \"^y\" $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n@@ -277,16 +277,16 @@ do\n \t'\n \n \ttest_expect_success \"grep $L (with --column, --only-matching)\" '\n-\t\t{\n-\t\t\techo ${HC}file:1:5:mmap\n-\t\t\techo ${HC}file:2:5:mmap\n-\t\t\techo ${HC}file:3:5:mmap\n-\t\t\techo ${HC}file:3:13:mmap\n-\t\t\techo ${HC}file:4:5:mmap\n-\t\t\techo ${HC}file:4:13:mmap\n-\t\t\techo ${HC}file:5:5:mmap\n-\t\t\techo ${HC}file:5:13:mmap\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}file:1:5:mmap\n+\t\t${HC}file:2:5:mmap\n+\t\t${HC}file:3:5:mmap\n+\t\t${HC}file:3:13:mmap\n+\t\t${HC}file:4:5:mmap\n+\t\t${HC}file:4:13:mmap\n+\t\t${HC}file:5:5:mmap\n+\t\t${HC}file:5:13:mmap\n+\t\tEOF\n \t\tgit grep --column -n -o -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n@@ -320,11 +320,11 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth -1 $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/a/v:1:vvv\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/a/v:1:vvv\n+\t\t${HC}t/v:1:vvv\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth -1 -n -e vvv $H >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --recursive -n -e vvv $H >actual &&\n@@ -332,9 +332,9 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth 0 $L\" '\n-\t\t{\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 0 -n -e vvv $H >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --no-recursive -n -e vvv $H >actual &&\n@@ -342,11 +342,11 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth 0 -- '*' $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/a/v:1:vvv\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/a/v:1:vvv\n+\t\t${HC}t/v:1:vvv\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 0 -n -e vvv $H -- \"*\" >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --no-recursive -n -e vvv $H -- \"*\" >actual &&\n@@ -354,18 +354,18 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth 1 $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/v:1:vvv\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 1 -n -e vvv $H >actual &&\n \t\ttest_cmp expected actual\n \t'\n \n \ttest_expect_success \"grep --max-depth 0 -- t $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 0 -n -e vvv $H -- t >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --no-recursive -n -e vvv $H -- t >actual &&\n@@ -373,10 +373,10 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth 0 -- . t $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/v:1:vvv\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 0 -n -e vvv $H -- . t >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --no-recursive -n -e vvv $H -- . t >actual &&\n@@ -384,10 +384,10 @@ do\n \t'\n \n \ttest_expect_success \"grep --max-depth 0 -- t . $L\" '\n-\t\t{\n-\t\t\techo ${HC}t/v:1:vvv\n-\t\t\techo ${HC}v:1:vvv\n-\t\t} >expected &&\n+\t\tcat >expected <<-EOF &&\n+\t\t${HC}t/v:1:vvv\n+\t\t${HC}v:1:vvv\n+\t\tEOF\n \t\tgit grep --max-depth 0 -n -e vvv $H -- t . >actual &&\n \t\ttest_cmp expected actual &&\n \t\tgit grep --no-recursive -n -e vvv $H -- t . >actual &&\n@@ -1314,10 +1314,10 @@ test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '\n '\n \n test_expect_success PCRE 'grep -P -v pattern' '\n-\t{\n-\t\techo \"ab:a+b*c\"\n-\t\techo \"ab:a+bc\"\n-\t} >expected &&\n+\tcat >expected <<-\\EOF &&\n+\tab:a+b*c\n+\tab:a+bc\n+\tEOF\n \tgit grep -P -v \"abc\" ab >actual &&\n \ttest_cmp expected actual\n '\n@@ -1331,10 +1331,10 @@ test_expect_success PCRE 'grep -P -i pattern' '\n '\n \n test_expect_success PCRE 'grep -P -w pattern' '\n-\t{\n-\t\techo \"hello_world:Hello world\"\n-\t\techo \"hello_world:HeLLo world\"\n-\t} >expected &&\n+\tcat >expected <<-\\EOF &&\n+\thello_world:Hello world\n+\thello_world:HeLLo world\n+\tEOF\n \tgit grep -P -w \"He((?i)ll)o\" hello_world >actual &&\n \ttest_cmp expected actual\n '\n@@ -1469,10 +1469,10 @@ test_expect_success 'grep -F pattern with grep.patternType=basic' '\n '\n \n test_expect_success 'grep -G pattern with grep.patternType=fixed' '\n-\t{\n-\t\techo \"ab:a+b*c\"\n-\t\techo \"ab:a+bc\"\n-\t} >expected &&\n+\tcat >expected <<-\\EOF &&\n+\tab:a+b*c\n+\tab:a+bc\n+\tEOF\n \tgit \\\n \t\t-c grep.patterntype=fixed \\\n \t\tgrep -G \"a+b\" ab >actual &&\n@@ -1480,11 +1480,11 @@ test_expect_success 'grep -G pattern with grep.patternType=fixed' '\n '\n \n test_expect_success 'grep -E pattern with grep.patternType=fixed' '\n-\t{\n-\t\techo \"ab:a+b*c\"\n-\t\techo \"ab:a+bc\"\n-\t\techo \"ab:abc\"\n-\t} >expected &&\n+\tcat >expected <<-\\EOF &&\n+\tab:a+b*c\n+\tab:a+bc\n+\tab:abc\n+\tEOF\n \tgit \\\n \t\t-c grep.patterntype=fixed \\\n \t\tgrep -E \"a+\" ab >actual &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443591","messageId":"20211209051115.52629-12-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 11/19] tests: fix broken &&-chains in `$(...)` command substitutions","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:07Z","receivedAt":"2021-12-09T05:12:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"The top-level &&-chain checker built into t/test-lib.sh causes tests to\nmagically exit with code 117 if the &&-chain is broken. However, it has\nthe shortcoming that the magic does not work within `{...}` groups,\n`(...)` subshells, `$(...)` substitutions, or within bodies of compound\nstatements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\npartly fills in the gap by catching broken &&-chains in `(...)`\nsubshells, but bugs can still lurk behind broken &&-chains in the other\ncases.\n\nFix broken &&-chains in `$(...)` command substitutions in order to\nreduce the number of possible lurking bugs.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n contrib/subtree/t/t7900-subtree.sh |  2 +-\n t/t0005-signals.sh                 |  2 +-\n t/t0060-path-utils.sh              |  4 ++--\n t/t1006-cat-file.sh                | 10 +++++-----\n t/t3600-rm.sh                      |  2 +-\n t/t7010-setup.sh                   |  2 +-\n 6 files changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 4153b65321..1c1f76f04a 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -1445,7 +1445,7 @@ test_expect_success 'subtree descendant check' '\n \t) &&\n \ttest_create_commit \"$test_count\" folder_subtree/0 &&\n \ttest_create_commit \"$test_count\" folder_subtree/b &&\n-\tcherry=$(cd \"$test_count\"; git rev-parse HEAD) &&\n+\tcherry=$(cd \"$test_count\" && git rev-parse HEAD) &&\n \t(\n \t\tcd \"$test_count\" &&\n \t\tgit checkout branch\ndiff --git a/t/t0005-signals.sh b/t/t0005-signals.sh\nindex a5ec6a0315..eba75a2490 100755\n--- a/t/t0005-signals.sh\n+++ b/t/t0005-signals.sh\n@@ -48,7 +48,7 @@ test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '\n '\n \n test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '\n-\tOUT=$( ((trap \"\" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&\n+\tOUT=$( ((trap \"\" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&\n \ttest_match_signal 13 \"$OUT\"\n '\n \ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex 34d1061f32..71a5d370cc 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -216,7 +216,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '\n \tmkdir second &&\n \tln -s ../first second/other &&\n \tmkdir third &&\n-\tdir=\"$(cd .git; pwd -P)\" &&\n+\tdir=\"$(cd .git && pwd -P)\" &&\n \tdir2=third/../second/other/.git &&\n \ttest \"$dir\" = \"$(test-tool path-utils real_path $dir2)\" &&\n \tfile=\"$dir\"/index &&\n@@ -224,7 +224,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '\n \tbasename=blub &&\n \ttest \"$dir/$basename\" = \"$(cd .git && test-tool path-utils real_path \"$basename\")\" &&\n \tln -s ../first/file .git/syml &&\n-\tsym=\"$(cd first; pwd -P)\"/file &&\n+\tsym=\"$(cd first && pwd -P)\"/file &&\n \ttest \"$sym\" = \"$(test-tool path-utils real_path \"$dir2/syml\")\"\n '\n \ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 658628375c..67a3f64c2d 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -211,14 +211,14 @@ done\n test_expect_success \"--batch-check for a non-existent named object\" '\n     test \"foobar42 missing\n foobar84 missing\" = \\\n-    \"$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)\"\n+    \"$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)\"\n '\n \n test_expect_success \"--batch-check for a non-existent hash\" '\n     test \"0000000000000000000000000000000000000042 missing\n 0000000000000000000000000000000000000084 missing\" = \\\n-    \"$( ( echo 0000000000000000000000000000000000000042;\n-\t echo_without_newline 0000000000000000000000000000000000000084; ) |\n+    \"$( ( echo 0000000000000000000000000000000000000042 &&\n+\t echo_without_newline 0000000000000000000000000000000000000084 ) |\n        git cat-file --batch-check)\"\n '\n \n@@ -226,8 +226,8 @@ test_expect_success \"--batch for an existent and a non-existent hash\" '\n     test \"$tag_sha1 tag $tag_size\n $tag_content\n 0000000000000000000000000000000000000000 missing\" = \\\n-    \"$( ( echo $tag_sha1;\n-\t echo_without_newline 0000000000000000000000000000000000000000; ) |\n+    \"$( ( echo $tag_sha1 &&\n+\t echo_without_newline 0000000000000000000000000000000000000000 ) |\n        git cat-file --batch)\"\n '\n \ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex bb9ef35dac..ed3952eb98 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -265,7 +265,7 @@ test_expect_success 'choking \"git rm\" should not let it die with cruft (induce S\n \n test_expect_success !MINGW 'choking \"git rm\" should not let it die with cruft (induce and check SIGPIPE)' '\n \tchoke_git_rm_setup &&\n-\tOUT=$( ((trap \"\" PIPE; git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n+\tOUT=$( ((trap \"\" PIPE && git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n \ttest_match_signal 13 \"$OUT\" &&\n \ttest_path_is_missing .git/index.lock\n '\ndiff --git a/t/t7010-setup.sh b/t/t7010-setup.sh\nindex 0335a9a158..520f96d09f 100755\n--- a/t/t7010-setup.sh\n+++ b/t/t7010-setup.sh\n@@ -137,7 +137,7 @@ test_expect_success 'setup deeper work tree' '\n \n test_expect_success 'add a directory outside the work tree' '(\n \tcd tester &&\n-\td1=\"$(cd .. ; pwd)\" &&\n+\td1=\"$(cd .. && pwd)\" &&\n \ttest_must_fail git add \"$d1\"\n )'\n \n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443592","messageId":"20211209051115.52629-11-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 10/19] tests: fix broken &&-chains in compound statements","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:06Z","receivedAt":"2021-12-09T05:12:25Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"The top-level &&-chain checker built into t/test-lib.sh causes tests to\nmagically exit with code 117 if the &&-chain is broken. However, it has\nthe shortcoming that the magic does not work within `{...}` groups,\n`(...)` subshells, `$(...)` substitutions, or within bodies of compound\nstatements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\npartly fills in the gap by catching broken &&-chains in `(...)`\nsubshells, but bugs can still lurk behind broken &&-chains in the other\ncases.\n\nFix broken &&-chains in compound statements in order to reduce the\nnumber of possible lurking bugs.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/perf/p0005-status.sh                 | 12 ++++++------\n t/perf/p0006-read-tree-checkout.sh     | 20 ++++++++++----------\n t/perf/p0007-write-cache.sh            |  4 ++--\n t/perf/p5302-pack-index.sh             |  2 +-\n t/perf/p5303-many-packs.sh             |  8 ++++----\n t/t1050-large.sh                       |  4 ++--\n t/t1300-config.sh                      |  2 +-\n t/t1700-split-index.sh                 |  4 ++--\n t/t2200-add-update.sh                  |  4 ++--\n t/t3070-wildmatch.sh                   |  2 +-\n t/t3700-add.sh                         |  4 ++--\n t/t4046-diff-unmerged.sh               |  2 +-\n t/t4123-apply-shrink.sh                |  4 ++--\n t/t4138-apply-ws-expansion.sh          | 20 ++++++++++----------\n t/t5302-pack-index.sh                  |  2 +-\n t/t5317-pack-objects-filter-objects.sh | 20 ++++++++++----------\n t/t5571-pre-push-hook.sh               |  2 +-\n t/t5616-partial-clone.sh               | 18 +++++++++---------\n t/t6005-rev-list-count.sh              |  8 ++++----\n t/t6009-rev-list-parent.sh             |  2 +-\n t/t6112-rev-list-filters-objects.sh    | 14 +++++++-------\n t/t6120-describe.sh                    |  7 +++++--\n t/t6407-merge-binary.sh                |  4 ++--\n t/t6412-merge-large-rename.sh          |  2 +-\n t/t7004-tag.sh                         |  7 +++++--\n t/t7519-status-fsmonitor.sh            |  2 +-\n t/t7602-merge-octopus-many.sh          |  2 +-\n t/t9130-git-svn-authors-file.sh        |  2 +-\n 28 files changed, 95 insertions(+), 89 deletions(-)\n\ndiff --git a/t/perf/p0005-status.sh b/t/perf/p0005-status.sh\nindex 0b0aa9858f..ca58d6c9b5 100755\n--- a/t/perf/p0005-status.sh\n+++ b/t/perf/p0005-status.sh\n@@ -24,17 +24,17 @@ test_perf_default_repo\n test_expect_success \"setup repo\" '\n \tif git rev-parse --verify refs/heads/p0006-ballast^{commit}\n \tthen\n-\t\techo Assuming synthetic repo from many-files.sh\n-\t\tgit branch br_base            master\n-\t\tgit branch br_ballast         p0006-ballast\n-\t\tgit config --local core.sparsecheckout 1\n+\t\techo Assuming synthetic repo from many-files.sh &&\n+\t\tgit branch br_base            master &&\n+\t\tgit branch br_ballast         p0006-ballast &&\n+\t\tgit config --local core.sparsecheckout 1 &&\n \t\tcat >.git/info/sparse-checkout <<-EOF\n \t\t/*\n \t\t!ballast/*\n \t\tEOF\n \telse\n-\t\techo Assuming non-synthetic repo...\n-\t\tgit branch br_base            $(git rev-list HEAD | tail -n 1)\n+\t\techo Assuming non-synthetic repo... &&\n+\t\tgit branch br_base            $(git rev-list HEAD | tail -n 1) &&\n \t\tgit branch br_ballast         HEAD\n \tfi &&\n \tgit checkout -q br_ballast &&\ndiff --git a/t/perf/p0006-read-tree-checkout.sh b/t/perf/p0006-read-tree-checkout.sh\nindex 78cc23fe2f..900b385c4b 100755\n--- a/t/perf/p0006-read-tree-checkout.sh\n+++ b/t/perf/p0006-read-tree-checkout.sh\n@@ -24,21 +24,21 @@ test_perf_default_repo\n test_expect_success \"setup repo\" '\n \tif git rev-parse --verify refs/heads/p0006-ballast^{commit}\n \tthen\n-\t\techo Assuming synthetic repo from many-files.sh\n-\t\tgit branch br_base            master\n-\t\tgit branch br_ballast         p0006-ballast^\n-\t\tgit branch br_ballast_alias   p0006-ballast^\n-\t\tgit branch br_ballast_plus_1  p0006-ballast\n-\t\tgit config --local core.sparsecheckout 1\n+\t\techo Assuming synthetic repo from many-files.sh &&\n+\t\tgit branch br_base            master &&\n+\t\tgit branch br_ballast         p0006-ballast^ &&\n+\t\tgit branch br_ballast_alias   p0006-ballast^ &&\n+\t\tgit branch br_ballast_plus_1  p0006-ballast &&\n+\t\tgit config --local core.sparsecheckout 1 &&\n \t\tcat >.git/info/sparse-checkout <<-EOF\n \t\t/*\n \t\t!ballast/*\n \t\tEOF\n \telse\n-\t\techo Assuming non-synthetic repo...\n-\t\tgit branch br_base            $(git rev-list HEAD | tail -n 1)\n-\t\tgit branch br_ballast         HEAD^ || error \"no ancestor commit from current head\"\n-\t\tgit branch br_ballast_alias   HEAD^\n+\t\techo Assuming non-synthetic repo... &&\n+\t\tgit branch br_base            $(git rev-list HEAD | tail -n 1) &&\n+\t\tgit branch br_ballast         HEAD^ || error \"no ancestor commit from current head\" &&\n+\t\tgit branch br_ballast_alias   HEAD^ &&\n \t\tgit branch br_ballast_plus_1  HEAD\n \tfi &&\n \tgit checkout -q br_ballast &&\ndiff --git a/t/perf/p0007-write-cache.sh b/t/perf/p0007-write-cache.sh\nindex 09595264f0..25d8ff7443 100755\n--- a/t/perf/p0007-write-cache.sh\n+++ b/t/perf/p0007-write-cache.sh\n@@ -9,8 +9,8 @@ test_perf_default_repo\n test_expect_success \"setup repo\" '\n \tif git rev-parse --verify refs/heads/p0006-ballast^{commit}\n \tthen\n-\t\techo Assuming synthetic repo from many-files.sh\n-\t\tgit config --local core.sparsecheckout 1\n+\t\techo Assuming synthetic repo from many-files.sh &&\n+\t\tgit config --local core.sparsecheckout 1 &&\n \t\tcat >.git/info/sparse-checkout <<-EOF\n \t\t/*\n \t\t!ballast/*\ndiff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh\nindex 228593d9ad..654cd9c86e 100755\n--- a/t/perf/p5302-pack-index.sh\n+++ b/t/perf/p5302-pack-index.sh\n@@ -21,7 +21,7 @@ test_expect_success 'set up thread-counting tests' '\n \tthreads= &&\n \twhile test $t -gt 0\n \tdo\n-\t\tthreads=\"$t $threads\"\n+\t\tthreads=\"$t $threads\" &&\n \t\tt=$((t / 2))\n \tdone\n '\ndiff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh\nindex 35c0cbdf49..58213fe171 100755\n--- a/t/perf/p5303-many-packs.sh\n+++ b/t/perf/p5303-many-packs.sh\n@@ -126,10 +126,10 @@ done\n # Measure pack loading with 10,000 packs.\n test_expect_success 'generate lots of packs' '\n \tfor i in $(test_seq 10000); do\n-\t\techo \"blob\"\n-\t\techo \"data <<EOF\"\n-\t\techo \"blob $i\"\n-\t\techo \"EOF\"\n+\t\techo \"blob\" &&\n+\t\techo \"data <<EOF\" &&\n+\t\techo \"blob $i\" &&\n+\t\techo \"EOF\" &&\n \t\techo \"checkpoint\"\n \tdone |\n \tgit -c fastimport.unpackLimit=0 fast-import\ndiff --git a/t/t1050-large.sh b/t/t1050-large.sh\nindex 6bc1d76fb1..99ff2866b7 100755\n--- a/t/t1050-large.sh\n+++ b/t/t1050-large.sh\n@@ -54,7 +54,7 @@ test_expect_success 'add a large file or two' '\n \tbad= count=0 idx= &&\n \tfor p in .git/objects/pack/pack-*.pack\n \tdo\n-\t\tcount=$(( $count + 1 ))\n+\t\tcount=$(( $count + 1 )) &&\n \t\tif test_path_is_file \"$p\" &&\n \t\t   idx=${p%.pack}.idx && test_path_is_file \"$idx\"\n \t\tthen\n@@ -78,7 +78,7 @@ test_expect_success 'add a large file or two' '\n \tbad= count=0 &&\n \tfor p in .git/objects/pack/pack-*.pack\n \tdo\n-\t\tcount=$(( $count + 1 ))\n+\t\tcount=$(( $count + 1 )) &&\n \t\tif test_path_is_file \"$p\" &&\n \t\t   idx=${p%.pack}.idx && test_path_is_file \"$idx\"\n \t\tthen\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 9ff46f3b04..9571649c42 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -717,7 +717,7 @@ test_expect_success bool '\n \trm -f result &&\n \tfor i in 1 2 3 4\n \tdo\n-\t    git config --bool --get bool.true$i >>result\n+\t    git config --bool --get bool.true$i >>result &&\n \t    git config --bool --get bool.false$i >>result\n \tdone &&\n \ttest_cmp expect result'\ndiff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh\nindex decd2527ed..b4ab166369 100755\n--- a/t/t1700-split-index.sh\n+++ b/t/t1700-split-index.sh\n@@ -48,10 +48,10 @@ test_expect_success 'enable split index' '\n \t# NEEDSWORK: Stop hard-coding checksums.\n \tif test \"$indexversion\" = \"4\"\n \tthen\n-\t\town=$(test_oid own_v4)\n+\t\town=$(test_oid own_v4) &&\n \t\tbase=$(test_oid base_v4)\n \telse\n-\t\town=$(test_oid own_v3)\n+\t\town=$(test_oid own_v3) &&\n \t\tbase=$(test_oid base_v3)\n \tfi &&\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 94c4cb0672..67b9cc752f 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -150,8 +150,8 @@ test_expect_success 'add -u resolves unmerged paths' '\n \t{\n \t\tfor path in path1 path2\n \t\tdo\n-\t\t\techo \"100644 $one 1\t$path\"\n-\t\t\techo \"100644 $two 2\t$path\"\n+\t\t\techo \"100644 $one 1\t$path\" &&\n+\t\t\techo \"100644 $two 2\t$path\" &&\n \t\t\techo \"100644 $three 3\t$path\"\n \t\tdone\n \t\techo \"100644 $one 1\tpath3\"\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex 72d5b014d8..f9539968e4 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -193,7 +193,7 @@ match() {\n \t\tfile=$(cat .git/expected_test_file) &&\n \t\tif should_create_test_file \"$file\"\n \t\tthen\n-\t\t\tdirs=${file%/*}\n+\t\t\tdirs=${file%/*} &&\n \t\t\tif test \"$file\" != \"$dirs\"\n \t\t\tthen\n \t\t\t\tmkdir -p -- \"$dirs\" &&\ndiff --git a/t/t3700-add.sh b/t/t3700-add.sh\nindex 283a66955d..23c3c214c5 100755\n--- a/t/t3700-add.sh\n+++ b/t/t3700-add.sh\n@@ -141,8 +141,8 @@ test_expect_success 'check correct prefix detection' '\n test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '\n \tfor s in 1 2 3\n \tdo\n-\t\techo $s > stage$s\n-\t\techo \"100755 $(git hash-object -w stage$s) $s\tfile\"\n+\t\techo $s > stage$s &&\n+\t\techo \"100755 $(git hash-object -w stage$s) $s\tfile\" &&\n \t\techo \"120000 $(printf $s | git hash-object -w -t blob --stdin) $s\tsymlink\"\n \tdone | git update-index --index-info &&\n \tgit config core.filemode 0 &&\ndiff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh\nindex ff7cfd884a..8c3d48e257 100755\n--- a/t/t4046-diff-unmerged.sh\n+++ b/t/t4046-diff-unmerged.sh\n@@ -18,7 +18,7 @@ test_expect_success setup '\n \t\t\tfor t in o x\n \t\t\tdo\n \t\t\t\tpath=\"$b$o$t\" &&\n-\t\t\t\tcase \"$path\" in ooo) continue ;; esac\n+\t\t\t\tcase \"$path\" in ooo) continue ;; esac &&\n \t\t\t\tpaths=\"$paths$path \" &&\n \t\t\t\tp=\"\t$path\" &&\n \t\t\t\tcase \"$b\" in x) echo \"$m1$p\" ;; esac &&\ndiff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh\nindex 984157f03b..51e69cf79e 100755\n--- a/t/t4123-apply-shrink.sh\n+++ b/t/t4123-apply-shrink.sh\n@@ -45,8 +45,8 @@ test_expect_success 'apply should fail gracefully' '\n \t\techo Oops, should not have succeeded\n \t\tfalse\n \telse\n-\t\tstatus=$?\n-\t\techo \"Status was $status\"\n+\t\tstatus=$? &&\n+\t\techo \"Status was $status\" &&\n \t\tif test -f .git/index.lock\n \t\tthen\n \t\t\techo Oops, should not have crashed\ndiff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh\nindex b19faeb67a..4ba52bbb61 100755\n--- a/t/t4138-apply-ws-expansion.sh\n+++ b/t/t4138-apply-ws-expansion.sh\n@@ -29,7 +29,7 @@ test_expect_success setup '\n \tx=1 &&\n \twhile test $x -lt $n\n \tdo\n-\t\tprintf \"%63s%d\\n\" \"\" $x >>after\n+\t\tprintf \"%63s%d\\n\" \"\" $x >>after &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tprintf \"\\t%s\\n\" d e f >>after &&\n@@ -40,7 +40,7 @@ test_expect_success setup '\n \tx=1 &&\n \twhile test $x -lt $n\n \tdo\n-\t\tprintf \"%63s%d\\n\" \"\" $x >>expect-2\n+\t\tprintf \"%63s%d\\n\" \"\" $x >>expect-2 &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tprintf \"%64s\\n\" d e f >>expect-2 &&\n@@ -52,7 +52,7 @@ test_expect_success setup '\n \tx=0 &&\n \twhile test $x -lt $n\n \tdo\n-\t\tprintf \"%63s%02d\\n\" \"\" $x >>after\n+\t\tprintf \"%63s%02d\\n\" \"\" $x >>after &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tprintf \"\\t%s\\n\" d e f >>after &&\n@@ -63,7 +63,7 @@ test_expect_success setup '\n \tx=0 &&\n \twhile test $x -lt $n\n \tdo\n-\t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-3\n+\t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-3 &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tprintf \"%64s\\n\" d e f >>expect-3 &&\n@@ -73,15 +73,15 @@ test_expect_success setup '\n \tx=0 &&\n \twhile test $x -lt 50\n \tdo\n-\t\tprintf \"\\t%02d\\n\" $x >>before\n+\t\tprintf \"\\t%02d\\n\" $x >>before &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tcat before >after &&\n \tprintf \"%64s\\n\" a b c >>after &&\n \twhile test $x -lt 100\n \tdo\n-\t\tprintf \"\\t%02d\\n\" $x >>before\n-\t\tprintf \"\\t%02d\\n\" $x >>after\n+\t\tprintf \"\\t%02d\\n\" $x >>before &&\n+\t\tprintf \"\\t%02d\\n\" $x >>after &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \ttest_expect_code 1 git diff --no-index before after >patch4.patch.raw &&\n@@ -90,15 +90,15 @@ test_expect_success setup '\n \tx=0 &&\n \twhile test $x -lt 50\n \tdo\n-\t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4\n+\t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4 &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \tcat test-4 >expect-4 &&\n \tprintf \"%64s\\n\" a b c >>expect-4 &&\n \twhile test $x -lt 100\n \tdo\n-\t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4\n-\t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-4\n+\t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4 &&\n+\t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-4 &&\n \t\tx=$(( $x + 1 ))\n \tdone &&\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 7c9d687367..8ee67df38f 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -14,7 +14,7 @@ test_expect_success 'setup' '\n \ti=1 &&\n \twhile test $i -le 100\n \tdo\n-\t\tiii=$(printf \"%03i\" $i)\n+\t\tiii=$(printf \"%03i\" $i) &&\n \t\ttest-tool genrandom \"bar\" 200 > wide_delta_$iii &&\n \t\ttest-tool genrandom \"baz $iii\" 50 >> wide_delta_$iii &&\n \t\ttest-tool genrandom \"foo\"$i 100 > deep_delta_$iii &&\ndiff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh\nindex 13ed3eb136..8fb6acae47 100755\n--- a/t/t5317-pack-objects-filter-objects.sh\n+++ b/t/t5317-pack-objects-filter-objects.sh\n@@ -16,8 +16,8 @@ test_expect_success 'setup r1' '\n \tgit init r1 &&\n \tfor n in 1 2 3 4 5\n \tdo\n-\t\techo \"This is file: $n\" > r1/file.$n\n-\t\tgit -C r1 add file.$n\n+\t\techo \"This is file: $n\" > r1/file.$n &&\n+\t\tgit -C r1 add file.$n &&\n \t\tgit -C r1 commit -m \"$n\"\n \tdone\n '\n@@ -116,8 +116,8 @@ test_expect_success 'setup r2' '\n \tgit init r2 &&\n \tfor n in 1000 10000\n \tdo\n-\t\tprintf \"%\"$n\"s\" X > r2/large.$n\n-\t\tgit -C r2 add large.$n\n+\t\tprintf \"%\"$n\"s\" X > r2/large.$n &&\n+\t\tgit -C r2 add large.$n &&\n \t\tgit -C r2 commit -m \"$n\"\n \tdone\n '\n@@ -278,9 +278,9 @@ test_expect_success 'setup r3' '\n \tmkdir r3/dir1 &&\n \tfor n in sparse1 sparse2\n \tdo\n-\t\techo \"This is file: $n\" > r3/$n\n-\t\tgit -C r3 add $n\n-\t\techo \"This is file: dir1/$n\" > r3/dir1/$n\n+\t\techo \"This is file: $n\" > r3/$n &&\n+\t\tgit -C r3 add $n &&\n+\t\techo \"This is file: dir1/$n\" > r3/dir1/$n &&\n \t\tgit -C r3 add dir1/$n\n \tdone &&\n \tgit -C r3 commit -m \"sparse\" &&\n@@ -331,9 +331,9 @@ test_expect_success 'setup r4' '\n \tmkdir r4/dir1 &&\n \tfor n in sparse1 sparse2\n \tdo\n-\t\techo \"This is file: $n\" > r4/$n\n-\t\tgit -C r4 add $n\n-\t\techo \"This is file: dir1/$n\" > r4/dir1/$n\n+\t\techo \"This is file: $n\" > r4/$n &&\n+\t\tgit -C r4 add $n &&\n+\t\techo \"This is file: dir1/$n\" > r4/dir1/$n &&\n \t\tgit -C r4 add dir1/$n\n \tdone &&\n \techo dir1/ >r4/pattern &&\ndiff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh\nindex ad8d5804f7..b043a279f1 100755\n--- a/t/t5571-pre-push-hook.sh\n+++ b/t/t5571-pre-push-hook.sh\n@@ -117,7 +117,7 @@ test_expect_success 'set up many-ref tests' '\n \t\tnr=1000\n \t\twhile test $nr -lt 2000\n \t\tdo\n-\t\t\tnr=$(( $nr + 1 ))\n+\t\t\tnr=$(( $nr + 1 )) &&\n \t\t\techo \"create refs/heads/b/$nr $COMMIT3\"\n \t\tdone\n \t} | git update-ref --stdin\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex cf3e82bdf5..23c156e399 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -16,9 +16,9 @@ test_expect_success 'setup normal src repo' '\n \tgit init src &&\n \tfor n in 1 2 3 4\n \tdo\n-\t\techo \"This is file: $n\" > src/file.$n.txt\n-\t\tgit -C src add file.$n.txt\n-\t\tgit -C src commit -m \"file $n\"\n+\t\techo \"This is file: $n\" > src/file.$n.txt &&\n+\t\tgit -C src add file.$n.txt &&\n+\t\tgit -C src commit -m \"file $n\" &&\n \t\tgit -C src ls-files -s file.$n.txt >>temp\n \tdone &&\n \tawk -f print_2.awk <temp | sort >expect_1.oids &&\n@@ -72,8 +72,8 @@ test_expect_success 'push new commits to server' '\n \tgit -C src remote add srv \"file://$(pwd)/srv.bare\" &&\n \tfor x in a b c d e\n \tdo\n-\t\techo \"Mod file.1.txt $x\" >>src/file.1.txt\n-\t\tgit -C src add file.1.txt\n+\t\techo \"Mod file.1.txt $x\" >>src/file.1.txt &&\n+\t\tgit -C src add file.1.txt &&\n \t\tgit -C src commit -m \"mod $x\"\n \tdone &&\n \tgit -C src blame main -- file.1.txt >expect.blame &&\n@@ -114,8 +114,8 @@ test_expect_success 'verify blame causes dynamic object fetch' '\n test_expect_success 'push new commits to server for file.2.txt' '\n \tfor x in a b c d e f\n \tdo\n-\t\techo \"Mod file.2.txt $x\" >>src/file.2.txt\n-\t\tgit -C src add file.2.txt\n+\t\techo \"Mod file.2.txt $x\" >>src/file.2.txt &&\n+\t\tgit -C src add file.2.txt &&\n \t\tgit -C src commit -m \"mod $x\"\n \tdone &&\n \tgit -C src push -u srv main\n@@ -135,8 +135,8 @@ test_expect_success 'override inherited filter-spec using --no-filter' '\n test_expect_success 'push new commits to server for file.3.txt' '\n \tfor x in a b c d e f\n \tdo\n-\t\techo \"Mod file.3.txt $x\" >>src/file.3.txt\n-\t\tgit -C src add file.3.txt\n+\t\techo \"Mod file.3.txt $x\" >>src/file.3.txt &&\n+\t\tgit -C src add file.3.txt &&\n \t\tgit -C src commit -m \"mod $x\"\n \tdone &&\n \tgit -C src push -u srv main\ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex 0b64822bf6..32b152095a 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -5,10 +5,10 @@ test_description='git rev-list --max-count and --skip test'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-    for n in 1 2 3 4 5 ; do \\\n-        echo $n > a ; \\\n-        git add a ; \\\n-        git commit -m \"$n\" ; \\\n+    for n in 1 2 3 4 5 ; do\n+        echo $n > a &&\n+        git add a &&\n+        git commit -m \"$n\"\n     done\n '\n \ndiff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh\nindex 63fa7c8313..dc8160aa45 100755\n--- a/t/t6009-rev-list-parent.sh\n+++ b/t/t6009-rev-list-parent.sh\n@@ -142,7 +142,7 @@ test_expect_success 'ancestors with the same commit time' '\n \n \ttest_tick_keep=$test_tick &&\n \tfor i in 1 2 3 4 5 6 7 8; do\n-\t\ttest_tick=$test_tick_keep\n+\t\ttest_tick=$test_tick_keep &&\n \t\ttest_commit t$i\n \tdone &&\n \tgit rev-list t1^! --not t$i >result &&\ndiff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh\nindex 4ade105db3..9848425192 100755\n--- a/t/t6112-rev-list-filters-objects.sh\n+++ b/t/t6112-rev-list-filters-objects.sh\n@@ -16,8 +16,8 @@ test_expect_success 'setup r1' '\n \tgit init r1 &&\n \tfor n in 1 2 3 4 5\n \tdo\n-\t\techo \"This is file: $n\" > r1/file.$n\n-\t\tgit -C r1 add file.$n\n+\t\techo \"This is file: $n\" > r1/file.$n &&\n+\t\tgit -C r1 add file.$n &&\n \t\tgit -C r1 commit -m \"$n\"\n \tdone\n '\n@@ -73,8 +73,8 @@ test_expect_success 'setup r2' '\n \tgit init r2 &&\n \tfor n in 1000 10000\n \tdo\n-\t\tprintf \"%\"$n\"s\" X > r2/large.$n\n-\t\tgit -C r2 add large.$n\n+\t\tprintf \"%\"$n\"s\" X > r2/large.$n &&\n+\t\tgit -C r2 add large.$n &&\n \t\tgit -C r2 commit -m \"$n\"\n \tdone\n '\n@@ -245,9 +245,9 @@ test_expect_success 'setup r3' '\n \tmkdir r3/dir1 &&\n \tfor n in sparse1 sparse2\n \tdo\n-\t\techo \"This is file: $n\" > r3/$n\n-\t\tgit -C r3 add $n\n-\t\techo \"This is file: dir1/$n\" > r3/dir1/$n\n+\t\techo \"This is file: $n\" > r3/$n &&\n+\t\tgit -C r3 add $n &&\n+\t\techo \"This is file: dir1/$n\" > r3/dir1/$n &&\n \t\tgit -C r3 add dir1/$n\n \tdone &&\n \tgit -C r3 commit -m \"sparse\" &&\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex bae2419150..9b2cc066f7 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -390,8 +390,11 @@ test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '\n committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200\n data <<EOF\n commit #$i\n-EOF\"\n-\t\ttest $i = 1 && echo \"from refs/heads/main^0\"\n+EOF\" &&\n+\t\tif test $i = 1\n+\t\tthen\n+\t\t\techo \"from refs/heads/main^0\"\n+\t\tfi &&\n \t\ti=$(($i + 1))\n \tdone | git fast-import &&\n \tgit checkout main &&\ndiff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh\nindex d4273f2575..4c4f690588 100755\n--- a/t/t6407-merge-binary.sh\n+++ b/t/t6407-merge-binary.sh\n@@ -47,7 +47,7 @@ test_expect_success resolve '\n \t\techo Oops, should not have succeeded\n \t\tfalse\n \telse\n-\t\tgit ls-files -s >current\n+\t\tgit ls-files -s >current &&\n \t\ttest_cmp expect current\n \tfi\n '\n@@ -62,7 +62,7 @@ test_expect_success recursive '\n \t\techo Oops, should not have succeeded\n \t\tfalse\n \telse\n-\t\tgit ls-files -s >current\n+\t\tgit ls-files -s >current &&\n \t\ttest_cmp expect current\n \tfi\n '\ndiff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh\nindex c50d315722..ed40801380 100755\n--- a/t/t6412-merge-large-rename.sh\n+++ b/t/t6412-merge-large-rename.sh\n@@ -47,7 +47,7 @@ test_rename() {\n \tgit commit -a -m change=$n &&\n \tgit checkout -b test$n HEAD^ &&\n \tfor i in $(count $n); do\n-\t\tgit rm $i\n+\t\tgit rm $i &&\n \t\tmake_text $i initial changed >$i.moved\n \tdone &&\n \tgit add . &&\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 082be85dff..660cde5a63 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1976,8 +1976,11 @@ test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a de\n committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200\n data <<EOF\n commit #$i\n-EOF\"\n-\t\ttest $i = 1 && echo \"from refs/heads/main^0\"\n+EOF\" &&\n+\t\tif test $i = 1\n+\t\tthen\n+\t\t\techo \"from refs/heads/main^0\"\n+\t\tfi &&\n \t\ti=$(($i + 1))\n \tdone | git fast-import &&\n \tgit checkout main &&\ndiff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh\nindex f488d930df..1b4160dc53 100755\n--- a/t/t7519-status-fsmonitor.sh\n+++ b/t/t7519-status-fsmonitor.sh\n@@ -248,7 +248,7 @@ do\n \t\tgit config core.preloadIndex $preload_val &&\n \t\tif test $preload_val = true\n \t\tthen\n-\t\t\tGIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX\n+\t\t\tGIT_TEST_PRELOAD_INDEX=$preload_val && export GIT_TEST_PRELOAD_INDEX\n \t\telse\n \t\t\tsane_unset GIT_TEST_PRELOAD_INDEX\n \t\tfi\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex a9c816b47f..ddf64dc5f7 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -29,7 +29,7 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n \trefs=\"\" &&\n \twhile test $i -le 30\n \tdo\n-\t\trefs=\"$refs c$i\"\n+\t\trefs=\"$refs c$i\" &&\n \t\ti=$(expr $i + 1)\n \tdone &&\n \tgit merge $refs &&\ndiff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh\nindex cb764bcadc..b4081fefba 100755\n--- a/t/t9130-git-svn-authors-file.sh\n+++ b/t/t9130-git-svn-authors-file.sh\n@@ -59,7 +59,7 @@ test_expect_success 'authors-file against globs' '\n \tgit svn clone --authors-file=svn-authors -s \"$svnrepo\"/aa aa-work &&\n \tfor i in bb ee cc\n \tdo\n-\t\tbranch=\"aa/branches/$i\"\n+\t\tbranch=\"aa/branches/$i\" &&\n \t\tsvn_cmd mkdir -m \"$branch\" --username $i \"$svnrepo/$branch\"\n \tdone\n \t'\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443593","messageId":"20211209051115.52629-10-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 09/19] tests: use test_write_lines() to generate line-oriented output","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:05Z","receivedAt":"2021-12-09T05:12:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Take advantage of test_write_lines() to generate line-oriented output\nrather than using for-loops or a series of `echo` commands. Not only is\ntest_write_lines() a natural fit for such a task, but there is less\nopportunity for a broken &&-chain.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t0020-crlf.sh                        | 28 ++++++-------\n t/t0026-eol-config.sh                  |  4 +-\n t/t1020-subdirectory.sh                |  4 +-\n t/t1512-rev-parse-disambiguation.sh    | 10 +----\n t/t2103-update-index-ignore-missing.sh |  2 +-\n t/t3417-rebase-whitespace-fix.sh       |  4 +-\n t/t4013-diff-various.sh                | 22 +++++-----\n t/t4014-format-patch.sh                | 26 ++++++------\n t/t4019-diff-wserror.sh                |  4 +-\n t/t4105-apply-fuzz.sh                  | 10 +----\n t/t4116-apply-reverse.sh               |  4 +-\n t/t4117-apply-reject.sh                | 20 ++-------\n t/t4118-apply-empty-context.sh         |  6 +--\n t/t4124-apply-ws-rule.sh               | 56 +++++++++++++-------------\n t/t4126-apply-empty.sh                 |  5 +--\n t/t4127-apply-same-fn.sh               |  5 +--\n t/t4151-am-abort.sh                    | 10 +----\n t/t5300-pack-object.sh                 | 12 +-----\n t/t5306-pack-nobase.sh                 |  2 +-\n t/t5611-clone-config.sh                |  2 +-\n t/t6019-rev-list-ancestry-path.sh      | 10 ++---\n t/t6060-merge-index.sh                 |  4 +-\n t/t6409-merge-subtree.sh               |  6 +--\n t/t6417-merge-ours-theirs.sh           |  5 +--\n t/t7501-commit-basic-functionality.sh  |  5 +--\n t/t8003-blame-corner-cases.sh          | 10 +----\n 26 files changed, 106 insertions(+), 170 deletions(-)\n\ndiff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh\nindex f25ae8b5e1..0bd6ff21c8 100755\n--- a/t/t0020-crlf.sh\n+++ b/t/t0020-crlf.sh\n@@ -22,10 +22,10 @@ test_expect_success setup '\n \n \tgit config core.autocrlf false &&\n \n-\tfor w in Hello world how are you; do echo $w; done >one &&\n+\ttest_write_lines Hello world how are you >one &&\n \tmkdir dir &&\n-\tfor w in I am very very fine thank you; do echo $w; done >dir/two &&\n-\tfor w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&\n+\ttest_write_lines I am very very fine thank you >dir/two &&\n+\ttest_write_lines Oh here is NULQin text here | q_to_nul >three &&\n \tgit add . &&\n \n \tgit commit -m initial &&\n@@ -35,7 +35,7 @@ test_expect_success setup '\n \ttwo=$(git rev-parse HEAD:dir/two) &&\n \tthree=$(git rev-parse HEAD:three) &&\n \n-\tfor w in Some extra lines here; do echo $w; done >>one &&\n+\ttest_write_lines Some extra lines here >>one &&\n \tgit diff >patch.file &&\n \tpatched=$(git hash-object --stdin <one) &&\n \tgit read-tree --reset -u HEAD\n@@ -46,7 +46,7 @@ test_expect_success 'safecrlf: autocrlf=input, all CRLF' '\n \tgit config core.autocrlf input &&\n \tgit config core.safecrlf true &&\n \n-\tfor w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&\n+\ttest_write_lines I am all CRLF | append_cr >allcrlf &&\n \ttest_must_fail git add allcrlf\n '\n \n@@ -55,7 +55,7 @@ test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' '\n \tgit config core.autocrlf input &&\n \tgit config core.safecrlf true &&\n \n-\tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&\n+\ttest_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&\n \ttest_must_fail git add mixed\n '\n \n@@ -64,7 +64,7 @@ test_expect_success 'safecrlf: autocrlf=true, all LF' '\n \tgit config core.autocrlf true &&\n \tgit config core.safecrlf true &&\n \n-\tfor w in I am all LF; do echo $w; done >alllf &&\n+\ttest_write_lines I am all LF >alllf &&\n \ttest_must_fail git add alllf\n '\n \n@@ -73,7 +73,7 @@ test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' '\n \tgit config core.autocrlf true &&\n \tgit config core.safecrlf true &&\n \n-\tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&\n+\ttest_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&\n \ttest_must_fail git add mixed\n '\n \n@@ -82,10 +82,10 @@ test_expect_success 'safecrlf: print warning only once' '\n \tgit config core.autocrlf input &&\n \tgit config core.safecrlf warn &&\n \n-\tfor w in I am all LF; do echo $w; done >doublewarn &&\n+\ttest_write_lines I am all LF >doublewarn &&\n \tgit add doublewarn &&\n \tgit commit -m \"nowarn\" &&\n-\tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&\n+\ttest_write_lines Oh here is CRLFQ in text | q_to_cr >doublewarn &&\n \tgit add doublewarn 2>err &&\n \tgrep \"CRLF will be replaced by LF\" err >err.warnings &&\n \ttest_line_count = 1 err.warnings\n@@ -103,7 +103,7 @@ test_expect_success 'safecrlf: no warning with safecrlf=false' '\n \tgit config core.autocrlf input &&\n \tgit config core.safecrlf false &&\n \n-\tfor w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&\n+\ttest_write_lines I am all CRLF | append_cr >allcrlf &&\n \tgit add allcrlf 2>err &&\n \ttest_must_be_empty err\n '\n@@ -351,9 +351,9 @@ test_expect_success 'setting up for new autocrlf tests' '\n \tgit config core.autocrlf false &&\n \tgit config core.safecrlf false &&\n \trm -rf .????* * &&\n-\tfor w in I am all LF; do echo $w; done >alllf &&\n-\tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&\n-\tfor w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&\n+\ttest_write_lines I am all LF >alllf &&\n+\ttest_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&\n+\ttest_write_lines I am all CRLF | append_cr >allcrlf &&\n \tgit add -A . &&\n \tgit commit -m \"alllf, allcrlf and mixed only\" &&\n \tgit tag -a -m \"message\" autocrlf-checkpoint\ndiff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh\nindex cdcafcdff7..f426a185bb 100755\n--- a/t/t0026-eol-config.sh\n+++ b/t/t0026-eol-config.sh\n@@ -15,8 +15,8 @@ test_expect_success setup '\n \n \techo \"one text\" > .gitattributes &&\n \n-\tfor w in Hello world how are you; do echo $w; done >one &&\n-\tfor w in I am very very fine thank you; do echo $w; done >two &&\n+\ttest_write_lines Hello world how are you >one &&\n+\ttest_write_lines I am very very fine thank you >two &&\n \tgit add . &&\n \n \tgit commit -m initial &&\ndiff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh\nindex da19c06fb5..9fdbb2af80 100755\n--- a/t/t1020-subdirectory.sh\n+++ b/t/t1020-subdirectory.sh\n@@ -11,9 +11,9 @@ test_description='Try various core-level commands in subdirectory.\n \n test_expect_success setup '\n \tlong=\"a b c d e f g h i j k l m n o p q r s t u v w x y z\" &&\n-\tfor c in $long; do echo $c; done >one &&\n+\ttest_write_lines $long >one &&\n \tmkdir dir &&\n-\tfor c in x y z $long a b c; do echo $c; done >dir/two &&\n+\ttest_write_lines x y z $long a b c >dir/two &&\n \tcp one original.one &&\n \tcp dir/two original.two\n '\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 7891a6becf..15188a408b 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -34,10 +34,7 @@ fi\n test_expect_success 'blob and tree' '\n \ttest_tick &&\n \t(\n-\t\tfor i in 0 1 2 3 4 5 6 7 8 9\n-\t\tdo\n-\t\t\techo $i\n-\t\tdone &&\n+\t\ttest_write_lines 0 1 2 3 4 5 6 7 8 9 &&\n \t\techo &&\n \t\techo b1rwzyc3\n \t) >a0blgqsjc &&\n@@ -204,10 +201,7 @@ test_expect_success 'more history' '\n \tgit checkout v1.0.0^0 &&\n \tgit mv a0blgqsjc f5518nwu &&\n \n-\tfor i in h62xsjeu j08bekfvt kg7xflhm\n-\tdo\n-\t\techo $i\n-\tdone >>f5518nwu &&\n+\ttest_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu &&\n \tgit add f5518nwu &&\n \n \ttest_tick &&\ndiff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh\nindex 0114f05228..6938ecca86 100755\n--- a/t/t2103-update-index-ignore-missing.sh\n+++ b/t/t2103-update-index-ignore-missing.sh\n@@ -23,7 +23,7 @@ test_expect_success basics '\n \ttest_cmp expect actual &&\n \n \tgit update-index --add one two three &&\n-\tfor i in one three two; do echo $i; done >expect &&\n+\ttest_write_lines one three two >expect &&\n \tgit ls-files >actual &&\n \ttest_cmp expect actual &&\n \ndiff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh\nindex 946e92f8da..96f2cf22fa 100755\n--- a/t/t3417-rebase-whitespace-fix.sh\n+++ b/t/t3417-rebase-whitespace-fix.sh\n@@ -115,9 +115,7 @@ test_expect_success 'at beginning of file' '\n \tgit config core.whitespace \"blank-at-eol\" &&\n \tcp beginning file &&\n \tgit commit -m beginning file &&\n-\tfor i in 1 2 3 4 5; do\n-\t\techo $i\n-\tdone >> file &&\n+\ttest_write_lines 1 2 3 4 5 >>file &&\n \tgit commit -m more file &&\n \tgit rebase --whitespace=fix HEAD^^ &&\n \ttest_cmp expect-beginning file\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 28683d059d..750aee17ea 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -19,8 +19,8 @@ test_expect_success setup '\n \n \tmkdir dir &&\n \tmkdir dir2 &&\n-\tfor i in 1 2 3; do echo $i; done >file0 &&\n-\tfor i in A B; do echo $i; done >dir/sub &&\n+\ttest_write_lines 1 2 3 >file0 &&\n+\ttest_write_lines A B >dir/sub &&\n \tcat file0 >file2 &&\n \tgit add file0 file2 dir/sub &&\n \tgit commit -m Initial &&\n@@ -32,8 +32,8 @@ test_expect_success setup '\n \tGIT_COMMITTER_DATE=\"2006-06-26 00:01:00 +0000\" &&\n \texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n \n-\tfor i in 4 5 6; do echo $i; done >>file0 &&\n-\tfor i in C D; do echo $i; done >>dir/sub &&\n+\ttest_write_lines 4 5 6 >>file0 &&\n+\ttest_write_lines C D >>dir/sub &&\n \trm -f file2 &&\n \tgit update-index --remove file0 file2 dir/sub &&\n \tgit commit -m \"Second${LF}${LF}This is the second commit.\" &&\n@@ -42,9 +42,9 @@ test_expect_success setup '\n \tGIT_COMMITTER_DATE=\"2006-06-26 00:02:00 +0000\" &&\n \texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n \n-\tfor i in A B C; do echo $i; done >file1 &&\n+\ttest_write_lines A B C >file1 &&\n \tgit add file1 &&\n-\tfor i in E F; do echo $i; done >>dir/sub &&\n+\ttest_write_lines E F >>dir/sub &&\n \tgit update-index dir/sub &&\n \tgit commit -m Third &&\n \n@@ -53,8 +53,8 @@ test_expect_success setup '\n \texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n \n \tgit checkout side &&\n-\tfor i in A B C; do echo $i; done >>file0 &&\n-\tfor i in 1 2; do echo $i; done >>dir/sub &&\n+\ttest_write_lines A B C >>file0 &&\n+\ttest_write_lines 1 2 >>dir/sub &&\n \tcat dir/sub >file3 &&\n \tgit add file3 &&\n \tgit update-index file0 dir/sub &&\n@@ -71,8 +71,8 @@ test_expect_success setup '\n \tGIT_COMMITTER_DATE=\"2006-06-26 00:05:00 +0000\" &&\n \texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n \n-\tfor i in A B C; do echo $i; done >>file0 &&\n-\tfor i in 1 2; do echo $i; done >>dir/sub &&\n+\ttest_write_lines A B C >>file0 &&\n+\ttest_write_lines 1 2 >>dir/sub &&\n \tgit update-index file0 dir/sub &&\n \n \tmkdir dir3 &&\n@@ -86,7 +86,7 @@ test_expect_success setup '\n \tGIT_COMMITTER_DATE=\"2006-06-26 00:06:00 +0000\" &&\n \texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n \tgit checkout -b rearrange initial &&\n-\tfor i in B A; do echo $i; done >dir/sub &&\n+\ttest_write_lines B A >dir/sub &&\n \tgit add dir/sub &&\n \tgit commit -m \"Rearranged lines in dir/sub\" &&\n \tgit checkout master &&\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 712d4b5ddf..eefe815fca 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -12,25 +12,25 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n test_expect_success setup '\n-\tfor i in 1 2 3 4 5 6 7 8 9 10; do echo \"$i\"; done >file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&\n \tcat file >elif &&\n \tgit add file elif &&\n \ttest_tick &&\n \tgit commit -m Initial &&\n \tgit checkout -b side &&\n \n-\tfor i in 1 2 5 6 A B C 7 8 9 10; do echo \"$i\"; done >file &&\n+\ttest_write_lines 1 2 5 6 A B C 7 8 9 10 >file &&\n \ttest_chmod +x elif &&\n \ttest_tick &&\n \tgit commit -m \"Side changes #1\" &&\n \n-\tfor i in D E F; do echo \"$i\"; done >>file &&\n+\ttest_write_lines D E F >>file &&\n \tgit update-index file &&\n \ttest_tick &&\n \tgit commit -m \"Side changes #2\" &&\n \tgit tag C2 &&\n \n-\tfor i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo \"$i\"; done >file &&\n+\ttest_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file &&\n \tgit update-index file &&\n \ttest_tick &&\n \tgit commit -m \"Side changes #3 with \\\\n backslash-n in it.\" &&\n@@ -43,18 +43,18 @@ test_expect_success setup '\n \n \tgit checkout side &&\n \tgit checkout -b patchid &&\n-\tfor i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo \"$i\"; done >file2 &&\n-\tfor i in 1 2 3 A 4 B C 7 8 9 10 D E F 5 6; do echo \"$i\"; done >file3 &&\n-\tfor i in 8 9 10; do echo \"$i\"; done >file &&\n+\ttest_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file2 &&\n+\ttest_write_lines 1 2 3 A 4 B C 7 8 9 10 D E F 5 6 >file3 &&\n+\ttest_write_lines 8 9 10 >file &&\n \tgit add file file2 file3 &&\n \ttest_tick &&\n \tgit commit -m \"patchid 1\" &&\n-\tfor i in 4 A B 7 8 9 10; do echo \"$i\"; done >file2 &&\n-\tfor i in 8 9 10 5 6; do echo \"$i\"; done >file3 &&\n+\ttest_write_lines 4 A B 7 8 9 10 >file2 &&\n+\ttest_write_lines 8 9 10 5 6 >file3 &&\n \tgit add file2 file3 &&\n \ttest_tick &&\n \tgit commit -m \"patchid 2\" &&\n-\tfor i in 10 5 6; do echo \"$i\"; done >file &&\n+\ttest_write_lines 10 5 6 >file &&\n \tgit add file &&\n \ttest_tick &&\n \tgit commit -m \"patchid 3\" &&\n@@ -653,7 +653,7 @@ test_expect_success 'excessive subject' '\n \tgit checkout side &&\n \tbefore=$(git hash-object file) &&\n \tbefore=$(git rev-parse --short $before) &&\n-\tfor i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo \"$i\"; done >>file &&\n+\ttest_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >>file &&\n \tafter=$(git hash-object file) &&\n \tafter=$(git rev-parse --short $after) &&\n \tgit update-index file &&\n@@ -1086,7 +1086,7 @@ test_expect_success TTY 'format-patch --stdout paginates' '\n test_expect_success 'format-patch handles multi-line subjects' '\n \trm -rf patches/ &&\n \techo content >>file &&\n-\tfor i in one two three; do echo $i; done >msg &&\n+\ttest_write_lines one two three >msg &&\n \tgit add file &&\n \tgit commit -F msg &&\n \tgit format-patch -o patches -1 &&\n@@ -1098,7 +1098,7 @@ test_expect_success 'format-patch handles multi-line subjects' '\n test_expect_success 'format-patch handles multi-line encoded subjects' '\n \trm -rf patches/ &&\n \techo content >>file &&\n-\tfor i in en två tre; do echo $i; done >msg &&\n+\ttest_write_lines en två tre >msg &&\n \tgit add file &&\n \tgit commit -F msg &&\n \tgit format-patch -o patches -1 &&\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex c68729ac09..d2b3109c2d 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -287,9 +287,9 @@ test_expect_success 'do not color trailing cr in context' '\n '\n \n test_expect_success 'color new trailing blank lines' '\n-\t{ echo a; echo b; echo; echo; } >x &&\n+\ttest_write_lines a b \"\" \"\" >x &&\n \tgit add x &&\n-\t{ echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&\n+\ttest_write_lines a \"\" \"\" \"\" c \"\" \"\" \"\" \"\" >x &&\n \tgit diff --color x >output &&\n \tcnt=$($grep_a \"${blue_grep}\" output | wc -l) &&\n \ttest $cnt = 2\ndiff --git a/t/t4105-apply-fuzz.sh b/t/t4105-apply-fuzz.sh\nindex 3266e39400..b80ec20801 100755\n--- a/t/t4105-apply-fuzz.sh\n+++ b/t/t4105-apply-fuzz.sh\n@@ -15,15 +15,9 @@ dotest () {\n \n test_expect_success setup '\n \n-\tfor i in 1 2 3 4 5 6 7 8 9 10 11 12\n-\tdo\n-\t\techo $i\n-\tdone >file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 >file &&\n \tgit update-index --add file &&\n-\tfor i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12\n-\tdo\n-\t\techo $i\n-\tdone >file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12 >file &&\n \tcat file >expect &&\n \tgit diff >O0.diff &&\n \ndiff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh\nindex b99e65c086..fa3fa44e51 100755\n--- a/t/t4116-apply-reverse.sh\n+++ b/t/t4116-apply-reverse.sh\n@@ -11,14 +11,14 @@ test_description='git apply in reverse\n \n test_expect_success setup '\n \n-\tfor i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&\n+\ttest_write_lines a b c d e f g h i j k l m n >file1 &&\n \tperl -pe \"y/ijk/\\\\000\\\\001\\\\002/\" <file1 >file2 &&\n \n \tgit add file1 file2 &&\n \tgit commit -m initial &&\n \tgit tag initial &&\n \n-\tfor i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&\n+\ttest_write_lines a b c g h i J K L m o n p q >file1 &&\n \tperl -pe \"y/mon/\\\\000\\\\001\\\\002/\" <file1 >file2 &&\n \n \tgit commit -a -m second &&\ndiff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh\nindex 0ee93fe845..c86d05a96f 100755\n--- a/t/t4117-apply-reject.sh\n+++ b/t/t4117-apply-reject.sh\n@@ -10,25 +10,16 @@ test_description='git apply with rejects\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tfor i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21\n-\tdo\n-\t\techo $i\n-\tdone >file1 &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >file1 &&\n \tcat file1 >saved.file1 &&\n \tgit update-index --add file1 &&\n \tgit commit -m initial &&\n \n-\tfor i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21\n-\tdo\n-\t\techo $i\n-\tdone >file1 &&\n+\ttest_write_lines 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21 >file1 &&\n \tgit diff >patch.1 &&\n \tcat file1 >clean &&\n \n-\tfor i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21\n-\tdo\n-\t\techo $i\n-\tdone >expected &&\n+\ttest_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21 >expected &&\n \n \tmv file1 file2 &&\n \tgit update-index --add --remove file1 file2 &&\n@@ -38,10 +29,7 @@ test_expect_success setup '\n \tmv saved.file1 file1 &&\n \tgit update-index --add --remove file1 file2 &&\n \n-\tfor i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21\n-\tdo\n-\t\techo $i\n-\tdone >file1 &&\n+\ttest_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21 >file1 &&\n \n \tcat file1 >saved.file1\n '\ndiff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh\nindex 65f2e4c3ef..1bce118600 100755\n--- a/t/t4118-apply-empty-context.sh\n+++ b/t/t4118-apply-empty-context.sh\n@@ -10,11 +10,7 @@ test_description='git apply with new style GNU diff with empty context\n . ./test-lib.sh\n \n test_expect_success setup '\n-\t{\n-\t\techo; echo;\n-\t\techo A; echo B; echo C;\n-\t\techo;\n-\t} >file1 &&\n+\ttest_write_lines \"\" \"\" A B C \"\" >file1 &&\n \tcat file1 >file1.orig &&\n \t{\n \t\tcat file1 &&\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex 0ca29821ec..ebff6c6883 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -230,9 +230,9 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '\n \ttest_might_fail git config --unset core.whitespace &&\n \trm -f .gitattributes &&\n \n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n-\t{ echo a; echo b; echo c; } >expect &&\n+\ttest_write_lines a b c >expect &&\n \t{ cat expect; echo; } >one &&\n \tgit diff -- one >patch &&\n \n@@ -242,10 +242,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '\n '\n \n test_expect_success 'blank at EOF with --whitespace=fix (2)' '\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n-\t{ echo a; echo c; } >expect &&\n-\t{ cat expect; echo; echo; } >one &&\n+\ttest_write_lines a b >expect &&\n+\t{ cat expect && test_write_lines \"\" \"\"; } >one &&\n \tgit diff -- one >patch &&\n \n \tgit checkout one &&\n@@ -254,10 +254,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (2)' '\n '\n \n test_expect_success 'blank at EOF with --whitespace=fix (3)' '\n-\t{ echo a; echo b; echo; } >one &&\n+\ttest_write_lines a b \"\" >one &&\n \tgit add one &&\n-\t{ echo a; echo c; echo; } >expect &&\n-\t{ cat expect; echo; echo; } >one &&\n+\ttest_write_lines a c \"\" >expect &&\n+\t{ cat expect && test_write_lines \"\" \"\"; } >one &&\n \tgit diff -- one >patch &&\n \n \tgit checkout one &&\n@@ -266,9 +266,9 @@ test_expect_success 'blank at EOF with --whitespace=fix (3)' '\n '\n \n test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '\n-\t{ echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&\n+\ttest_write_lines a b \"\" \"\" \"\" \"\" \"\" d >one &&\n \tgit add one &&\n-\t{ echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&\n+\ttest_write_lines a b \"\" \"\" \"\" \"\" \"\" \"\" d >expect &&\n \tcp expect one &&\n \tgit diff -- one >patch &&\n \n@@ -278,7 +278,7 @@ test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '\n '\n \n test_expect_success 'blank at EOF with --whitespace=warn' '\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n \techo >>one &&\n \tcat one >expect &&\n@@ -291,7 +291,7 @@ test_expect_success 'blank at EOF with --whitespace=warn' '\n '\n \n test_expect_success 'blank at EOF with --whitespace=error' '\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n \tcat one >expect &&\n \techo >>one &&\n@@ -304,7 +304,7 @@ test_expect_success 'blank at EOF with --whitespace=error' '\n '\n \n test_expect_success 'blank but not empty at EOF' '\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n \techo \"   \" >>one &&\n \tcat one >expect &&\n@@ -317,13 +317,13 @@ test_expect_success 'blank but not empty at EOF' '\n '\n \n test_expect_success 'applying beyond EOF requires one non-blank context line' '\n-\t{ echo; echo; echo; echo; } >one &&\n+\ttest_write_lines \"\" \"\" \"\" \"\" >one &&\n \tgit add one &&\n-\t{ echo b; } >>one &&\n+\techo b >>one &&\n \tgit diff -- one >patch &&\n \n \tgit checkout one &&\n-\t{ echo a; echo; } >one &&\n+\ttest_write_lines a \"\" >one &&\n \tcp one expect &&\n \ttest_must_fail git apply --whitespace=fix patch &&\n \ttest_cmp expect one &&\n@@ -333,7 +333,7 @@ test_expect_success 'applying beyond EOF requires one non-blank context line' '\n \n test_expect_success 'tons of blanks at EOF should not apply' '\n \tfor i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do\n-\t\techo; echo; echo; echo;\n+\t\ttest_write_lines \"\" \"\" \"\" \"\"\n \tdone >one &&\n \tgit add one &&\n \techo a >>one &&\n@@ -362,9 +362,9 @@ test_expect_success 'missing blank line at end with --whitespace=fix' '\n '\n \n test_expect_success 'two missing blank lines at end with --whitespace=fix' '\n-\t{ echo a; echo; echo b; echo c; } >one &&\n+\ttest_write_lines a \"\" b c >one &&\n \tcp one no-blank-lines &&\n-\t{ echo; echo; } >>one &&\n+\ttest_write_lines \"\" \"\" >>one &&\n \tgit add one &&\n \techo d >>one &&\n \tcp one expect &&\n@@ -381,9 +381,9 @@ test_expect_success 'two missing blank lines at end with --whitespace=fix' '\n '\n \n test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '\n-\t{ echo a; echo; } >one &&\n+\ttest_write_lines a \"\" >one &&\n \tgit add one &&\n-\t{ echo b; echo a; echo; } >one &&\n+\ttest_write_lines b a \"\" >one &&\n \tcp one expect &&\n \tgit diff -- one >patch &&\n \techo a >one &&\n@@ -393,10 +393,10 @@ test_expect_success 'missing blank line at end, insert before end, --whitespace=\n '\n \n test_expect_success 'shrink file with tons of missing blanks at end of file' '\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tcp one no-blank-lines &&\n \tfor i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do\n-\t\techo; echo; echo; echo;\n+\t\ttest_write_lines \"\" \"\" \"\" \"\"\n \tdone >>one &&\n \tgit add one &&\n \techo a >one &&\n@@ -412,9 +412,9 @@ test_expect_success 'shrink file with tons of missing blanks at end of file' '\n '\n \n test_expect_success 'missing blanks at EOF must only match blank lines' '\n-\t{ echo a; echo b; } >one &&\n+\ttest_write_lines a b >one &&\n \tgit add one &&\n-\t{ echo c; echo d; } >>one &&\n+\ttest_write_lines c d >>one &&\n \tgit diff -- one >patch &&\n \n \techo a >one &&\n@@ -434,9 +434,9 @@ test_expect_success 'missing blank line should match context line with spaces' '\n \tgit add one &&\n \techo d >>one &&\n \tgit diff -- one >patch &&\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tcp one expect &&\n-\t{ echo; echo d; } >>expect &&\n+\ttest_write_lines \"\" d >>expect &&\n \tgit add one &&\n \n \tgit apply --whitespace=fix patch &&\n@@ -455,7 +455,7 @@ test_expect_success 'same, but with the --ignore-space-option' '\n \techo d >>one &&\n \tcp one expect &&\n \tgit diff -- one >patch &&\n-\t{ echo a; echo b; echo c; } >one &&\n+\ttest_write_lines a b c >one &&\n \tgit add one &&\n \n \tgit checkout-index -f one &&\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex ceb6a79fe0..7391a5a1d2 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -9,10 +9,7 @@ test_expect_success setup '\n \tgit add empty &&\n \ttest_tick &&\n \tgit commit -m initial &&\n-\tfor i in a b c d e\n-\tdo\n-\t\techo $i\n-\tdone >empty &&\n+\ttest_write_lines a b c d e >empty &&\n \tcat empty >expect &&\n \tgit diff |\n \tsed -e \"/^diff --git/d\" \\\ndiff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh\nindex 305b7e649e..079872c714 100755\n--- a/t/t4127-apply-same-fn.sh\n+++ b/t/t4127-apply-same-fn.sh\n@@ -10,10 +10,7 @@ modify () {\n }\n \n test_expect_success setup '\n-\tfor i in a b c d e f g h i j k l m\n-\tdo\n-\t\techo $i\n-\tdone >same_fn &&\n+\ttest_write_lines a b c d e f g h i j k l m >same_fn &&\n \tcp same_fn other_fn &&\n \tgit add same_fn other_fn &&\n \tgit commit -m initial\ndiff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh\nindex 2374151662..5ed7e22827 100755\n--- a/t/t4151-am-abort.sh\n+++ b/t/t4151-am-abort.sh\n@@ -5,10 +5,7 @@ test_description='am --abort'\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tfor i in a b c d e f g\n-\tdo\n-\t\techo $i\n-\tdone >file-1 &&\n+\ttest_write_lines a b c d e f g >file-1 &&\n \tcp file-1 file-2 &&\n \ttest_tick &&\n \tgit add file-1 file-2 &&\n@@ -43,10 +40,7 @@ do\n \n \t\ttest_must_fail git am$with3 000[1245]-*.patch &&\n \t\tgit log --pretty=tformat:%s >actual &&\n-\t\tfor i in 3 2 initial\n-\t\tdo\n-\t\t\techo $i\n-\t\tdone >expect &&\n+\t\ttest_write_lines 3 2 initial >expect &&\n \t\ttest_cmp expect actual\n \t'\n \ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex e13a884207..f9877d42d7 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -361,11 +361,7 @@ test_expect_success 'unpacking with --strict' '\n \tST=$(git write-tree) &&\n \tgit rev-list --objects \"$LIST\" \"$LI\" \"$ST\" >actual &&\n \tPACK5=$( git pack-objects test-5 <actual ) &&\n-\tPACK6=$( (\n-\t\t\techo \"$LIST\"\n-\t\t\techo \"$LI\"\n-\t\t\techo \"$ST\"\n-\t\t ) | git pack-objects test-6 ) &&\n+\tPACK6=$( test_write_lines \"$LIST\" \"$LI\" \"$ST\" | git pack-objects test-6 ) &&\n \ttest_create_repo test-5 &&\n \t(\n \t\tcd test-5 &&\n@@ -408,11 +404,7 @@ test_expect_success 'index-pack with --strict' '\n \tST=$(git write-tree) &&\n \tgit rev-list --objects \"$LIST\" \"$LI\" \"$ST\" >actual &&\n \tPACK5=$( git pack-objects test-5 <actual ) &&\n-\tPACK6=$( (\n-\t\t\techo \"$LIST\"\n-\t\t\techo \"$LI\"\n-\t\t\techo \"$ST\"\n-\t\t ) | git pack-objects test-6 ) &&\n+\tPACK6=$( test_write_lines \"$LIST\" \"$LI\" \"$ST\" | git pack-objects test-6 ) &&\n \ttest_create_repo test-7 &&\n \t(\n \t\tcd test-7 &&\ndiff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh\nindex f4931c0c2a..51973f4a51 100755\n--- a/t/t5306-pack-nobase.sh\n+++ b/t/t5306-pack-nobase.sh\n@@ -12,7 +12,7 @@ test_description='git-pack-object with missing base\n #\n test_expect_success \\\n     'setup base' \\\n-    'for a in a b c d e f g h i; do echo $a >>text; done &&\n+    'test_write_lines a b c d e f g h i >text &&\n      echo side >side &&\n      git update-index --add text side &&\n      A=$(echo A | git commit-tree $(git write-tree)) &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex f8625f9158..4b3877216e 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -17,7 +17,7 @@ test_expect_success 'clone -c sets config in cloned repo' '\n test_expect_success 'clone -c can set multi-keys' '\n \trm -rf child &&\n \tgit clone -c core.foo=bar -c core.foo=baz . child &&\n-\t{ echo bar; echo baz; } >expect &&\n+\ttest_write_lines bar baz >expect &&\n \tgit --git-dir=child/.git config --get-all core.foo >actual &&\n \ttest_cmp expect actual\n '\ndiff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh\nindex 20adbece65..af57a04b7f 100755\n--- a/t/t6019-rev-list-ancestry-path.sh\n+++ b/t/t6019-rev-list-ancestry-path.sh\n@@ -51,7 +51,7 @@ test_expect_success setup '\n '\n \n test_expect_success 'rev-list D..M' '\n-\tfor c in E F G H I J K L M; do echo $c; done >expect &&\n+\ttest_write_lines E F G H I J K L M >expect &&\n \tgit rev-list --format=%s D..M |\n \tsed -e \"/^commit /d\" |\n \tsort >actual &&\n@@ -59,7 +59,7 @@ test_expect_success 'rev-list D..M' '\n '\n \n test_expect_success 'rev-list --ancestry-path D..M' '\n-\tfor c in E F H I J L M; do echo $c; done >expect &&\n+\ttest_write_lines E F H I J L M >expect &&\n \tgit rev-list --ancestry-path --format=%s D..M |\n \tsed -e \"/^commit /d\" |\n \tsort >actual &&\n@@ -81,7 +81,7 @@ test_expect_success 'rev-list --ancestry-path D..M -- M.t' '\n '\n \n test_expect_success 'rev-list F...I' '\n-\tfor c in F G H I; do echo $c; done >expect &&\n+\ttest_write_lines F G H I >expect &&\n \tgit rev-list --format=%s F...I |\n \tsed -e \"/^commit /d\" |\n \tsort >actual &&\n@@ -89,7 +89,7 @@ test_expect_success 'rev-list F...I' '\n '\n \n test_expect_success 'rev-list --ancestry-path F...I' '\n-\tfor c in F H I; do echo $c; done >expect &&\n+\ttest_write_lines F H I >expect &&\n \tgit rev-list --ancestry-path --format=%s F...I |\n \tsed -e \"/^commit /d\" |\n \tsort >actual &&\n@@ -111,7 +111,7 @@ test_expect_success 'rev-list --ancestry-path G..M -- G.t' '\n '\n \n test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '\n-\tfor c in G L; do echo $c; done >expect &&\n+\ttest_write_lines G L >expect &&\n \tgit rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |\n \tsed -e \"/^commit /d\" |\n \tsort >actual &&\ndiff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh\nindex ddf34f0115..ed449abe55 100755\n--- a/t/t6060-merge-index.sh\n+++ b/t/t6060-merge-index.sh\n@@ -4,9 +4,7 @@ test_description='basic git merge-index / git-merge-one-file tests'\n . ./test-lib.sh\n \n test_expect_success 'setup diverging branches' '\n-\tfor i in 1 2 3 4 5 6 7 8 9 10; do\n-\t\techo $i\n-\tdone >file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&\n \tgit add file &&\n \tgit commit -m base &&\n \tgit tag base &&\ndiff --git a/t/t6409-merge-subtree.sh b/t/t6409-merge-subtree.sh\nindex ba7890ec52..e9ba6f1690 100755\n--- a/t/t6409-merge-subtree.sh\n+++ b/t/t6409-merge-subtree.sh\n@@ -10,7 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n test_expect_success setup '\n \n \ts=\"1 2 3 4 5 6 7 8\" &&\n-\tfor i in $s; do echo $i; done >hello &&\n+\ttest_write_lines $s >hello &&\n \tgit add hello &&\n \tgit commit -m initial &&\n \tgit checkout -b side &&\n@@ -18,7 +18,7 @@ test_expect_success setup '\n \tgit add hello &&\n \tgit commit -m second &&\n \tgit checkout main &&\n-\tfor i in mundo $s; do echo $i; done >hello &&\n+\ttest_write_lines mundo $s >hello &&\n \tgit add hello &&\n \tgit commit -m main\n \n@@ -27,7 +27,7 @@ test_expect_success setup '\n test_expect_success 'subtree available and works like recursive' '\n \n \tgit merge -s subtree side &&\n-\tfor i in mundo $s world; do echo $i; done >expect &&\n+\ttest_write_lines mundo $s world >expect &&\n \ttest_cmp expect hello\n \n '\ndiff --git a/t/t6417-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh\nindex ec065d6a65..62d1406119 100755\n--- a/t/t6417-merge-ours-theirs.sh\n+++ b/t/t6417-merge-ours-theirs.sh\n@@ -7,10 +7,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tfor i in 1 2 3 4 5 6 7 8 9\n-\tdo\n-\t\techo \"$i\"\n-\tdone >file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >file &&\n \tgit add file &&\n \tcp file elif &&\n \tgit commit -m initial &&\ndiff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh\nindex 512ae2781f..fb5417d5e7 100755\n--- a/t/t7501-commit-basic-functionality.sh\n+++ b/t/t7501-commit-basic-functionality.sh\n@@ -667,10 +667,7 @@ test_expect_success 'amend can copy notes' '\n \n test_expect_success 'commit a file whose name is a dash' '\n \tgit reset --hard &&\n-\tfor i in 1 2 3 4 5\n-\tdo\n-\t\techo $i\n-\tdone >./- &&\n+\ttest_write_lines 1 2 3 4 5 >./- &&\n \tgit add ./- &&\n \ttest_tick &&\n \tgit commit -m \"add dash\" >output </dev/null &&\ndiff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh\nindex da80f815ce..d751d48b7d 100755\n--- a/t/t8003-blame-corner-cases.sh\n+++ b/t/t8003-blame-corner-cases.sh\n@@ -13,14 +13,8 @@ test_expect_success setup '\n \techo B B B B B >two &&\n \techo C C C C C >tres &&\n \techo ABC >mouse &&\n-\tfor i in 1 2 3 4 5 6 7 8 9\n-\tdo\n-\t\techo $i\n-\tdone >nine_lines &&\n-\tfor i in 1 2 3 4 5 6 7 8 9 a\n-\tdo\n-\t\techo $i\n-\tdone >ten_lines &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >nine_lines &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 a >ten_lines &&\n \tgit add one two tres mouse nine_lines ten_lines &&\n \ttest_tick &&\n \tGIT_AUTHOR_NAME=Initial git commit -m Initial &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443594","messageId":"20211209051115.52629-13-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 12/19] tests: fix broken &&-chains in `{...}` groups","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:08Z","receivedAt":"2021-12-09T05:12:27Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"The top-level &&-chain checker built into t/test-lib.sh causes tests to\nmagically exit with code 117 if the &&-chain is broken. However, it has\nthe shortcoming that the magic does not work within `{...}` groups,\n`(...)` subshells, `$(...)` substitutions, or within bodies of compound\nstatements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\npartly fills in the gap by catching broken &&-chains in `(...)`\nsubshells, but bugs can still lurk behind broken &&-chains in the other\ncases.\n\nFix broken &&-chains in `{...}` groups in order to reduce the number of\npossible lurking bugs.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t0021-conversion.sh                  |  8 ++++----\n t/t0069-oidtree.sh                     | 12 ++++++------\n t/t1006-cat-file.sh                    |  2 +-\n t/t1300-config.sh                      |  2 +-\n t/t1403-show-ref.sh                    |  8 ++++----\n t/t2200-add-update.sh                  | 12 ++++++------\n t/t2201-add-update-typechange.sh       | 10 +++++-----\n t/t4023-diff-rename-typechange.sh      |  6 +++---\n t/t4124-apply-ws-rule.sh               |  2 +-\n t/t4150-am.sh                          |  2 +-\n t/t4212-log-corrupt.sh                 |  8 ++++----\n t/t5316-pack-delta-depth.sh            |  7 +++++--\n t/t5510-fetch.sh                       |  2 +-\n t/t5515-fetch-merge-logic.sh           | 12 ++++++------\n t/t5562-http-backend-content-length.sh |  2 +-\n t/t5570-git-daemon.sh                  |  2 +-\n t/t5571-pre-push-hook.sh               |  2 +-\n t/t7513-interpret-trailers.sh          |  2 +-\n t/t8002-blame.sh                       |  2 +-\n 19 files changed, 53 insertions(+), 50 deletions(-)\n\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex 1a1a69ad92..bb3de2701a 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -76,13 +76,13 @@ test_expect_success setup '\n \tgit config filter.rot13.clean ./rot13.sh &&\n \n \t{\n-\t    echo \"*.t filter=rot13\"\n+\t    echo \"*.t filter=rot13\" &&\n \t    echo \"*.i ident\"\n \t} >.gitattributes &&\n \n \t{\n-\t    echo a b c d e f g h i j k l m\n-\t    echo n o p q r s t u v w x y z\n+\t    echo a b c d e f g h i j k l m &&\n+\t    echo n o p q r s t u v w x y z &&\n \t    echo '\\''$Id$'\\''\n \t} >test &&\n \tcat test >test.t &&\n@@ -159,7 +159,7 @@ test_expect_success expanded_in_repo '\n \t\tprintf \"\\$Id: NoTerminatingSymbolAtEOF\"\n \t} >expected-output-crlf &&\n \t{\n-\t\techo \"expanded-keywords ident\"\n+\t\techo \"expanded-keywords ident\" &&\n \t\techo \"expanded-keywords-crlf ident text eol=crlf\"\n \t} >>.gitattributes &&\n \ndiff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh\nindex 74cc59bf8a..889db50818 100755\n--- a/t/t0069-oidtree.sh\n+++ b/t/t0069-oidtree.sh\n@@ -28,7 +28,7 @@ test_expect_success 'oidtree insert and contains' '\n \tEOF\n \t{\n \t\techoid insert 444 1 2 3 4 5 a b c d e &&\n-\t\techoid contains 44 441 440 444 4440 4444\n+\t\techoid contains 44 441 440 444 4440 4444 &&\n \t\techo clear\n \t} | test-tool oidtree >actual &&\n \ttest_cmp expect actual\n@@ -37,11 +37,11 @@ test_expect_success 'oidtree insert and contains' '\n test_expect_success 'oidtree each' '\n \techoid \"\" 123 321 321 >expect &&\n \t{\n-\t\techoid insert f 9 8 123 321 a b c d e\n-\t\techo each 12300\n-\t\techo each 3211\n-\t\techo each 3210\n-\t\techo each 32100\n+\t\techoid insert f 9 8 123 321 a b c d e &&\n+\t\techo each 12300 &&\n+\t\techo each 3211 &&\n+\t\techo each 3210 &&\n+\t\techo each 32100 &&\n \t\techo clear\n \t} | test-tool oidtree >actual &&\n \ttest_cmp expect actual\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 67a3f64c2d..f6f00c7039 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -283,7 +283,7 @@ test_expect_success \"--batch-check with multiple sha1s gives correct format\" '\n \n test_expect_success 'setup blobs which are likely to delta' '\n \ttest-tool genrandom foo 10240 >foo &&\n-\t{ cat foo; echo plus; } >foo-plus &&\n+\t{ cat foo && echo plus; } >foo-plus &&\n \tgit add foo foo-plus &&\n \tgit commit -m foo &&\n \tcat >blobs <<-\\EOF\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 9571649c42..516dd8bfa8 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -901,7 +901,7 @@ test_expect_success 'get --expiry-date' '\n \tEOF\n \t: \"work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)\" &&\n \t{\n-\t\techo \"$rel_out $(git config --expiry-date date.valid1)\"\n+\t\techo \"$rel_out $(git config --expiry-date date.valid1)\" &&\n \t\tgit config --expiry-date date.valid2 &&\n \t\tgit config --expiry-date date.valid3 &&\n \t\tgit config --expiry-date date.valid4 &&\ndiff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\nindex 17d3cc1405..bbc01aae34 100755\n--- a/t/t1403-show-ref.sh\n+++ b/t/t1403-show-ref.sh\n@@ -78,7 +78,7 @@ test_expect_success 'show-ref --verify -q' '\n test_expect_success 'show-ref -d' '\n \t{\n \t\techo $(git rev-parse refs/tags/A) refs/tags/A &&\n-\t\techo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\"\n+\t\techo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\" &&\n \t\techo $(git rev-parse refs/tags/C) refs/tags/C\n \t} >expect &&\n \tgit show-ref -d A C >actual &&\n@@ -148,7 +148,7 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '\n \n \t{\n \t\techo $(git rev-parse HEAD) HEAD &&\n-\t\techo $(git rev-parse refs/heads/B) refs/heads/B\n+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n \t\techo $(git rev-parse refs/tags/B) refs/tags/B\n \t} >expect &&\n \tgit show-ref --head B >actual &&\n@@ -156,8 +156,8 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '\n \n \t{\n \t\techo $(git rev-parse HEAD) HEAD &&\n-\t\techo $(git rev-parse refs/heads/B) refs/heads/B\n-\t\techo $(git rev-parse refs/tags/B) refs/tags/B\n+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n+\t\techo $(git rev-parse refs/tags/B) refs/tags/B &&\n \t\techo $(git rev-parse refs/tags/B^0) \"refs/tags/B^{}\"\n \t} >expect &&\n \tgit show-ref --head -d B >actual &&\ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 67b9cc752f..d2ef0041f9 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -153,10 +153,10 @@ test_expect_success 'add -u resolves unmerged paths' '\n \t\t\techo \"100644 $one 1\t$path\" &&\n \t\t\techo \"100644 $two 2\t$path\" &&\n \t\t\techo \"100644 $three 3\t$path\"\n-\t\tdone\n-\t\techo \"100644 $one 1\tpath3\"\n-\t\techo \"100644 $one 1\tpath4\"\n-\t\techo \"100644 $one 3\tpath5\"\n+\t\tdone &&\n+\t\techo \"100644 $one 1\tpath3\" &&\n+\t\techo \"100644 $one 1\tpath4\" &&\n+\t\techo \"100644 $one 3\tpath5\" &&\n \t\techo \"100644 $one 3\tpath6\"\n \t} |\n \tgit update-index --index-info &&\n@@ -173,8 +173,8 @@ test_expect_success 'add -u resolves unmerged paths' '\n \tgit add -u &&\n \tgit ls-files -s path1 path2 path3 path4 path5 path6 >actual &&\n \t{\n-\t\techo \"100644 $three 0\tpath1\"\n-\t\techo \"100644 $two 0\tpath3\"\n+\t\techo \"100644 $three 0\tpath1\" &&\n+\t\techo \"100644 $two 0\tpath3\" &&\n \t\techo \"100644 $two 0\tpath5\"\n \t} >expect &&\n \ttest_cmp expect actual\ndiff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh\nindex a4eec0a346..687be974d4 100755\n--- a/t/t2201-add-update-typechange.sh\n+++ b/t/t2201-add-update-typechange.sh\n@@ -97,17 +97,17 @@ test_expect_success modify '\n \t\t\"\n \t} >expect &&\n \t{\n-\t\tcat expect\n-\t\techo \":100644 160000 $_empty $ZERO_OID T\tyonk\"\n+\t\tcat expect &&\n+\t\techo \":100644 160000 $_empty $ZERO_OID T\tyonk\" &&\n \t\techo \":100644 000000 $_empty $ZERO_OID D\tzifmia\"\n \t} >expect-files &&\n \t{\n-\t\tcat expect\n+\t\tcat expect &&\n \t\techo \":000000 160000 $ZERO_OID $ZERO_OID A\tyonk\"\n \t} >expect-index &&\n \t{\n-\t\techo \"100644 $_empty 0\tnitfol\"\n-\t\techo \"160000 $yomin 0\tyomin\"\n+\t\techo \"100644 $_empty 0\tnitfol\" &&\n+\t\techo \"160000 $yomin 0\tyomin\" &&\n \t\techo \"160000 $yonk 0\tyonk\"\n \t} >expect-final\n '\ndiff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh\nindex 47d6f35dcc..7cb9909293 100755\n--- a/t/t4023-diff-rename-typechange.sh\n+++ b/t/t4023-diff-rename-typechange.sh\n@@ -55,7 +55,7 @@ test_expect_success 'cross renames to be detected for regular files' '\n \n \tgit diff-tree five six -r --name-status -B -M | sort >actual &&\n \t{\n-\t\techo \"R100\tfoo\tbar\"\n+\t\techo \"R100\tfoo\tbar\" &&\n \t\techo \"R100\tbar\tfoo\"\n \t} | sort >expect &&\n \ttest_cmp expect actual\n@@ -66,7 +66,7 @@ test_expect_success 'cross renames to be detected for typechange' '\n \n \tgit diff-tree one two -r --name-status -B -M | sort >actual &&\n \t{\n-\t\techo \"R100\tfoo\tbar\"\n+\t\techo \"R100\tfoo\tbar\" &&\n \t\techo \"R100\tbar\tfoo\"\n \t} | sort >expect &&\n \ttest_cmp expect actual\n@@ -78,7 +78,7 @@ test_expect_success 'moves and renames' '\n \tgit diff-tree three four -r --name-status -B -M | sort >actual &&\n \t{\n \t\t# see -B -M (#6) in t4008\n-\t\techo \"C100\tfoo\tbar\"\n+\t\techo \"C100\tfoo\tbar\" &&\n \t\techo \"T100\tfoo\"\n \t} | sort >expect &&\n \ttest_cmp expect actual\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex ebff6c6883..ec5c10d2a0 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -233,7 +233,7 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '\n \ttest_write_lines a b c >one &&\n \tgit add one &&\n \ttest_write_lines a b c >expect &&\n-\t{ cat expect; echo; } >one &&\n+\t{ cat expect && echo; } >one &&\n \tgit diff -- one >patch &&\n \n \tgit checkout one &&\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 2aaaa0d7de..103cd39148 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -116,7 +116,7 @@ test_expect_success setup '\n \t\tgit format-patch --stdout first | sed -e \"1d\"\n \t} | append_cr >patch1-crlf.eml &&\n \t{\n-\t\tprintf \"%255s\\\\n\" \"\"\n+\t\tprintf \"%255s\\\\n\" \"\" &&\n \t\techo \"X-Fake-Field: Line One\" &&\n \t\techo \"X-Fake-Field: Line Two\" &&\n \t\techo \"X-Fake-Field: Line Three\" &&\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 03b952c90d..0244888a5a 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -20,10 +20,10 @@ test_expect_success 'fsck notices broken commit' '\n \n test_expect_success 'git log with broken author email' '\n \t{\n-\t\techo commit $(cat broken_email.hash)\n-\t\techo \"Author: A U Thor <author@example.com>\"\n-\t\techo \"Date:   Thu Apr 7 15:13:13 2005 -0700\"\n-\t\techo\n+\t\techo commit $(cat broken_email.hash) &&\n+\t\techo \"Author: A U Thor <author@example.com>\" &&\n+\t\techo \"Date:   Thu Apr 7 15:13:13 2005 -0700\" &&\n+\t\techo &&\n \t\techo \"    foo\"\n \t} >expect.out &&\n \ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex 759169d074..df524f7b6d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -57,8 +57,11 @@ test_expect_success 'create series of packs' '\n \t\tgit commit -m $i &&\n \t\tcur=$(git rev-parse HEAD^{tree}) &&\n \t\t{\n-\t\t\ttest -n \"$prev\" && echo \"-$prev\"\n-\t\t\techo $cur\n+\t\t\tif test -n \"$prev\"\n+\t\t\tthen\n+\t\t\t\techo \"-$prev\"\n+\t\t\tfi &&\n+\t\t\techo $cur &&\n \t\t\techo \"$(git rev-parse :file) file\"\n \t\t} | git pack-objects --stdout >tmp &&\n \t\tgit index-pack --stdin --fix-thin <tmp || return 1\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 1892d6615a..01468ce6d8 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -71,7 +71,7 @@ test_expect_success \"fetch test for-merge\" '\n \tmain_in_two=$(cd ../two && git rev-parse main) &&\n \tone_in_two=$(cd ../two && git rev-parse one) &&\n \t{\n-\t\techo \"$one_in_two\t\"\n+\t\techo \"$one_in_two\t\" &&\n \t\techo \"$main_in_two\tnot-for-merge\"\n \t} >expected &&\n \tcut -f -2 .git/FETCH_HEAD >actual &&\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 9d440e2821..c69cfd5c64 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -191,17 +191,17 @@ do\n \t\tcp \"$expect_r\" expect_r &&\n \t\tconvert_expected expect_r sed_script &&\n \t\t{\n-\t\t\techo \"# $cmd\"\n-\t\t\tset x $cmd; shift\n-\t\t\tgit symbolic-ref HEAD refs/heads/$1 ; shift\n-\t\t\trm -f .git/FETCH_HEAD\n+\t\t\techo \"# $cmd\" &&\n+\t\t\tset x $cmd && shift &&\n+\t\t\tgit symbolic-ref HEAD refs/heads/$1 && shift &&\n+\t\t\trm -f .git/FETCH_HEAD &&\n \t\t\tgit for-each-ref \\\n \t\t\t\trefs/heads refs/remotes/rem refs/tags |\n \t\t\twhile read val type refname\n \t\t\tdo\n \t\t\t\tgit update-ref -d \"$refname\" \"$val\"\n-\t\t\tdone\n-\t\t\tgit fetch \"$@\" >/dev/null\n+\t\t\tdone &&\n+\t\t\tgit fetch \"$@\" >/dev/null &&\n \t\t\tcat .git/FETCH_HEAD\n \t\t} >\"$actual_f\" &&\n \t\tgit show-ref >\"$actual_r\" &&\ndiff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh\nindex 05a58069b0..b68ec22d3f 100755\n--- a/t/t5562-http-backend-content-length.sh\n+++ b/t/t5562-http-backend-content-length.sh\n@@ -63,7 +63,7 @@ test_expect_success 'setup' '\n \thash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&\n \t{\n \t\tprintf \"%s %s refs/heads/newbranch\\\\0report-status object-format=%s\\\\n\" \\\n-\t\t\t\"$ZERO_OID\" \"$hash_next\" \"$(test_oid algo)\" | packetize_raw\n+\t\t\t\"$ZERO_OID\" \"$hash_next\" \"$(test_oid algo)\" | packetize_raw &&\n \t\tprintf 0000 &&\n \t\techo \"$hash_next\" | git pack-objects --stdout\n \t} >push_body &&\ndiff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh\nindex b87ca06a58..1131503b76 100755\n--- a/t/t5570-git-daemon.sh\n+++ b/t/t5570-git-daemon.sh\n@@ -194,7 +194,7 @@ test_expect_success 'hostname cannot break out of directory' '\n \n test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '\n \t{\n-\t\tprintf \"git-upload-pack /interp.git\\n\\0host=localhost\" | packetize_raw\n+\t\tprintf \"git-upload-pack /interp.git\\n\\0host=localhost\" | packetize_raw &&\n \t\tprintf \"0000\"\n \t} >input &&\n \tfake_nc \"$GIT_DAEMON_HOST_PORT\" <input >output &&\ndiff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh\nindex b043a279f1..80e86d8284 100755\n--- a/t/t5571-pre-push-hook.sh\n+++ b/t/t5571-pre-push-hook.sh\n@@ -114,7 +114,7 @@ test_expect_success 'push to URL' '\n \n test_expect_success 'set up many-ref tests' '\n \t{\n-\t\tnr=1000\n+\t\tnr=1000 &&\n \t\twhile test $nr -lt 2000\n \t\tdo\n \t\t\tnr=$(( $nr + 1 )) &&\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 04885d0a5e..97f10905d2 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -156,7 +156,7 @@ test_expect_success 'with config option on the command line' '\n \t\tAcked-by: Johan\n \t\tReviewed-by: Peff\n \tEOF\n-\t{ echo; echo \"Acked-by: Johan\"; } |\n+\t{ echo && echo \"Acked-by: Johan\"; } |\n \tgit -c \"trailer.Acked-by.ifexists=addifdifferent\" interpret-trailers \\\n \t\t--trailer \"Reviewed-by: Peff\" --trailer \"Acked-by: Johan\" >actual &&\n \ttest_cmp expected actual\ndiff --git a/t/t8002-blame.sh b/t/t8002-blame.sh\nindex 5bb302b1ba..ee4fdd8f18 100755\n--- a/t/t8002-blame.sh\n+++ b/t/t8002-blame.sh\n@@ -97,7 +97,7 @@ test_expect_success 'set up abbrev tests' '\n \ttest_commit abbrev &&\n \tsha1=$(git rev-parse --verify HEAD) &&\n \tcheck_abbrev () {\n-\t\texpect=$1; shift\n+\t\texpect=$1 && shift &&\n \t\techo $sha1 | cut -c 1-$expect >expect &&\n \t\tgit blame \"$@\" abbrev.t >actual &&\n \t\tperl -lne \"/[0-9a-f]+/ and print \\$&\" <actual >actual.sha &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443595","messageId":"20211209051115.52629-14-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 13/19] tests: apply modern idiom for signaling test failure","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:09Z","receivedAt":"2021-12-09T05:12:28Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Simplify the way these tests signal failure by employing the modern\nidiom of making the `if` or `case` statement resolve to false when an\nerror is detected.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t2102-update-index-symlinks.sh | 2 +-\n t/t3402-rebase-merge.sh          | 8 ++++----\n t/t3700-add.sh                   | 2 +-\n 3 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh\nindex 22f2c730ae..9b11130ab9 100755\n--- a/t/t2102-update-index-symlinks.sh\n+++ b/t/t2102-update-index-symlinks.sh\n@@ -25,7 +25,7 @@ test_expect_success \\\n 'the index entry must still be a symbolic link' '\n case \"$(git ls-files --stage --cached symlink)\" in\n 120000\" \"*symlink) echo pass;;\n-*) echo fail; git ls-files --stage --cached symlink; (exit 1);;\n+*) echo fail; git ls-files --stage --cached symlink; false;;\n esac'\n \n test_done\ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nindex cfde68f193..7e46f4ca85 100755\n--- a/t/t3402-rebase-merge.sh\n+++ b/t/t3402-rebase-merge.sh\n@@ -68,7 +68,7 @@ test_expect_success 'merge and rebase should match' '\n \tif test -s difference\n \tthen\n \t\tcat difference\n-\t\t(exit 1)\n+\t\tfalse\n \telse\n \t\techo happy\n \tfi\n@@ -102,7 +102,7 @@ test_expect_success 'merge and rebase should match' '\n \tif test -s difference\n \tthen\n \t\tcat difference\n-\t\t(exit 1)\n+\t\tfalse\n \telse\n \t\techo happy\n \tfi\n@@ -117,7 +117,7 @@ test_expect_success 'picking rebase' '\n \t\techo happy\n \telse\n \t\tgit show-branch\n-\t\t(exit 1)\n+\t\tfalse\n \tfi &&\n \tf=$(git diff-tree --name-only HEAD^ HEAD) &&\n \tg=$(git diff-tree --name-only HEAD^^ HEAD^) &&\n@@ -127,7 +127,7 @@ test_expect_success 'picking rebase' '\n \t*)\n \t\techo \"$f\"\n \t\techo \"$g\"\n-\t\t(exit 1)\n+\t\tfalse\n \tesac\n '\n \ndiff --git a/t/t3700-add.sh b/t/t3700-add.sh\nindex 23c3c214c5..6902807ff8 100755\n--- a/t/t3700-add.sh\n+++ b/t/t3700-add.sh\n@@ -177,7 +177,7 @@ test_expect_success 'git add --refresh' '\n \tgit read-tree HEAD &&\n \tcase \"$(git diff-index HEAD -- foo)\" in\n \t:100644\" \"*\"M\tfoo\") echo pass;;\n-\t*) echo fail; (exit 1);;\n+\t*) echo fail; false;;\n \tesac &&\n \tgit add --refresh -- foo &&\n \ttest -z \"$(git diff-index HEAD -- foo)\"\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443596","messageId":"20211209051115.52629-15-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 14/19] tests: apply modern idiom for exiting loop upon failure","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:10Z","receivedAt":"2021-12-09T05:12:29Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Rather than maintaining a flag indicating a failure within a loop and\naborting the test when the loop ends if the flag is set, modern practice\nis to signal the failure immediately by exiting the loop early via\n`return 1` (or `exit 1` if inside a subshell). Simplify these loops by\nfollowing the modern idiom.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t1050-large.sh                | 26 ++++++++------------------\n t/t5505-remote.sh               |  6 ++----\n t/t9400-git-cvsserver-server.sh |  5 ++---\n 3 files changed, 12 insertions(+), 25 deletions(-)\n\ndiff --git a/t/t1050-large.sh b/t/t1050-large.sh\nindex 99ff2866b7..0e4267c723 100755\n--- a/t/t1050-large.sh\n+++ b/t/t1050-large.sh\n@@ -51,27 +51,21 @@ EOF\n test_expect_success 'add a large file or two' '\n \tgit add large1 huge large2 &&\n \t# make sure we got a single packfile and no loose objects\n-\tbad= count=0 idx= &&\n+\tcount=0 idx= &&\n \tfor p in .git/objects/pack/pack-*.pack\n \tdo\n \t\tcount=$(( $count + 1 )) &&\n-\t\tif test_path_is_file \"$p\" &&\n-\t\t   idx=${p%.pack}.idx && test_path_is_file \"$idx\"\n-\t\tthen\n-\t\t\tcontinue\n-\t\tfi\n-\t\tbad=t\n+\t\ttest_path_is_file \"$p\" &&\n+\t\tidx=${p%.pack}.idx &&\n+\t\ttest_path_is_file \"$idx\" || return 1\n \tdone &&\n-\ttest -z \"$bad\" &&\n \ttest $count = 1 &&\n \tcnt=$(git show-index <\"$idx\" | wc -l) &&\n \ttest $cnt = 2 &&\n \tfor l in .git/objects/$OIDPATH_REGEX\n \tdo\n-\t\ttest_path_is_file \"$l\" || continue\n-\t\tbad=t\n+\t\ttest_path_is_missing \"$l\" || return 1\n \tdone &&\n-\ttest -z \"$bad\" &&\n \n \t# attempt to add another copy of the same\n \tgit add large3 &&\n@@ -79,14 +73,10 @@ test_expect_success 'add a large file or two' '\n \tfor p in .git/objects/pack/pack-*.pack\n \tdo\n \t\tcount=$(( $count + 1 )) &&\n-\t\tif test_path_is_file \"$p\" &&\n-\t\t   idx=${p%.pack}.idx && test_path_is_file \"$idx\"\n-\t\tthen\n-\t\t\tcontinue\n-\t\tfi\n-\t\tbad=t\n+\t\ttest_path_is_file \"$p\" &&\n+\t\tidx=${p%.pack}.idx &&\n+\t\ttest_path_is_file \"$idx\" || return 1\n \tdone &&\n-\ttest -z \"$bad\" &&\n \ttest $count = 1\n '\n \ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex e6e3c8f552..5ef8db481c 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -1332,7 +1332,6 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '\n \t(\n \t\tcd test &&\n \t\tgit tag -a -m \"Some tag\" some-tag main &&\n-\t\texit_with=true &&\n \t\tfor type in commit tag tree blob\n \t\tdo\n \t\t\tif test \"$type\" = \"blob\"\n@@ -1348,9 +1347,8 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '\n \t\t\t\tpush origin $oid:dst 2>err &&\n \t\t\ttest_i18ngrep \"error: The destination you\" err &&\n \t\t\ttest_i18ngrep ! \"hint: Did you mean\" err ||\n-\t\t\texit_with=false\n-\t\tdone &&\n-\t\t$exit_with\n+\t\t\texit 1\n+\t\tdone\n \t)\n '\n \ndiff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\nindex 17f988edd2..a6a73effde 100755\n--- a/t/t9400-git-cvsserver-server.sh\n+++ b/t/t9400-git-cvsserver-server.sh\n@@ -350,10 +350,9 @@ test_expect_success 'cvs update (subdirectories)' \\\n \ttest_cmp \"$dir/$filename\" \"../$dir/$filename\"; then\n         :\n       else\n-        echo >failure\n+        exit 1\n       fi\n-    done) &&\n-   test ! -f failure'\n+    done)'\n \n cd \"$WORKDIR\"\n test_expect_success 'cvs update (delete file)' \\\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443598","messageId":"20211209051115.52629-17-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 16/19] t0000-t3999: detect and signal failure within loop","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:12Z","receivedAt":"2021-12-09T05:12:30Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Failures within `for` and `while` loops can go unnoticed if not detected\nand signaled manually since the loop itself does not abort when a\ncontained command fails, nor will a failure necessarily be detected when\nthe loop finishes since the loop returns the exit code of the last\ncommand it ran on the final iteration, which may not be the command\nwhich failed. Therefore, detect and signal failures manually within\nloops using the idiom `|| return 1` (or `|| exit 1` within subshells).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/perf/p0100-globbing.sh            | 4 ++--\n t/perf/p1400-update-ref.sh          | 4 ++--\n t/perf/p1451-fsck-skip-list.sh      | 2 +-\n t/perf/p3400-rebase.sh              | 2 +-\n t/perf/p5302-pack-index.sh          | 2 +-\n t/perf/p5303-many-packs.sh          | 2 +-\n t/perf/p7519-fsmonitor.sh           | 8 ++++----\n t/t0008-ignores.sh                  | 2 +-\n t/t0011-hashmap.sh                  | 4 ++--\n t/t0021-conversion.sh               | 8 ++++----\n t/t0095-bloom.sh                    | 4 ++--\n t/t0410-partial-clone.sh            | 2 +-\n t/t1050-large.sh                    | 4 ++--\n t/t1091-sparse-checkout-builtin.sh  | 2 +-\n t/t1300-config.sh                   | 2 +-\n t/t1400-update-ref.sh               | 4 ++--\n t/t1403-show-ref.sh                 | 4 ++--\n t/t1410-reflog.sh                   | 4 ++--\n t/t1512-rev-parse-disambiguation.sh | 2 +-\n t/t2004-checkout-cache-temp.sh      | 4 ++--\n t/t2012-checkout-last.sh            | 4 ++--\n t/t2200-add-update.sh               | 2 +-\n t/t2203-add-intent.sh               | 2 +-\n t/t3202-show-branch.sh              | 8 ++++----\n t/t3303-notes-subtrees.sh           | 6 +++---\n t/t3305-notes-fanout.sh             | 4 ++--\n t/t3404-rebase-interactive.sh       | 4 ++--\n t/t3501-revert-cherry-pick.sh       | 2 +-\n t/t3508-cherry-pick-many-commits.sh | 2 +-\n t/t3700-add.sh                      | 2 +-\n t/t3920-crlf-messages.sh            | 4 ++--\n 31 files changed, 55 insertions(+), 55 deletions(-)\n\ndiff --git a/t/perf/p0100-globbing.sh b/t/perf/p0100-globbing.sh\nindex dd18a9ce2b..439e9c8e3c 100755\n--- a/t/perf/p0100-globbing.sh\n+++ b/t/perf/p0100-globbing.sh\n@@ -19,9 +19,9 @@ test_expect_success 'setup' '\n \t\tprintf \"a\" >>refname &&\n \t\tfor j in $(test_seq 1 $i)\n \t\tdo\n-\t\t\tprintf \"a*\" >>refglob.$i\n+\t\t\tprintf \"a*\" >>refglob.$i || return 1\n \t\tdone &&\n-\t\techo b >>refglob.$i\n+\t\techo b >>refglob.$i || return 1\n \tdone &&\n \ttest_commit test $(cat refname).t \"\" $(cat refname).t\n '\ndiff --git a/t/perf/p1400-update-ref.sh b/t/perf/p1400-update-ref.sh\nindex dda8a74866..a75969cbb1 100755\n--- a/t/perf/p1400-update-ref.sh\n+++ b/t/perf/p1400-update-ref.sh\n@@ -13,7 +13,7 @@ test_expect_success \"setup\" '\n \tdo\n \t\tprintf \"start\\ncreate refs/heads/%d PRE\\ncommit\\n\" $i &&\n \t\tprintf \"start\\nupdate refs/heads/%d POST PRE\\ncommit\\n\" $i &&\n-\t\tprintf \"start\\ndelete refs/heads/%d POST\\ncommit\\n\" $i\n+\t\tprintf \"start\\ndelete refs/heads/%d POST\\ncommit\\n\" $i || return 1\n \tdone >instructions\n '\n \n@@ -22,7 +22,7 @@ test_perf \"update-ref\" '\n \tdo\n \t\tgit update-ref refs/heads/branch PRE &&\n \t\tgit update-ref refs/heads/branch POST PRE &&\n-\t\tgit update-ref -d refs/heads/branch\n+\t\tgit update-ref -d refs/heads/branch || return 1\n \tdone\n '\n \ndiff --git a/t/perf/p1451-fsck-skip-list.sh b/t/perf/p1451-fsck-skip-list.sh\nindex c2b97d2487..f767d834f2 100755\n--- a/t/perf/p1451-fsck-skip-list.sh\n+++ b/t/perf/p1451-fsck-skip-list.sh\n@@ -15,7 +15,7 @@ test_expect_success \"setup $n bad commits\" '\n \t\techo \"committer C <c@example.com> 1234567890 +0000\" &&\n \t\techo \"data <<EOF\" &&\n \t\techo \"$i.Q.\" &&\n-\t\techo \"EOF\"\n+\t\techo \"EOF\" || return 1\n \tdone | q_to_nul | git fast-import\n '\n \ndiff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh\nindex 43d5a34e8c..e6b0277729 100755\n--- a/t/perf/p3400-rebase.sh\n+++ b/t/perf/p3400-rebase.sh\n@@ -22,7 +22,7 @@ test_expect_success 'setup rebasing on top of a lot of changes' '\n \t\tgit add unrelated-file$i &&\n \t\ttest_tick &&\n \t\tgit commit -m commit$i-reverse unrelated-file$i ||\n-\t\tbreak\n+\t\treturn 1\n \tdone &&\n \tgit checkout to-rebase &&\n \ttest_commit our-patch interesting-file\ndiff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh\nindex 654cd9c86e..c16f6a3ff6 100755\n--- a/t/perf/p5302-pack-index.sh\n+++ b/t/perf/p5302-pack-index.sh\n@@ -22,7 +22,7 @@ test_expect_success 'set up thread-counting tests' '\n \twhile test $t -gt 0\n \tdo\n \t\tthreads=\"$t $threads\" &&\n-\t\tt=$((t / 2))\n+\t\tt=$((t / 2)) || return 1\n \tdone\n '\n \ndiff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh\nindex 58213fe171..af173a7b73 100755\n--- a/t/perf/p5303-many-packs.sh\n+++ b/t/perf/p5303-many-packs.sh\n@@ -130,7 +130,7 @@ test_expect_success 'generate lots of packs' '\n \t\techo \"data <<EOF\" &&\n \t\techo \"blob $i\" &&\n \t\techo \"EOF\" &&\n-\t\techo \"checkpoint\"\n+\t\techo \"checkpoint\" || return 1\n \tdone |\n \tgit -c fastimport.unpackLimit=0 fast-import\n '\ndiff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh\nindex 5eb5044a10..c8be58f3c7 100755\n--- a/t/perf/p7519-fsmonitor.sh\n+++ b/t/perf/p7519-fsmonitor.sh\n@@ -119,10 +119,10 @@ test_expect_success \"one time repo setup\" '\n \tfi &&\n \n \tmkdir 1_file 10_files 100_files 1000_files 10000_files &&\n-\tfor i in $(test_seq 1 10); do touch 10_files/$i; done &&\n-\tfor i in $(test_seq 1 100); do touch 100_files/$i; done &&\n-\tfor i in $(test_seq 1 1000); do touch 1000_files/$i; done &&\n-\tfor i in $(test_seq 1 10000); do touch 10000_files/$i; done &&\n+\tfor i in $(test_seq 1 10); do touch 10_files/$i || return 1; done &&\n+\tfor i in $(test_seq 1 100); do touch 100_files/$i || return 1; done &&\n+\tfor i in $(test_seq 1 1000); do touch 1000_files/$i || return 1; done &&\n+\tfor i in $(test_seq 1 10000); do touch 10000_files/$i || return 1; done &&\n \tgit add 1_file 10_files 100_files 1000_files 10000_files &&\n \tgit commit -qm \"Add files\" &&\n \ndiff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh\nindex 42d2314804..5575dade8e 100755\n--- a/t/t0008-ignores.sh\n+++ b/t/t0008-ignores.sh\n@@ -200,7 +200,7 @@ test_expect_success 'setup' '\n \tdo\n \t\t: >$dir/not-ignored &&\n \t\t: >$dir/ignored-and-untracked &&\n-\t\t: >$dir/ignored-but-in-index\n+\t\t: >$dir/ignored-but-in-index || return 1\n \tdone &&\n \tgit add -f ignored-but-in-index a/ignored-but-in-index &&\n \tcat <<-\\EOF >a/.gitignore &&\ndiff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh\nindex e094975b13..1cb6aa6824 100755\n--- a/t/t0011-hashmap.sh\n+++ b/t/t0011-hashmap.sh\n@@ -220,7 +220,7 @@ test_expect_success 'grow / shrink' '\n \tfor n in $(test_seq 51)\n \tdo\n \t\techo put key$n value$n >> in &&\n-\t\techo NULL >> expect\n+\t\techo NULL >> expect || return 1\n \tdone &&\n \techo size >> in &&\n \techo 64 51 >> expect &&\n@@ -231,7 +231,7 @@ test_expect_success 'grow / shrink' '\n \tfor n in $(test_seq 12)\n \tdo\n \t\techo remove key$n >> in &&\n-\t\techo value$n >> expect\n+\t\techo value$n >> expect || return 1\n \tdone &&\n \techo size >> in &&\n \techo 256 40 >> expect &&\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex bb3de2701a..bad37abad2 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -285,7 +285,7 @@ test_expect_success 'required filter with absent smudge field' '\n test_expect_success 'filtering large input to small output should use little memory' '\n \ttest_config filter.devnull.clean \"cat >/dev/null\" &&\n \ttest_config filter.devnull.required true &&\n-\tfor i in $(test_seq 1 30); do printf \"%1048576d\" 1; done >30MB &&\n+\tfor i in $(test_seq 1 30); do printf \"%1048576d\" 1 || return 1; done >30MB &&\n \techo \"30MB filter=devnull\" >.gitattributes &&\n \tGIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB\n '\n@@ -303,7 +303,7 @@ test_expect_success 'filter that does not read is fine' '\n test_expect_success EXPENSIVE 'filter large file' '\n \ttest_config filter.largefile.smudge cat &&\n \ttest_config filter.largefile.clean cat &&\n-\tfor i in $(test_seq 1 2048); do printf \"%1048576d\" 1; done >2GB &&\n+\tfor i in $(test_seq 1 2048); do printf \"%1048576d\" 1 || return 1; done >2GB &&\n \techo \"2GB filter=largefile\" >.gitattributes &&\n \tgit add 2GB 2>err &&\n \ttest_must_be_empty err &&\n@@ -643,7 +643,7 @@ test_expect_success PERL 'required process filter should process multiple packet\n \t\tfor FILE in \"$TEST_ROOT\"/*.file\n \t\tdo\n \t\t\tcp \"$FILE\" . &&\n-\t\t\trot13.sh <\"$FILE\" >\"$FILE.rot13\"\n+\t\t\trot13.sh <\"$FILE\" >\"$FILE.rot13\" || return 1\n \t\tdone &&\n \n \t\techo \"*.file filter=protocol\" >.gitattributes &&\n@@ -682,7 +682,7 @@ test_expect_success PERL 'required process filter should process multiple packet\n \n \t\tfor FILE in *.file\n \t\tdo\n-\t\t\ttest_cmp_committed_rot13 \"$TEST_ROOT/$FILE\" $FILE\n+\t\t\ttest_cmp_committed_rot13 \"$TEST_ROOT/$FILE\" $FILE || return 1\n \t\tdone\n \t)\n '\ndiff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh\nindex 7e4ab1795f..5945973552 100755\n--- a/t/t0095-bloom.sh\n+++ b/t/t0095-bloom.sh\n@@ -84,7 +84,7 @@ test_expect_success 'get bloom filter for commit with 10 changes' '\n \tmkdir smallDir &&\n \tfor i in $(test_seq 0 9)\n \tdo\n-\t\techo $i >smallDir/$i\n+\t\techo $i >smallDir/$i || return 1\n \tdone &&\n \tgit add smallDir &&\n \tgit commit -m \"commit with 10 changes\" &&\n@@ -102,7 +102,7 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '\n \tmkdir bigDir &&\n \tfor i in $(test_seq 0 511)\n \tdo\n-\t\techo $i >bigDir/$i\n+\t\techo $i >bigDir/$i || return 1\n \tdone &&\n \tgit add bigDir &&\n \tgit commit -m \"commit with 513 changes\" &&\ndiff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh\nindex c76485b1b6..f17abd298c 100755\n--- a/t/t0410-partial-clone.sh\n+++ b/t/t0410-partial-clone.sh\n@@ -469,7 +469,7 @@ test_expect_success 'rev-list dies for missing objects on cmd line' '\n \t\tgit -C repo rev-list --ignore-missing --objects \\\n \t\t\t--exclude-promisor-objects \"$OBJ\" &&\n \t\tgit -C repo rev-list --ignore-missing --objects-edge-aggressive \\\n-\t\t\t--exclude-promisor-objects \"$OBJ\"\n+\t\t\t--exclude-promisor-objects \"$OBJ\" || return 1\n \tdone\n '\n \ndiff --git a/t/t1050-large.sh b/t/t1050-large.sh\nindex 0e4267c723..4f3aa17c99 100755\n--- a/t/t1050-large.sh\n+++ b/t/t1050-large.sh\n@@ -105,7 +105,7 @@ test_expect_success 'packsize limit' '\n \t\tcount=0 &&\n \t\tfor pi in .git/objects/pack/pack-*.idx\n \t\tdo\n-\t\t\ttest_path_is_file \"$pi\" && count=$(( $count + 1 ))\n+\t\t\ttest_path_is_file \"$pi\" && count=$(( $count + 1 )) || return 1\n \t\tdone &&\n \t\ttest $count = 2 &&\n \n@@ -118,7 +118,7 @@ test_expect_success 'packsize limit' '\n \n \t\tfor pi in .git/objects/pack/pack-*.idx\n \t\tdo\n-\t\t\tgit show-index <\"$pi\"\n+\t\t\tgit show-index <\"$pi\" || return 1\n \t\tdone |\n \t\tsed -e \"s/^[0-9]* \\([0-9a-f]*\\) .*/\\1/\" |\n \t\tsort >actual &&\ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 272ba1b566..5d53dbfbde 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -586,7 +586,7 @@ test_expect_success 'pattern-checks: contained glob characters' '\n \t\t!/*/\n \t\tsomething$c-else/\n \t\tEOF\n-\t\tcheck_read_tree_errors repo \"a\" \"disabling cone pattern matching\"\n+\t\tcheck_read_tree_errors repo \"a\" \"disabling cone pattern matching\" || return 1\n \tdone\n '\n \ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 516dd8bfa8..50ee5dc828 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -718,7 +718,7 @@ test_expect_success bool '\n \tfor i in 1 2 3 4\n \tdo\n \t    git config --bool --get bool.true$i >>result &&\n-\t    git config --bool --get bool.false$i >>result\n+\t    git config --bool --get bool.false$i >>result || return 1\n \tdone &&\n \ttest_cmp expect result'\n \ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 0d4f73acaa..bd58c6c897 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -1368,7 +1368,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches\n (\n \tfor i in $(test_seq 33)\n \tdo\n-\t\techo \"create refs/heads/$i HEAD\"\n+\t\techo \"create refs/heads/$i HEAD\" || exit 1\n \tdone >large_input &&\n \trun_with_limited_open_files git update-ref --stdin <large_input &&\n \tgit rev-parse --verify -q refs/heads/33\n@@ -1379,7 +1379,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches\n (\n \tfor i in $(test_seq 33)\n \tdo\n-\t\techo \"delete refs/heads/$i HEAD\"\n+\t\techo \"delete refs/heads/$i HEAD\" || exit 1\n \tdone >large_input &&\n \trun_with_limited_open_files git update-ref --stdin <large_input &&\n \ttest_must_fail git rev-parse --verify -q refs/heads/33\ndiff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\nindex bbc01aae34..405da58b75 100755\n--- a/t/t1403-show-ref.sh\n+++ b/t/t1403-show-ref.sh\n@@ -123,14 +123,14 @@ test_expect_success 'show-ref -d' '\n test_expect_success 'show-ref --heads, --tags, --head, pattern' '\n \tfor branch in B main side\n \tdo\n-\t\techo $(git rev-parse refs/heads/$branch) refs/heads/$branch\n+\t\techo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1\n \tdone >expect.heads &&\n \tgit show-ref --heads >actual &&\n \ttest_cmp expect.heads actual &&\n \n \tfor tag in A B C\n \tdo\n-\t\techo $(git rev-parse refs/tags/$tag) refs/tags/$tag\n+\t\techo $(git rev-parse refs/tags/$tag) refs/tags/$tag || return 1\n \tdone >expect.tags &&\n \tgit show-ref --tags >actual &&\n \ttest_cmp expect.tags actual &&\ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex d42f067ff8..d7ddf7612d 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -349,12 +349,12 @@ test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '\n \t\tprintf \"$zf%02d $zf%02d %s\\t\" $i $(($i+1)) \"$ident\" &&\n \t\tif test $i = 75; then\n \t\t\tfor j in $(test_seq 1 89); do\n-\t\t\t\tprintf X\n+\t\t\t\tprintf X || return 1\n \t\t\tdone\n \t\telse\n \t\t\tprintf X\n \t\tfi &&\n-\t\tprintf \"\\n\"\n+\t\tprintf \"\\n\" || return 1\n \tdone >.git/logs/refs/heads/reflogskip &&\n \tgit rev-parse reflogskip@{73} >actual &&\n \techo ${zf}03 >expect &&\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 15188a408b..b0119bf8bc 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -381,7 +381,7 @@ test_expect_success 'ambiguous commits are printed by type first, then hash orde\n \tdo\n \t\tgrep $type objects >$type.objects &&\n \t\tsort $type.objects >$type.objects.sorted &&\n-\t\ttest_cmp $type.objects.sorted $type.objects\n+\t\ttest_cmp $type.objects.sorted $type.objects || return 1\n \tdone\n '\n \ndiff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh\nindex 9bb503a975..b16d69ca4a 100755\n--- a/t/t2004-checkout-cache-temp.sh\n+++ b/t/t2004-checkout-cache-temp.sh\n@@ -57,7 +57,7 @@ test_expect_success 'checkout all stage 0 to temporary files' '\n \t\ttest $(grep $f actual | cut \"-d\t\" -f2) = $f &&\n \t\tp=$(grep $f actual | cut \"-d\t\" -f1) &&\n \t\ttest -f $p &&\n-\t\ttest $(cat $p) = tree1$f\n+\t\ttest $(cat $p) = tree1$f || return 1\n \tdone\n '\n \n@@ -85,7 +85,7 @@ test_expect_success 'checkout all stage 2 to temporary files' '\n \t\ttest $(grep $f actual | cut \"-d\t\" -f2) = $f &&\n \t\tp=$(grep $f actual | cut \"-d\t\" -f1) &&\n \t\ttest -f $p &&\n-\t\ttest $(cat $p) = tree2$f\n+\t\ttest $(cat $p) = tree2$f || return 1\n \tdone\n '\n \ndiff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh\nindex 0e7d47ab31..42601d5a31 100755\n--- a/t/t2012-checkout-last.sh\n+++ b/t/t2012-checkout-last.sh\n@@ -49,14 +49,14 @@ test_expect_success '\"checkout -\" detaches again' '\n test_expect_success 'more switches' '\n \tfor i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1\n \tdo\n-\t\tgit checkout -b branch$i\n+\t\tgit checkout -b branch$i || return 1\n \tdone\n '\n \n more_switches () {\n \tfor i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1\n \tdo\n-\t\tgit checkout branch$i\n+\t\tgit checkout branch$i || return 1\n \tdone\n }\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex d2ef0041f9..6993fcbe7a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -152,7 +152,7 @@ test_expect_success 'add -u resolves unmerged paths' '\n \t\tdo\n \t\t\techo \"100644 $one 1\t$path\" &&\n \t\t\techo \"100644 $two 2\t$path\" &&\n-\t\t\techo \"100644 $three 3\t$path\"\n+\t\t\techo \"100644 $three 3\t$path\" || return 1\n \t\tdone &&\n \t\techo \"100644 $one 1\tpath3\" &&\n \t\techo \"100644 $one 1\tpath4\" &&\ndiff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh\nindex cf0175ad6e..db7ca55998 100755\n--- a/t/t2203-add-intent.sh\n+++ b/t/t2203-add-intent.sh\n@@ -116,7 +116,7 @@ test_expect_success 'cache-tree does not ignore dir that has i-t-a entries' '\n \t\tmkdir 2 &&\n \t\tfor f in 1 2/1 2/2 3\n \t\tdo\n-\t\t\techo \"$f\" >\"$f\"\n+\t\t\techo \"$f\" >\"$f\" || return 1\n \t\tdone &&\n \t\tgit add 1 2/2 3 &&\n \t\tgit add -N 2/1 &&\ndiff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh\nindex ad9902a06b..1e54a33103 100755\n--- a/t/t3202-show-branch.sh\n+++ b/t/t3202-show-branch.sh\n@@ -9,7 +9,7 @@ test_expect_success 'setup' '\n \tfor i in $(test_seq 1 10)\n \tdo\n \t\tgit checkout -b branch$i initial &&\n-\t\ttest_commit --no-tag branch$i\n+\t\ttest_commit --no-tag branch$i || return 1\n \tdone &&\n \tgit for-each-ref \\\n \t\t--sort=version:refname \\\n@@ -49,7 +49,7 @@ test_expect_success 'show-branch with more than 8 branches' '\n test_expect_success 'show-branch with showbranch.default' '\n \tfor branch in $(cat branches.sorted)\n \tdo\n-\t\ttest_config showbranch.default $branch --add\n+\t\ttest_config showbranch.default $branch --add || return 1\n \tdone &&\n \tgit show-branch >actual &&\n \ttest_cmp expect actual\n@@ -124,7 +124,7 @@ test_expect_success 'show branch --merge-base with one argument' '\n \tdo\n \t\tgit rev-parse $branch >expect &&\n \t\tgit show-branch --merge-base $branch >actual &&\n-\t\ttest_cmp expect actual\n+\t\ttest_cmp expect actual || return 1\n \tdone\n '\n \n@@ -133,7 +133,7 @@ test_expect_success 'show branch --merge-base with two arguments' '\n \tdo\n \t\tgit rev-parse initial >expect &&\n \t\tgit show-branch --merge-base initial $branch >actual &&\n-\t\ttest_cmp expect actual\n+\t\ttest_cmp expect actual || return 1\n \tdone\n '\n \ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex d47ce00f69..7ce056e617 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -30,7 +30,7 @@ verify_notes () {\n \twhile [ $i -gt 0 ]; do\n \t\techo \"    commit #$i\" &&\n \t\techo \"    note for commit #$i\" &&\n-\t\ti=$(($i-1));\n+\t\ti=$(($i-1)) || return 1\n \tdone > expect &&\n \ttest_cmp expect output\n }\n@@ -42,7 +42,7 @@ test_expect_success \"setup: create $number_of_commits commits\" '\n \t\twhile [ $nr -lt $number_of_commits ]; do\n \t\t\tnr=$(($nr+1)) &&\n \t\t\ttest_tick &&\n-\t\t\tcat <<INPUT_END\n+\t\t\tcat <<INPUT_END || return 1\n commit refs/heads/main\n committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n data <<COMMIT\n@@ -178,7 +178,7 @@ verify_concatenated_notes () {\n \t\techo \"    first note for commit #$i\" &&\n \t\techo \"    \" &&\n \t\techo \"    second note for commit #$i\" &&\n-\t\ti=$(($i-1));\n+\t\ti=$(($i-1)) || return 1\n \tdone > expect &&\n \ttest_cmp expect output\n }\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 94c1b02251..836b6f22bf 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -57,7 +57,7 @@ test_expect_success 'many notes created correctly with git-notes' '\n \tdo\n \t\techo \"    commit #$i\" &&\n \t\techo \"    note #$i\" &&\n-\t\ti=$(($i - 1));\n+\t\ti=$(($i - 1)) || return 1\n \tdone > expect &&\n \ttest_cmp expect output\n '\n@@ -106,7 +106,7 @@ test_expect_success 'most notes deleted correctly with git-notes' '\n \tdo\n \t\techo \"    commit #$i\" &&\n \t\techo \"    note #$i\" &&\n-\t\ti=$(($i - 1));\n+\t\ti=$(($i - 1)) || return 1\n \tdone > expect &&\n \ttest_cmp expect output\n '\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 12eb226957..f6a9890d66 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -826,7 +826,7 @@ test_expect_success 'always cherry-pick with --no-ff' '\n \tdo\n \t\ttest ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&\n \t\tgit diff HEAD~$p original-no-ff-branch~$p > out &&\n-\t\ttest_must_be_empty out\n+\t\ttest_must_be_empty out || return 1\n \tdone &&\n \ttest_cmp_rev HEAD~3 original-no-ff-branch~3 &&\n \tgit diff HEAD~3 original-no-ff-branch~3 > out &&\n@@ -1341,7 +1341,7 @@ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '\n \t\ttest_seq 5 | sed \"s/$double/&&/\" >seq &&\n \t\tgit add seq &&\n \t\ttest_tick &&\n-\t\tgit commit -m seq-$double\n+\t\tgit commit -m seq-$double || return 1\n \tdone &&\n \tgit tag seq-onto &&\n \tgit reset --hard HEAD~2 &&\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex 4b5b607673..8617efaaf1 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -19,7 +19,7 @@ test_expect_success setup '\n \n \tfor l in a b c d e f g h i j k l m n o\n \tdo\n-\t\techo $l$l$l$l$l$l$l$l$l\n+\t\techo $l$l$l$l$l$l$l$l$l || return 1\n \tdone >oops &&\n \n \ttest_tick &&\ndiff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh\nindex e8375d1c97..2d53ce754c 100755\n--- a/t/t3508-cherry-pick-many-commits.sh\n+++ b/t/t3508-cherry-pick-many-commits.sh\n@@ -29,7 +29,7 @@ test_expect_success setup '\n \t\tgit add file1 &&\n \t\ttest_tick &&\n \t\tgit commit -m \"$val\" &&\n-\t\tgit tag $val\n+\t\tgit tag $val || return 1\n \tdone\n '\n \ndiff --git a/t/t3700-add.sh b/t/t3700-add.sh\nindex 6902807ff8..b1f90ba325 100755\n--- a/t/t3700-add.sh\n+++ b/t/t3700-add.sh\n@@ -143,7 +143,7 @@ test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries'\n \tdo\n \t\techo $s > stage$s &&\n \t\techo \"100755 $(git hash-object -w stage$s) $s\tfile\" &&\n-\t\techo \"120000 $(printf $s | git hash-object -w -t blob --stdin) $s\tsymlink\"\n+\t\techo \"120000 $(printf $s | git hash-object -w -t blob --stdin) $s\tsymlink\" || return 1\n \tdone | git update-index --index-info &&\n \tgit config core.filemode 0 &&\n \tgit config core.symlinks 0 &&\ndiff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh\nindex a8ad5462d9..0276edbe3d 100755\n--- a/t/t3920-crlf-messages.sh\n+++ b/t/t3920-crlf-messages.sh\n@@ -70,7 +70,7 @@ test_crlf_subject_body_and_contents() {\n \t\t\tfor ref in ${LIB_CRLF_BRANCHES}\n \t\t\tdo\n \t\t\t\tcat .crlf-${file}-\\\"\\${ref}\\\".txt >>expect &&\n-\t\t\t\tprintf \\\"\\n\\\" >>expect\n+\t\t\t\tprintf \\\"\\n\\\" >>expect || return 1\n \t\t\tdone &&\n \t\t\tgit $command_and_args --format=\\\"%${atom}\\\" >actual &&\n \t\t\ttest_cmp expect actual\n@@ -90,7 +90,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '\n \tdo\n \t\tprintf \"  \" >>expect &&\n \t\tcat .crlf-subject-${branch}.txt >>expect &&\n-\t\tprintf \"\\n\" >>expect\n+\t\tprintf \"\\n\" >>expect || return 1\n \tdone &&\n \tgit branch -v >tmp &&\n \t# Remove first two columns, and the line for the currently checked out branch\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443597","messageId":"20211209051115.52629-16-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 15/19] tests: simplify by dropping unnecessary `for` loops","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:11Z","receivedAt":"2021-12-09T05:12:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Rather than manually looping over a set of items and plugging those\nitems into a template string which is printed repeatedly, achieve the\nsame effect by taking advantage of `printf` which loops over its\narguments automatically.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t3005-ls-files-relative.sh      | 10 ++--------\n t/t3600-rm.sh                     |  5 +----\n t/t4025-hunk-header.sh            | 10 ++--------\n t/t4125-apply-ws-fuzz.sh          |  5 +----\n t/t6416-recursive-corner-cases.sh | 30 ++++++------------------------\n t/t7110-reset-merge.sh            |  2 +-\n t/t9400-git-cvsserver-server.sh   |  2 +-\n 7 files changed, 14 insertions(+), 50 deletions(-)\n\ndiff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh\nindex 6ba8b589cd..fbfa210a50 100755\n--- a/t/t3005-ls-files-relative.sh\n+++ b/t/t3005-ls-files-relative.sh\n@@ -39,10 +39,7 @@ test_expect_success 'ls-files with mixed levels' '\n test_expect_success 'ls-files -c' '\n \t(\n \t\tcd top/sub &&\n-\t\tfor f in ../y*\n-\t\tdo\n-\t\t\techo \"error: pathspec $SQ$f$SQ did not match any file(s) known to git\"\n-\t\tdone >expect.err &&\n+\t\tprintf \"error: pathspec $SQ%s$SQ did not match any file(s) known to git\\n\" ../y* >expect.err &&\n \t\techo \"Did you forget to ${SQ}git add${SQ}?\" >>expect.err &&\n \t\tls ../x* >expect.out &&\n \t\ttest_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&\n@@ -54,10 +51,7 @@ test_expect_success 'ls-files -c' '\n test_expect_success 'ls-files -o' '\n \t(\n \t\tcd top/sub &&\n-\t\tfor f in ../x*\n-\t\tdo\n-\t\t\techo \"error: pathspec $SQ$f$SQ did not match any file(s) known to git\"\n-\t\tdone >expect.err &&\n+\t\tprintf \"error: pathspec $SQ%s$SQ did not match any file(s) known to git\\n\" ../x* >expect.err &&\n \t\techo \"Did you forget to ${SQ}git add${SQ}?\" >>expect.err &&\n \t\tls ../y* >expect.out &&\n \t\ttest_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex ed3952eb98..e74a318ac3 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -274,10 +274,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '\n \tgit reset -q --hard &&\n \ttest_when_finished \"rm -f .git/index.lock msg && git reset -q --hard\" &&\n \tblob=$(echo blob | git hash-object -w --stdin) &&\n-\tfor stage in 1 2 3\n-\tdo\n-\t\techo \"100644 $blob $stage\tblob\"\n-\tdone | git update-index --index-info &&\n+\tprintf \"100644 $blob %d\\tblob\\n\" 1 2 3 | git update-index --index-info &&\n \tgit rm blob >msg 2>&1 &&\n \ttest_i18ngrep ! \"needs merge\" msg &&\n \ttest_must_fail git ls-files -s --error-unmatch blob\ndiff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh\nindex 6356961de4..5397cb7d42 100755\n--- a/t/t4025-hunk-header.sh\n+++ b/t/t4025-hunk-header.sh\n@@ -14,15 +14,9 @@ test_expect_success setup '\n \n \t(\n \t\techo \"A $NS\" &&\n-\t\tfor c in B C D E F G H I J K\n-\t\tdo\n-\t\t\techo \"  $c\"\n-\t\tdone &&\n+\t\tprintf \"  %s\\n\" B C D E F G H I J K &&\n \t\techo \"L  $NS\" &&\n-\t\tfor c in M N O P Q R S T U V\n-\t\tdo\n-\t\t\techo \"  $c\"\n-\t\tdone\n+\t\tprintf \"  %s\\n\" M N O P Q R S T U V\n \t) >file &&\n \tgit add file &&\n \ndiff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh\nindex 9671de7999..090987c89b 100755\n--- a/t/t4125-apply-ws-fuzz.sh\n+++ b/t/t4125-apply-ws-fuzz.sh\n@@ -10,10 +10,7 @@ test_expect_success setup '\n \tgit add file &&\n \n \t# file-0 is full of whitespace breakages\n-\tfor l in a bb c d eeee f ggg h\n-\tdo\n-\t\techo \"$l \"\n-\tdone >file-0 &&\n+\tprintf \"%s \\n\" a bb c d eeee f ggg h >file-0 &&\n \n \t# patch-0 creates a whitespace broken file\n \tcat file-0 >file &&\ndiff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh\nindex 84f5082366..690c8482b1 100755\n--- a/t/t6416-recursive-corner-cases.sh\n+++ b/t/t6416-recursive-corner-cases.sh\n@@ -24,14 +24,8 @@ test_expect_success 'setup basic criss-cross + rename with no modifications' '\n \t\tcd basic-rename &&\n \n \t\tten=\"0 1 2 3 4 5 6 7 8 9\" &&\n-\t\tfor i in $ten\n-\t\tdo\n-\t\t\techo line $i in a sample file\n-\t\tdone >one &&\n-\t\tfor i in $ten\n-\t\tdo\n-\t\t\techo line $i in another sample file\n-\t\tdone >two &&\n+\t\tprintf \"line %d in a sample file\\n\" $ten >one &&\n+\t\tprintf \"line %d in another sample file\\n\" $ten >two &&\n \t\tgit add one two &&\n \t\ttest_tick && git commit -m initial &&\n \n@@ -96,14 +90,8 @@ test_expect_success 'setup criss-cross + rename merges with basic modification'\n \t\tcd rename-modify &&\n \n \t\tten=\"0 1 2 3 4 5 6 7 8 9\" &&\n-\t\tfor i in $ten\n-\t\tdo\n-\t\t\techo line $i in a sample file\n-\t\tdone >one &&\n-\t\tfor i in $ten\n-\t\tdo\n-\t\t\techo line $i in another sample file\n-\t\tdone >two &&\n+\t\tprintf \"line %d in a sample file\\n\" $ten >one &&\n+\t\tprintf \"line %d in another sample file\\n\" $ten >two &&\n \t\tgit add one two &&\n \t\ttest_tick && git commit -m initial &&\n \n@@ -1588,10 +1576,7 @@ test_expect_success 'setup nested conflicts' '\n \t\tcd nested_conflicts &&\n \n \t\t# Create some related files now\n-\t\tfor i in $(test_seq 1 10)\n-\t\tdo\n-\t\t\techo Random base content line $i\n-\t\tdone >initial &&\n+\t\tprintf \"Random base content line %d\\n\" $(test_seq 1 10) >initial &&\n \n \t\tcp initial b_L1 &&\n \t\tcp initial b_R1 &&\n@@ -1777,10 +1762,7 @@ test_expect_success 'setup virtual merge base with nested conflicts' '\n \t\tcd virtual_merge_base_has_nested_conflicts &&\n \n \t\t# Create some related files now\n-\t\tfor i in $(test_seq 1 10)\n-\t\tdo\n-\t\t\techo Random base content line $i\n-\t\tdone >content &&\n+\t\tprintf \"Random base content line %d\\n\" $(test_seq 1 10) >content &&\n \n \t\t# Setup original commit\n \t\tgit add content &&\ndiff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh\nindex a82a07a04a..3d62e10b53 100755\n--- a/t/t7110-reset-merge.sh\n+++ b/t/t7110-reset-merge.sh\n@@ -8,7 +8,7 @@ test_description='Tests for \"git reset\" with \"--merge\" and \"--keep\" options'\n . ./test-lib.sh\n \n test_expect_success setup '\n-    for i in 1 2 3; do echo line $i; done >file1 &&\n+    printf \"line %d\\n\" 1 2 3 >file1 &&\n     cat file1 >file2 &&\n     git add file1 file2 &&\n     test_tick &&\ndiff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\nindex a6a73effde..a60fe2e19f 100755\n--- a/t/t9400-git-cvsserver-server.sh\n+++ b/t/t9400-git-cvsserver-server.sh\n@@ -591,7 +591,7 @@ test_expect_success 'cvs annotate' '\n     cd cvswork &&\n     GIT_CONFIG=\"$git_config\" cvs annotate merge >../out &&\n     sed -e \"s/ .*//\" ../out >../actual &&\n-    for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&\n+    printf \"1.%d\\n\" 3 1 1 1 1 1 1 1 2 4 >../expect &&\n     test_cmp ../expect ../actual\n '\n \n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443599","messageId":"20211209051115.52629-18-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 17/19] t4000-t4999: detect and signal failure within loop","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:13Z","receivedAt":"2021-12-09T05:12:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Failures within `for` and `while` loops can go unnoticed if not detected\nand signaled manually since the loop itself does not abort when a\ncontained command fails, nor will a failure necessarily be detected when\nthe loop finishes since the loop returns the exit code of the last\ncommand it ran on the final iteration, which may not be the command\nwhich failed. Therefore, detect and signal failures manually within\nloops using the idiom `|| return 1` (or `|| exit 1` within subshells).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t4001-diff-rename.sh          |  2 +-\n t/t4012-diff-binary.sh          |  2 +-\n t/t4014-format-patch.sh         |  6 +++---\n t/t4015-diff-whitespace.sh      |  4 ++--\n t/t4018-diff-funcname.sh        |  2 +-\n t/t4024-diff-optimize-common.sh |  2 +-\n t/t4038-diff-combined.sh        |  2 +-\n t/t4046-diff-unmerged.sh        |  8 ++++----\n t/t4049-diff-stat-count.sh      |  2 +-\n t/t4052-stat-output.sh          |  2 +-\n t/t4057-diff-combined-paths.sh  | 16 ++++++++--------\n t/t4124-apply-ws-rule.sh        |  4 ++--\n t/t4138-apply-ws-expansion.sh   | 16 ++++++++--------\n t/t4205-log-pretty-formats.sh   |  2 +-\n t/t4211-line-log.sh             |  2 +-\n t/t4216-log-bloom.sh            |  4 ++--\n 16 files changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh\nindex 68f2ebca58..3dc9047044 100755\n--- a/t/t4001-diff-rename.sh\n+++ b/t/t4001-diff-rename.sh\n@@ -174,7 +174,7 @@ test_expect_success 'setup for many rename source candidates' '\n \tdo\n \t\tfor j in 0 1 2 3 4 5 6 7 8 9;\n \t\tdo\n-\t\t\techo \"$i$j\" >\"path$i$j\"\n+\t\t\techo \"$i$j\" >\"path$i$j\" || return 1\n \t\tdone\n \tdone &&\n \tgit add \"path??\" &&\ndiff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh\nindex 33ff588ebc..b0f16ff5c0 100755\n--- a/t/t4012-diff-binary.sh\n+++ b/t/t4012-diff-binary.sh\n@@ -122,7 +122,7 @@ test_expect_success 'diff --stat with binary files and big change count' '\n \ti=0 &&\n \twhile test $i -lt 10000; do\n \t\techo $i &&\n-\t\ti=$(($i + 1))\n+\t\ti=$(($i + 1)) || return 1\n \tdone >textfile &&\n \tgit add textfile &&\n \tgit diff --cached --stat binfile textfile >output &&\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex eefe815fca..7dc5a5c736 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -325,7 +325,7 @@ test_expect_success 'filename length limit' '\n \t\tmax=$(\n \t\t\tfor patch in 000[1-9]-*.patch\n \t\t\tdo\n-\t\t\t\techo \"$patch\" | wc -c\n+\t\t\t\techo \"$patch\" | wc -c || exit 1\n \t\t\tdone |\n \t\t\tsort -nr |\n \t\t\thead -n 1\n@@ -343,7 +343,7 @@ test_expect_success 'filename length limit from config' '\n \t\tmax=$(\n \t\t\tfor patch in 000[1-9]-*.patch\n \t\t\tdo\n-\t\t\t\techo \"$patch\" | wc -c\n+\t\t\t\techo \"$patch\" | wc -c || exit 1\n \t\t\tdone |\n \t\t\tsort -nr |\n \t\t\thead -n 1\n@@ -361,7 +361,7 @@ test_expect_success 'filename limit applies only to basename' '\n \t\tmax=$(\n \t\t\tfor patch in patches/000[1-9]-*.patch\n \t\t\tdo\n-\t\t\t\techo \"${patch#patches/}\" | wc -c\n+\t\t\t\techo \"${patch#patches/}\" | wc -c || exit 1\n \t\t\tdone |\n \t\t\tsort -nr |\n \t\t\thead -n 1\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 2c13b62d3c..ca5adabe14 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -843,7 +843,7 @@ test_expect_success 'whitespace changes with modification reported (diffstat)' '\n \n test_expect_success 'whitespace-only changes reported across renames (diffstat)' '\n \tgit reset --hard &&\n-\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\"; done >x &&\n+\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\" || return 1; done >x &&\n \tgit add x &&\n \tgit commit -m \"base\" &&\n \tsed -e \"5s/^/ /\" x >z &&\n@@ -859,7 +859,7 @@ test_expect_success 'whitespace-only changes reported across renames (diffstat)'\n \n test_expect_success 'whitespace-only changes reported across renames' '\n \tgit reset --hard HEAD~1 &&\n-\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\"; done >x &&\n+\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\" || return 1; done >x &&\n \tgit add x &&\n \thash_x=$(git hash-object x) &&\n \tbefore=$(git rev-parse --short \"$hash_x\") &&\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 740696c8f7..42a2b9a13b 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -75,7 +75,7 @@ test_expect_success 'last regexp must not be negated' '\n test_expect_success 'setup hunk header tests' '\n \tfor i in $diffpatterns\n \tdo\n-\t\techo \"$i-* diff=$i\"\n+\t\techo \"$i-* diff=$i\" || return 1\n \tdone > .gitattributes &&\n \n \t# add all test files to the index\ndiff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh\nindex 6b44ce1493..b98ac0a0c0 100755\n--- a/t/t4024-diff-optimize-common.sh\n+++ b/t/t4024-diff-optimize-common.sh\n@@ -148,7 +148,7 @@ test_expect_success 'diff -U0' '\n \n \tfor n in $sample\n \tdo\n-\t\tgit diff -U0 file-?$n\n+\t\tgit diff -U0 file-?$n || return 1\n \tdone | zc >actual &&\n \ttest_cmp expect actual\n \ndiff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh\nindex aeac203c42..9a292bac70 100755\n--- a/t/t4038-diff-combined.sh\n+++ b/t/t4038-diff-combined.sh\n@@ -100,7 +100,7 @@ test_expect_success 'setup for --cc --raw' '\n \tfor i in $(test_seq 1 40)\n \tdo\n \t\tblob=$(echo file$i | git hash-object --stdin -w) &&\n-\t\ttrees=\"$trees$(echo \"100644 blob $blob\tfile\" | git mktree)$LF\"\n+\t\ttrees=\"$trees$(echo \"100644 blob $blob\tfile\" | git mktree)$LF\" || return 1\n \tdone\n '\n \ndiff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh\nindex 8c3d48e257..1828a72e0b 100755\n--- a/t/t4046-diff-unmerged.sh\n+++ b/t/t4046-diff-unmerged.sh\n@@ -37,7 +37,7 @@ test_expect_success 'diff-files -0' '\n \tfor path in $paths\n \tdo\n \t\t>\"$path\" &&\n-\t\techo \":000000 100644 $ZERO_OID $ZERO_OID U\t$path\"\n+\t\techo \":000000 100644 $ZERO_OID $ZERO_OID U\t$path\" || return 1\n \tdone >diff-files-0.expect &&\n \tgit diff-files -0 >diff-files-0.actual &&\n \ttest_cmp diff-files-0.expect diff-files-0.actual\n@@ -50,7 +50,7 @@ test_expect_success 'diff-files -1' '\n \t\techo \":000000 100644 $ZERO_OID $ZERO_OID U\t$path\" &&\n \t\tcase \"$path\" in\n \t\tx??) echo \":100644 100644 $blob1 $ZERO_OID M\t$path\"\n-\t\tesac\n+\t\tesac || return 1\n \tdone >diff-files-1.expect &&\n \tgit diff-files -1 >diff-files-1.actual &&\n \ttest_cmp diff-files-1.expect diff-files-1.actual\n@@ -63,7 +63,7 @@ test_expect_success 'diff-files -2' '\n \t\techo \":000000 100644 $ZERO_OID $ZERO_OID U\t$path\" &&\n \t\tcase \"$path\" in\n \t\t?x?) echo \":100644 100644 $blob2 $ZERO_OID M\t$path\"\n-\t\tesac\n+\t\tesac || return 1\n \tdone >diff-files-2.expect &&\n \tgit diff-files -2 >diff-files-2.actual &&\n \ttest_cmp diff-files-2.expect diff-files-2.actual &&\n@@ -78,7 +78,7 @@ test_expect_success 'diff-files -3' '\n \t\techo \":000000 100644 $ZERO_OID $ZERO_OID U\t$path\" &&\n \t\tcase \"$path\" in\n \t\t??x) echo \":100644 100644 $blob3 $ZERO_OID M\t$path\"\n-\t\tesac\n+\t\tesac || return 1\n \tdone >diff-files-3.expect &&\n \tgit diff-files -3 >diff-files-3.actual &&\n \ttest_cmp diff-files-3.expect diff-files-3.actual\ndiff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh\nindex 53061b104e..e74baa0635 100755\n--- a/t/t4049-diff-stat-count.sh\n+++ b/t/t4049-diff-stat-count.sh\n@@ -51,7 +51,7 @@ test_expect_success 'exclude unmerged entries from total file count' '\n \tgit rm -f d &&\n \tfor stage in 1 2 3\n \tdo\n-\t\tsed -e \"s/ 0\ta/ $stage\td/\" x\n+\t\tsed -e \"s/ 0\ta/ $stage\td/\" x || return 1\n \tdone |\n \tgit update-index --index-info &&\n \techo d >d &&\ndiff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh\nindex 9eba436211..b5c281edaa 100755\n--- a/t/t4052-stat-output.sh\n+++ b/t/t4052-stat-output.sh\n@@ -101,7 +101,7 @@ test_expect_success 'preparation for big change tests' '\n \ti=0 &&\n \twhile test $i -lt 1000\n \tdo\n-\t\techo $i && i=$(($i + 1))\n+\t\techo $i && i=$(($i + 1)) || return 1\n \tdone >abcd &&\n \tgit commit -m message abcd\n '\ndiff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh\nindex 7e5b74f72e..04b8a1542a 100755\n--- a/t/t4057-diff-combined-paths.sh\n+++ b/t/t4057-diff-combined-paths.sh\n@@ -18,13 +18,13 @@ test_expect_success 'trivial merge - combine-diff empty' '\n \tfor i in $(test_seq 1 9)\n \tdo\n \t\techo $i >$i.txt &&\n-\t\tgit add $i.txt\n+\t\tgit add $i.txt || return 1\n \tdone &&\n \tgit commit -m \"init\" &&\n \tgit checkout -b side &&\n \tfor i in $(test_seq 2 9)\n \tdo\n-\t\techo $i/2 >>$i.txt\n+\t\techo $i/2 >>$i.txt || return 1\n \tdone &&\n \tgit commit -a -m \"side 2-9\" &&\n \tgit checkout main &&\n@@ -40,14 +40,14 @@ test_expect_success 'only one truly conflicting path' '\n \tgit checkout side &&\n \tfor i in $(test_seq 2 9)\n \tdo\n-\t\techo $i/3 >>$i.txt\n+\t\techo $i/3 >>$i.txt || return 1\n \tdone &&\n \techo \"4side\" >>4.txt &&\n \tgit commit -a -m \"side 2-9 +4\" &&\n \tgit checkout main &&\n \tfor i in $(test_seq 1 9)\n \tdo\n-\t\techo $i/3 >>$i.txt\n+\t\techo $i/3 >>$i.txt || return 1\n \tdone &&\n \techo \"4main\" >>4.txt &&\n \tgit commit -a -m \"main 1-9 +4\" &&\n@@ -69,13 +69,13 @@ test_expect_success 'merge introduces new file' '\n \tgit checkout side &&\n \tfor i in $(test_seq 5 9)\n \tdo\n-\t\techo $i/4 >>$i.txt\n+\t\techo $i/4 >>$i.txt || return 1\n \tdone &&\n \tgit commit -a -m \"side 5-9\" &&\n \tgit checkout main &&\n \tfor i in $(test_seq 1 3)\n \tdo\n-\t\techo $i/4 >>$i.txt\n+\t\techo $i/4 >>$i.txt || return 1\n \tdone &&\n \tgit commit -a -m \"main 1-3 +4hello\" &&\n \tgit merge side &&\n@@ -90,13 +90,13 @@ test_expect_success 'merge removed a file' '\n \tgit checkout side &&\n \tfor i in $(test_seq 5 9)\n \tdo\n-\t\techo $i/5 >>$i.txt\n+\t\techo $i/5 >>$i.txt || return 1\n \tdone &&\n \tgit commit -a -m \"side 5-9\" &&\n \tgit checkout main &&\n \tfor i in $(test_seq 1 3)\n \tdo\n-\t\techo $i/4 >>$i.txt\n+\t\techo $i/4 >>$i.txt || return 1\n \tdone &&\n \tgit commit -a -m \"main 1-3\" &&\n \tgit merge side &&\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex ec5c10d2a0..485c7d2d12 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -333,7 +333,7 @@ test_expect_success 'applying beyond EOF requires one non-blank context line' '\n \n test_expect_success 'tons of blanks at EOF should not apply' '\n \tfor i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do\n-\t\ttest_write_lines \"\" \"\" \"\" \"\"\n+\t\ttest_write_lines \"\" \"\" \"\" \"\" || return 1\n \tdone >one &&\n \tgit add one &&\n \techo a >>one &&\n@@ -396,7 +396,7 @@ test_expect_success 'shrink file with tons of missing blanks at end of file' '\n \ttest_write_lines a b c >one &&\n \tcp one no-blank-lines &&\n \tfor i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do\n-\t\ttest_write_lines \"\" \"\" \"\" \"\"\n+\t\ttest_write_lines \"\" \"\" \"\" \"\" || return 1\n \tdone >>one &&\n \tgit add one &&\n \techo a >one &&\ndiff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh\nindex 4ba52bbb61..8bbf8260fa 100755\n--- a/t/t4138-apply-ws-expansion.sh\n+++ b/t/t4138-apply-ws-expansion.sh\n@@ -30,7 +30,7 @@ test_expect_success setup '\n \twhile test $x -lt $n\n \tdo\n \t\tprintf \"%63s%d\\n\" \"\" $x >>after &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tprintf \"\\t%s\\n\" d e f >>after &&\n \ttest_expect_code 1 git diff --no-index before after >patch2.patch.raw &&\n@@ -41,7 +41,7 @@ test_expect_success setup '\n \twhile test $x -lt $n\n \tdo\n \t\tprintf \"%63s%d\\n\" \"\" $x >>expect-2 &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tprintf \"%64s\\n\" d e f >>expect-2 &&\n \n@@ -53,7 +53,7 @@ test_expect_success setup '\n \twhile test $x -lt $n\n \tdo\n \t\tprintf \"%63s%02d\\n\" \"\" $x >>after &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tprintf \"\\t%s\\n\" d e f >>after &&\n \ttest_expect_code 1 git diff --no-index before after >patch3.patch.raw &&\n@@ -64,7 +64,7 @@ test_expect_success setup '\n \twhile test $x -lt $n\n \tdo\n \t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-3 &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tprintf \"%64s\\n\" d e f >>expect-3 &&\n \n@@ -74,7 +74,7 @@ test_expect_success setup '\n \twhile test $x -lt 50\n \tdo\n \t\tprintf \"\\t%02d\\n\" $x >>before &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tcat before >after &&\n \tprintf \"%64s\\n\" a b c >>after &&\n@@ -82,7 +82,7 @@ test_expect_success setup '\n \tdo\n \t\tprintf \"\\t%02d\\n\" $x >>before &&\n \t\tprintf \"\\t%02d\\n\" $x >>after &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \ttest_expect_code 1 git diff --no-index before after >patch4.patch.raw &&\n \tsed -e \"s/before/test-4/\" -e \"s/after/test-4/\" patch4.patch.raw >patch4.patch &&\n@@ -91,7 +91,7 @@ test_expect_success setup '\n \twhile test $x -lt 50\n \tdo\n \t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4 &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \tcat test-4 >expect-4 &&\n \tprintf \"%64s\\n\" a b c >>expect-4 &&\n@@ -99,7 +99,7 @@ test_expect_success setup '\n \tdo\n \t\tprintf \"%63s%02d\\n\" \"\" $x >>test-4 &&\n \t\tprintf \"%63s%02d\\n\" \"\" $x >>expect-4 &&\n-\t\tx=$(( $x + 1 ))\n+\t\tx=$(( $x + 1 )) || return 1\n \tdone &&\n \n \tgit config core.whitespace tab-in-indent,tabwidth=63 &&\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 5865daa8f8..f4ce7c59a4 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -976,7 +976,7 @@ test_expect_success '%(describe) vs git describe' '\n \t\telse\n \t\t\t: >expect-contains-bad\n \t\tfi &&\n-\t\techo \"$hash $desc\"\n+\t\techo \"$hash $desc\" || return 1\n \tdone >expect &&\n \ttest_path_exists expect-contains-good &&\n \ttest_path_exists expect-contains-bad &&\ndiff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh\nindex 560127cc07..ac9e4d0928 100755\n--- a/t/t4211-line-log.sh\n+++ b/t/t4211-line-log.sh\n@@ -137,7 +137,7 @@ test_expect_success 'range_set_union' '\n \ttest_seq 1000 > c.c &&\n \tgit add c.c &&\n \tgit commit -m \"modify many lines\" &&\n-\tgit log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)\n+\tgit log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c || return 1; done)\n '\n \n test_expect_success '-s shows only line-log commits' '\ndiff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh\nindex 50f206db55..41355f038b 100755\n--- a/t/t4216-log-bloom.sh\n+++ b/t/t4216-log-bloom.sh\n@@ -376,7 +376,7 @@ test_expect_success 'Bloom generation backfills empty commits' '\n \t\tcd empty &&\n \t\tfor i in $(test_seq 1 6)\n \t\tdo\n-\t\t\tgit commit --allow-empty -m \"$i\"\n+\t\t\tgit commit --allow-empty -m \"$i\" || return 1\n \t\tdone &&\n \n \t\t# Generate Bloom filters for empty commits 1-6, two at a time.\n@@ -389,7 +389,7 @@ test_expect_success 'Bloom generation backfills empty commits' '\n \t\t\ttest_filter_computed 2 trace.event &&\n \t\t\ttest_filter_not_computed 4 trace.event &&\n \t\t\ttest_filter_trunc_empty 2 trace.event &&\n-\t\t\ttest_filter_trunc_large 0 trace.event\n+\t\t\ttest_filter_trunc_large 0 trace.event || return 1\n \t\tdone &&\n \n \t\t# Finally, make sure that once all commits have filters, that\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443600","messageId":"20211209051115.52629-20-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 19/19] t6000-t9999: detect and signal failure within loop","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:15Z","receivedAt":"2021-12-09T05:12:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Failures within `for` and `while` loops can go unnoticed if not detected\nand signaled manually since the loop itself does not abort when a\ncontained command fails, nor will a failure necessarily be detected when\nthe loop finishes since the loop returns the exit code of the last\ncommand it ran on the final iteration, which may not be the command\nwhich failed. Therefore, detect and signal failures manually within\nloops using the idiom `|| return 1` (or `|| exit 1` within subshells).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n contrib/mw-to-git/t/t9365-continuing-queries.sh |  2 +-\n t/annotate-tests.sh                             |  2 +-\n t/t6005-rev-list-count.sh                       |  2 +-\n t/t6009-rev-list-parent.sh                      |  4 ++--\n t/t6101-rev-parse-parents.sh                    |  2 +-\n t/t6112-rev-list-filters-objects.sh             |  8 ++++----\n t/t6120-describe.sh                             |  6 +++---\n t/t6132-pathspec-exclude.sh                     |  2 +-\n t/t6200-fmt-merge-msg.sh                        |  2 +-\n t/t6300-for-each-ref.sh                         |  2 +-\n t/t6412-merge-large-rename.sh                   |  8 ++++----\n t/t6430-merge-recursive.sh                      |  2 +-\n t/t6600-test-reach.sh                           |  4 ++--\n t/t7004-tag.sh                                  |  2 +-\n t/t7505-prepare-commit-msg-hook.sh              |  2 +-\n t/t7600-merge.sh                                |  2 +-\n t/t7602-merge-octopus-many.sh                   |  2 +-\n t/t7603-merge-reduce-heads.sh                   |  4 ++--\n t/t7700-repack.sh                               |  2 +-\n t/t8014-blame-ignore-fuzzy.sh                   |  4 ++--\n t/t9104-git-svn-follow-parent.sh                |  4 ++--\n t/t9130-git-svn-authors-file.sh                 |  4 ++--\n t/t9134-git-svn-ignore-paths.sh                 | 16 ++++++++--------\n t/t9138-git-svn-authors-prog.sh                 |  2 +-\n t/t9146-git-svn-empty-dirs.sh                   |  4 ++--\n t/t9147-git-svn-include-paths.sh                | 16 ++++++++--------\n t/t9152-svn-empty-dirs-after-gc.sh              |  2 +-\n t/t9304-fast-import-marks.sh                    |  2 +-\n t/t9400-git-cvsserver-server.sh                 |  4 ++--\n t/t9800-git-p4-basic.sh                         |  2 +-\n t/t9818-git-p4-block.sh                         |  6 +++---\n t/t9902-completion.sh                           |  4 ++--\n 32 files changed, 65 insertions(+), 65 deletions(-)\n\ndiff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh\nindex 016454749f..d3e7312659 100755\n--- a/contrib/mw-to-git/t/t9365-continuing-queries.sh\n+++ b/contrib/mw-to-git/t/t9365-continuing-queries.sh\n@@ -12,7 +12,7 @@ test_expect_success 'creating page w/ >500 revisions' '\n \tfor i in $(test_seq 501)\n \tdo\n \t\techo \"creating revision $i\" &&\n-\t\twiki_editpage foo \"revision $i<br/>\" true\n+\t\twiki_editpage foo \"revision $i<br/>\" true || return 1\n \tdone\n '\n \ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex d3b299e75c..09e86f9ba0 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -161,7 +161,7 @@ test_expect_success 'blame huge graft' '\n \t\t\tGIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \\\n \t\t\tgit commit -a -m \"$i$j\" &&\n \t\t\tcommit=$(git rev-parse --verify HEAD) &&\n-\t\t\tgraft=\"$graft$commit \"\n+\t\t\tgraft=\"$graft$commit \" || return 1\n \t\tdone\n \tdone &&\n \tprintf \"%s \" $graft >.git/info/grafts &&\ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex 32b152095a..06d801032b 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -8,7 +8,7 @@ test_expect_success 'setup' '\n     for n in 1 2 3 4 5 ; do\n         echo $n > a &&\n         git add a &&\n-        git commit -m \"$n\"\n+        git commit -m \"$n\" || return 1\n     done\n '\n \ndiff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh\nindex dc8160aa45..5a67bbc760 100755\n--- a/t/t6009-rev-list-parent.sh\n+++ b/t/t6009-rev-list-parent.sh\n@@ -124,7 +124,7 @@ test_expect_success 'dodecapus' '\n \t\tgit checkout -b root$i five &&\n \t\ttest_commit $i &&\n \t\troots=\"$roots root$i\" ||\n-\t\treturn\n+\t\treturn 1\n \tdone &&\n \tgit checkout main &&\n \ttest_tick &&\n@@ -143,7 +143,7 @@ test_expect_success 'ancestors with the same commit time' '\n \ttest_tick_keep=$test_tick &&\n \tfor i in 1 2 3 4 5 6 7 8; do\n \t\ttest_tick=$test_tick_keep &&\n-\t\ttest_commit t$i\n+\t\ttest_commit t$i || return 1\n \tdone &&\n \tgit rev-list t1^! --not t$i >result &&\n \ttest_must_be_empty result\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex 78b5851780..c571fa5179 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -32,7 +32,7 @@ test_expect_success 'setup' '\n \t\ttest_tick &&\n \t\tgit commit --allow-empty -m \"$i\" &&\n \t\tcommit=$(git rev-parse --verify HEAD) &&\n-\t\tprintf \"$commit \" >>.git/info/grafts\n+\t\tprintf \"$commit \" >>.git/info/grafts || return 1\n \tdone\n '\n \ndiff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh\nindex 9848425192..8d9d6604f0 100755\n--- a/t/t6112-rev-list-filters-objects.sh\n+++ b/t/t6112-rev-list-filters-objects.sh\n@@ -18,7 +18,7 @@ test_expect_success 'setup r1' '\n \tdo\n \t\techo \"This is file: $n\" > r1/file.$n &&\n \t\tgit -C r1 add file.$n &&\n-\t\tgit -C r1 commit -m \"$n\"\n+\t\tgit -C r1 commit -m \"$n\" || return 1\n \tdone\n '\n \n@@ -75,7 +75,7 @@ test_expect_success 'setup r2' '\n \tdo\n \t\tprintf \"%\"$n\"s\" X > r2/large.$n &&\n \t\tgit -C r2 add large.$n &&\n-\t\tgit -C r2 commit -m \"$n\"\n+\t\tgit -C r2 commit -m \"$n\" || return 1\n \tdone\n '\n \n@@ -248,7 +248,7 @@ test_expect_success 'setup r3' '\n \t\techo \"This is file: $n\" > r3/$n &&\n \t\tgit -C r3 add $n &&\n \t\techo \"This is file: dir1/$n\" > r3/dir1/$n &&\n-\t\tgit -C r3 add dir1/$n\n+\t\tgit -C r3 add dir1/$n || return 1\n \tdone &&\n \tgit -C r3 commit -m \"sparse\" &&\n \techo dir1/ >pattern1 &&\n@@ -672,7 +672,7 @@ test_expect_success 'rev-list W/ --missing=print' '\n \n \tfor id in `cat expected | sed \"s|..|&/|\"`\n \tdo\n-\t\trm r1/.git/objects/$id\n+\t\trm r1/.git/objects/$id || return 1\n \tdone &&\n \n \tgit -C r1 rev-list --quiet --missing=print --objects HEAD >revs &&\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 9b2cc066f7..d8af2bb9d2 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -262,7 +262,7 @@ test_expect_success 'name-rev --all' '\n \t>expect.unsorted &&\n \tfor rev in $(git rev-list --all)\n \tdo\n-\t\tgit name-rev $rev >>expect.unsorted\n+\t\tgit name-rev $rev >>expect.unsorted || return 1\n \tdone &&\n \tsort <expect.unsorted >expect &&\n \tgit name-rev --all >actual.unsorted &&\n@@ -275,7 +275,7 @@ test_expect_success 'name-rev --stdin' '\n \tfor rev in $(git rev-list --all)\n \tdo\n \t\tname=$(git name-rev --name-only $rev) &&\n-\t\techo \"$rev ($name)\" >>expect.unsorted\n+\t\techo \"$rev ($name)\" >>expect.unsorted || return 1\n \tdone &&\n \tsort <expect.unsorted >expect &&\n \tgit rev-list --all | git name-rev --stdin >actual.unsorted &&\n@@ -395,7 +395,7 @@ EOF\" &&\n \t\tthen\n \t\t\techo \"from refs/heads/main^0\"\n \t\tfi &&\n-\t\ti=$(($i + 1))\n+\t\ti=$(($i + 1)) || return 1\n \tdone | git fast-import &&\n \tgit checkout main &&\n \tgit tag far-far-away HEAD^ &&\ndiff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh\nindex 30328b87f0..8ff1d76f79 100755\n--- a/t/t6132-pathspec-exclude.sh\n+++ b/t/t6132-pathspec-exclude.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup' '\n \t\tfi &&\n \t\t: >$p &&\n \t\tgit add $p &&\n-\t\tgit commit -m $p\n+\t\tgit commit -m $p || return 1\n \tdone &&\n \tgit log --oneline --format=%s >actual &&\n \tcat <<EOF >expect &&\ndiff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh\nindex 06c5fb5615..5cf7b7673e 100755\n--- a/t/t6200-fmt-merge-msg.sh\n+++ b/t/t6200-fmt-merge-msg.sh\n@@ -519,7 +519,7 @@ test_expect_success 'merge-msg lots of commits' '\n \t\twhile test $i -gt 9\n \t\tdo\n \t\t\techo \"  $i\" &&\n-\t\t\ti=$(($i-1))\n+\t\t\ti=$(($i-1)) || return 1\n \t\tdone &&\n \t\techo \"  ...\"\n \t} >expected &&\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex aae57908f8..abc8e1495a 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -1338,7 +1338,7 @@ test_expect_success ':remotename and :remoteref' '\n \t\t\techo \"${pair#*=}\" >expect &&\n \t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n \t\t\t\trefs/heads/main >actual &&\n-\t\t\ttest_cmp expect actual\n+\t\t\ttest_cmp expect actual || exit 1\n \t\tdone &&\n \t\tgit branch push-simple &&\n \t\tgit config branch.push-simple.pushRemote from &&\ndiff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh\nindex ed40801380..ca018d11f5 100755\n--- a/t/t6412-merge-large-rename.sh\n+++ b/t/t6412-merge-large-rename.sh\n@@ -37,18 +37,18 @@ test_rename() {\n \ttest_might_fail git branch -D test$n &&\n \tgit reset --hard initial &&\n \tfor i in $(count $n); do\n-\t\tmake_text $i initial initial >$i\n+\t\tmake_text $i initial initial >$i || return 1\n \tdone &&\n \tgit add . &&\n \tgit commit -m add=$n &&\n \tfor i in $(count $n); do\n-\t\tmake_text $i changed initial >$i\n+\t\tmake_text $i changed initial >$i || return 1\n \tdone &&\n \tgit commit -a -m change=$n &&\n \tgit checkout -b test$n HEAD^ &&\n \tfor i in $(count $n); do\n \t\tgit rm $i &&\n-\t\tmake_text $i initial changed >$i.moved\n+\t\tmake_text $i initial changed >$i.moved || return 1\n \tdone &&\n \tgit add . &&\n \tgit commit -m change+rename=$n &&\n@@ -79,7 +79,7 @@ test_expect_success 'setup large simple rename' '\n \n \tgit reset --hard initial &&\n \tfor i in $(count 200); do\n-\t\tmake_text foo bar baz >$i\n+\t\tmake_text foo bar baz >$i || return 1\n \tdone &&\n \tgit add . &&\n \tgit commit -m create-files &&\ndiff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh\nindex a0efe7cb6d..07067bb347 100755\n--- a/t/t6430-merge-recursive.sh\n+++ b/t/t6430-merge-recursive.sh\n@@ -706,7 +706,7 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '\n \t# more trees than static slots used by oid_to_hex()\n \tfor commit in $c0 $c2 $c4 $c5 $c6 $c7\n \tdo\n-\t\tgit rev-parse \"$commit^{tree}\"\n+\t\tgit rev-parse \"$commit^{tree}\" || return 1\n \tdone >trees &&\n \n \t# ignore the return code; it only fails because the input is weird...\ndiff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh\nindex 3d7a62ddab..338a9c46a2 100755\n--- a/t/t6600-test-reach.sh\n+++ b/t/t6600-test-reach.sh\n@@ -32,7 +32,7 @@ test_expect_success 'setup' '\n \tdo\n \t\ttest_commit \"1-$i\" &&\n \t\tgit branch -f commit-1-$i &&\n-\t\tgit tag -a -m \"1-$i\" tag-1-$i commit-1-$i\n+\t\tgit tag -a -m \"1-$i\" tag-1-$i commit-1-$i || return 1\n \tdone &&\n \tfor j in $(test_seq 1 9)\n \tdo\n@@ -46,7 +46,7 @@ test_expect_success 'setup' '\n \t\tdo\n \t\t\tgit merge commit-$j-$i -m \"$x-$i\" &&\n \t\t\tgit branch -f commit-$x-$i &&\n-\t\t\tgit tag -a -m \"$x-$i\" tag-$x-$i commit-$x-$i\n+\t\t\tgit tag -a -m \"$x-$i\" tag-$x-$i commit-$x-$i || return 1\n \t\tdone\n \tdone &&\n \tgit commit-graph write --reachable &&\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 660cde5a63..25a26563dd 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1981,7 +1981,7 @@ EOF\" &&\n \t\tthen\n \t\t\techo \"from refs/heads/main^0\"\n \t\tfi &&\n-\t\ti=$(($i + 1))\n+\t\ti=$(($i + 1)) || return 1\n \tdone | git fast-import &&\n \tgit checkout main &&\n \tgit tag far-far-away HEAD^ &&\ndiff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh\nindex 2a07c70867..e39c809ca4 100755\n--- a/t/t7505-prepare-commit-msg-hook.sh\n+++ b/t/t7505-prepare-commit-msg-hook.sh\n@@ -16,7 +16,7 @@ test_expect_success 'set up commits for rebasing' '\n \ttest_commit rebase-b b bb &&\n \tfor i in $(test_seq 1 13)\n \tdo\n-\t\ttest_commit rebase-$i c $i\n+\t\ttest_commit rebase-$i c $i || return 1\n \tdone &&\n \tgit checkout main &&\n \ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex c773e30b3f..f0f6fda150 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -967,7 +967,7 @@ test_expect_success 'set up mod-256 conflict scenario' '\n \t# 256 near-identical stanzas...\n \tfor i in $(test_seq 1 256); do\n \t\tfor j in 1 2 3 4 5; do\n-\t\t\techo $i-$j\n+\t\t\techo $i-$j || return 1\n \t\tdone\n \tdone >file &&\n \tgit add file &&\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex ddf64dc5f7..ff085b086c 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -30,7 +30,7 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n \twhile test $i -le 30\n \tdo\n \t\trefs=\"$refs c$i\" &&\n-\t\ti=$(expr $i + 1)\n+\t\ti=$(expr $i + 1) || return 1\n \tdone &&\n \tgit merge $refs &&\n \ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\ndiff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh\nindex 27cd94ad6f..4887ca705b 100755\n--- a/t/t7603-merge-reduce-heads.sh\n+++ b/t/t7603-merge-reduce-heads.sh\n@@ -95,7 +95,7 @@ test_expect_success 'setup' '\n \t\techo $i > $i.c &&\n \t\tgit add $i.c &&\n \t\tgit commit -m $i &&\n-\t\tgit tag $i\n+\t\tgit tag $i || return 1\n \tdone &&\n \tgit reset --hard A &&\n \tfor i in F G H I\n@@ -103,7 +103,7 @@ test_expect_success 'setup' '\n \t\techo $i > $i.c &&\n \t\tgit add $i.c &&\n \t\tgit commit -m $i &&\n-\t\tgit tag $i\n+\t\tgit tag $i || return 1\n \tdone\n '\n \ndiff --git a/t/t7700-repack.sh b/t/t7700-repack.sh\nindex 0260ad6f0e..4693f8dc2b 100755\n--- a/t/t7700-repack.sh\n+++ b/t/t7700-repack.sh\n@@ -117,7 +117,7 @@ test_expect_success 'packed obs in alternate ODB kept pack are repacked' '\n \t\t\trm alt_objects/pack/$base_name.keep\n \t\telse\n \t\t\ttouch alt_objects/pack/$base_name.keep\n-\t\tfi\n+\t\tfi || return 1\n \tdone &&\n \tgit repack -a -d &&\n \ttest_no_missing_in_packs\ndiff --git a/t/t8014-blame-ignore-fuzzy.sh b/t/t8014-blame-ignore-fuzzy.sh\nindex e68e6115a6..0bd0341301 100755\n--- a/t/t8014-blame-ignore-fuzzy.sh\n+++ b/t/t8014-blame-ignore-fuzzy.sh\n@@ -310,7 +310,7 @@ test_expect_success setup '\n \t\t\techo \"$line\" >>\"$i\" &&\n \t\t\tgit add \"$i\" &&\n \t\t\ttest_tick &&\n-\t\t\tGIT_AUTHOR_NAME=\"$line_count\" git commit -m \"$line_count\"\n+\t\t\tGIT_AUTHOR_NAME=\"$line_count\" git commit -m \"$line_count\" || return 1\n \t\tdone <\"a$i\"\n \tdone &&\n \n@@ -318,7 +318,7 @@ test_expect_success setup '\n \tdo\n \t\t# Overwrite the files with the final content.\n \t\tcp b$i $i &&\n-\t\tgit add $i\n+\t\tgit add $i || return 1\n \tdone &&\n \ttest_tick &&\n \ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex 67eed2fefc..c7d8e0bf00 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -117,7 +117,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '\n \tmkdir -p import/trunk/subversion/bindings/swig/perl/t &&\n \tfor i in a b c ; do \\\n \t  echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&\n-\t  echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \\\n+\t  echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t || return 1\n \tdone &&\n \t  echo \"bad delete test\" > \\\n \t   import/trunk/subversion/bindings/swig/perl/t/larger-parent &&\n@@ -134,7 +134,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '\n \t\tsvn mv t native/t &&\n \t\tfor i in a b c\n \t\tdo\n-\t\t\tsvn mv $i.pm native/$i.pm\n+\t\t\tsvn mv $i.pm native/$i.pm || return 1\n \t\tdone &&\n \t\techo z >>native/t/c.t &&\n \t\tpoke native/t/c.t &&\ndiff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh\nindex b4081fefba..90325db909 100755\n--- a/t/t9130-git-svn-authors-file.sh\n+++ b/t/t9130-git-svn-authors-file.sh\n@@ -15,7 +15,7 @@ EOF\n test_expect_success 'setup svnrepo' '\n \tfor i in aa bb cc dd\n \tdo\n-\t\tsvn_cmd mkdir -m $i --username $i \"$svnrepo\"/$i\n+\t\tsvn_cmd mkdir -m $i --username $i \"$svnrepo\"/$i || return 1\n \tdone\n \t'\n \n@@ -60,7 +60,7 @@ test_expect_success 'authors-file against globs' '\n \tfor i in bb ee cc\n \tdo\n \t\tbranch=\"aa/branches/$i\" &&\n-\t\tsvn_cmd mkdir -m \"$branch\" --username $i \"$svnrepo/$branch\"\n+\t\tsvn_cmd mkdir -m \"$branch\" --username $i \"$svnrepo/$branch\" || return 1\n \tdone\n \t'\n \ndiff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh\nindex fff49c4100..4a77eb9f60 100755\n--- a/t/t9134-git-svn-ignore-paths.sh\n+++ b/t/t9134-git-svn-ignore-paths.sh\n@@ -27,7 +27,7 @@ test_expect_success 'setup test repository' '\n test_expect_success 'clone an SVN repository with ignored www directory' '\n \tgit svn clone --ignore-paths=\"^www\" \"$svnrepo\" g &&\n \techo test_qqq > expect &&\n-\tfor i in g/*/*.txt; do cat $i >> expect2; done &&\n+\tfor i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&\n \ttest_cmp expect expect2\n '\n \n@@ -36,7 +36,7 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '\n \t( cd c && git svn fetch --ignore-paths=\"^www\" ) &&\n \trm expect2 &&\n \techo test_qqq > expect &&\n-\tfor i in c/*/*.txt; do cat $i >> expect2; done &&\n+\tfor i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&\n \ttest_cmp expect expect2\n '\n \n@@ -62,7 +62,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -73,7 +73,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '\n \t\tcd c &&\n \t\tgit svn rebase --ignore-paths=\"^www\" &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -94,7 +94,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -105,7 +105,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '\n \t\tcd c &&\n \t\tgit svn rebase --ignore-paths=\"^www\" &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -127,7 +127,7 @@ test_expect_success 'update git svn-cloned repo again (config ignore)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\nygg\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -138,7 +138,7 @@ test_expect_success 'update git svn-cloned repo again (option ignore)' '\n \t\tcd c &&\n \t\tgit svn rebase --ignore-paths=\"^www\" &&\n \t\tprintf \"test_qqq\\nb\\nygg\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\ndiff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh\nindex 027b416720..784ec7fc2d 100755\n--- a/t/t9138-git-svn-authors-prog.sh\n+++ b/t/t9138-git-svn-authors-prog.sh\n@@ -27,7 +27,7 @@ test_expect_success 'svn-authors setup' '\n test_expect_success 'setup svnrepo' '\n \tfor i in aa bb cc-sub dd-sub ee-foo ff\n \tdo\n-\t\tsvn mkdir -m $i --username $i \"$svnrepo\"/$i\n+\t\tsvn mkdir -m $i --username $i \"$svnrepo\"/$i || return 1\n \tdone\n '\n \ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 5f91c0d68b..80cb55fee7 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -8,7 +8,7 @@ test_description='git svn creates empty directories'\n test_expect_success 'initialize repo' '\n \tfor i in a b c d d/e d/e/f \"weird file name\"\n \tdo\n-\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\"\n+\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\" || return 1\n \tdone\n '\n \n@@ -102,7 +102,7 @@ test_expect_success 'git svn mkdirs -r works' '\n test_expect_success 'initialize trunk' '\n \tfor i in trunk trunk/a trunk/\"weird file name\"\n \tdo\n-\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\"\n+\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\" || return 1\n \tdone\n '\n \ndiff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh\nindex d292bf9f55..257fc8f2f8 100755\n--- a/t/t9147-git-svn-include-paths.sh\n+++ b/t/t9147-git-svn-include-paths.sh\n@@ -28,7 +28,7 @@ test_expect_success 'setup test repository' '\n test_expect_success 'clone an SVN repository with filter to include qqq directory' '\n \tgit svn clone --include-paths=\"qqq\" \"$svnrepo\" g &&\n \techo test_qqq > expect &&\n-\tfor i in g/*/*.txt; do cat $i >> expect2; done &&\n+\tfor i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&\n \ttest_cmp expect expect2\n '\n \n@@ -38,7 +38,7 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '\n \t( cd c && git svn fetch --include-paths=\"qqq\" ) &&\n \trm expect2 &&\n \techo test_qqq > expect &&\n-\tfor i in c/*/*.txt; do cat $i >> expect2; done &&\n+\tfor i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&\n \ttest_cmp expect expect2\n '\n \n@@ -64,7 +64,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -75,7 +75,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '\n \t\tcd c &&\n \t\tgit svn rebase --include-paths=\"qqq\" &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -96,7 +96,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -107,7 +107,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '\n \t\tcd c &&\n \t\tgit svn rebase --include-paths=\"qqq\" &&\n \t\tprintf \"test_qqq\\nb\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -129,7 +129,7 @@ test_expect_success 'update git svn-cloned repo again (config include)' '\n \t\tcd g &&\n \t\tgit svn rebase &&\n \t\tprintf \"test_qqq\\nb\\nygg\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\n@@ -140,7 +140,7 @@ test_expect_success 'update git svn-cloned repo again (option include)' '\n \t\tcd c &&\n \t\tgit svn rebase --include-paths=\"qqq\" &&\n \t\tprintf \"test_qqq\\nb\\nygg\\n\" > expect &&\n-\t\tfor i in */*.txt; do cat $i >> expect2; done &&\n+\t\tfor i in */*.txt; do cat $i >> expect2 || exit 1; done &&\n \t\ttest_cmp expect2 expect &&\n \t\trm expect expect2\n \t)\ndiff --git a/t/t9152-svn-empty-dirs-after-gc.sh b/t/t9152-svn-empty-dirs-after-gc.sh\nindex 89f285d082..a597c42f77 100755\n--- a/t/t9152-svn-empty-dirs-after-gc.sh\n+++ b/t/t9152-svn-empty-dirs-after-gc.sh\n@@ -8,7 +8,7 @@ test_description='git svn creates empty directories, calls git gc, makes sure th\n test_expect_success 'initialize repo' '\n \tfor i in a b c d d/e d/e/f \"weird file name\"\n \tdo\n-\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\"\n+\t\tsvn_cmd mkdir -m \"mkdir $i\" \"$svnrepo\"/\"$i\" || return 1\n \tdone\n '\n \ndiff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh\nindex d4359dba21..bed01c99ea 100755\n--- a/t/t9304-fast-import-marks.sh\n+++ b/t/t9304-fast-import-marks.sh\n@@ -16,7 +16,7 @@ test_expect_success 'setup large marks file' '\n \tblob=$(git rev-parse HEAD:one.t) &&\n \tfor i in $(test_seq 1024 16384)\n \tdo\n-\t\techo \":$i $blob\"\n+\t\techo \":$i $blob\" || return 1\n \tdone >>marks\n '\n \ndiff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\nindex a60fe2e19f..dd378df730 100755\n--- a/t/t9400-git-cvsserver-server.sh\n+++ b/t/t9400-git-cvsserver-server.sh\n@@ -338,7 +338,7 @@ test_expect_success 'cvs update (subdirectories)' \\\n   '(for dir in A A/B A/B/C A/D E; do\n       mkdir $dir &&\n       echo \"test file in $dir\" >\"$dir/file_in_$(echo $dir|sed -e \"s#/# #g\")\"  &&\n-      git add $dir\n+      git add $dir || exit 1\n    done) &&\n    git commit -q -m \"deep sub directory structure\" &&\n    git push gitcvs.git >/dev/null &&\n@@ -381,7 +381,7 @@ test_expect_success 'cvs update (merge)' \\\n    for i in 1 2 3 4 5 6 7\n    do\n      echo Line $i >>merge &&\n-     echo Line $i >>expected\n+     echo Line $i >>expected || return 1\n    done &&\n    echo Line 8 >>expected &&\n    git add merge &&\ndiff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh\nindex 81bc8e8da1..806005a793 100755\n--- a/t/t9800-git-p4-basic.sh\n+++ b/t/t9800-git-p4-basic.sh\n@@ -171,7 +171,7 @@ test_expect_success 'clone using non-numeric revision ranges' '\n \t\t\tcd \"$git\" &&\n \t\t\tgit ls-files >lines &&\n \t\t\ttest_line_count = 8 lines\n-\t\t)\n+\t\t) || return 1\n \tdone\n '\n \ndiff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh\nindex 0db7ab9918..de591d875c 100755\n--- a/t/t9818-git-p4-block.sh\n+++ b/t/t9818-git-p4-block.sh\n@@ -92,11 +92,11 @@ test_expect_success 'Add some more files' '\n \tfor i in $(test_seq 0 10)\n \tdo\n \t\tp4_add_file \"included/x$i\" &&\n-\t\tp4_add_file \"excluded/x$i\"\n+\t\tp4_add_file \"excluded/x$i\" || return 1\n \tdone &&\n \tfor i in $(test_seq 0 10)\n \tdo\n-\t\tp4_add_file \"excluded/y$i\"\n+\t\tp4_add_file \"excluded/y$i\" || return 1\n \tdone\n '\n \n@@ -123,7 +123,7 @@ test_expect_success 'Create a repo with multiple depot paths' '\n \tdo\n \t\tfor i in $(test_seq 1 10)\n \t\tdo\n-\t\t\tp4_add_file \"$p/file$p$i\"\n+\t\t\tp4_add_file \"$p/file$p$i\" || return 1\n \t\tdone\n \tdone\n '\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 518203fbe0..caa9d2dc9b 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -879,7 +879,7 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer\n \t\trefs/remotes/remote/branch-in-remote\n \tdo\n \t\tgit update-ref $remote_ref main &&\n-\t\ttest_when_finished \"git update-ref -d $remote_ref\"\n+\t\ttest_when_finished \"git update-ref -d $remote_ref\" || return 1\n \tdone &&\n \t(\n \t\tcur= &&\n@@ -1052,7 +1052,7 @@ test_expect_success '__git_refs - only matching refs - checkout DWIMery' '\n \t\trefs/remotes/remote/branch-in-remote\n \tdo\n \t\tgit update-ref $remote_ref main &&\n-\t\ttest_when_finished \"git update-ref -d $remote_ref\"\n+\t\ttest_when_finished \"git update-ref -d $remote_ref\" || return 1\n \tdone &&\n \t(\n \t\tcur=mat &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443601","messageId":"20211209051115.52629-19-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH 18/19] t5000-t5999: detect and signal failure within loop","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T05:11:14Z","receivedAt":"2021-12-09T05:12:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Failures within `for` and `while` loops can go unnoticed if not detected\nand signaled manually since the loop itself does not abort when a\ncontained command fails, nor will a failure necessarily be detected when\nthe loop finishes since the loop returns the exit code of the last\ncommand it ran on the final iteration, which may not be the command\nwhich failed. Therefore, detect and signal failures manually within\nloops using the idiom `|| return 1` (or `|| exit 1` within subshells).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n t/t5000-tar-tree.sh                    |  4 ++--\n t/t5003-archive-zip.sh                 |  2 +-\n t/t5004-archive-corner-cases.sh        |  6 +++---\n t/t5100-mailinfo.sh                    |  2 +-\n t/t5300-pack-object.sh                 |  6 +++---\n t/t5307-pack-missing-commit.sh         |  2 +-\n t/t5310-pack-bitmaps.sh                |  2 +-\n t/t5317-pack-objects-filter-objects.sh | 10 +++++-----\n t/t5318-commit-graph.sh                |  6 +++---\n t/t5319-multi-pack-index.sh            | 10 +++++-----\n t/t5322-pack-objects-sparse.sh         |  4 ++--\n t/t5325-reverse-index.sh               |  2 +-\n t/t5500-fetch-pack.sh                  |  8 ++++----\n t/t5502-quickfetch.sh                  |  2 +-\n t/t5510-fetch.sh                       |  2 +-\n t/t5515-fetch-merge-logic.sh           |  4 ++--\n t/t5552-skipping-fetch-negotiator.sh   | 10 +++++-----\n t/t5571-pre-push-hook.sh               |  2 +-\n t/t5616-partial-clone.sh               | 12 ++++++------\n t/t5702-protocol-v2.sh                 |  4 ++--\n 20 files changed, 50 insertions(+), 50 deletions(-)\n\ndiff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh\nindex 2c88d1c159..7f8d2ab0a7 100755\n--- a/t/t5000-tar-tree.sh\n+++ b/t/t5000-tar-tree.sh\n@@ -77,7 +77,7 @@ check_tar() {\n \t\t\t\t\tpath=$(get_pax_header $header path) &&\n \t\t\t\t\tif test -n \"$path\"\n \t\t\t\t\tthen\n-\t\t\t\t\t\tmv \"$data\" \"$path\"\n+\t\t\t\t\t\tmv \"$data\" \"$path\" || exit 1\n \t\t\t\t\tfi\n \t\t\t\tfi\n \t\t\tdone\n@@ -133,7 +133,7 @@ test_expect_success 'populate workdir' '\n \t\tfor depth in 1 2 3 4 5\n \t\tdo\n \t\t\tmkdir $p &&\n-\t\t\tcd $p\n+\t\t\tcd $p || exit 1\n \t\tdone &&\n \t\techo text >file_with_long_path\n \t) &&\ndiff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh\nindex 1e6d18b140..d726964307 100755\n--- a/t/t5003-archive-zip.sh\n+++ b/t/t5003-archive-zip.sh\n@@ -106,7 +106,7 @@ test_expect_success \\\n      printf \"A\\$Format:%s\\$O\" \"$SUBSTFORMAT\" >a/substfile1 &&\n      printf \"A not substituted O\" >a/substfile2 &&\n      (p=long_path_to_a_file && cd a &&\n-      for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&\n+      for depth in 1 2 3 4 5; do mkdir $p && cd $p || exit 1; done &&\n       echo text >file_with_long_path)\n '\n \ndiff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh\nindex 2d32d0ed12..ae508e2162 100755\n--- a/t/t5004-archive-corner-cases.sh\n+++ b/t/t5004-archive-corner-cases.sh\n@@ -131,7 +131,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '\n \tdo\n \t\tfor b in 0 1 2 3 4 5 6 7 8 9 a b c d e f\n \t\tdo\n-\t\t\t: >00/$a$b\n+\t\t\t: >00/$a$b || return 1\n \t\tdone\n \tdone &&\n \tgit add 00 &&\n@@ -143,7 +143,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '\n \tdo\n \t\tfor d in 0 1 2 3 4 5 6 7 8 9 a b c d e f\n \t\tdo\n-\t\t\techo \"040000 tree $subtree\t$c$d\"\n+\t\t\techo \"040000 tree $subtree\t$c$d\" || return 1\n \t\tdone\n \tdone >tree &&\n \ttree=$(git mktree <tree) &&\n@@ -171,7 +171,7 @@ test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \\\n \t# create tree containing 65500 entries of that blob\n \tfor i in $(test_seq 1 65500)\n \tdo\n-\t\techo \"100644 blob $blob\t$i\"\n+\t\techo \"100644 blob $blob\t$i\" || return 1\n \tdone >tree &&\n \ttree=$(git mktree <tree) &&\n \ndiff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh\nindex 141b29f031..cebad1048c 100755\n--- a/t/t5100-mailinfo.sh\n+++ b/t/t5100-mailinfo.sh\n@@ -122,7 +122,7 @@ test_expect_success 'mailinfo unescapes with --mboxrd' '\n \tdo\n \t\tgit mailinfo mboxrd/msg mboxrd/patch \\\n \t\t  <mboxrd/$i >mboxrd/out &&\n-\t\ttest_cmp \"$DATA/${i}mboxrd\" mboxrd/msg\n+\t\ttest_cmp \"$DATA/${i}mboxrd\" mboxrd/msg || return 1\n \tdone &&\n \tsp=\" \" &&\n \techo \"From \" >expect &&\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex f9877d42d7..2fd845187e 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -347,7 +347,7 @@ test_expect_success 'unpacking with --strict' '\n \t\tfor i in 0 1 2 3 4 5 6 7 8 9\n \t\tdo\n \t\t\to=$(echo $j$i | git hash-object -w --stdin) &&\n-\t\t\techo \"100644 $o\t0 $j$i\"\n+\t\t\techo \"100644 $o\t0 $j$i\" || return 1\n \t\tdone\n \tdone >LIST &&\n \trm -f .git/index &&\n@@ -390,7 +390,7 @@ test_expect_success 'index-pack with --strict' '\n \t\tfor i in 0 1 2 3 4 5 6 7 8 9\n \t\tdo\n \t\t\to=$(echo $j$i | git hash-object -w --stdin) &&\n-\t\t\techo \"100644 $o\t0 $j$i\"\n+\t\t\techo \"100644 $o\t0 $j$i\" || return 1\n \t\tdone\n \tdone >LIST &&\n \trm -f .git/index &&\n@@ -586,7 +586,7 @@ test_expect_success 'setup for --stdin-packs tests' '\n \t\tfor id in A B C\n \t\tdo\n \t\t\tgit pack-objects .git/objects/pack/pack-$id \\\n-\t\t\t\t--incremental --revs <<-EOF\n+\t\t\t\t--incremental --revs <<-EOF || exit 1\n \t\t\trefs/tags/$id\n \t\t\tEOF\n \t\tdone &&\ndiff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh\nindex f4338abb78..fa4bc269fe 100755\n--- a/t/t5307-pack-missing-commit.sh\n+++ b/t/t5307-pack-missing-commit.sh\n@@ -11,7 +11,7 @@ test_expect_success setup '\n \t\tgit add \"file$i\" &&\n \t\ttest_tick &&\n \t\tgit commit -m \"$i\" &&\n-\t\tgit tag \"tag$i\"\n+\t\tgit tag \"tag$i\" || return 1\n \tdone &&\n \tobj=$(git rev-parse --verify tag3) &&\n \tfanout=$(expr \"$obj\" : \"\\(..\\)\") &&\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nindex dcf03d324a..783cd96290 100755\n--- a/t/t5310-pack-bitmaps.sh\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -230,7 +230,7 @@ test_expect_success 'pack reuse respects --honor-pack-keep' '\n \ttest_when_finished \"rm -f .git/objects/pack/*.keep\" &&\n \tfor i in .git/objects/pack/*.pack\n \tdo\n-\t\t>${i%.pack}.keep\n+\t\t>${i%.pack}.keep || return 1\n \tdone &&\n \treusable_pack --honor-pack-keep >empty.pack &&\n \tgit index-pack empty.pack &&\ndiff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh\nindex 8fb6acae47..33b740ce62 100755\n--- a/t/t5317-pack-objects-filter-objects.sh\n+++ b/t/t5317-pack-objects-filter-objects.sh\n@@ -18,7 +18,7 @@ test_expect_success 'setup r1' '\n \tdo\n \t\techo \"This is file: $n\" > r1/file.$n &&\n \t\tgit -C r1 add file.$n &&\n-\t\tgit -C r1 commit -m \"$n\"\n+\t\tgit -C r1 commit -m \"$n\" || return 1\n \tdone\n '\n \n@@ -118,7 +118,7 @@ test_expect_success 'setup r2' '\n \tdo\n \t\tprintf \"%\"$n\"s\" X > r2/large.$n &&\n \t\tgit -C r2 add large.$n &&\n-\t\tgit -C r2 commit -m \"$n\"\n+\t\tgit -C r2 commit -m \"$n\" || return 1\n \tdone\n '\n \n@@ -281,7 +281,7 @@ test_expect_success 'setup r3' '\n \t\techo \"This is file: $n\" > r3/$n &&\n \t\tgit -C r3 add $n &&\n \t\techo \"This is file: dir1/$n\" > r3/dir1/$n &&\n-\t\tgit -C r3 add dir1/$n\n+\t\tgit -C r3 add dir1/$n || return 1\n \tdone &&\n \tgit -C r3 commit -m \"sparse\" &&\n \techo dir1/ >pattern1 &&\n@@ -334,7 +334,7 @@ test_expect_success 'setup r4' '\n \t\techo \"This is file: $n\" > r4/$n &&\n \t\tgit -C r4 add $n &&\n \t\techo \"This is file: dir1/$n\" > r4/dir1/$n &&\n-\t\tgit -C r4 add dir1/$n\n+\t\tgit -C r4 add dir1/$n || return 1\n \tdone &&\n \techo dir1/ >r4/pattern &&\n \tgit -C r4 add pattern &&\n@@ -409,7 +409,7 @@ test_expect_success 'setup r1 - delete loose blobs' '\n \n \tfor id in `cat expected | sed \"s|..|&/|\"`\n \tdo\n-\t\trm r1/.git/objects/$id\n+\t\trm r1/.git/objects/$id || return 1\n \tdone\n '\n \ndiff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh\nindex f516fda7cc..edb728f77c 100755\n--- a/t/t5318-commit-graph.sh\n+++ b/t/t5318-commit-graph.sh\n@@ -64,7 +64,7 @@ test_expect_success 'create commits and repack' '\n \tfor i in $(test_seq 3)\n \tdo\n \t\ttest_commit $i &&\n-\t\tgit branch commits/$i\n+\t\tgit branch commits/$i || return 1\n \tdone &&\n \tgit repack\n '\n@@ -147,13 +147,13 @@ test_expect_success 'Add more commits' '\n \tfor i in $(test_seq 4 5)\n \tdo\n \t\ttest_commit $i &&\n-\t\tgit branch commits/$i\n+\t\tgit branch commits/$i || return 1\n \tdone &&\n \tgit reset --hard commits/2 &&\n \tfor i in $(test_seq 6 7)\n \tdo\n \t\ttest_commit $i &&\n-\t\tgit branch commits/$i\n+\t\tgit branch commits/$i || return 1\n \tdone &&\n \tgit reset --hard commits/2 &&\n \tgit merge commits/4 &&\ndiff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh\nindex 3f69e43178..86b0372dc8 100755\n--- a/t/t5319-multi-pack-index.sh\n+++ b/t/t5319-multi-pack-index.sh\n@@ -93,7 +93,7 @@ test_expect_success 'create objects' '\n \ttest_commit initial &&\n \tfor i in $(test_seq 1 5)\n \tdo\n-\t\tgenerate_objects $i\n+\t\tgenerate_objects $i || return 1\n \tdone &&\n \tcommit_and_list_objects\n '\n@@ -155,7 +155,7 @@ test_expect_success 'corrupt idx reports errors' '\n test_expect_success 'add more objects' '\n \tfor i in $(test_seq 6 10)\n \tdo\n-\t\tgenerate_objects $i\n+\t\tgenerate_objects $i || return 1\n \tdone &&\n \tcommit_and_list_objects\n '\n@@ -203,7 +203,7 @@ test_expect_success 'add more packs' '\n \tdo\n \t\tgenerate_objects $j &&\n \t\tcommit_and_list_objects &&\n-\t\tgit pack-objects --index-version=2 $objdir/pack/test-pack <obj-list\n+\t\tgit pack-objects --index-version=2 $objdir/pack/test-pack <obj-list || return 1\n \tdone\n '\n \n@@ -594,7 +594,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '\n \tmkdir objects64/pack &&\n \tfor i in $(test_seq 1 11)\n \tdo\n-\t\tgenerate_objects 11\n+\t\tgenerate_objects 11 || return 1\n \tdone &&\n \tcommit_and_list_objects &&\n \tpack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&\n@@ -638,7 +638,7 @@ test_expect_success 'setup expire tests' '\n \t\tgit update-index --add large_file.txt &&\n \t\tfor i in $(test_seq 1 20)\n \t\tdo\n-\t\t\ttest_commit $i\n+\t\t\ttest_commit $i || exit 1\n \t\tdone &&\n \t\tgit branch A HEAD &&\n \t\tgit branch B HEAD~8 &&\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex 61cb907a90..d39958c066 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -14,7 +14,7 @@ test_expect_success 'setup repo' '\n \t\tfor j in $(test_seq 1 3)\n \t\tdo\n \t\t\tmkdir f$i/f$j &&\n-\t\t\techo $j >f$i/f$j/data.txt\n+\t\t\techo $j >f$i/f$j/data.txt || return 1\n \t\tdone\n \tdone &&\n \tgit add . &&\n@@ -23,7 +23,7 @@ test_expect_success 'setup repo' '\n \tdo\n \t\tgit checkout -b topic$i main &&\n \t\techo change-$i >f$i/f$i/data.txt &&\n-\t\tgit commit -a -m \"Changed f$i/f$i/data.txt\"\n+\t\tgit commit -a -m \"Changed f$i/f$i/data.txt\" || return 1\n \tdone &&\n \tcat >packinput.txt <<-EOF &&\n \ttopic1\ndiff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh\nindex da453f68d6..d042d26f2b 100755\n--- a/t/t5325-reverse-index.sh\n+++ b/t/t5325-reverse-index.sh\n@@ -46,7 +46,7 @@ test_expect_success 'index-pack with --[no-]rev-index' '\n \t\ttest_path_exists $rev &&\n \n \t\ttest_index_pack \"$conf\" --no-rev-index &&\n-\t\ttest_path_is_missing $rev\n+\t\ttest_path_is_missing $rev || return 1\n \tdone\n '\n \ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 8a5d3492c7..f0dc4e6968 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -95,7 +95,7 @@ test_expect_success 'setup' '\n \twhile [ $cur -le 10 ]; do\n \t\tadd A$cur $(eval echo \\$A$prev) &&\n \t\tprev=$cur &&\n-\t\tcur=$(($cur+1))\n+\t\tcur=$(($cur+1)) || return 1\n \tdone &&\n \tadd B1 $A1 &&\n \tgit update-ref refs/heads/A \"$ATIP\" &&\n@@ -112,7 +112,7 @@ test_expect_success 'post 1st pull setup' '\n \twhile [ $cur -le 65 ]; do\n \t\tadd B$cur $(eval echo \\$B$prev) &&\n \t\tprev=$cur &&\n-\t\tcur=$(($cur+1))\n+\t\tcur=$(($cur+1)) || return 1\n \tdone\n '\n \n@@ -464,11 +464,11 @@ test_expect_success 'fetch creating new shallow root' '\n test_expect_success 'setup tests for the --stdin parameter' '\n \tfor head in C D E F\n \tdo\n-\t\tadd $head\n+\t\tadd $head || return 1\n \tdone &&\n \tfor head in A B C D E F\n \tdo\n-\t\tgit tag $head $head\n+\t\tgit tag $head $head || return 1\n \tdone &&\n \tcat >input <<-\\EOF &&\n \trefs/heads/C\ndiff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh\nindex 8c05c7715b..b160f8b7fb 100755\n--- a/t/t5502-quickfetch.sh\n+++ b/t/t5502-quickfetch.sh\n@@ -130,7 +130,7 @@ test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '\n \tfor i in 0 1 2 3 4 5 6 7 8 9; do\n \t\tfor j in 0 1 2 3 4 5 6 7 8 9; do\n \t\t\tfor k in 0 1 2 3 4 5 6 7 8 9; do\n-\t\t\t\techo \"$branchprefix$i$j$k\" >> .git/packed-refs\n+\t\t\t\techo \"$branchprefix$i$j$k\" >> .git/packed-refs || return 1\n \t\t\tdone\n \t\tdone\n \tdone &&\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 01468ce6d8..ce18c20acd 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -550,7 +550,7 @@ test_expect_success 'bundle should record HEAD correctly' '\n \tgit bundle list-heads bundle5 >actual &&\n \tfor h in HEAD refs/heads/main\n \tdo\n-\t\techo \"$(git rev-parse --verify $h) $h\"\n+\t\techo \"$(git rev-parse --verify $h) $h\" || return 1\n \tdone >expect &&\n \ttest_cmp expect actual\n \ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex c69cfd5c64..320d26796d 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -133,7 +133,7 @@ test_expect_success setup '\n \t\tgit config branch.br-$remote-merge.merge refs/heads/three &&\n \t\tgit config branch.br-$remote-octopus.remote $remote &&\n \t\tgit config branch.br-$remote-octopus.merge refs/heads/one &&\n-\t\tgit config --add branch.br-$remote-octopus.merge two\n+\t\tgit config --add branch.br-$remote-octopus.merge two || return 1\n \tdone &&\n \tbuild_script sed_script\n '\n@@ -199,7 +199,7 @@ do\n \t\t\t\trefs/heads refs/remotes/rem refs/tags |\n \t\t\twhile read val type refname\n \t\t\tdo\n-\t\t\t\tgit update-ref -d \"$refname\" \"$val\"\n+\t\t\t\tgit update-ref -d \"$refname\" \"$val\" || return 1\n \t\t\tdone &&\n \t\t\tgit fetch \"$@\" >/dev/null &&\n \t\t\tcat .git/FETCH_HEAD\ndiff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh\nindex 7b9fb4ff02..165427d57e 100755\n--- a/t/t5552-skipping-fetch-negotiator.sh\n+++ b/t/t5552-skipping-fetch-negotiator.sh\n@@ -48,7 +48,7 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc\n \tgit init client &&\n \tfor i in $(test_seq 7)\n \tdo\n-\t\ttest_commit -C client c$i\n+\t\ttest_commit -C client c$i || return 1\n \tdone &&\n \n \t# We send: \"c7\" (skip 1) \"c5\" (skip 2) \"c2\" (skip 4). After that, since\n@@ -68,7 +68,7 @@ test_expect_success 'when two skips collide, favor the larger one' '\n \tgit init client &&\n \tfor i in $(test_seq 11)\n \tdo\n-\t\ttest_commit -C client c$i\n+\t\ttest_commit -C client c$i || return 1\n \tdone &&\n \tgit -C client checkout c5 &&\n \ttest_commit -C client c5side &&\n@@ -155,14 +155,14 @@ test_expect_success 'do not send \"have\" with ancestors of commits that server AC\n \tfor i in $(test_seq 8)\n \tdo\n \t\tgit -C client checkout --orphan b$i &&\n-\t\ttest_commit -C client b$i.c0\n+\t\ttest_commit -C client b$i.c0 || return 1\n \tdone &&\n \tfor j in $(test_seq 19)\n \tdo\n \t\tfor i in $(test_seq 8)\n \t\tdo\n \t\t\tgit -C client checkout b$i &&\n-\t\t\ttest_commit -C client b$i.c$j\n+\t\t\ttest_commit -C client b$i.c$j || return 1\n \t\tdone\n \tdone &&\n \n@@ -201,7 +201,7 @@ test_expect_success 'do not send \"have\" with ancestors of commits that server AC\n \t# should still send the others (in this test, just check b2).\n \tfor i in $(test_seq 0 8)\n \tdo\n-\t\thave_not_sent b1.c$i\n+\t\thave_not_sent b1.c$i || return 1\n \tdone &&\n \thave_sent b2.c1 b2.c0\n '\ndiff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh\nindex 80e86d8284..660f876eec 100755\n--- a/t/t5571-pre-push-hook.sh\n+++ b/t/t5571-pre-push-hook.sh\n@@ -118,7 +118,7 @@ test_expect_success 'set up many-ref tests' '\n \t\twhile test $nr -lt 2000\n \t\tdo\n \t\t\tnr=$(( $nr + 1 )) &&\n-\t\t\techo \"create refs/heads/b/$nr $COMMIT3\"\n+\t\t\techo \"create refs/heads/b/$nr $COMMIT3\" || return 1\n \t\tdone\n \t} | git update-ref --stdin\n '\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex 23c156e399..34469b6ac1 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -19,7 +19,7 @@ test_expect_success 'setup normal src repo' '\n \t\techo \"This is file: $n\" > src/file.$n.txt &&\n \t\tgit -C src add file.$n.txt &&\n \t\tgit -C src commit -m \"file $n\" &&\n-\t\tgit -C src ls-files -s file.$n.txt >>temp\n+\t\tgit -C src ls-files -s file.$n.txt >>temp || return 1\n \tdone &&\n \tawk -f print_2.awk <temp | sort >expect_1.oids &&\n \ttest_line_count = 4 expect_1.oids\n@@ -74,7 +74,7 @@ test_expect_success 'push new commits to server' '\n \tdo\n \t\techo \"Mod file.1.txt $x\" >>src/file.1.txt &&\n \t\tgit -C src add file.1.txt &&\n-\t\tgit -C src commit -m \"mod $x\"\n+\t\tgit -C src commit -m \"mod $x\" || return 1\n \tdone &&\n \tgit -C src blame main -- file.1.txt >expect.blame &&\n \tgit -C src push -u srv main\n@@ -116,7 +116,7 @@ test_expect_success 'push new commits to server for file.2.txt' '\n \tdo\n \t\techo \"Mod file.2.txt $x\" >>src/file.2.txt &&\n \t\tgit -C src add file.2.txt &&\n-\t\tgit -C src commit -m \"mod $x\"\n+\t\tgit -C src commit -m \"mod $x\" || return 1\n \tdone &&\n \tgit -C src push -u srv main\n '\n@@ -137,7 +137,7 @@ test_expect_success 'push new commits to server for file.3.txt' '\n \tdo\n \t\techo \"Mod file.3.txt $x\" >>src/file.3.txt &&\n \t\tgit -C src add file.3.txt &&\n-\t\tgit -C src commit -m \"mod $x\"\n+\t\tgit -C src commit -m \"mod $x\" || return 1\n \tdone &&\n \tgit -C src push -u srv main\n '\n@@ -385,7 +385,7 @@ setup_triangle () {\n \tfor i in $(test_seq 1 100)\n \tdo\n \t\techo \"make the tree big\" >server/file$i &&\n-\t\tgit -C server add file$i\n+\t\tgit -C server add file$i || return 1\n \tdone &&\n \tgit -C server commit -m \"initial\" &&\n \tgit clone --bare --filter=tree:0 \"file://$(pwd)/server\" client &&\n@@ -669,7 +669,7 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor\n \tfor i in $(test_seq 10)\n \tdo\n \t\techo \"this is a line\" >>\"$SERVER/foo.txt\" &&\n-\t\techo \"this is another line\" >>\"$SERVER/have.txt\"\n+\t\techo \"this is another line\" >>\"$SERVER/have.txt\" || return 1\n \tdone &&\n \tgit -C \"$SERVER\" add foo.txt have.txt &&\n \tgit -C \"$SERVER\" commit -m bar &&\ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex d527cf6c49..ed8a071961 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -747,7 +747,7 @@ test_expect_success 'clone big repository with http:// using protocol v2' '\n \t\techo \"data 0\" &&\n \t\techo \"M 644 inline bla.txt\" &&\n \t\techo \"data 4\" &&\n-\t\techo \"bla\"\n+\t\techo \"bla\" || return 1\n \tdone | git -C \"$HTTPD_DOCUMENT_ROOT_PATH/big\" fast-import &&\n \n \tGIT_TRACE_PACKET=\"$(pwd)/log\" GIT_TRACE_CURL=\"$(pwd)/log\" git \\\n@@ -942,7 +942,7 @@ test_expect_success 'part of packfile response provided as URI' '\n \t\t\tthen\n \t\t\t\t>h2found\n \t\t\tfi\n-\t\tfi\n+\t\tfi || return 1\n \tdone &&\n \ttest -f hfound &&\n \ttest -f h2found &&\n-- \n2.34.1.307.g9b7440fafd\n\n"},{"id":"443647","messageId":"CABPp-BGBZ6_CqbUg3=sK2b4yELC5NHHyH68_df22n=t=hARH_g@mail.gmail.com","threadId":"57057","inReplyTo":"20211209051115.52629-3-sunshine@sunshineco.com","subject":"Re: [PATCH 02/19] t1010: fix unnoticed failure on Windows","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-09T16:27:55Z","receivedAt":"2021-12-09T16:31:08Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 9, 2021 at 12:22 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Microsoft Windows, a directory name should never end with a period.\n> Quoting from Microsoft documentation[1]:\n>\n>     Do not end a file or directory name with a space or a period.\n>     Although the underlying file system may support such names, the\n>     Windows shell and user interface does not.\n>\n> Naming a directory with a trailing period is indeed perilous:\n>\n>     % git init foo\n>     % cd foo\n>     % mkdir a.\n>     % git status\n>     warning: could not open directory 'a./': No such file or directory\n>\n> The t1010 \"setup\" test:\n>\n>     for d in a a. a0\n>     do\n>         mkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n>         git add \"$d\"\n>     done &&\n>\n> runs afoul of this Windows limitation, as can be observed when running\n> the test verbosely:\n>\n>     error: open(\"a./one\"): No such file or directory\n>     error: unable to index file 'a./one'\n>     fatal: adding files failed\n>\n> The reason this problem has gone unnoticed for so long is twofold.\n> First, the failed `git add` is swallowed silently because the loop is\n> not terminated explicitly by `|| return 1` to signal the failure.\n> Second, none of the tests in this script care about the actual directory\n> names or even the number of tree entries.\n\nIs this true?  The names look like they were selected on the basis that\n    '.' < '/' < '0'\n\n> They care only that the tree\n> synthesized in the index and created by `git write-tree` matches the\n> tree created by the output of `git ls-tree` fed into `git mktree`, and\n> the failure of `git add \"a./one\"` doesn't change that outcome.\n\nWe have multiple paths in the code that write tree structures, and\nit's important that the order be\n  100644 blob $OID1 a.\n  040000 tree $OID2 a\n  100644 blob $OID3 a0\n\ni.e. that 'a' as a tree object sorts as though it were actually named\n'a/'.  I suspect the code might have been making sure that the\ndifferent paths creating tree objects did so consistently, and the\nspecial handling of subdirectories is the edge case that needs careful\nchecks.\n\n> Skipping these tests on Windows by, for instance, checking the\n> FUNNYNAMES predicate would avoid the problem, however, the funny-looking\n> name is not what is being tested here. Rather, the tests are about\n> checking that `git mktree` produces stable results for various input\n> conditions, such as when the input order is not consistent or when an\n> object is missing.\n>\n> Therefore, resolve the problem simply by using a directory name which is\n> legal on Windows (i.e. \"a-\" rather than \"a.\"). While at it, add the\n> missing `|| return 1` to the loop body in order to catch this sort of\n> problem in the future.\n\nThe choice to replace '.' with '-' is excellent since '-' < '/' as well.\n\n>\n> [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n>\n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>  t/t1010-mktree.sh | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh\n> index 48bfad07ab..3c08194526 100755\n> --- a/t/t1010-mktree.sh\n> +++ b/t/t1010-mktree.sh\n> @@ -6,10 +6,10 @@ TEST_PASSES_SANITIZE_LEAK=true\n>  . ./test-lib.sh\n>\n>  test_expect_success setup '\n> -       for d in a a. a0\n> +       for d in a a- a0\n>         do\n>                 mkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n> -               git add \"$d\"\n> +               git add \"$d\" || return 1\n>         done &&\n>         echo zero >one &&\n>         git update-index --add --info-only one &&\n> --\n> 2.34.1.307.g9b7440fafd\n\nThe patch itself looks good; I'm just slightly unsure about the one\nclaim in the commit message.\n"},{"id":"443649","messageId":"CAPig+cR0eKhz+ncWb4v9dSY0A03P+K0+WT90J2cBKvLqT8DXrA@mail.gmail.com","threadId":"57057","inReplyTo":"CABPp-BGBZ6_CqbUg3=sK2b4yELC5NHHyH68_df22n=t=hARH_g@mail.gmail.com","subject":"Re: [PATCH 02/19] t1010: fix unnoticed failure on Windows","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T16:45:23Z","receivedAt":"2021-12-09T16:45:40Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 9, 2021 at 11:28 AM Elijah Newren <newren@gmail.com> wrote:\n> On Thu, Dec 9, 2021 at 12:22 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > The reason this problem has gone unnoticed for so long is twofold.\n> > First, the failed `git add` is swallowed silently because the loop is\n> > not terminated explicitly by `|| return 1` to signal the failure.\n> > Second, none of the tests in this script care about the actual directory\n> > names or even the number of tree entries.\n>\n> Is this true?  The names look like they were selected on the basis that\n>     '.' < '/' < '0'\n>\n> We have multiple paths in the code that write tree structures, and\n> it's important that the order be\n>   100644 blob $OID1 a.\n>   040000 tree $OID2 a\n>   100644 blob $OID3 a0\n>\n> i.e. that 'a' as a tree object sorts as though it were actually named\n> 'a/'.  I suspect the code might have been making sure that the\n> different paths creating tree objects did so consistently, and the\n> special handling of subdirectories is the edge case that needs careful\n> checks.\n\nI meant it in the sense that the tests don't care about the literal\nnames \"a\", \"a.\", \"a0\". They do care about ordering...\n\n> > Skipping these tests on Windows by, for instance, checking the\n> > FUNNYNAMES predicate would avoid the problem, however, the funny-looking\n> > name is not what is being tested here. Rather, the tests are about\n> > checking that `git mktree` produces stable results for various input\n> > conditions, such as when the input order is not consistent or when an\n> > object is missing.\n\n... which I hoped to convey to readers by saying \"stable results ...\nwhen input order is not consistent\", but I guess I wasn't clear enough\nor obvious enough, or this mention is too far removed from the \"don't\ncare about the actual names\" statement.\n\n> > Therefore, resolve the problem simply by using a directory name which is\n> > legal on Windows (i.e. \"a-\" rather than \"a.\"). While at it, add the\n> > missing `|| return 1` to the loop body in order to catch this sort of\n> > problem in the future.\n>\n> The choice to replace '.' with '-' is excellent since '-' < '/' as well.\n\nI can rewrite the earlier paragraph to mention name ordering so it's\nmore obvious that it has significance.\n\nI am a bit hesitant about spamming the list with a reroll of this\nlengthy series just for this change, though I can certainly do it if\nyou consider it important.\n"},{"id":"443650","messageId":"CABPp-BGBhxbvh_Ob=bR8U9f-3muiBzWmFruKLXd=K7420i8COw@mail.gmail.com","threadId":"57057","inReplyTo":"20211209051115.52629-12-sunshine@sunshineco.com","subject":"Re: [PATCH 11/19] tests: fix broken &&-chains in `$(...)` command substitutions","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-09T16:44:29Z","receivedAt":"2021-12-09T16:46:41Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> The top-level &&-chain checker built into t/test-lib.sh causes tests to\n> magically exit with code 117 if the &&-chain is broken. However, it has\n> the shortcoming that the magic does not work within `{...}` groups,\n> `(...)` subshells, `$(...)` substitutions, or within bodies of compound\n> statements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\n> partly fills in the gap by catching broken &&-chains in `(...)`\n> subshells, but bugs can still lurk behind broken &&-chains in the other\n> cases.\n>\n> Fix broken &&-chains in `$(...)` command substitutions in order to\n> reduce the number of possible lurking bugs.\n>\n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>  contrib/subtree/t/t7900-subtree.sh |  2 +-\n>  t/t0005-signals.sh                 |  2 +-\n>  t/t0060-path-utils.sh              |  4 ++--\n>  t/t1006-cat-file.sh                | 10 +++++-----\n>  t/t3600-rm.sh                      |  2 +-\n>  t/t7010-setup.sh                   |  2 +-\n>  6 files changed, 11 insertions(+), 11 deletions(-)\n>\n> diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\n> index 4153b65321..1c1f76f04a 100755\n> --- a/contrib/subtree/t/t7900-subtree.sh\n> +++ b/contrib/subtree/t/t7900-subtree.sh\n> @@ -1445,7 +1445,7 @@ test_expect_success 'subtree descendant check' '\n>         ) &&\n>         test_create_commit \"$test_count\" folder_subtree/0 &&\n>         test_create_commit \"$test_count\" folder_subtree/b &&\n> -       cherry=$(cd \"$test_count\"; git rev-parse HEAD) &&\n> +       cherry=$(cd \"$test_count\" && git rev-parse HEAD) &&\n>         (\n>                 cd \"$test_count\" &&\n>                 git checkout branch\n> diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh\n> index a5ec6a0315..eba75a2490 100755\n> --- a/t/t0005-signals.sh\n> +++ b/t/t0005-signals.sh\n> @@ -48,7 +48,7 @@ test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '\n>  '\n>\n>  test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '\n> -       OUT=$( ((trap \"\" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> +       OUT=$( ((trap \"\" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&\n\nShouldn't the second ';' be replaced with '&&' as well?\n\n>         test_match_signal 13 \"$OUT\"\n>  '\n>\n> diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\n> index 34d1061f32..71a5d370cc 100755\n> --- a/t/t0060-path-utils.sh\n> +++ b/t/t0060-path-utils.sh\n> @@ -216,7 +216,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '\n>         mkdir second &&\n>         ln -s ../first second/other &&\n>         mkdir third &&\n> -       dir=\"$(cd .git; pwd -P)\" &&\n> +       dir=\"$(cd .git && pwd -P)\" &&\n>         dir2=third/../second/other/.git &&\n>         test \"$dir\" = \"$(test-tool path-utils real_path $dir2)\" &&\n>         file=\"$dir\"/index &&\n> @@ -224,7 +224,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '\n>         basename=blub &&\n>         test \"$dir/$basename\" = \"$(cd .git && test-tool path-utils real_path \"$basename\")\" &&\n>         ln -s ../first/file .git/syml &&\n> -       sym=\"$(cd first; pwd -P)\"/file &&\n> +       sym=\"$(cd first && pwd -P)\"/file &&\n>         test \"$sym\" = \"$(test-tool path-utils real_path \"$dir2/syml\")\"\n>  '\n>\n> diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\n> index 658628375c..67a3f64c2d 100755\n> --- a/t/t1006-cat-file.sh\n> +++ b/t/t1006-cat-file.sh\n> @@ -211,14 +211,14 @@ done\n>  test_expect_success \"--batch-check for a non-existent named object\" '\n>      test \"foobar42 missing\n>  foobar84 missing\" = \\\n> -    \"$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)\"\n> +    \"$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)\"\n>  '\n>\n>  test_expect_success \"--batch-check for a non-existent hash\" '\n>      test \"0000000000000000000000000000000000000042 missing\n>  0000000000000000000000000000000000000084 missing\" = \\\n> -    \"$( ( echo 0000000000000000000000000000000000000042;\n> -        echo_without_newline 0000000000000000000000000000000000000084; ) |\n> +    \"$( ( echo 0000000000000000000000000000000000000042 &&\n> +        echo_without_newline 0000000000000000000000000000000000000084 ) |\n>         git cat-file --batch-check)\"\n>  '\n>\n> @@ -226,8 +226,8 @@ test_expect_success \"--batch for an existent and a non-existent hash\" '\n>      test \"$tag_sha1 tag $tag_size\n>  $tag_content\n>  0000000000000000000000000000000000000000 missing\" = \\\n> -    \"$( ( echo $tag_sha1;\n> -        echo_without_newline 0000000000000000000000000000000000000000; ) |\n> +    \"$( ( echo $tag_sha1 &&\n> +        echo_without_newline 0000000000000000000000000000000000000000 ) |\n>         git cat-file --batch)\"\n>  '\n>\n> diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\n> index bb9ef35dac..ed3952eb98 100755\n> --- a/t/t3600-rm.sh\n> +++ b/t/t3600-rm.sh\n> @@ -265,7 +265,7 @@ test_expect_success 'choking \"git rm\" should not let it die with cruft (induce S\n>\n>  test_expect_success !MINGW 'choking \"git rm\" should not let it die with cruft (induce and check SIGPIPE)' '\n>         choke_git_rm_setup &&\n> -       OUT=$( ((trap \"\" PIPE; git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n> +       OUT=$( ((trap \"\" PIPE && git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n\nSame here; shouldn't the second ';' be replaced with '&&' as well?\n\n>         test_match_signal 13 \"$OUT\" &&\n>         test_path_is_missing .git/index.lock\n>  '\n> diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh\n> index 0335a9a158..520f96d09f 100755\n> --- a/t/t7010-setup.sh\n> +++ b/t/t7010-setup.sh\n> @@ -137,7 +137,7 @@ test_expect_success 'setup deeper work tree' '\n>\n>  test_expect_success 'add a directory outside the work tree' '(\n>         cd tester &&\n> -       d1=\"$(cd .. ; pwd)\" &&\n> +       d1=\"$(cd .. && pwd)\" &&\n>         test_must_fail git add \"$d1\"\n>  )'\n>\n> --\n> 2.34.1.307.g9b7440fafd\n>\n"},{"id":"443651","messageId":"CABPp-BGrjPKJAp_-yOH37d84Y0LhGwTOSboOm9xFYXp+68BrWw@mail.gmail.com","threadId":"57057","inReplyTo":"20211209051115.52629-16-sunshine@sunshineco.com","subject":"Re: [PATCH 15/19] tests: simplify by dropping unnecessary `for` loops","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-09T16:50:31Z","receivedAt":"2021-12-09T16:53:07Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> Rather than manually looping over a set of items and plugging those\n> items into a template string which is printed repeatedly, achieve the\n> same effect by taking advantage of `printf` which loops over its\n> arguments automatically.\n\nI didn't know this about printf; handy.\n\nPatch looks good.\n\n>\n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>  t/t3005-ls-files-relative.sh      | 10 ++--------\n>  t/t3600-rm.sh                     |  5 +----\n>  t/t4025-hunk-header.sh            | 10 ++--------\n>  t/t4125-apply-ws-fuzz.sh          |  5 +----\n>  t/t6416-recursive-corner-cases.sh | 30 ++++++------------------------\n>  t/t7110-reset-merge.sh            |  2 +-\n>  t/t9400-git-cvsserver-server.sh   |  2 +-\n>  7 files changed, 14 insertions(+), 50 deletions(-)\n>\n> diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh\n> index 6ba8b589cd..fbfa210a50 100755\n> --- a/t/t3005-ls-files-relative.sh\n> +++ b/t/t3005-ls-files-relative.sh\n> @@ -39,10 +39,7 @@ test_expect_success 'ls-files with mixed levels' '\n>  test_expect_success 'ls-files -c' '\n>         (\n>                 cd top/sub &&\n> -               for f in ../y*\n> -               do\n> -                       echo \"error: pathspec $SQ$f$SQ did not match any file(s) known to git\"\n> -               done >expect.err &&\n> +               printf \"error: pathspec $SQ%s$SQ did not match any file(s) known to git\\n\" ../y* >expect.err &&\n>                 echo \"Did you forget to ${SQ}git add${SQ}?\" >>expect.err &&\n>                 ls ../x* >expect.out &&\n>                 test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&\n> @@ -54,10 +51,7 @@ test_expect_success 'ls-files -c' '\n>  test_expect_success 'ls-files -o' '\n>         (\n>                 cd top/sub &&\n> -               for f in ../x*\n> -               do\n> -                       echo \"error: pathspec $SQ$f$SQ did not match any file(s) known to git\"\n> -               done >expect.err &&\n> +               printf \"error: pathspec $SQ%s$SQ did not match any file(s) known to git\\n\" ../x* >expect.err &&\n>                 echo \"Did you forget to ${SQ}git add${SQ}?\" >>expect.err &&\n>                 ls ../y* >expect.out &&\n>                 test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&\n> diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\n> index ed3952eb98..e74a318ac3 100755\n> --- a/t/t3600-rm.sh\n> +++ b/t/t3600-rm.sh\n> @@ -274,10 +274,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '\n>         git reset -q --hard &&\n>         test_when_finished \"rm -f .git/index.lock msg && git reset -q --hard\" &&\n>         blob=$(echo blob | git hash-object -w --stdin) &&\n> -       for stage in 1 2 3\n> -       do\n> -               echo \"100644 $blob $stage       blob\"\n> -       done | git update-index --index-info &&\n> +       printf \"100644 $blob %d\\tblob\\n\" 1 2 3 | git update-index --index-info &&\n>         git rm blob >msg 2>&1 &&\n>         test_i18ngrep ! \"needs merge\" msg &&\n>         test_must_fail git ls-files -s --error-unmatch blob\n> diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh\n> index 6356961de4..5397cb7d42 100755\n> --- a/t/t4025-hunk-header.sh\n> +++ b/t/t4025-hunk-header.sh\n> @@ -14,15 +14,9 @@ test_expect_success setup '\n>\n>         (\n>                 echo \"A $NS\" &&\n> -               for c in B C D E F G H I J K\n> -               do\n> -                       echo \"  $c\"\n> -               done &&\n> +               printf \"  %s\\n\" B C D E F G H I J K &&\n>                 echo \"L  $NS\" &&\n> -               for c in M N O P Q R S T U V\n> -               do\n> -                       echo \"  $c\"\n> -               done\n> +               printf \"  %s\\n\" M N O P Q R S T U V\n>         ) >file &&\n>         git add file &&\n>\n> diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh\n> index 9671de7999..090987c89b 100755\n> --- a/t/t4125-apply-ws-fuzz.sh\n> +++ b/t/t4125-apply-ws-fuzz.sh\n> @@ -10,10 +10,7 @@ test_expect_success setup '\n>         git add file &&\n>\n>         # file-0 is full of whitespace breakages\n> -       for l in a bb c d eeee f ggg h\n> -       do\n> -               echo \"$l \"\n> -       done >file-0 &&\n> +       printf \"%s \\n\" a bb c d eeee f ggg h >file-0 &&\n>\n>         # patch-0 creates a whitespace broken file\n>         cat file-0 >file &&\n> diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh\n> index 84f5082366..690c8482b1 100755\n> --- a/t/t6416-recursive-corner-cases.sh\n> +++ b/t/t6416-recursive-corner-cases.sh\n> @@ -24,14 +24,8 @@ test_expect_success 'setup basic criss-cross + rename with no modifications' '\n>                 cd basic-rename &&\n>\n>                 ten=\"0 1 2 3 4 5 6 7 8 9\" &&\n> -               for i in $ten\n> -               do\n> -                       echo line $i in a sample file\n> -               done >one &&\n> -               for i in $ten\n> -               do\n> -                       echo line $i in another sample file\n> -               done >two &&\n> +               printf \"line %d in a sample file\\n\" $ten >one &&\n> +               printf \"line %d in another sample file\\n\" $ten >two &&\n>                 git add one two &&\n>                 test_tick && git commit -m initial &&\n>\n> @@ -96,14 +90,8 @@ test_expect_success 'setup criss-cross + rename merges with basic modification'\n>                 cd rename-modify &&\n>\n>                 ten=\"0 1 2 3 4 5 6 7 8 9\" &&\n> -               for i in $ten\n> -               do\n> -                       echo line $i in a sample file\n> -               done >one &&\n> -               for i in $ten\n> -               do\n> -                       echo line $i in another sample file\n> -               done >two &&\n> +               printf \"line %d in a sample file\\n\" $ten >one &&\n> +               printf \"line %d in another sample file\\n\" $ten >two &&\n>                 git add one two &&\n>                 test_tick && git commit -m initial &&\n>\n> @@ -1588,10 +1576,7 @@ test_expect_success 'setup nested conflicts' '\n>                 cd nested_conflicts &&\n>\n>                 # Create some related files now\n> -               for i in $(test_seq 1 10)\n> -               do\n> -                       echo Random base content line $i\n> -               done >initial &&\n> +               printf \"Random base content line %d\\n\" $(test_seq 1 10) >initial &&\n>\n>                 cp initial b_L1 &&\n>                 cp initial b_R1 &&\n> @@ -1777,10 +1762,7 @@ test_expect_success 'setup virtual merge base with nested conflicts' '\n>                 cd virtual_merge_base_has_nested_conflicts &&\n>\n>                 # Create some related files now\n> -               for i in $(test_seq 1 10)\n> -               do\n> -                       echo Random base content line $i\n> -               done >content &&\n> +               printf \"Random base content line %d\\n\" $(test_seq 1 10) >content &&\n>\n>                 # Setup original commit\n>                 git add content &&\n> diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh\n> index a82a07a04a..3d62e10b53 100755\n> --- a/t/t7110-reset-merge.sh\n> +++ b/t/t7110-reset-merge.sh\n> @@ -8,7 +8,7 @@ test_description='Tests for \"git reset\" with \"--merge\" and \"--keep\" options'\n>  . ./test-lib.sh\n>\n>  test_expect_success setup '\n> -    for i in 1 2 3; do echo line $i; done >file1 &&\n> +    printf \"line %d\\n\" 1 2 3 >file1 &&\n>      cat file1 >file2 &&\n>      git add file1 file2 &&\n>      test_tick &&\n> diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\n> index a6a73effde..a60fe2e19f 100755\n> --- a/t/t9400-git-cvsserver-server.sh\n> +++ b/t/t9400-git-cvsserver-server.sh\n> @@ -591,7 +591,7 @@ test_expect_success 'cvs annotate' '\n>      cd cvswork &&\n>      GIT_CONFIG=\"$git_config\" cvs annotate merge >../out &&\n>      sed -e \"s/ .*//\" ../out >../actual &&\n> -    for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&\n> +    printf \"1.%d\\n\" 3 1 1 1 1 1 1 1 2 4 >../expect &&\n>      test_cmp ../expect ../actual\n>  '\n>\n> --\n> 2.34.1.307.g9b7440fafd\n>\n"},{"id":"443652","messageId":"CAPig+cQzTnQiwT4Nmpp8qYJOaRpy2pK7DDOu42Wk-1TfmbUxSg@mail.gmail.com","threadId":"57057","inReplyTo":"CABPp-BGBhxbvh_Ob=bR8U9f-3muiBzWmFruKLXd=K7420i8COw@mail.gmail.com","subject":"Re: [PATCH 11/19] tests: fix broken &&-chains in `$(...)` command substitutions","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T16:53:47Z","receivedAt":"2021-12-09T16:54:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 9, 2021 at 11:44 AM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> >  test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '\n> > -       OUT=$( ((trap \"\" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> > +       OUT=$( ((trap \"\" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&\n>\n> Shouldn't the second ';' be replaced with '&&' as well?\n\nThanks for reading so carefully. In this case, the answer is \"no\", the\nsemicolon is correct. This code legitimately wants to capture in the\nOUT variable the numeric exit status of the command preceding `echo\n$?`. If the semicolon is replaced with `&&`, then the echo won't be\nexecuted if the exit status is non-zero, but we want `echo` to be\nexecuted regardless of the exit status. So, the code is correct with\nthe semicolon, and would be incorrect with `&&`. (I hope I'm\nexplaining this well enough to make sense.)\n\nIt's this sort of special case which accounts for why the new linter\n(as mentioned in the cover letter) has special understanding that a\nbroken &&-chain can be legitimate in certain circumstances, such as\nexplicit handling of `$?`.\n\n> > -       OUT=$( ((trap \"\" PIPE; git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n> > +       OUT=$( ((trap \"\" PIPE && git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n>\n> Same here; shouldn't the second ';' be replaced with '&&' as well?\n\nSame answer as above.\n"},{"id":"443653","messageId":"CABPp-BGkMNTAzPqGQ0S8sKenhX8L0CBep23TrdODqEU1BJeTdA@mail.gmail.com","threadId":"57057","inReplyTo":"CAPig+cQzTnQiwT4Nmpp8qYJOaRpy2pK7DDOu42Wk-1TfmbUxSg@mail.gmail.com","subject":"Re: [PATCH 11/19] tests: fix broken &&-chains in `$(...)` command substitutions","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-09T16:57:59Z","receivedAt":"2021-12-09T17:01:31Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Dec 9, 2021 at 8:53 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Thu, Dec 9, 2021 at 11:44 AM Elijah Newren <newren@gmail.com> wrote:\n> > On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > >  test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '\n> > > -       OUT=$( ((trap \"\" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> > > +       OUT=$( ((trap \"\" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> >\n> > Shouldn't the second ';' be replaced with '&&' as well?\n>\n> Thanks for reading so carefully. In this case, the answer is \"no\", the\n> semicolon is correct. This code legitimately wants to capture in the\n> OUT variable the numeric exit status of the command preceding `echo\n> $?`. If the semicolon is replaced with `&&`, then the echo won't be\n> executed if the exit status is non-zero, but we want `echo` to be\n> executed regardless of the exit status. So, the code is correct with\n> the semicolon, and would be incorrect with `&&`. (I hope I'm\n> explaining this well enough to make sense.)\n>\n> It's this sort of special case which accounts for why the new linter\n> (as mentioned in the cover letter) has special understanding that a\n> broken &&-chain can be legitimate in certain circumstances, such as\n> explicit handling of `$?`.\n\nAh, right, you had mentioned this in the cover letter.  Short term\nmemory on my part, I guess.  Thanks for explaining...again.  :-)\n\n> > > -       OUT=$( ((trap \"\" PIPE; git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n> > > +       OUT=$( ((trap \"\" PIPE && git rm -n \"some-file-*\"; echo $? 1>&3) | :) 3>&1 ) &&\n> >\n> > Same here; shouldn't the second ';' be replaced with '&&' as well?\n>\n> Same answer as above.\n"},{"id":"443654","messageId":"CABPp-BFM5ZbFAzVfvDE3=zm6Q4LN2fWthPP8WH5kbgVPSxomtA@mail.gmail.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"Re: [PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-12-09T17:02:54Z","receivedAt":"2021-12-09T17:05:34Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> A while back, Peff successfully nerd-sniped[1] me into tackling a\n> long-brewing idea I had about (possibly) improving `chainlint`\n> performance by linting all tests in all scripts with a single command\n> invocation instead of running `sed` 25300+ times (once for each test).\n>\n> Beside the obvious improvement of checking all tests in all scripts at\n> once, the new linter is also a good deal smarter than chainlint.sed and\n> understands not just shell syntax but also some semantics of test\n> construction, unlike chainlint.sed which is merely heuristics-based.\n> For instance, the new linter recognizes cases when a broken &&-chain is\n> legitimate, such as when `$?` is handled explicitly or when a failure is\n> signaled directly with `false`, in which case the &&-chain leading up to\n> the `false` is immaterial, as well as other cases. Unlike chainlint.sed,\n> it recognizes that a semicolon after the last command in a compound\n> statement is harmless, thus won't interpret the semicolon as breaking\n> the &&-chain.\n>\n> The new linter also provides considerably better coverage for broken\n> &&-chains. The &&-chain checker built into t/test-lib.sh, which causes\n> tests to magically exit with code 117 if the &&-chain is broken, only\n> works for top-level command invocations. The magic doesn't work within\n> `{...}` groups, `(...)` subshells, `$(...)` substitutions, or within\n> bodies of compound statements, such as `if`, `for`, `while`, `case`,\n> etc. chainlint.sed partly fills the gap by catching broken &&-chains in\n> `(...)` subshells one level deep, but bugs can still lurk behind broken\n> &&-chains in the other cases. The new linter catches broken &&-chains\n> within all those constructs to any depth.\n>\n> Another important improvement is that the new linter understands that\n> shell loops do not terminate automatically when a command in the loop\n> body fails, and that the condition needs to be handled explicitly by the\n> test author by using `|| return 1` (or `|| exit 1` in a subshell) within\n> the loop body to flag the failure. Consequently, the new linter will\n> complain when a loop is lacking `|| return 1` (or `|| exit 1`). There\n> are a number of other improvements, as well, but the above are some of\n> the more important ones.\n>\n> Although the new chainlint implementation has been complete for several\n> months, I'm still working out how to organize its patch series. In the\n> meantime, _this_ patch series fixes problems discovered by the new\n> linter due to its improved coverage and extra semantic knowledge about\n> Git tests. As much as possible, I resisted the temptation to make\n> ancillary cleanups (including indentation fixes) to tests even when such\n> cleanups would be obvious improvements. Avoiding such unrelated cleanups\n> should make the long patches in this series, which touch a lot of tests,\n> easier to review (--color-words helps a lot here).\n\nI have to admit to starting to skim once I got to the last four\npatches, since they were a bit longer and all the same type of change.\n\nYou did an excellent job of explaining the changes and presented them\nin a logical fashion.  The few things I thought I caught, you've\nalready answered were already correct.  I do think making the second\ncommit message be a bit clearer about the importance of the ordering\nwould be helpful.  Anyway:\n\nReviewed-by: Elijah Newren <newren@gmail.com>\n\n> This series merges cleanly with 'next' but conflicts with a couple topics\n> in 'seen':\n>\n> * jh/builtin-fsmonitor-part2\n>\n>   t/perf/p7519-fsmonitor.sh\n>     simple resolution: keep all changes from jh/builtin-fsmonitor-part2\n>     (it obviates the need for the fixes made by this series)\n>\n> * ms/customizable-ident-expansion\n>\n>   t/t0021-conversion.sh\n>     this is a messy conflict but resolution is simple enough: keep all\n>     the changes made by ms/customizable-ident-expansion and throw away\n>     the changes by this series; this will leave a few broken &&-chains\n>     in t0021-conversion.sh but there are a few other topics in 'seen'\n>     with such problems already, so it has company; anyhow, \"What's\n>     Cooking\" indicates that ms/customizable-ident-expansion is going to\n>     be discarded, so it may not be worth worrying about it\n>\n> [1]: https://lore.kernel.org/git/YJzGcZpZ+E9R0gYd@coredump.intra.peff.net/\n>\n> Eric Sunshine (19):\n>   t/lib-pager: use sane_unset() to avoid breaking &&-chain\n>   t1010: fix unnoticed failure on Windows\n>   t1020: avoid aborting entire test script when one test fails\n>   t4202: clarify intent by creating expected content less cleverly\n>   t5516: drop unnecessary subshell and command invocation\n>   t6300: make `%(raw:size) --shell` test more robust\n>   t9107: use shell parameter expansion to avoid breaking &&-chain\n>   tests: simplify construction of large blocks of text\n>   tests: use test_write_lines() to generate line-oriented output\n>   tests: fix broken &&-chains in compound statements\n>   tests: fix broken &&-chains in `$(...)` command substitutions\n>   tests: fix broken &&-chains in `{...}` groups\n>   tests: apply modern idiom for signaling test failure\n>   tests: apply modern idiom for exiting loop upon failure\n>   tests: simplify by dropping unnecessary `for` loops\n>   t0000-t3999: detect and signal failure within loop\n>   t4000-t4999: detect and signal failure within loop\n>   t5000-t5999: detect and signal failure within loop\n>   t6000-t9999: detect and signal failure within loop\n>\n>  .../mw-to-git/t/t9365-continuing-queries.sh   |   2 +-\n>  contrib/subtree/t/t7900-subtree.sh            |   2 +-\n>  t/annotate-tests.sh                           |   2 +-\n>  t/lib-pager.sh                                |   2 +-\n>  t/perf/p0005-status.sh                        |  12 +-\n>  t/perf/p0006-read-tree-checkout.sh            |  20 +-\n>  t/perf/p0007-write-cache.sh                   |   4 +-\n>  t/perf/p0100-globbing.sh                      |   4 +-\n>  t/perf/p1400-update-ref.sh                    |   4 +-\n>  t/perf/p1451-fsck-skip-list.sh                |   2 +-\n>  t/perf/p3400-rebase.sh                        |   2 +-\n>  t/perf/p5302-pack-index.sh                    |   4 +-\n>  t/perf/p5303-many-packs.sh                    |  10 +-\n>  t/perf/p7519-fsmonitor.sh                     |   8 +-\n>  t/t0005-signals.sh                            |   2 +-\n>  t/t0008-ignores.sh                            |   2 +-\n>  t/t0011-hashmap.sh                            |   4 +-\n>  t/t0020-crlf.sh                               |  28 +-\n>  t/t0021-conversion.sh                         |  60 ++--\n>  t/t0026-eol-config.sh                         |   4 +-\n>  t/t0060-path-utils.sh                         |   4 +-\n>  t/t0069-oidtree.sh                            |  12 +-\n>  t/t0095-bloom.sh                              |   4 +-\n>  t/t0410-partial-clone.sh                      |   2 +-\n>  t/t1006-cat-file.sh                           |  12 +-\n>  t/t1010-mktree.sh                             |   4 +-\n>  t/t1020-subdirectory.sh                       |  10 +-\n>  t/t1050-large.sh                              |  34 +-\n>  t/t1091-sparse-checkout-builtin.sh            |   2 +-\n>  t/t1300-config.sh                             |   6 +-\n>  t/t1400-update-ref.sh                         |   4 +-\n>  t/t1403-show-ref.sh                           |  12 +-\n>  t/t1410-reflog.sh                             |   4 +-\n>  t/t1512-rev-parse-disambiguation.sh           |  12 +-\n>  t/t1700-split-index.sh                        |   4 +-\n>  t/t2004-checkout-cache-temp.sh                |   4 +-\n>  t/t2012-checkout-last.sh                      |   4 +-\n>  t/t2102-update-index-symlinks.sh              |   2 +-\n>  t/t2103-update-index-ignore-missing.sh        |   2 +-\n>  t/t2200-add-update.sh                         |  18 +-\n>  t/t2201-add-update-typechange.sh              |  10 +-\n>  t/t2203-add-intent.sh                         |   2 +-\n>  t/t3005-ls-files-relative.sh                  |  10 +-\n>  t/t3070-wildmatch.sh                          |   2 +-\n>  t/t3202-show-branch.sh                        |   8 +-\n>  t/t3303-notes-subtrees.sh                     |   6 +-\n>  t/t3305-notes-fanout.sh                       |   4 +-\n>  t/t3402-rebase-merge.sh                       |   8 +-\n>  t/t3404-rebase-interactive.sh                 |   4 +-\n>  t/t3417-rebase-whitespace-fix.sh              |   4 +-\n>  t/t3501-revert-cherry-pick.sh                 |   2 +-\n>  t/t3508-cherry-pick-many-commits.sh           |   2 +-\n>  t/t3600-rm.sh                                 |   7 +-\n>  t/t3700-add.sh                                |   8 +-\n>  t/t3920-crlf-messages.sh                      |   4 +-\n>  t/t4001-diff-rename.sh                        |   2 +-\n>  t/t4012-diff-binary.sh                        |   2 +-\n>  t/t4013-diff-various.sh                       |  22 +-\n>  t/t4014-format-patch.sh                       |  32 +-\n>  t/t4015-diff-whitespace.sh                    |   4 +-\n>  t/t4018-diff-funcname.sh                      |   2 +-\n>  t/t4019-diff-wserror.sh                       |   4 +-\n>  t/t4023-diff-rename-typechange.sh             |   6 +-\n>  t/t4024-diff-optimize-common.sh               |   2 +-\n>  t/t4025-hunk-header.sh                        |  10 +-\n>  t/t4038-diff-combined.sh                      |   2 +-\n>  t/t4046-diff-unmerged.sh                      |  10 +-\n>  t/t4049-diff-stat-count.sh                    |   2 +-\n>  t/t4052-stat-output.sh                        |   2 +-\n>  t/t4057-diff-combined-paths.sh                |  16 +-\n>  t/t4105-apply-fuzz.sh                         |  10 +-\n>  t/t4106-apply-stdin.sh                        |   5 +-\n>  t/t4116-apply-reverse.sh                      |   4 +-\n>  t/t4117-apply-reject.sh                       |  20 +-\n>  t/t4118-apply-empty-context.sh                |   6 +-\n>  t/t4123-apply-shrink.sh                       |   4 +-\n>  t/t4124-apply-ws-rule.sh                      |  58 ++--\n>  t/t4125-apply-ws-fuzz.sh                      |   5 +-\n>  t/t4126-apply-empty.sh                        |   5 +-\n>  t/t4127-apply-same-fn.sh                      |   5 +-\n>  t/t4138-apply-ws-expansion.sh                 |  36 +-\n>  t/t4150-am.sh                                 |   2 +-\n>  t/t4151-am-abort.sh                           |  10 +-\n>  t/t4202-log.sh                                |  42 +--\n>  t/t4205-log-pretty-formats.sh                 |   2 +-\n>  t/t4211-line-log.sh                           |   2 +-\n>  t/t4212-log-corrupt.sh                        |   8 +-\n>  t/t4216-log-bloom.sh                          |   4 +-\n>  t/t5000-tar-tree.sh                           |   4 +-\n>  t/t5003-archive-zip.sh                        |   2 +-\n>  t/t5004-archive-corner-cases.sh               |   6 +-\n>  t/t5100-mailinfo.sh                           |   2 +-\n>  t/t5300-pack-object.sh                        |  18 +-\n>  t/t5302-pack-index.sh                         |   2 +-\n>  t/t5306-pack-nobase.sh                        |   2 +-\n>  t/t5307-pack-missing-commit.sh                |   2 +-\n>  t/t5310-pack-bitmaps.sh                       |   2 +-\n>  t/t5316-pack-delta-depth.sh                   |   7 +-\n>  t/t5317-pack-objects-filter-objects.sh        |  30 +-\n>  t/t5318-commit-graph.sh                       |   6 +-\n>  t/t5319-multi-pack-index.sh                   |  10 +-\n>  t/t5322-pack-objects-sparse.sh                |   4 +-\n>  t/t5325-reverse-index.sh                      |   2 +-\n>  t/t5500-fetch-pack.sh                         |   8 +-\n>  t/t5502-quickfetch.sh                         |   2 +-\n>  t/t5505-remote.sh                             |   6 +-\n>  t/t5510-fetch.sh                              |  14 +-\n>  t/t5515-fetch-merge-logic.sh                  |  38 +--\n>  t/t5516-fetch-push.sh                         |   5 +-\n>  t/t5552-skipping-fetch-negotiator.sh          |  10 +-\n>  t/t5562-http-backend-content-length.sh        |   2 +-\n>  t/t5570-git-daemon.sh                         |   2 +-\n>  t/t5571-pre-push-hook.sh                      |   6 +-\n>  t/t5611-clone-config.sh                       |   2 +-\n>  t/t5616-partial-clone.sh                      |  30 +-\n>  t/t5702-protocol-v2.sh                        |   4 +-\n>  t/t6005-rev-list-count.sh                     |   8 +-\n>  t/t6009-rev-list-parent.sh                    |   6 +-\n>  t/t6019-rev-list-ancestry-path.sh             |  10 +-\n>  t/t6060-merge-index.sh                        |   4 +-\n>  t/t6101-rev-parse-parents.sh                  |   2 +-\n>  t/t6112-rev-list-filters-objects.sh           |  22 +-\n>  t/t6120-describe.sh                           |  13 +-\n>  t/t6132-pathspec-exclude.sh                   |   2 +-\n>  t/t6200-fmt-merge-msg.sh                      |   2 +-\n>  t/t6300-for-each-ref.sh                       |   7 +-\n>  t/t6406-merge-attr.sh                         |   8 +-\n>  t/t6407-merge-binary.sh                       |   4 +-\n>  t/t6409-merge-subtree.sh                      |   6 +-\n>  t/t6411-merge-filemode.sh                     |   8 +-\n>  t/t6412-merge-large-rename.sh                 |  10 +-\n>  t/t6416-recursive-corner-cases.sh             |  30 +-\n>  t/t6417-merge-ours-theirs.sh                  |   5 +-\n>  t/t6430-merge-recursive.sh                    |   2 +-\n>  t/t6600-test-reach.sh                         |   4 +-\n>  t/t7004-tag.sh                                |   9 +-\n>  t/t7010-setup.sh                              |   2 +-\n>  t/t7110-reset-merge.sh                        |   2 +-\n>  t/t7501-commit-basic-functionality.sh         |   5 +-\n>  t/t7505-prepare-commit-msg-hook.sh            |   2 +-\n>  t/t7513-interpret-trailers.sh                 |   2 +-\n>  t/t7519-status-fsmonitor.sh                   |   2 +-\n>  t/t7600-merge.sh                              |   2 +-\n>  t/t7602-merge-octopus-many.sh                 |   4 +-\n>  t/t7603-merge-reduce-heads.sh                 |   4 +-\n>  t/t7700-repack.sh                             |   2 +-\n>  t/t7810-grep.sh                               | 310 +++++++++---------\n>  t/t8002-blame.sh                              |   2 +-\n>  t/t8003-blame-corner-cases.sh                 |  10 +-\n>  t/t8014-blame-ignore-fuzzy.sh                 |   4 +-\n>  t/t9104-git-svn-follow-parent.sh              |   4 +-\n>  t/t9107-git-svn-migrate.sh                    |   8 +-\n>  t/t9130-git-svn-authors-file.sh               |   6 +-\n>  t/t9134-git-svn-ignore-paths.sh               |  16 +-\n>  t/t9138-git-svn-authors-prog.sh               |   2 +-\n>  t/t9146-git-svn-empty-dirs.sh                 |   4 +-\n>  t/t9147-git-svn-include-paths.sh              |  16 +-\n>  t/t9152-svn-empty-dirs-after-gc.sh            |   2 +-\n>  t/t9304-fast-import-marks.sh                  |   2 +-\n>  t/t9400-git-cvsserver-server.sh               |  11 +-\n>  t/t9800-git-p4-basic.sh                       |   2 +-\n>  t/t9818-git-p4-block.sh                       |   6 +-\n>  t/t9902-completion.sh                         |   4 +-\n>  163 files changed, 740 insertions(+), 847 deletions(-)\n>\n> --\n> 2.34.1.307.g9b7440fafd\n>\n"},{"id":"443669","messageId":"CAPig+cRD_DzisMo-8ZuT4NzESEe4i2vPk_1Y-_JTeV9rbdwkLg@mail.gmail.com","threadId":"57057","inReplyTo":"CABPp-BFM5ZbFAzVfvDE3=zm6Q4LN2fWthPP8WH5kbgVPSxomtA@mail.gmail.com","subject":"Re: [PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-09T19:17:32Z","receivedAt":"2021-12-09T19:17:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 9, 2021 at 12:03 PM Elijah Newren <newren@gmail.com> wrote:\n> On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > Although the new chainlint implementation has been complete for several\n> > months, I'm still working out how to organize its patch series. In the\n> > meantime, _this_ patch series fixes problems discovered by the new\n> > linter due to its improved coverage and extra semantic knowledge about\n> > Git tests. As much as possible, I resisted the temptation to make\n> > ancillary cleanups (including indentation fixes) to tests even when such\n> > cleanups would be obvious improvements. Avoiding such unrelated cleanups\n> > should make the long patches in this series, which touch a lot of tests,\n> > easier to review (--color-words helps a lot here).\n>\n> I have to admit to starting to skim once I got to the last four\n> patches, since they were a bit longer and all the same type of change.\n\nUnderstandable. Those bulk-change patches tend to be mind-numbing to\nreview, though `git diff --color-words` helps out somewhat (at least\nby making it easier to skim the changes).\n\n> You did an excellent job of explaining the changes and presented them\n> in a logical fashion.  The few things I thought I caught, you've\n> already answered were already correct.  I do think making the second\n> commit message be a bit clearer about the importance of the ordering\n> would be helpful.  Anyway:\n>\n> Reviewed-by: Elijah Newren <newren@gmail.com>\n\nThanks. I'll wait a couple days and resend with a clarified commit\nmessage for the second patch unless, perhaps, Junio would accept a\nresend of just that patch so I don't have to spam the list again.\n"},{"id":"443757","messageId":"YbMZMITRqyHmNC7Z@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-5-sunshine@sunshineco.com","subject":"Re: [PATCH 04/19] t4202: clarify intent by creating expected content less cleverly","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:09:04Z","receivedAt":"2021-12-10T09:09:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:00AM -0500, Eric Sunshine wrote:\n\n> Several tests assign the output of `$(...)` command substitution to an\n> \"expect\" variable, taking advantage of the fact that `$(...)` folds out\n> the final line terminator while leaving internal line terminators\n> intact. They do this because the \"actual\" string with which \"expect\"\n> will be compared is shaped the same way. However, this intent (having\n> internal line terminators, but no final line terminator) is not\n> necessarily obvious at first glance and may confuse casual readers. The\n> intent can be made more obvious by using `printf` instead, with which\n> line termination is stated clearly:\n> \n>     printf \"sixth\\nthird\"\n> \n> In fact, many other tests in this script already use `printf` for\n> precisely this purpose, thus it is an established pattern. Therefore,\n> convert these tests to employ `printf`, as well.\n\nSeems reasonable. I don't think these tests actually care about the lack\nof trailing newline, so another option is to use tformat. Or its shorter\ncousin, --format. E.g.:\n\n> -\tactual=$(git log --pretty=\"format:%s\" --diff-filter=M HEAD) &&\n> -\texpect=$(echo second) &&\n> -\tverbose test \"$actual\" = \"$expect\"\n> +\tgit log --pretty=\"format:%s\" --diff-filter=M HEAD >actual &&\n> +\tprintf \"second\" >expect &&\n> +\ttest_cmp expect actual\n\nbecomes:\n\n  git log --format=%s --diff-filter=M HEAD >actual &&\n  echo second >expect &&\n  test_cmp expect actual\n\nwhich is even less magical. But if the existing pattern is there in\nnearby tests, I don't have any problem with following it.\n\n> While at it, modernize the tests to use test_cmp() to compare the\n> expected and actual output rather than using the semi-deprecated\n> `verbose test \"$x\" = \"$y\"`.\n\nYay. Happy to see more \"verbose\" calls cleaned up.\n\n-Peff\n"},{"id":"443758","messageId":"YbMZkdaEt6Q+MtR5@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-6-sunshine@sunshineco.com","subject":"Re: [PATCH 05/19] t5516: drop unnecessary subshell and command invocation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:10:41Z","receivedAt":"2021-12-10T09:10:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:01AM -0500, Eric Sunshine wrote:\n\n> To create its \"expect\" file, this test pipes into `sort` the output of\n> `git for-each-ref` and a copy of that same output but with a minor\n> textual transformation applied. To do so, it employs a subshell and\n> commands `cat` and `sed` even though the same result can be accomplished\n> by `sed` alone (without a subshell).\n\nClever. The ordering of output from sed is different than the original,\nbut because it all gets fed into sort anyway, that's OK.\n\nIn theory it could change the output of a stable sort of lines which\nmatch (which won't be totally identical, because you are sorting with\n-k3), but it seems we don't care in this instance.\n\n-Peff\n"},{"id":"443759","messageId":"YbMaadyT+YmZeGlB@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-7-sunshine@sunshineco.com","subject":"Re: [PATCH 06/19] t6300: make `%(raw:size) --shell` test more robust","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:14:17Z","receivedAt":"2021-12-10T09:14:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:02AM -0500, Eric Sunshine wrote:\n\n> This test populates its `expect` file solely by appending content but\n> fails to ensure that the file starts out empty. The test succeeds only\n> because no earlier test populated a file of the exact same name, however\n> this is an accident waiting to happen. Make the test more robust by\n> ensuring that it contains exactly the intended content.\n\nAgreed.\n\n> While at it, simplify the implementation via a straightforward `sed`\n> application and by avoiding dropping out of the single-quote context\n> within the test body (thus eliminating a hard-to-digest combination of\n> apostrophes and backslashes).\n\nI find them equally ugly. :) The most confusing thing is that we are not\ndoing any shell quoting at all, just adding single-quote wrappers. But\nthat is OK because %(raw:size) should never need quoting.\n\nYour solution seems fine to me.\n\n-Peff\n"},{"id":"443760","messageId":"YbMcYwj/Mvf/PoFF@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-10-sunshine@sunshineco.com","subject":"Re: [PATCH 09/19] tests: use test_write_lines() to generate line-oriented output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:22:43Z","receivedAt":"2021-12-10T09:22:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:05AM -0500, Eric Sunshine wrote:\n\n> Take advantage of test_write_lines() to generate line-oriented output\n> rather than using for-loops or a series of `echo` commands. Not only is\n> test_write_lines() a natural fit for such a task, but there is less\n> opportunity for a broken &&-chain.\n\nMakes sense. A few of these append like this:\n\n> -\tfor w in Some extra lines here; do echo $w; done >>one &&\n> +\ttest_write_lines Some extra lines here >>one &&\n\nwhich made me wonder if the original really wanted to append, or if they\nmeant:\n\n  for w in Some extra lines here; do echo $w >>one; done\n\nin the first place. In which case you could write \">one\". But in the\ncases I peeked at, they really are appending to a file that already\nexisted. And at any rate, your conversions are all faithful to the\noriginal, which is the right thing to do to avoid introducing bugs.\n\n>  test_expect_success 'color new trailing blank lines' '\n> -\t{ echo a; echo b; echo; echo; } >x &&\n> +\ttest_write_lines a b \"\" \"\" >x &&\n>  \tgit add x &&\n> -\t{ echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&\n> +\ttest_write_lines a \"\" \"\" \"\" c \"\" \"\" \"\" \"\" >x &&\n\nSome of these I think might be more readable as here-docs. But I think\nkeeping to the minimal change here makes sense (and I admit I do not\noverly care much either way; it was just on my mind from the last\npatch).\n\n-Peff\n"},{"id":"443762","messageId":"YbMddynehrDgsSWO@coredump.intra.peff.net","threadId":"57057","inReplyTo":"CAPig+cQzTnQiwT4Nmpp8qYJOaRpy2pK7DDOu42Wk-1TfmbUxSg@mail.gmail.com","subject":"Re: [PATCH 11/19] tests: fix broken &&-chains in `$(...)` command substitutions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:27:19Z","receivedAt":"2021-12-10T09:27:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 11:53:47AM -0500, Eric Sunshine wrote:\n\n> On Thu, Dec 9, 2021 at 11:44 AM Elijah Newren <newren@gmail.com> wrote:\n> > On Wed, Dec 8, 2021 at 11:39 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > >  test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '\n> > > -       OUT=$( ((trap \"\" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> > > +       OUT=$( ((trap \"\" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&\n> >\n> > Shouldn't the second ';' be replaced with '&&' as well?\n> \n> Thanks for reading so carefully. In this case, the answer is \"no\", the\n> semicolon is correct. This code legitimately wants to capture in the\n> OUT variable the numeric exit status of the command preceding `echo\n> $?`. If the semicolon is replaced with `&&`, then the echo won't be\n> executed if the exit status is non-zero, but we want `echo` to be\n> executed regardless of the exit status. So, the code is correct with\n> the semicolon, and would be incorrect with `&&`. (I hope I'm\n> explaining this well enough to make sense.)\n\nThat makes sense to me. I wondered why it was even worth changing the\nearlier semi-colon in that case, then, but...\n\n> It's this sort of special case which accounts for why the new linter\n> (as mentioned in the cover letter) has special understanding that a\n> broken &&-chain can be legitimate in certain circumstances, such as\n> explicit handling of `$?`.\n\n...your unseen magic script explains it. :)\n\nAll of the changes here look reasonable. We'd either want to know about\nfailure (e.g., \"cd\") or don't expect it to fail (e.g., \"echo\").\n\nThese \"trap\" calls are probably fine. I can't imagine why they'd fail,\nbut being a weird shell builtin I wonder if it's possible for them to\nfail in odd circumstances. I'm happy to leave that as a hypothetical\nuntil we see it in practice.\n\n-Peff\n"},{"id":"443763","messageId":"YbMd4cuRs9aiPWbX@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-13-sunshine@sunshineco.com","subject":"Re: [PATCH 12/19] tests: fix broken &&-chains in `{...}` groups","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:29:05Z","receivedAt":"2021-12-10T09:29:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:08AM -0500, Eric Sunshine wrote:\n\n> The top-level &&-chain checker built into t/test-lib.sh causes tests to\n> magically exit with code 117 if the &&-chain is broken. However, it has\n> the shortcoming that the magic does not work within `{...}` groups,\n> `(...)` subshells, `$(...)` substitutions, or within bodies of compound\n> statements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\n> partly fills in the gap by catching broken &&-chains in `(...)`\n> subshells, but bugs can still lurk behind broken &&-chains in the other\n> cases.\n> \n> Fix broken &&-chains in `{...}` groups in order to reduce the number of\n> possible lurking bugs.\n\nSeems good. This is mostly stuff we don't expect to fail (mostly\n\"echo\"), so I doubt they're important on their own. But getting a clean\nstate for the linter _is_ important.\n\n-Peff\n"},{"id":"443764","messageId":"YbMel5UZm9ESM5Jr@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-14-sunshine@sunshineco.com","subject":"Re: [PATCH 13/19] tests: apply modern idiom for signaling test failure","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:32:07Z","receivedAt":"2021-12-10T09:32:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:09AM -0500, Eric Sunshine wrote:\n\n> Simplify the way these tests signal failure by employing the modern\n> idiom of making the `if` or `case` statement resolve to false when an\n> error is detected.\n\nYeah, these are pretty non-idiomatic, but...\n\n> diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\n> index cfde68f193..7e46f4ca85 100755\n> --- a/t/t3402-rebase-merge.sh\n> +++ b/t/t3402-rebase-merge.sh\n> @@ -68,7 +68,7 @@ test_expect_success 'merge and rebase should match' '\n>  \tif test -s difference\n>  \tthen\n>  \t\tcat difference\n> -\t\t(exit 1)\n> +\t\tfalse\n>  \telse\n>  \t\techo happy\n>  \tfi\n\n...I'd have said the idiom here is just:\n\n  git diff-tree -r test-rebase test-merge >difference &&\n  test -s difference\n\nThe extra \"cat\" and \"happy\" are verbose output that we usually skip in\nfavor of letting \"-x\" logging do the talking (and leaving the failed\nstate so you can \"cat difference\" yourself).\n\nThat said, I'm OK with this minimal change in the name of keeping creep\nout of the series.\n\n-Peff\n"},{"id":"443765","messageId":"YbMfhGCCO2nvTVo6@coredump.intra.peff.net","threadId":"57057","inReplyTo":"20211209051115.52629-15-sunshine@sunshineco.com","subject":"Re: [PATCH 14/19] tests: apply modern idiom for exiting loop upon failure","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:36:04Z","receivedAt":"2021-12-10T09:36:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 12:11:10AM -0500, Eric Sunshine wrote:\n\n> diff --git a/t/t1050-large.sh b/t/t1050-large.sh\n> index 99ff2866b7..0e4267c723 100755\n> --- a/t/t1050-large.sh\n> +++ b/t/t1050-large.sh\n> @@ -51,27 +51,21 @@ EOF\n>  test_expect_success 'add a large file or two' '\n>  \tgit add large1 huge large2 &&\n>  \t# make sure we got a single packfile and no loose objects\n> -\tbad= count=0 idx= &&\n> +\tcount=0 idx= &&\n>  \tfor p in .git/objects/pack/pack-*.pack\n>  \tdo\n>  \t\tcount=$(( $count + 1 )) &&\n> -\t\tif test_path_is_file \"$p\" &&\n> -\t\t   idx=${p%.pack}.idx && test_path_is_file \"$idx\"\n> -\t\tthen\n> -\t\t\tcontinue\n> -\t\tfi\n> -\t\tbad=t\n> +\t\ttest_path_is_file \"$p\" &&\n> +\t\tidx=${p%.pack}.idx &&\n> +\t\ttest_path_is_file \"$idx\" || return 1\n>  \tdone &&\n> -\ttest -z \"$bad\" &&\n\nThanks goodness. I had to read the original loop several times to\nunderstand what it was trying to do. The post-image is much nicer.\n\n> diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh\n> index 17f988edd2..a6a73effde 100755\n> --- a/t/t9400-git-cvsserver-server.sh\n> +++ b/t/t9400-git-cvsserver-server.sh\n> @@ -350,10 +350,9 @@ test_expect_success 'cvs update (subdirectories)' \\\n>  \ttest_cmp \"$dir/$filename\" \"../$dir/$filename\"; then\n>          :\n>        else\n> -        echo >failure\n> +        exit 1\n>        fi\n> -    done) &&\n> -   test ! -f failure'\n> +    done)'\n\nThese all look good. I had to blink a few times to see the subshell in\nthis one (it's finished by the closing paren after \"done\", for other\nreviewers).\n\n-Peff\n"},{"id":"443766","messageId":"20211210093827.a5s5mby3if5f7a4u@fs","threadId":"57057","inReplyTo":"20211209051115.52629-13-sunshine@sunshineco.com","subject":"Re: [PATCH 12/19] tests: fix broken &&-chains in `{...}` groups","fromName":"Fabian Stelzer","fromEmail":"fs@gigacodes.de","sentAt":"2021-12-10T09:38:27Z","receivedAt":"2021-12-10T09:38:37Z","isPatch":true,"sender":{"key":"fs@gigacodes.de","avatar":"https://avatars.githubusercontent.com/u/564858?v=4"},"body":"On 09.12.2021 00:11, Eric Sunshine wrote:\n>The top-level &&-chain checker built into t/test-lib.sh causes tests to\n>magically exit with code 117 if the &&-chain is broken. However, it has\n>the shortcoming that the magic does not work within `{...}` groups,\n>`(...)` subshells, `$(...)` substitutions, or within bodies of compound\n>statements, such as `if`, `for`, `while`, `case`, etc. `chainlint.sed`\n>partly fills in the gap by catching broken &&-chains in `(...)`\n>subshells, but bugs can still lurk behind broken &&-chains in the other\n>cases.\n>\n>Fix broken &&-chains in `{...}` groups in order to reduce the number of\n>possible lurking bugs.\n>\n>Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n>---\n>\n>diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\n>index 1a1a69ad92..bb3de2701a 100755\n>--- a/t/t0021-conversion.sh\n>+++ b/t/t0021-conversion.sh\n>@@ -76,13 +76,13 @@ test_expect_success setup '\n> \tgit config filter.rot13.clean ./rot13.sh &&\n>\n> \t{\n>-\t    echo \"*.t filter=rot13\"\n>+\t    echo \"*.t filter=rot13\" &&\n> \t    echo \"*.i ident\"\n> \t} >.gitattributes &&\n>\n> \t{\n>-\t    echo a b c d e f g h i j k l m\n>-\t    echo n o p q r s t u v w x y z\n>+\t    echo a b c d e f g h i j k l m &&\n>+\t    echo n o p q r s t u v w x y z &&\n> \t    echo '\\''$Id$'\\''\n> \t} >test &&\n> \tcat test >test.t &&\n>@@ -159,7 +159,7 @@ test_expect_success expanded_in_repo '\n> \t\tprintf \"\\$Id: NoTerminatingSymbolAtEOF\"\n> \t} >expected-output-crlf &&\n> \t{\n>-\t\techo \"expanded-keywords ident\"\n>+\t\techo \"expanded-keywords ident\" &&\n> \t\techo \"expanded-keywords-crlf ident text eol=crlf\"\n> \t} >>.gitattributes &&\n>\n\nWouldn't some of these be better off as heredocs as well?\nThere are a couple more below. I personally don't much mind either way but \nsince you changed quite a few in an earlier commit why not these?\n\n>diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh\n>index 74cc59bf8a..889db50818 100755\n>--- a/t/t0069-oidtree.sh\n>+++ b/t/t0069-oidtree.sh\n>@@ -28,7 +28,7 @@ test_expect_success 'oidtree insert and contains' '\n> \tEOF\n> \t{\n> \t\techoid insert 444 1 2 3 4 5 a b c d e &&\n>-\t\techoid contains 44 441 440 444 4440 4444\n>+\t\techoid contains 44 441 440 444 4440 4444 &&\n> \t\techo clear\n> \t} | test-tool oidtree >actual &&\n> \ttest_cmp expect actual\n>@@ -37,11 +37,11 @@ test_expect_success 'oidtree insert and contains' '\n> test_expect_success 'oidtree each' '\n> \techoid \"\" 123 321 321 >expect &&\n> \t{\n>-\t\techoid insert f 9 8 123 321 a b c d e\n>-\t\techo each 12300\n>-\t\techo each 3211\n>-\t\techo each 3210\n>-\t\techo each 32100\n>+\t\techoid insert f 9 8 123 321 a b c d e &&\n>+\t\techo each 12300 &&\n>+\t\techo each 3211 &&\n>+\t\techo each 3210 &&\n>+\t\techo each 32100 &&\n> \t\techo clear\n> \t} | test-tool oidtree >actual &&\n> \ttest_cmp expect actual\n>diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\n>index 67a3f64c2d..f6f00c7039 100755\n>--- a/t/t1006-cat-file.sh\n>+++ b/t/t1006-cat-file.sh\n>@@ -283,7 +283,7 @@ test_expect_success \"--batch-check with multiple sha1s gives correct format\" '\n>\n> test_expect_success 'setup blobs which are likely to delta' '\n> \ttest-tool genrandom foo 10240 >foo &&\n>-\t{ cat foo; echo plus; } >foo-plus &&\n>+\t{ cat foo && echo plus; } >foo-plus &&\n> \tgit add foo foo-plus &&\n> \tgit commit -m foo &&\n> \tcat >blobs <<-\\EOF\n>diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n>index 9571649c42..516dd8bfa8 100755\n>--- a/t/t1300-config.sh\n>+++ b/t/t1300-config.sh\n>@@ -901,7 +901,7 @@ test_expect_success 'get --expiry-date' '\n> \tEOF\n> \t: \"work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)\" &&\n> \t{\n>-\t\techo \"$rel_out $(git config --expiry-date date.valid1)\"\n>+\t\techo \"$rel_out $(git config --expiry-date date.valid1)\" &&\n> \t\tgit config --expiry-date date.valid2 &&\n> \t\tgit config --expiry-date date.valid3 &&\n> \t\tgit config --expiry-date date.valid4 &&\n>diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\n>index 17d3cc1405..bbc01aae34 100755\n>--- a/t/t1403-show-ref.sh\n>+++ b/t/t1403-show-ref.sh\n>@@ -78,7 +78,7 @@ test_expect_success 'show-ref --verify -q' '\n> test_expect_success 'show-ref -d' '\n> \t{\n> \t\techo $(git rev-parse refs/tags/A) refs/tags/A &&\n>-\t\techo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\"\n>+\t\techo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\" &&\n> \t\techo $(git rev-parse refs/tags/C) refs/tags/C\n> \t} >expect &&\n> \tgit show-ref -d A C >actual &&\n>@@ -148,7 +148,7 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '\n>\n> \t{\n> \t\techo $(git rev-parse HEAD) HEAD &&\n>-\t\techo $(git rev-parse refs/heads/B) refs/heads/B\n>+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n> \t\techo $(git rev-parse refs/tags/B) refs/tags/B\n> \t} >expect &&\n> \tgit show-ref --head B >actual &&\n>@@ -156,8 +156,8 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '\n>\n> \t{\n> \t\techo $(git rev-parse HEAD) HEAD &&\n>-\t\techo $(git rev-parse refs/heads/B) refs/heads/B\n>-\t\techo $(git rev-parse refs/tags/B) refs/tags/B\n>+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n>+\t\techo $(git rev-parse refs/tags/B) refs/tags/B &&\n> \t\techo $(git rev-parse refs/tags/B^0) \"refs/tags/B^{}\"\n> \t} >expect &&\n> \tgit show-ref --head -d B >actual &&\n>diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\n>index 67b9cc752f..d2ef0041f9 100755\n>--- a/t/t2200-add-update.sh\n>+++ b/t/t2200-add-update.sh\n>@@ -153,10 +153,10 @@ test_expect_success 'add -u resolves unmerged paths' '\n> \t\t\techo \"100644 $one 1\t$path\" &&\n> \t\t\techo \"100644 $two 2\t$path\" &&\n> \t\t\techo \"100644 $three 3\t$path\"\n>-\t\tdone\n>-\t\techo \"100644 $one 1\tpath3\"\n>-\t\techo \"100644 $one 1\tpath4\"\n>-\t\techo \"100644 $one 3\tpath5\"\n>+\t\tdone &&\n>+\t\techo \"100644 $one 1\tpath3\" &&\n>+\t\techo \"100644 $one 1\tpath4\" &&\n>+\t\techo \"100644 $one 3\tpath5\" &&\n> \t\techo \"100644 $one 3\tpath6\"\n> \t} |\n> \tgit update-index --index-info &&\n>@@ -173,8 +173,8 @@ test_expect_success 'add -u resolves unmerged paths' '\n> \tgit add -u &&\n> \tgit ls-files -s path1 path2 path3 path4 path5 path6 >actual &&\n> \t{\n>-\t\techo \"100644 $three 0\tpath1\"\n>-\t\techo \"100644 $two 0\tpath3\"\n>+\t\techo \"100644 $three 0\tpath1\" &&\n>+\t\techo \"100644 $two 0\tpath3\" &&\n> \t\techo \"100644 $two 0\tpath5\"\n> \t} >expect &&\n> \ttest_cmp expect actual\n>diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh\n>index a4eec0a346..687be974d4 100755\n>--- a/t/t2201-add-update-typechange.sh\n>+++ b/t/t2201-add-update-typechange.sh\n>@@ -97,17 +97,17 @@ test_expect_success modify '\n> \t\t\"\n> \t} >expect &&\n> \t{\n>-\t\tcat expect\n>-\t\techo \":100644 160000 $_empty $ZERO_OID T\tyonk\"\n>+\t\tcat expect &&\n>+\t\techo \":100644 160000 $_empty $ZERO_OID T\tyonk\" &&\n> \t\techo \":100644 000000 $_empty $ZERO_OID D\tzifmia\"\n> \t} >expect-files &&\n> \t{\n>-\t\tcat expect\n>+\t\tcat expect &&\n> \t\techo \":000000 160000 $ZERO_OID $ZERO_OID A\tyonk\"\n> \t} >expect-index &&\n> \t{\n>-\t\techo \"100644 $_empty 0\tnitfol\"\n>-\t\techo \"160000 $yomin 0\tyomin\"\n>+\t\techo \"100644 $_empty 0\tnitfol\" &&\n>+\t\techo \"160000 $yomin 0\tyomin\" &&\n> \t\techo \"160000 $yonk 0\tyonk\"\n> \t} >expect-final\n> '\n>diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh\n>index 47d6f35dcc..7cb9909293 100755\n>--- a/t/t4023-diff-rename-typechange.sh\n>+++ b/t/t4023-diff-rename-typechange.sh\n>@@ -55,7 +55,7 @@ test_expect_success 'cross renames to be detected for regular files' '\n>\n> \tgit diff-tree five six -r --name-status -B -M | sort >actual &&\n> \t{\n>-\t\techo \"R100\tfoo\tbar\"\n>+\t\techo \"R100\tfoo\tbar\" &&\n> \t\techo \"R100\tbar\tfoo\"\n> \t} | sort >expect &&\n> \ttest_cmp expect actual\n>@@ -66,7 +66,7 @@ test_expect_success 'cross renames to be detected for typechange' '\n>\n> \tgit diff-tree one two -r --name-status -B -M | sort >actual &&\n> \t{\n>-\t\techo \"R100\tfoo\tbar\"\n>+\t\techo \"R100\tfoo\tbar\" &&\n> \t\techo \"R100\tbar\tfoo\"\n> \t} | sort >expect &&\n> \ttest_cmp expect actual\n>@@ -78,7 +78,7 @@ test_expect_success 'moves and renames' '\n> \tgit diff-tree three four -r --name-status -B -M | sort >actual &&\n> \t{\n> \t\t# see -B -M (#6) in t4008\n>-\t\techo \"C100\tfoo\tbar\"\n>+\t\techo \"C100\tfoo\tbar\" &&\n> \t\techo \"T100\tfoo\"\n> \t} | sort >expect &&\n> \ttest_cmp expect actual\n>diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\n>index ebff6c6883..ec5c10d2a0 100755\n>--- a/t/t4124-apply-ws-rule.sh\n>+++ b/t/t4124-apply-ws-rule.sh\n>@@ -233,7 +233,7 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '\n> \ttest_write_lines a b c >one &&\n> \tgit add one &&\n> \ttest_write_lines a b c >expect &&\n>-\t{ cat expect; echo; } >one &&\n>+\t{ cat expect && echo; } >one &&\n> \tgit diff -- one >patch &&\n>\n> \tgit checkout one &&\n>diff --git a/t/t4150-am.sh b/t/t4150-am.sh\n>index 2aaaa0d7de..103cd39148 100755\n>--- a/t/t4150-am.sh\n>+++ b/t/t4150-am.sh\n>@@ -116,7 +116,7 @@ test_expect_success setup '\n> \t\tgit format-patch --stdout first | sed -e \"1d\"\n> \t} | append_cr >patch1-crlf.eml &&\n> \t{\n>-\t\tprintf \"%255s\\\\n\" \"\"\n>+\t\tprintf \"%255s\\\\n\" \"\" &&\n> \t\techo \"X-Fake-Field: Line One\" &&\n> \t\techo \"X-Fake-Field: Line Two\" &&\n> \t\techo \"X-Fake-Field: Line Three\" &&\n>diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\n>index 03b952c90d..0244888a5a 100755\n>--- a/t/t4212-log-corrupt.sh\n>+++ b/t/t4212-log-corrupt.sh\n>@@ -20,10 +20,10 @@ test_expect_success 'fsck notices broken commit' '\n>\n> test_expect_success 'git log with broken author email' '\n> \t{\n>-\t\techo commit $(cat broken_email.hash)\n>-\t\techo \"Author: A U Thor <author@example.com>\"\n>-\t\techo \"Date:   Thu Apr 7 15:13:13 2005 -0700\"\n>-\t\techo\n>+\t\techo commit $(cat broken_email.hash) &&\n>+\t\techo \"Author: A U Thor <author@example.com>\" &&\n>+\t\techo \"Date:   Thu Apr 7 15:13:13 2005 -0700\" &&\n>+\t\techo &&\n> \t\techo \"    foo\"\n> \t} >expect.out &&\n>\n>diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\n>index 759169d074..df524f7b6d 100755\n>--- a/t/t5316-pack-delta-depth.sh\n>+++ b/t/t5316-pack-delta-depth.sh\n>@@ -57,8 +57,11 @@ test_expect_success 'create series of packs' '\n> \t\tgit commit -m $i &&\n> \t\tcur=$(git rev-parse HEAD^{tree}) &&\n> \t\t{\n>-\t\t\ttest -n \"$prev\" && echo \"-$prev\"\n>-\t\t\techo $cur\n>+\t\t\tif test -n \"$prev\"\n>+\t\t\tthen\n>+\t\t\t\techo \"-$prev\"\n>+\t\t\tfi &&\n>+\t\t\techo $cur &&\n> \t\t\techo \"$(git rev-parse :file) file\"\n> \t\t} | git pack-objects --stdout >tmp &&\n> \t\tgit index-pack --stdin --fix-thin <tmp || return 1\n>diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n>index 1892d6615a..01468ce6d8 100755\n>--- a/t/t5510-fetch.sh\n>+++ b/t/t5510-fetch.sh\n>@@ -71,7 +71,7 @@ test_expect_success \"fetch test for-merge\" '\n> \tmain_in_two=$(cd ../two && git rev-parse main) &&\n> \tone_in_two=$(cd ../two && git rev-parse one) &&\n> \t{\n>-\t\techo \"$one_in_two\t\"\n>+\t\techo \"$one_in_two\t\" &&\n> \t\techo \"$main_in_two\tnot-for-merge\"\n> \t} >expected &&\n> \tcut -f -2 .git/FETCH_HEAD >actual &&\n>diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\n>index 9d440e2821..c69cfd5c64 100755\n>--- a/t/t5515-fetch-merge-logic.sh\n>+++ b/t/t5515-fetch-merge-logic.sh\n>@@ -191,17 +191,17 @@ do\n> \t\tcp \"$expect_r\" expect_r &&\n> \t\tconvert_expected expect_r sed_script &&\n> \t\t{\n>-\t\t\techo \"# $cmd\"\n>-\t\t\tset x $cmd; shift\n>-\t\t\tgit symbolic-ref HEAD refs/heads/$1 ; shift\n>-\t\t\trm -f .git/FETCH_HEAD\n>+\t\t\techo \"# $cmd\" &&\n>+\t\t\tset x $cmd && shift &&\n>+\t\t\tgit symbolic-ref HEAD refs/heads/$1 && shift &&\n>+\t\t\trm -f .git/FETCH_HEAD &&\n> \t\t\tgit for-each-ref \\\n> \t\t\t\trefs/heads refs/remotes/rem refs/tags |\n> \t\t\twhile read val type refname\n> \t\t\tdo\n> \t\t\t\tgit update-ref -d \"$refname\" \"$val\"\n>-\t\t\tdone\n>-\t\t\tgit fetch \"$@\" >/dev/null\n>+\t\t\tdone &&\n>+\t\t\tgit fetch \"$@\" >/dev/null &&\n> \t\t\tcat .git/FETCH_HEAD\n> \t\t} >\"$actual_f\" &&\n> \t\tgit show-ref >\"$actual_r\" &&\n>diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh\n>index 05a58069b0..b68ec22d3f 100755\n>--- a/t/t5562-http-backend-content-length.sh\n>+++ b/t/t5562-http-backend-content-length.sh\n>@@ -63,7 +63,7 @@ test_expect_success 'setup' '\n> \thash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&\n> \t{\n> \t\tprintf \"%s %s refs/heads/newbranch\\\\0report-status object-format=%s\\\\n\" \\\n>-\t\t\t\"$ZERO_OID\" \"$hash_next\" \"$(test_oid algo)\" | packetize_raw\n>+\t\t\t\"$ZERO_OID\" \"$hash_next\" \"$(test_oid algo)\" | packetize_raw &&\n> \t\tprintf 0000 &&\n> \t\techo \"$hash_next\" | git pack-objects --stdout\n> \t} >push_body &&\n>diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh\n>index b87ca06a58..1131503b76 100755\n>--- a/t/t5570-git-daemon.sh\n>+++ b/t/t5570-git-daemon.sh\n>@@ -194,7 +194,7 @@ test_expect_success 'hostname cannot break out of directory' '\n>\n> test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '\n> \t{\n>-\t\tprintf \"git-upload-pack /interp.git\\n\\0host=localhost\" | packetize_raw\n>+\t\tprintf \"git-upload-pack /interp.git\\n\\0host=localhost\" | packetize_raw &&\n> \t\tprintf \"0000\"\n> \t} >input &&\n> \tfake_nc \"$GIT_DAEMON_HOST_PORT\" <input >output &&\n>diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh\n>index b043a279f1..80e86d8284 100755\n>--- a/t/t5571-pre-push-hook.sh\n>+++ b/t/t5571-pre-push-hook.sh\n>@@ -114,7 +114,7 @@ test_expect_success 'push to URL' '\n>\n> test_expect_success 'set up many-ref tests' '\n> \t{\n>-\t\tnr=1000\n>+\t\tnr=1000 &&\n> \t\twhile test $nr -lt 2000\n> \t\tdo\n> \t\t\tnr=$(( $nr + 1 )) &&\n>diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\n>index 04885d0a5e..97f10905d2 100755\n>--- a/t/t7513-interpret-trailers.sh\n>+++ b/t/t7513-interpret-trailers.sh\n>@@ -156,7 +156,7 @@ test_expect_success 'with config option on the command line' '\n> \t\tAcked-by: Johan\n> \t\tReviewed-by: Peff\n> \tEOF\n>-\t{ echo; echo \"Acked-by: Johan\"; } |\n>+\t{ echo && echo \"Acked-by: Johan\"; } |\n> \tgit -c \"trailer.Acked-by.ifexists=addifdifferent\" interpret-trailers \\\n> \t\t--trailer \"Reviewed-by: Peff\" --trailer \"Acked-by: Johan\" >actual &&\n> \ttest_cmp expected actual\n>diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh\n>index 5bb302b1ba..ee4fdd8f18 100755\n>--- a/t/t8002-blame.sh\n>+++ b/t/t8002-blame.sh\n>@@ -97,7 +97,7 @@ test_expect_success 'set up abbrev tests' '\n> \ttest_commit abbrev &&\n> \tsha1=$(git rev-parse --verify HEAD) &&\n> \tcheck_abbrev () {\n>-\t\texpect=$1; shift\n>+\t\texpect=$1 && shift &&\n> \t\techo $sha1 | cut -c 1-$expect >expect &&\n> \t\tgit blame \"$@\" abbrev.t >actual &&\n> \t\tperl -lne \"/[0-9a-f]+/ and print \\$&\" <actual >actual.sha &&\n>-- \n>2.34.1.307.g9b7440fafd\n>\n"},{"id":"443767","messageId":"YbMgL6A+/12GTeuf@coredump.intra.peff.net","threadId":"57057","inReplyTo":"CAPig+cRD_DzisMo-8ZuT4NzESEe4i2vPk_1Y-_JTeV9rbdwkLg@mail.gmail.com","subject":"Re: [PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-12-10T09:38:55Z","receivedAt":"2021-12-10T09:38:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 09, 2021 at 02:17:32PM -0500, Eric Sunshine wrote:\n\n> > I have to admit to starting to skim once I got to the last four\n> > patches, since they were a bit longer and all the same type of change.\n> \n> Understandable. Those bulk-change patches tend to be mind-numbing to\n> review, though `git diff --color-words` helps out somewhat (at least\n> by making it easier to skim the changes).\n\nI likewise think I may have phased in and out of consciousness on those\nlast four.\n\n> > You did an excellent job of explaining the changes and presented them\n> > in a logical fashion.  The few things I thought I caught, you've\n> > already answered were already correct.  I do think making the second\n> > commit message be a bit clearer about the importance of the ordering\n> > would be helpful.  Anyway:\n> >\n> > Reviewed-by: Elijah Newren <newren@gmail.com>\n> \n> Thanks. I'll wait a couple days and resend with a clarified commit\n> message for the second patch unless, perhaps, Junio would accept a\n> resend of just that patch so I don't have to spam the list again.\n\nThese looked good to me. I left a few comments, but nothing that I think\nwould trigger a re-roll.\n\n-Peff\n"},{"id":"443769","messageId":"20211210095303.xx3fxxtp5ww3s24x@fs","threadId":"57057","inReplyTo":"20211209051115.52629-18-sunshine@sunshineco.com","subject":"Re: [PATCH 17/19] t4000-t4999: detect and signal failure within loop","fromName":"Fabian Stelzer","fromEmail":"fs@gigacodes.de","sentAt":"2021-12-10T09:53:03Z","receivedAt":"2021-12-10T09:53:09Z","isPatch":true,"sender":{"key":"fs@gigacodes.de","avatar":"https://avatars.githubusercontent.com/u/564858?v=4"},"body":"On 09.12.2021 00:11, Eric Sunshine wrote:\n>Failures within `for` and `while` loops can go unnoticed if not detected\n>and signaled manually since the loop itself does not abort when a\n>contained command fails, nor will a failure necessarily be detected when\n>the loop finishes since the loop returns the exit code of the last\n>command it ran on the final iteration, which may not be the command\n>which failed. Therefore, detect and signal failures manually within\n>loops using the idiom `|| return 1` (or `|| exit 1` within subshells).\n>\n>Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n>---\n>diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\n>index 2c13b62d3c..ca5adabe14 100755\n>--- a/t/t4015-diff-whitespace.sh\n>+++ b/t/t4015-diff-whitespace.sh\n>@@ -843,7 +843,7 @@ test_expect_success 'whitespace changes with modification reported (diffstat)' '\n>\n> test_expect_success 'whitespace-only changes reported across renames (diffstat)' '\n> \tgit reset --hard &&\n>-\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\"; done >x &&\n>+\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\" || return 1; done >x &&\n> \tgit add x &&\n> \tgit commit -m \"base\" &&\n> \tsed -e \"5s/^/ /\" x >z &&\n>@@ -859,7 +859,7 @@ test_expect_success 'whitespace-only changes reported across renames (diffstat)'\n>\n> test_expect_success 'whitespace-only changes reported across renames' '\n> \tgit reset --hard HEAD~1 &&\n>-\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\"; done >x &&\n>+\tfor i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\" || return 1; done >x &&\n> \tgit add x &&\n> \thash_x=$(git hash-object x) &&\n> \tbefore=$(git rev-parse --short \"$hash_x\") &&\n\nI understand why the `|| return` in loops makes sense. But for these very \nsimple ones just using `echo` I'll probably be confused if a linter starts \nto complain about them (probably depending on how specific the lint error \nwill be).\n"},{"id":"443771","messageId":"20211210095757.gu7w4n2rqulx2dvg@fs","threadId":"57057","inReplyTo":"YbMgL6A+/12GTeuf@coredump.intra.peff.net","subject":"Re: [PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Fabian Stelzer","fromEmail":"fs@gigacodes.de","sentAt":"2021-12-10T09:57:57Z","receivedAt":"2021-12-10T09:58:02Z","isPatch":true,"sender":{"key":"fs@gigacodes.de","avatar":"https://avatars.githubusercontent.com/u/564858?v=4"},"body":"On 10.12.2021 04:38, Jeff King wrote:\n>On Thu, Dec 09, 2021 at 02:17:32PM -0500, Eric Sunshine wrote:\n>\n>> > I have to admit to starting to skim once I got to the last four\n>> > patches, since they were a bit longer and all the same type of change.\n>>\n>> Understandable. Those bulk-change patches tend to be mind-numbing to\n>> review, though `git diff --color-words` helps out somewhat (at least\n>> by making it easier to skim the changes).\n>\n>I likewise think I may have phased in and out of consciousness on those\n>last four.\n>\n>> > You did an excellent job of explaining the changes and presented them\n>> > in a logical fashion.  The few things I thought I caught, you've\n>> > already answered were already correct.  I do think making the second\n>> > commit message be a bit clearer about the importance of the ordering\n>> > would be helpful.  Anyway:\n>> >\n>> > Reviewed-by: Elijah Newren <newren@gmail.com>\n>>\n>> Thanks. I'll wait a couple days and resend with a clarified commit\n>> message for the second patch unless, perhaps, Junio would accept a\n>> resend of just that patch so I don't have to spam the list again.\n>\n>These looked good to me. I left a few comments, but nothing that I think\n>would trigger a re-roll.\n>\n\nVery nice work and good explanations. I learned a few new things :)\n\nThanks\n"},{"id":"443868","messageId":"CAPig+cQVcv5m_9n8F1d1jo4fbLiFvDYTcBCCUcwrXdT3FJvowA@mail.gmail.com","threadId":"57057","inReplyTo":"YbMcYwj/Mvf/PoFF@coredump.intra.peff.net","subject":"Re: [PATCH 09/19] tests: use test_write_lines() to generate line-oriented output","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T06:59:33Z","receivedAt":"2021-12-11T06:59:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:22 AM Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 09, 2021 at 12:11:05AM -0500, Eric Sunshine wrote:\n> >  test_expect_success 'color new trailing blank lines' '\n> > -     { echo a; echo b; echo; echo; } >x &&\n> > +     test_write_lines a b \"\" \"\" >x &&\n> >       git add x &&\n> > -     { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&\n> > +     test_write_lines a \"\" \"\" \"\" c \"\" \"\" \"\" \"\" >x &&\n>\n> Some of these I think might be more readable as here-docs. But I think\n> keeping to the minimal change here makes sense (and I admit I do not\n> overly care much either way; it was just on my mind from the last\n> patch).\n\nIndeed, it's a judgment call, but I recall that it was all the\nembedded and trailing blank lines in these particular cases which\npushed me toward test_write_lines() rather than here-docs since\ntest_write_lines() makes it way more compact, which perhaps makes it a\nbit easier to get the number and placement of the blank lines correct.\nThe corresponding here-doc would have been:\n\n    cat <<-\\EOF >x &&\n    a\n\n\n\n    c\n\n\n\n\n   EOF\n"},{"id":"443871","messageId":"CAPig+cQ98aac8die8Sw8cnu9rN=ciMW91QO51mVtcNS=boQtsw@mail.gmail.com","threadId":"57057","inReplyTo":"YbMd4cuRs9aiPWbX@coredump.intra.peff.net","subject":"Re: [PATCH 12/19] tests: fix broken &&-chains in `{...}` groups","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T07:14:43Z","receivedAt":"2021-12-11T07:14:56Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:29 AM Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 09, 2021 at 12:11:08AM -0500, Eric Sunshine wrote:\n> > Fix broken &&-chains in `{...}` groups in order to reduce the number of\n> > possible lurking bugs.\n>\n> Seems good. This is mostly stuff we don't expect to fail (mostly\n> \"echo\"), so I doubt they're important on their own. But getting a clean\n> state for the linter _is_ important.\n\nIn this patch, I think the only &&-chain which really matters (i.e.\ncould hide a genuine failure if broken) is t5515-fetch-merge-logic.sh.\nAside from most of these being unlikely to fail (`echo`), most of the\nexit codes are being lost down pipes anyhow. As such, I had a hard\ntime justifying this patch since it exists mostly to satisfy the\nlinter which isn't smart enough to distinguish between the cases. What\nultimately convinced me that this patch was worthwhile was (1) that\nthere are legitimate cases, such as t5515-fetch-merge-logic.sh, where\nwe really do want to be told about the broken &&-chain, and (2) that\nit's easier to have a single simple rule which we can point test\nauthors at (\"chain all your test commands with `&&`\") rather than\ncomplex rules laying out cases when you do and don't need to maintain\nthe &&-chain.\n"},{"id":"443872","messageId":"CAPig+cRDKM3E5ZEV-A8Ze7pzDnM+s=xs0ioW_qXoM3yYfu7jSw@mail.gmail.com","threadId":"57057","inReplyTo":"20211210093827.a5s5mby3if5f7a4u@fs","subject":"Re: [PATCH 12/19] tests: fix broken &&-chains in `{...}` groups","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T07:32:19Z","receivedAt":"2021-12-11T07:32:32Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:38 AM Fabian Stelzer <fs@gigacodes.de> wrote:\n> On 09.12.2021 00:11, Eric Sunshine wrote:\n> >Fix broken &&-chains in `{...}` groups in order to reduce the number of\n> >possible lurking bugs.\n> >       {\n> >-          echo \"*.t filter=rot13\"\n> >+          echo \"*.t filter=rot13\" &&\n> >           echo \"*.i ident\"\n> >       } >.gitattributes &&\n> >       {\n> >-              echo \"expanded-keywords ident\"\n> >+              echo \"expanded-keywords ident\" &&\n> >               echo \"expanded-keywords-crlf ident text eol=crlf\"\n> >       } >>.gitattributes &&\n> >\n>\n> Wouldn't some of these be better off as heredocs as well?\n> There are a couple more below. I personally don't much mind either way but\n> since you changed quite a few in an earlier commit why not these?\n\nIt's been months since I made these changes, but I think there were at\nleast a couple reasons for not converting these to here-docs. First,\nin these cases, there were only one or two missing `&&` per block. Had\nI bulk converted them to here-docs, it would have made for a much more\nnoisy patch, which would have taxed reviewers more, and\nreviewer-fatigue is a real concern when crafting a lengthy patch\nseries like this one. In the \"here-doc conversion\" patch, on the other\nhand, many of those cases involved a significant number of missing\n`&&`; often every line was missing `&&`. So, the changes in that patch\nwas going to be very noisy anyhow, whether I added missing `&&` or\nconverted to here-docs.\n\nSecond...\n\n> >       {\n> >               echoid insert 444 1 2 3 4 5 a b c d e &&\n> >-              echoid contains 44 441 440 444 4440 4444\n> >+              echoid contains 44 441 440 444 4440 4444 &&\n> >               echo clear\n> >       } | test-tool oidtree >actual &&\n\n... there are a number of cases like this which look like they could\neasily be converted to here-doc, but in fact `echoid` is a function\ncall, so a here-doc wouldn't work. Also...\n\n> >       {\n> >               echo $(git rev-parse refs/tags/A) refs/tags/A &&\n> >-              echo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\"\n> >+              echo $(git rev-parse refs/tags/A^0) \"refs/tags/A^{}\" &&\n> >               echo $(git rev-parse refs/tags/C) refs/tags/C\n> >       } >expect &&\n\n... this sort of thing could certainly become a here-doc because\n$(...) will work in a here-doc, but when there is a preponderance of\nthis sort of `{ echo && ... }` block in the test script, it would feel\ninconsistent to convert a few of them to here-docs.\n"},{"id":"443874","messageId":"CAPig+cTb9hBu7SPjh-Zeo=5LsbiSa=r43e_yXOpgq_5Ghg0Dcg@mail.gmail.com","threadId":"57057","inReplyTo":"YbMel5UZm9ESM5Jr@coredump.intra.peff.net","subject":"Re: [PATCH 13/19] tests: apply modern idiom for signaling test failure","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T07:47:07Z","receivedAt":"2021-12-11T07:47:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:32 AM Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 09, 2021 at 12:11:09AM -0500, Eric Sunshine wrote:\n> >       if test -s difference\n> >       then\n> >               cat difference\n> > -             (exit 1)\n> > +             false\n> >       else\n> >               echo happy\n> >       fi\n>\n> ...I'd have said the idiom here is just:\n>\n>   git diff-tree -r test-rebase test-merge >difference &&\n>   test -s difference\n>\n> The extra \"cat\" and \"happy\" are verbose output that we usually skip in\n> favor of letting \"-x\" logging do the talking (and leaving the failed\n> state so you can \"cat difference\" yourself).\n>\n> That said, I'm OK with this minimal change in the name of keeping creep\n> out of the series.\n\nIndeed, there's plenty of odd cruft like this in old test scripts\nwhich could eventually use good cleanups such as the one you suggest\nhere. But I'm also OK with (indeed prefer) this minimal change for the\npresent in order to reduce likelihood of reviewer fatigue in this\nalready lengthy patch series.\n"},{"id":"443876","messageId":"CAPig+cQ7y+DqOTbtdtPJ6=pgwE-zZnE=E+ixvNkHaTMWEXjRew@mail.gmail.com","threadId":"57057","inReplyTo":"20211210095303.xx3fxxtp5ww3s24x@fs","subject":"Re: [PATCH 17/19] t4000-t4999: detect and signal failure within loop","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T08:06:51Z","receivedAt":"2021-12-11T08:07:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:53 AM Fabian Stelzer <fs@gigacodes.de> wrote:\n> On 09.12.2021 00:11, Eric Sunshine wrote:\n> >-      for i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\"; done >x &&\n> >+      for i in 1 2 3 4 5 6 7 8 9; do echo \"$i$i$i$i$i$i\" || return 1; done >x &&\n>\n> I understand why the `|| return` in loops makes sense. But for these very\n> simple ones just using `echo` I'll probably be confused if a linter starts\n> to complain about them (probably depending on how specific the lint error\n> will be).\n\nI understand your concern and had second thoughts about flagging this\nsort of thing since there are so many loops which obviously shouldn't\nfail, thus inserting `|| return 1` in them is just busy work to pacify\na linter which isn't smart enough to distinguish the important cases\nfrom the unimportant ones. As such, I had some trouble justifying (to\nmyself) that this linter complaint is really a good idea, but\neventually convinced myself that it's worthwhile in the long run for\nthe same reasons I mentioned in my response to another patch in this\nseries. Namely, (1) there are good number of cases in which the loop\nbody is populated with \"real\" commands which could fail, so we really\ndo want to be told about missing `|| return 1` in general, and (2)\nit's easier to have a single simple rule which we can point test\nauthors at (\"end all loop bodies with `|| return 1` (or `|| exit 1` in\na subshell)\") rather than complex rules laying out cases when you must\nor need not use `|| return 1`.\n"},{"id":"443877","messageId":"CAPig+cT4U-HSCV4kDHLEi7T+8Pi0o+eJjki+c5pmQdx2DTWcjg@mail.gmail.com","threadId":"57057","inReplyTo":"20211210095757.gu7w4n2rqulx2dvg@fs","subject":"Re: [PATCH 00/19] tests: fix broken &&-chains & abort loops on error","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T08:16:47Z","receivedAt":"2021-12-11T08:17:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Dec 10, 2021 at 4:58 AM Fabian Stelzer <fs@gigacodes.de> wrote:\n> On 10.12.2021 04:38, Jeff King wrote:\n> >On Thu, Dec 09, 2021 at 02:17:32PM -0500, Eric Sunshine wrote:\n> >> Thanks. I'll wait a couple days and resend with a clarified commit\n> >> message for the second patch unless, perhaps, Junio would accept a\n> >> resend of just that patch so I don't have to spam the list again.\n> >\n> >These looked good to me. I left a few comments, but nothing that I think\n> >would trigger a re-roll.\n>\n> Very nice work and good explanations. I learned a few new things :)\n\nThanks Elijah, Peff, and Fabian for reading through the series.\n"},{"id":"443881","messageId":"20211211095837.26387-1-sunshine@sunshineco.com","threadId":"57057","inReplyTo":"20211209051115.52629-1-sunshine@sunshineco.com","subject":"[PATCH v1.1 2/19] t1010: fix unnoticed failure on Windows","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-11T09:58:37Z","receivedAt":"2021-12-11T10:00:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Microsoft Windows, a directory name should never end with a period.\nQuoting from Microsoft documentation[1]:\n\n    Do not end a file or directory name with a space or a period.\n    Although the underlying file system may support such names, the\n    Windows shell and user interface does not.\n\nNaming a directory with a trailing period is indeed perilous:\n\n    % git init foo\n    % cd foo\n    % mkdir a.\n    % git status\n    warning: could not open directory 'a./': No such file or directory\n\nThe t1010 \"setup\" test:\n\n    for d in a a. a0\n    do\n        mkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n        git add \"$d\"\n    done &&\n\nruns afoul of this Windows limitation, as can be observed when running\nthe test verbosely:\n\n    error: open(\"a./one\"): No such file or directory\n    error: unable to index file 'a./one'\n    fatal: adding files failed\n\nThe reason this problem has gone unnoticed for so long is twofold.\nFirst, the failed `git add` is swallowed silently because the loop is\nnot terminated explicitly by `|| return 1` to signal the failure.\nSecond, none of the tests in this script care about the literal\ndirectory names (\"a\", \"a.\", \"a0\") or the specific number of tree\nentries. They care instead about the order of entries in the tree, and\nthat the tree synthesized in the index and created by `git write-tree`\nmatches the tree created by the output of `git ls-tree` fed into `git\nmktree`, thus the absence of \"a./one\" has no impact on the tests.\n\nSkipping these tests on Windows by, for instance, checking the\nFUNNYNAMES predicate would avoid the problem, however, the funny-looking\nname is not what is being tested here. Rather, the tests are about\nchecking that `git mktree` produces stable results for various input\nconditions, such as when the input order is not consistent or when an\nobject is missing.\n\nTherefore, resolve the problem simply by using a directory name which is\nlegal on Windows and sorts the same as \"a.\". While at it, add the\nmissing `|| return 1` to the loop body in order to catch this sort of\nproblem in the future.\n\n[1]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\nReviewed-by: Elijah Newren <newren@gmail.com>\n---\n\nThis is a re-roll of patch 2/19 of es/test-chain-lint[1] to improve the\ncommit message in response to Elijah's review comments[2,3] since the v1\ncommit message was perhaps a bit too subtle[4].\n\nRather than spamming the list with a full v2 re-roll, I decided to\nre-roll just this one patch with its minor commit message rewrite since\nit was the only change requested, thus should be less of a burden on\nreviewers, though perhaps a bit more burden on Junio (but it's such a\nminor change that the original commit message is probably good enough\nshould Junio not pick up this single-patch re-roll).\n\n[1]: https://lore.kernel.org/git/20211209051115.52629-1-sunshine@sunshineco.com/\n[2]: https://lore.kernel.org/git/CABPp-BGBZ6_CqbUg3=sK2b4yELC5NHHyH68_df22n=t=hARH_g@mail.gmail.com/\n[3]: https://lore.kernel.org/git/CABPp-BFM5ZbFAzVfvDE3=zm6Q4LN2fWthPP8WH5kbgVPSxomtA@mail.gmail.com/\n[4]: https://lore.kernel.org/git/CAPig+cR0eKhz+ncWb4v9dSY0A03P+K0+WT90J2cBKvLqT8DXrA@mail.gmail.com/\n\nRange-diff against v1:\n1:  4e7d8e8c7e ! 1:  95d419ed90 t1010: fix unnoticed failure on Windows\n    @@ Commit message\n         The reason this problem has gone unnoticed for so long is twofold.\n         First, the failed `git add` is swallowed silently because the loop is\n         not terminated explicitly by `|| return 1` to signal the failure.\n    -    Second, none of the tests in this script care about the actual directory\n    -    names or even the number of tree entries. They care only that the tree\n    -    synthesized in the index and created by `git write-tree` matches the\n    -    tree created by the output of `git ls-tree` fed into `git mktree`, and\n    -    the failure of `git add \"a./one\"` doesn't change that outcome.\n    +    Second, none of the tests in this script care about the literal\n    +    directory names (\"a\", \"a.\", \"a0\") or the specific number of tree\n    +    entries. They care instead about the order of entries in the tree, and\n    +    that the tree synthesized in the index and created by `git write-tree`\n    +    matches the tree created by the output of `git ls-tree` fed into `git\n    +    mktree`, thus the absence of \"a./one\" has no impact on the tests.\n     \n         Skipping these tests on Windows by, for instance, checking the\n         FUNNYNAMES predicate would avoid the problem, however, the funny-looking\n    @@ Commit message\n         object is missing.\n     \n         Therefore, resolve the problem simply by using a directory name which is\n    -    legal on Windows (i.e. \"a-\" rather than \"a.\"). While at it, add the\n    +    legal on Windows and sorts the same as \"a.\". While at it, add the\n         missing `|| return 1` to the loop body in order to catch this sort of\n         problem in the future.\n     \n         [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n     \n         Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n    +    Reviewed-by: Elijah Newren <newren@gmail.com>\n     \n      ## t/t1010-mktree.sh ##\n     @@ t/t1010-mktree.sh: TEST_PASSES_SANITIZE_LEAK=true\n\n t/t1010-mktree.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh\nindex 48bfad07ab..3c08194526 100755\n--- a/t/t1010-mktree.sh\n+++ b/t/t1010-mktree.sh\n@@ -6,10 +6,10 @@ TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-\tfor d in a a. a0\n+\tfor d in a a- a0\n \tdo\n \t\tmkdir \"$d\" && echo \"$d/one\" >\"$d/one\" &&\n-\t\tgit add \"$d\"\n+\t\tgit add \"$d\" || return 1\n \tdone &&\n \techo zero >one &&\n \tgit update-index --add --info-only one &&\n-- \n2.34.1.397.gfae76fe5da\n\n"}]}