git@vger.kernel.org mailing list mirror (one of many)
 help / Atom feed
* [PATCH 00/29] t: detect and fix broken &&-chains in subshells
@ 2018-06-26  7:29 Eric Sunshine
  2018-06-26  7:29 ` [PATCH 01/29] t7508: use test_when_finished() instead of managing exit code manually Eric Sunshine
                   ` (31 more replies)
  0 siblings, 32 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

The --chain-lint[1] option detects breakage in the top-level &&-chain of
tests. This series undertakes the more complex task of teaching it to
also detect &&-chain breakage within subshells. See patch 29/29 for the
gory details of how that's done.

The series is built atop 'master', however, it has a conflict with an
in-flight topic. In particular, patch 1/29 rewrites a test in t7508 in
'master' to avoid &&-chain breakage. 'jc/clean-after-sanity-tests' in
'next' performs pretty much the same rewrite. If this series is queued
atop 'master', then it needs patch 1/29; if it is queued somewhere above
'jc/clean-after-sanity-tests', then 1/29 can be dropped.

Aside from identifying a rather significant number of &&-chain breaks,
repairing those broken chains uncovered genuine bugs in several tests
which were hidden by missing &&-chain links. Those bugs are also fixed
by this series. I would appreciate if the following people would
double-check my fixes:

Stefan Bellar - 8/29 "t7400" and (especially) 13/29 "lib-submodule-update"
Jonathan Tan - 10/29 "t9001"
Elijah Newren - 6/29 "t6036"

In order to keep the noise level down and ease review burden, please
take into account that I largely avoided modernizations and cleanups,
and limited changes to merely adding "&&" even when some other
transformation would have made the fix nicer overall. (For example, I
resisted the urge to replace a series of 'echo' statements in a subshell
with a simple here-doc.)

Finally, although all simple &&-chain fixes originally inhabited a
single patch, they were split out into several patches to avoid hitting
vger.kernel.org's message size limit. However, when queuing, all patches
titled "t????: fix broken &&-chains in subshells" could be squashed into
a single patch titled "t: fix broken &&-chains in subshells".

[1]: https://public-inbox.org/git/20150320100429.GA17354@peff.net/

Eric Sunshine (29):
  t7508: use test_when_finished() instead of managing exit code manually
  t0001: use "{...}" block around "||" expression rather than subshell
  t1300: use sane_unset() to avoid breaking &&-chain
  t3303: use standard here-doc tag "EOF" to avoid fooling --chain-lint
  t5505: modernize and simplify hard-to-digest test
  t6036: fix broken "merge fails but has appropriate contents" tests
  t7201: drop pointless "exit 0" at end of subshell
  t7400: fix broken "submodule add/reconfigure --force" test
  t7810: use test_expect_code() instead of hand-rolled comparison
  t9001: fix broken "invoke hook" test
  t9104: use "{...}" block around "||" expression rather than subshell
  t9401: drop unnecessary nested subshell
  t/lib-submodule-update: fix broken "replace submodule must-fail" test
  t: drop subshell with missing &&-chain in favor of simpler construct
  t: drop unnecessary terminating semicolons in subshell
  t: use test_might_fail() instead of manipulating exit code manually
  t: use test_must_fail() instead of checking exit code manually
  t0000-t0999: fix broken &&-chains in subshells
  t1000-t1999: fix broken &&-chains in subshells
  t2000-t2999: fix broken &&-chains in subshells
  t3000-t3999: fix broken &&-chains in subshells
  t3030: fix broken &&-chains in subshells
  t4000-t4999: fix broken &&-chains in subshells
  t5000-t5999: fix broken &&-chains in subshells
  t6000-t6999: fix broken &&-chains in subshells
  t7000-t7999: fix broken &&-chains in subshells
  t9000-t9999: fix broken &&-chains in subshells
  t9119: fix broken &&-chains in subshells
  t/test-lib: teach --chain-lint to detect broken &&-chains in subshells

 t/lib-submodule-update.sh                     |  10 +-
 t/t0000-basic.sh                              |   2 +-
 t/t0001-init.sh                               |   2 +-
 t/t0003-attributes.sh                         |  24 +-
 t/t0021-conversion.sh                         |   4 +-
 t/t0090-cache-tree.sh                         |   2 +-
 t/t1004-read-tree-m-u-wf.sh                   |   8 +-
 t/t1005-read-tree-reset.sh                    |  10 +-
 t/t1008-read-tree-overlay.sh                  |   2 +-
 t/t1020-subdirectory.sh                       |   2 +-
 t/t1050-large.sh                              |   6 +-
 t/t1300-config.sh                             |   2 +-
 t/t1411-reflog-show.sh                        |   6 +-
 t/t1507-rev-parse-upstream.sh                 |   6 +-
 t/t1512-rev-parse-disambiguation.sh           |   6 +-
 t/t1700-split-index.sh                        |   2 +-
 t/t2016-checkout-patch.sh                     |  24 +-
 t/t2103-update-index-ignore-missing.sh        |   2 +-
 t/t2202-add-addremove.sh                      |  14 +-
 t/t3000-ls-files-others.sh                    |   2 +-
 t/t3006-ls-files-long.sh                      |   2 +-
 t/t3008-ls-files-lazy-init-name-hash.sh       |   8 +-
 t/t3030-merge-recursive.sh                    | 340 +++++++++---------
 t/t3050-subprojects-fetch.sh                  |   8 +-
 t/t3102-ls-tree-wildcards.sh                  |   2 +-
 t/t3301-notes.sh                              |   8 +-
 t/t3303-notes-subtrees.sh                     |  12 +-
 t/t3400-rebase.sh                             |   8 +-
 t/t3402-rebase-merge.sh                       |   4 +-
 t/t3404-rebase-interactive.sh                 |   6 +-
 t/t3418-rebase-continue.sh                    |   4 +-
 t/t3700-add.sh                                |   8 +-
 t/t3701-add-interactive.sh                    |  16 +-
 t/t3904-stash-patch.sh                        |   8 +-
 t/t4001-diff-rename.sh                        |   2 +-
 t/t4010-diff-pathspec.sh                      |   4 +-
 t/t4012-diff-binary.sh                        |   6 +-
 t/t4025-hunk-header.sh                        |   8 +-
 t/t4041-diff-submodule-option.sh              |   4 +-
 t/t4060-diff-submodule-option-diff-format.sh  |   2 +-
 t/t4121-apply-diffs.sh                        |   2 +-
 t/t5300-pack-object.sh                        |   2 +-
 t/t5302-pack-index.sh                         |   2 +-
 t/t5400-send-pack.sh                          |   4 +-
 t/t5401-update-hooks.sh                       |   4 +-
 t/t5405-send-pack-rewind.sh                   |   3 +-
 t/t5406-remote-rejects.sh                     |   2 +-
 t/t5500-fetch-pack.sh                         |   2 +-
 t/t5505-remote.sh                             |  10 +-
 t/t5512-ls-remote.sh                          |   4 +-
 t/t5516-fetch-push.sh                         |  10 +-
 t/t5517-push-mirror.sh                        |  10 +-
 t/t5526-fetch-submodules.sh                   |   2 +-
 t/t5531-deep-submodule-push.sh                |   2 +-
 t/t5543-atomic-push.sh                        |   2 +-
 t/t5601-clone.sh                              |   2 +-
 t/t5605-clone-local.sh                        |   2 +-
 t/t5801-remote-helpers.sh                     |   2 +-
 t/t6010-merge-base.sh                         |   2 +-
 t/t6029-merge-subtree.sh                      |  16 +-
 t/t6036-recursive-corner-cases.sh             |  14 +-
 t/t6042-merge-rename-corner-cases.sh          |   8 +-
 t/t6043-merge-rename-directories.sh           |   2 +-
 t/t7001-mv.sh                                 |   2 +-
 t/t7105-reset-patch.sh                        |  12 +-
 t/t7201-co.sh                                 |  41 ++-
 t/t7301-clean-interactive.sh                  |  41 ++-
 t/t7400-submodule-basic.sh                    |  12 +-
 t/t7406-submodule-update.sh                   |   4 +-
 t/t7408-submodule-reference.sh                |   2 +-
 t/t7501-commit.sh                             |  56 +--
 t/t7506-status-submodule.sh                   |  10 +-
 t/t7508-status.sh                             |  20 +-
 t/t7610-mergetool.sh                          |   8 +-
 t/t7810-grep.sh                               |   7 +-
 t/t9001-send-email.sh                         |  10 +-
 t/t9100-git-svn-basic.sh                      |   2 +-
 t/t9101-git-svn-props.sh                      |   2 +-
 t/t9104-git-svn-follow-parent.sh              |   4 +-
 t/t9119-git-svn-info.sh                       | 120 +++----
 t/t9122-git-svn-author.sh                     |   6 +-
 t/t9129-git-svn-i18n-commitencoding.sh        |   2 +-
 t/t9130-git-svn-authors-file.sh               |   4 +-
 t/t9134-git-svn-ignore-paths.sh               |   6 +-
 t/t9137-git-svn-dcommit-clobber-series.sh     |   2 +-
 t/t9138-git-svn-authors-prog.sh               |   6 +-
 t/t9146-git-svn-empty-dirs.sh                 |  12 +-
 t/t9147-git-svn-include-paths.sh              |   6 +-
 t/t9164-git-svn-dcommit-concurrent.sh         |   2 +-
 ...65-git-svn-fetch-merge-branch-of-branch.sh |   2 +-
 t/t9200-git-cvsexportcommit.sh                |   6 +-
 t/t9400-git-cvsserver-server.sh               |   6 +-
 t/t9401-git-cvsserver-crlf.sh                 |   4 +-
 t/t9600-cvsimport.sh                          |   2 +-
 t/t9806-git-p4-options.sh                     |   2 +-
 t/t9810-git-p4-rcs.sh                         |   2 +-
 t/t9811-git-p4-label-import.sh                |   2 +-
 t/t9814-git-p4-rename.sh                      |  16 +-
 t/t9815-git-p4-submit-fail.sh                 |   2 +-
 t/t9830-git-p4-symlink-dir.sh                 |   2 +-
 t/t9831-git-p4-triggers.sh                    |   2 +-
 t/t9902-completion.sh                         |   4 +-
 t/t9903-bash-prompt.sh                        |   2 +-
 t/test-lib.sh                                 | 245 ++++++++++++-
 104 files changed, 810 insertions(+), 593 deletions(-)

-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 01/29] t7508: use test_when_finished() instead of managing exit code manually
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 02/29] t0001: use "{...}" block around "||" expression rather than subshell Eric Sunshine
                   ` (30 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test manages its own exit code in order to perform a cleanup action
unconditionally, whether the test succeeds or fails overall. In doing
so, it intentionally breaks the &&-chain. Such manual exit code
management to ensure cleanup predates the invention of
test_when_finished().

An upcoming change will teach --chain-lint to detect &&-chain breakage
inside subshells, so this manual exit code management with intentional
&&-chain breakage will run afoul of --chain-lint. Therefore, replace
the manual exit code handling with test_when_finished() and a normal
&&-chain. While at it, drop the now-unnecessary subshell.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---

Notes:
    This series is built atop 'master'. If the series is queued there,
    this patch is needed to avoid test-suite breakage. However, the
    issue fixed by this patch is already also fixed by
    'jc/clean-after-sanity-tests' in 'next' (although, that patch
    doesn't bother dropping the now-unnecessary subshell, like this
    patch does.)

 t/t7508-status.sh | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 18a40257fb..67bf4393bb 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1099,18 +1099,14 @@ EOF
 '
 
 test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
-	(
-		chmod a-w .git &&
-		# make dir1/tracked stat-dirty
-		>dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
-		git status -s >output &&
-		! grep dir1/tracked output &&
-		# make sure "status" succeeded without writing index out
-		git diff-files | grep dir1/tracked
-	)
-	status=$?
-	chmod 775 .git
-	(exit $status)
+	chmod a-w .git &&
+	test_when_finished "chmod 775 .git" &&
+	# make dir1/tracked stat-dirty
+	>dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+	git status -s >output &&
+	! grep dir1/tracked output &&
+	# make sure "status" succeeded without writing index out
+	git diff-files | grep dir1/tracked
 '
 
 (cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 02/29] t0001: use "{...}" block around "||" expression rather than subshell
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
  2018-06-26  7:29 ` [PATCH 01/29] t7508: use test_when_finished() instead of managing exit code manually Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 03/29] t1300: use sane_unset() to avoid breaking &&-chain Eric Sunshine
                   ` (29 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test uses (... || true) as a shorthand for an if-then-else
statement. The subshell prevents it from breaking the top-level
&&-chain.

However, an upcoming change will teach --chain-lint to check the
&&-chain inside subshells. Although it specially recognizes and allows
(... || git ...) and (... || test*), it intentionally avoids treating
(... || true) specially since such a construct typically can be
expressed more naturally with test_might_fail() and a normal &&-chain.

This case is special, though, since the invoked command, 'setfacl',
might not even exist (indeed, MacOS has no such command) which is not a
valid use-case for test_might_fail(). Sidestep the issue by wrapping the
"||" expression in a "{...}" block instead of a subshell since "{...}"
blocks are not checked for &&-chain breakage.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t0001-init.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index c413bff9cf..fa44a55a5b 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -260,7 +260,7 @@ test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shar
 		# the repository itself should follow "shared"
 		mkdir newdir &&
 		# Remove a default ACL if possible.
-		(setfacl -k newdir 2>/dev/null || true) &&
+		{ setfacl -k newdir 2>/dev/null || true; } &&
 		umask 002 &&
 		git init --bare --shared=0660 newdir/a/b/c &&
 		test_path_is_dir newdir/a/b/c/refs &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 03/29] t1300: use sane_unset() to avoid breaking &&-chain
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
  2018-06-26  7:29 ` [PATCH 01/29] t7508: use test_when_finished() instead of managing exit code manually Eric Sunshine
  2018-06-26  7:29 ` [PATCH 02/29] t0001: use "{...}" block around "||" expression rather than subshell Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 04/29] t3303: use standard here-doc tag "EOF" to avoid fooling --chain-lint Eric Sunshine
                   ` (28 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test intentionally breaks the &&-chain after "unset HOME" since it
doesn't know if 'unset' will succeed or fail. However, an upcoming
change will teach --chain-lint to detect &&-chain breakage inside
subshells, and it will catch this broken &&-chain. Instead, use
sane_unset() which can be safely linked into the &&-chain.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t1300-config.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 03c223708e..24706ba412 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -888,7 +888,7 @@ EOF
 
 test_expect_success !MINGW 'get --path copes with unset $HOME' '
 	(
-		unset HOME;
+		sane_unset HOME &&
 		test_must_fail git config --get --path path.home \
 			>result 2>msg &&
 		git config --get --path path.normal >>result &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 04/29] t3303: use standard here-doc tag "EOF" to avoid fooling --chain-lint
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (2 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 03/29] t1300: use sane_unset() to avoid breaking &&-chain Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 05/29] t5505: modernize and simplify hard-to-digest test Eric Sunshine
                   ` (27 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

An upcoming change will teach --chain-lint to detect &&-chain breakage
inside subshells. The check works by performing textual transformations
on the test to link the subshell body directly into the parent's
&&-chain. Special care is taken with the final statement in a
subshell. For instance:

    statement1 &&
    (
        statement2
    ) &&
    statement3

is transformed to:

    statement1 &&
    statement2 &&
    statement3

Notice that "statement2" gains a "&&".

Special care is is taken with here-docs since "&&" needs to be added to
the "<<EOF" line which opens the here-doc, not the "EOF" line which
closes it. For practical reasons (namely, transformations are performed
line-by-line), only here-docs tagged with "EOF" are recognized.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3303-notes-subtrees.sh | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index 704aee81ef..e353faa50b 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -39,7 +39,7 @@ test_expect_success "setup: create $number_of_commits commits" '
 		while [ $nr -lt $number_of_commits ]; do
 			nr=$(($nr+1)) &&
 			test_tick &&
-			cat <<INPUT_END
+			cat <<EOF
 commit refs/heads/master
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
@@ -47,15 +47,15 @@ commit #$nr
 COMMIT
 
 M 644 inline file
-data <<EOF
+data <<DATA
 file in commit #$nr
-EOF
+DATA
 
-INPUT_END
+EOF
 
 		done &&
 		test_tick &&
-		cat <<INPUT_END
+		cat <<EOF
 commit refs/notes/commits
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
@@ -64,7 +64,7 @@ COMMIT
 
 deleteall
 
-INPUT_END
+EOF
 
 	) |
 	git fast-import --quiet &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 05/29] t5505: modernize and simplify hard-to-digest test
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (3 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 04/29] t3303: use standard here-doc tag "EOF" to avoid fooling --chain-lint Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests Eric Sunshine
                   ` (26 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test uses a subshell within a subshell but is formatted in such a
way as to suggests that the inner subshell is a sibling rather than a
child, which makes it difficult to digest the test's structure and
intent.

Worse, the inner subshell performs cleanup of actions from earlier in
the test, however, a failure between the initial actions and the cleanup
will prevent the cleanup from taking place.

Fix these problems by modernizing and simplifying the test and by using
test_when_finished() for the cleanup action.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t5505-remote.sh | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index a6c0178f3a..3552b51b4c 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -348,17 +348,13 @@ URL: $(pwd)/one
 EOF
 
 test_expect_success 'prune --dry-run' '
-	(
-		cd one &&
-		git branch -m side2 side) &&
+	git -C one branch -m side2 side &&
+	test_when_finished "git -C one branch -m side side2" &&
 	(
 		cd test &&
 		git remote prune --dry-run origin >output &&
 		git rev-parse refs/remotes/origin/side2 &&
 		test_must_fail git rev-parse refs/remotes/origin/side &&
-	(
-		cd ../one &&
-		git branch -m side side2) &&
 		test_i18ncmp expect output
 	)
 '
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (4 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 05/29] t5505: modernize and simplify hard-to-digest test Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  8:44   ` Elijah Newren
  2018-06-26  7:29 ` [PATCH 07/29] t7201: drop pointless "exit 0" at end of subshell Eric Sunshine
                   ` (25 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

These tests reference non-existent object "c" when they really mean to
be referencing "C", however, this error went unnoticed due to a broken
&&-chain later in the test. Fix these errors, as well as the broken
&&-chains behind which they hid.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t6036-recursive-corner-cases.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index b5621303d6..b32ff8e1db 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -506,10 +506,10 @@ test_expect_success 'merge of D & E2 fails but has appropriate contents' '
 		test_line_count = 2 out &&
 
 		git rev-parse >expect    \
-			B:a   E2:a/file  c:a/file   A:ignore-me &&
+			B:a   E2:a/file  C:a/file   A:ignore-me &&
 		git rev-parse   >actual   \
 			:2:a  :3:a/file  :1:a/file  :0:ignore-me &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		test_path_is_file a~HEAD
 	)
@@ -533,10 +533,10 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
 		test_line_count = 2 out &&
 
 		git rev-parse >expect    \
-			B:a   E2:a/file  c:a/file   A:ignore-me &&
+			B:a   E2:a/file  C:a/file   A:ignore-me &&
 		git rev-parse   >actual   \
 			:3:a  :2:a/file  :1:a/file  :0:ignore-me &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		test_path_is_file a~D^0
 	)
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 07/29] t7201: drop pointless "exit 0" at end of subshell
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (5 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test Eric Sunshine
                   ` (24 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test employs a for-loop inside a subshell and correctly aborts the
loop and fails the test overall (via "exit 1") if any iteration of the
for-loop fails. Otherwise, it exits the subshell with an explicit
"exit 0", presumably to indicate that all iterations of the for-loop
succeeded. The &&-chain is (perhaps deliberately) broken between the
for-loop and the "exit 0".

An upcoming change will teach --chain-lint to detect &&-chain breakage
inside subshells, thus the missing &&-chain link will run afoul of
--chain-lint. Rather than fixing the &&-chain breakage, instead just
drop the unnecessary "exit 0".

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t7201-co.sh | 1 -
 1 file changed, 1 deletion(-)

diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index ab9da61da3..8d8a63a24b 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -673,7 +673,6 @@ test_expect_success 'custom merge driver with checkout -m' '
 		do
 			grep $t arm || exit 1
 		done
-		exit 0
 	) &&
 
 	mv arm expect &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (6 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 07/29] t7201: drop pointless "exit 0" at end of subshell Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-27 18:04   ` Stefan Beller
  2018-06-26  7:29 ` [PATCH 09/29] t7810: use test_expect_code() instead of hand-rolled comparison Eric Sunshine
                   ` (23 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test has been dysfunctional since it was added by 619acfc78c
(submodule add: extend force flag to add existing repos, 2016-10-06),
however, two problems early in the test went unnoticed due to a broken
&&-chain later in the test.

First, it tries configuring the submodule with repository "bogus-url",
however, "git submodule add" insists that the repository be either an
absolute URL or a relative pathname requiring prefix "./" or "../" (this
is true even with --force), but "bogus-url" does not meet those
criteria, thus the command fails.

Second, it then tries configuring a submodule with a path which is
.gitignore'd, which is disallowed. This restriction can be overridden
with --force, but the test neglects to use that option.

Fix both problems, as well as the broken &&-chain behind which they hid.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t7400-submodule-basic.sh | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 812db137b8..401adaed32 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -171,12 +171,12 @@ test_expect_success 'submodule add to .gitignored path with --force' '
 test_expect_success 'submodule add to reconfigure existing submodule with --force' '
 	(
 		cd addtest-ignore &&
-		git submodule add --force bogus-url submod &&
-		git submodule add -b initial "$submodurl" submod-branch &&
-		test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
-		test "bogus-url" = "$(git config submodule.submod.url)" &&
+		git submodule add --force /bogus-url submod &&
+		git submodule add --force -b initial "$submodurl" submod-branch &&
+		test "/bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
+		test "/bogus-url" = "$(git config submodule.submod.url)" &&
 		# Restore the url
-		git submodule add --force "$submodurl" submod
+		git submodule add --force "$submodurl" submod &&
 		test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" &&
 		test "$submodurl" = "$(git config submodule.submod.url)"
 	)
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 09/29] t7810: use test_expect_code() instead of hand-rolled comparison
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (7 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 10/29] t9001: fix broken "invoke hook" test Eric Sunshine
                   ` (22 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test manually checks the exit code of git-grep for a particular
value. In doing so, it breaks the &&-chain. An upcoming change will
teach --chain-lint to check the &&-chain inside subshells, so this
manual exit code handling will run afoul of the &&-chain check.
Therefore, replace the manual handling with test_expect_code() and a
normal &&-chain.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t7810-grep.sh | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 1797f632a3..fecee602c1 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -845,10 +845,9 @@ test_expect_success 'grep from a subdirectory to search wider area (1)' '
 test_expect_success 'grep from a subdirectory to search wider area (2)' '
 	mkdir -p s &&
 	(
-		cd s || exit 1
-		( git grep xxyyzz .. >out ; echo $? >status )
-		! test -s out &&
-		test 1 = $(cat status)
+		cd s &&
+		test_expect_code 1 git grep xxyyzz .. >out &&
+		! test -s out
 	)
 '
 
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 10/29] t9001: fix broken "invoke hook" test
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (8 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 09/29] t7810: use test_expect_code() instead of hand-rolled comparison Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26 17:07   ` Jonathan Tan
  2018-06-26  7:29 ` [PATCH 11/29] t9104: use "{...}" block around "||" expression rather than subshell Eric Sunshine
                   ` (21 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test has been dysfunctional since it was added by 6489660b4b
(send-email: support validate hook, 2017-05-12), however, the problem
went unnoticed due to a broken &&-chain late in the test.

The test wants to verify that a non-zero exit code from the
'sendemail-validate' hook causes git-send-email to abort with a
particular error message. A command which is expected to fail should be
run with 'test_must_fail', however, the tests neglects to do so.

Fix this problem, as well as the broken &&-chain behind which the
problem hid.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t9001-send-email.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index e80eacbb1b..776769fe0d 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1966,11 +1966,11 @@ test_expect_success $PREREQ 'invoke hook' '
 
 		# Verify error message when a patch is rejected by the hook
 		sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
-		git send-email \
+		test_must_fail git send-email \
 			--from="Example <nobody@example.com>" \
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/../fake.sendmail" \
-			../another.patch 2>err
+			../another.patch 2>err &&
 		test_i18ngrep "rejected by sendemail-validate hook" err
 	)
 '
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 11/29] t9104: use "{...}" block around "||" expression rather than subshell
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (9 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 10/29] t9001: fix broken "invoke hook" test Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 12/29] t9401: drop unnecessary nested subshell Eric Sunshine
                   ` (20 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test uses "(... || svn ...)" as a shorthand for an if-then-else
statement. The subshell prevents it from breaking the top-level
&&-chain.

However, an upcoming change will teach --chain-lint to check the
&&-chain inside subshells. Although it takes special care to allow
"||" inside subshells, it only recognizes "(... || git ...)" and
"(... || test*), so the "||" in this test will trip up --chain-lint.

A test later in this same script employs the same "... || svn ..."
construct, however, it wraps it in a "{...}" block instead of a
subshell. Therefore, rather than adding "(... || svn ...)" as a yet
another --chain-lint special case, follow suit and make this test use
"{...}", as well.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t9104-git-svn-follow-parent.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 5e0ad19177..f9aa734d4e 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -53,10 +53,10 @@ test_expect_success 'init and fetch from one svn-remote' '
         '
 
 test_expect_success 'follow deleted parent' '
-        (svn_cmd cp -m "resurrecting trunk as junk" \
+	{ svn_cmd cp -m "resurrecting trunk as junk" \
                "$svnrepo"/trunk@2 "$svnrepo"/junk ||
          svn cp -m "resurrecting trunk as junk" \
-               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
+		-r2 "$svnrepo"/trunk "$svnrepo"/junk; } &&
         git config --add svn-remote.svn.fetch \
           junk:refs/remotes/svn/junk &&
         git svn fetch -i svn/thunk &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 12/29] t9401: drop unnecessary nested subshell
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (10 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 11/29] t9104: use "{...}" block around "||" expression rather than subshell Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 13/29] t/lib-submodule-update: fix broken "replace submodule must-fail" test Eric Sunshine
                   ` (19 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test employs an unnecessary nested subshell:

    (cd foo &&
     statement1 &&
     (cd bar &&
      statement2))

An upcoming change will teach --chain-lint to check the &&-chain in
subshells. The check works by performing textual transformations on the
test to link the subshell body directly into the parent's &&-chain. It
employs heuristics to identify the extent of a subshell, however,
closing two subshells on a single line like this will fool it.

Rather than extending the heuristics even further for this one-off case,
just drop the pointless nested subshell.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t9401-git-cvsserver-crlf.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index 84787eee9a..8b8d7ac34a 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -288,9 +288,9 @@ test_expect_success 'add bin (guess)' '
 test_expect_success 'remove files (guess)' '
     (cd cvswork &&
     GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h &&
-    (cd subdir &&
+    cd subdir &&
     GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin
-    )) &&
+    ) &&
     marked_as cvswork/subdir withCr.bin -kb &&
     marked_as cvswork/subdir file.h ""
 '
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 13/29] t/lib-submodule-update: fix broken "replace submodule must-fail" test
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (11 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 12/29] t9401: drop unnecessary nested subshell Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-27 18:30   ` [PATCH] t/lib-submodule-update: fix absorbing test Stefan Beller
  2018-06-26  7:29 ` [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct Eric Sunshine
                   ` (18 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

This test has been dysfunctional since it was added by 259f3ee296
(lib-submodule-update.sh: define tests for recursing into submodules,
2017-03-14), however, problems went unnoticed due to a broken &&-chain
toward the end of the test.

The test wants to verify that replacing a submodule containing a .git
directory must fail. All other "must fail" tests in this script invoke
the supplied command as 'test_must_fail', however, this test neglects to
do so.

To make matters worse, the command actually succeeds even though it's
not supposed to. Presumably, this is a "known breakage", which means
that the entire test should be marked 'test_expect_failure', however, it
is instead marked 'test_expect_success'.

Fix both problems, as well as the broken &&-chain behind which these
problems hid.

While at it, fix broken &&-chains in a couple neighboring tests.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/lib-submodule-update.sh | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 1f38a85371..8a2edee1cb 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -755,7 +755,7 @@ test_submodule_recursing_with_args_common() {
 			: >sub1/untrackedfile &&
 			test_must_fail $command replace_sub1_with_file &&
 			test_superproject_content origin/add_sub1 &&
-			test_submodule_content sub1 origin/add_sub1
+			test_submodule_content sub1 origin/add_sub1 &&
 			test -f sub1/untracked_file
 		)
 	'
@@ -842,7 +842,7 @@ test_submodule_switch_recursing_with_args () {
 			cd submodule_update &&
 			git branch -t add_sub1 origin/add_sub1 &&
 			: >sub1 &&
-			echo sub1 >.git/info/exclude
+			echo sub1 >.git/info/exclude &&
 			$command add_sub1 &&
 			test_superproject_content origin/add_sub1 &&
 			test_submodule_content sub1 origin/add_sub1
@@ -959,7 +959,7 @@ test_submodule_forced_switch_recursing_with_args () {
 		)
 	'
 	# ... absorbing a .git directory.
-	test_expect_success "$command: replace submodule containing a .git directory with a directory must fail" '
+	test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
 		prolog &&
 		reset_work_tree_to_interested add_sub1 &&
 		(
@@ -967,9 +967,9 @@ test_submodule_forced_switch_recursing_with_args () {
 			git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
 			replace_gitfile_with_git_dir sub1 &&
 			rm -rf .git/modules/sub1 &&
-			$command replace_sub1_with_directory &&
+			test_must_fail $command replace_sub1_with_directory &&
 			test_superproject_content origin/replace_sub1_with_directory &&
-			test_submodule_content sub1 origin/modify_sub1
+			test_submodule_content sub1 origin/modify_sub1 &&
 			test_git_directory_exists sub1
 		)
 	'
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (12 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 13/29] t/lib-submodule-update: fix broken "replace submodule must-fail" test Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26 19:31   ` Junio C Hamano
  2018-06-26  7:29 ` [PATCH 15/29] t: drop unnecessary terminating semicolons in subshell Eric Sunshine
                   ` (17 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

These tests employ a noisy subshell (with missing &&-chain) to feed
input into Git commands:

    (echo a; echo b; echo c) | git some-command ...

Drop the subshell in favor of a simple 'printf':

    printf "%s\n" a b c | git some-command ...

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t0090-cache-tree.sh         |  2 +-
 t/t2016-checkout-patch.sh     | 24 ++++++++++----------
 t/t3404-rebase-interactive.sh |  6 ++---
 t/t3701-add-interactive.sh    | 16 +++++++-------
 t/t3904-stash-patch.sh        |  8 +++----
 t/t7105-reset-patch.sh        | 12 +++++-----
 t/t7301-clean-interactive.sh  | 41 +++++++++++++++++------------------
 t/t7501-commit.sh             |  4 ++--
 t/t7610-mergetool.sh          |  8 +++----
 9 files changed, 60 insertions(+), 61 deletions(-)

diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 0c61268fd2..f86cc76c9d 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -156,7 +156,7 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
 		return 44;
 	}
 	EOT
-	(echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
+	printf "%s\n" p 1 "" s n y q |
 	git commit --interactive -m foo &&
 	test_cache_tree
 '
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 9cd0ac4ba3..4dcad0f4a2 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -20,33 +20,33 @@ test_expect_success PERL 'setup' '
 
 test_expect_success PERL 'saying "n" does nothing' '
 	set_and_save_state dir/foo work head &&
-	(echo n; echo n) | git checkout -p &&
+	printf "%s\n" n n | git checkout -p &&
 	verify_saved_state bar &&
 	verify_saved_state dir/foo
 '
 
 test_expect_success PERL 'git checkout -p' '
-	(echo n; echo y) | git checkout -p &&
+	printf "%s\n" n y | git checkout -p &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
 test_expect_success PERL 'git checkout -p with staged changes' '
 	set_state dir/foo work index &&
-	(echo n; echo y) | git checkout -p &&
+	printf "%s\n" n y | git checkout -p &&
 	verify_saved_state bar &&
 	verify_state dir/foo index index
 '
 
 test_expect_success PERL 'git checkout -p HEAD with NO staged changes: abort' '
 	set_and_save_state dir/foo work head &&
-	(echo n; echo y; echo n) | git checkout -p HEAD &&
+	printf "%s\n" n y n | git checkout -p HEAD &&
 	verify_saved_state bar &&
 	verify_saved_state dir/foo
 '
 
 test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
-	(echo n; echo y; echo y) | git checkout -p HEAD &&
+	printf "%s\n" n y y | git checkout -p HEAD &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
@@ -54,14 +54,14 @@ test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
 test_expect_success PERL 'git checkout -p HEAD with change already staged' '
 	set_state dir/foo index index &&
 	# the third n is to get out in case it mistakenly does not apply
-	(echo n; echo y; echo n) | git checkout -p HEAD &&
+	printf "%s\n" n y n | git checkout -p HEAD &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
 test_expect_success PERL 'git checkout -p HEAD^' '
 	# the third n is to get out in case it mistakenly does not apply
-	(echo n; echo y; echo n) | git checkout -p HEAD^ &&
+	printf "%s\n" n y n | git checkout -p HEAD^ &&
 	verify_saved_state bar &&
 	verify_state dir/foo parent parent
 '
@@ -69,7 +69,7 @@ test_expect_success PERL 'git checkout -p HEAD^' '
 test_expect_success PERL 'git checkout -p handles deletion' '
 	set_state dir/foo work index &&
 	rm dir/foo &&
-	(echo n; echo y) | git checkout -p &&
+	printf "%s\n" n y | git checkout -p &&
 	verify_saved_state bar &&
 	verify_state dir/foo index index
 '
@@ -81,21 +81,21 @@ test_expect_success PERL 'git checkout -p handles deletion' '
 
 test_expect_success PERL 'path limiting works: dir' '
 	set_state dir/foo work head &&
-	(echo y; echo n) | git checkout -p dir &&
+	printf "%s\n" y n | git checkout -p dir &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
 test_expect_success PERL 'path limiting works: -- dir' '
 	set_state dir/foo work head &&
-	(echo y; echo n) | git checkout -p -- dir &&
+	printf "%s\n" y n | git checkout -p -- dir &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
 test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
 	# the third n is to get out in case it mistakenly does not apply
-	(echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+	printf "%s\n" y n n | git checkout -p HEAD^ -- dir &&
 	verify_saved_state bar &&
 	verify_state dir/foo parent parent
 '
@@ -103,7 +103,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
 test_expect_success PERL 'path limiting works: foo inside dir' '
 	set_state dir/foo work head &&
 	# the third n is to get out in case it mistakenly does not apply
-	(echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+	printf "%s\n" y n n | (cd dir && git checkout -p foo) &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 352a52e59d..0dea186289 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -509,7 +509,7 @@ test_expect_success 'interrupted squash works as expected' '
 	one=$(git rev-parse HEAD~3) &&
 	set_fake_editor &&
 	test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
-	(echo one; echo two; echo four) > conflict &&
+	printf "%s\n" one two four > conflict &&
 	git add conflict &&
 	test_must_fail git rebase --continue &&
 	echo resolved > conflict &&
@@ -523,10 +523,10 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
 	one=$(git rev-parse HEAD~3) &&
 	set_fake_editor &&
 	test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
-	(echo one; echo four) > conflict &&
+	printf "%s\n" one four > conflict &&
 	git add conflict &&
 	test_must_fail git rebase --continue &&
-	(echo one; echo two; echo four) > conflict &&
+	printf "%s\n" one two four > conflict &&
 	git add conflict &&
 	test_must_fail git rebase --continue &&
 	echo resolved > conflict &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index b170fb02b8..7eec7bb3bb 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -46,13 +46,13 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'diff works (initial)' '
-	(echo d; echo 1) | git add -i >output &&
+	printf "%s\n" d 1 | git add -i >output &&
 	sed -ne "/new file/,/content/p" <output >diff &&
 	diff_cmp expected diff
 '
 test_expect_success 'revert works (initial)' '
 	git add file &&
-	(echo r; echo 1) | git add -i &&
+	printf "%s\n" r 1 | git add -i &&
 	git ls-files >output &&
 	! grep . output
 '
@@ -83,13 +83,13 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'diff works (commit)' '
-	(echo d; echo 1) | git add -i >output &&
+	printf "%s\n" d 1 | git add -i >output &&
 	sed -ne "/^index/,/content/p" <output >diff &&
 	diff_cmp expected diff
 '
 test_expect_success 'revert works (commit)' '
 	git add file &&
-	(echo r; echo 1) | git add -i &&
+	printf "%s\n" r 1 | git add -i &&
 	git add -i </dev/null >output &&
 	grep "unchanged *+3/-0 file" output
 '
@@ -102,7 +102,7 @@ test_expect_success 'setup expected' '
 
 test_expect_success 'dummy edit works' '
 	test_set_editor : &&
-	(echo e; echo a) | git add -p &&
+	printf "%s\n" e a | git add -p &&
 	git diff > diff &&
 	diff_cmp expected diff
 '
@@ -127,7 +127,7 @@ test_expect_success 'setup fake editor' '
 
 test_expect_success 'bad edit rejected' '
 	git reset &&
-	(echo e; echo n; echo d) | git add -p >output &&
+	printf "%s\n" e n d | git add -p >output &&
 	grep "hunk does not apply" output
 '
 
@@ -140,7 +140,7 @@ test_expect_success 'setup patch' '
 
 test_expect_success 'garbage edit rejected' '
 	git reset &&
-	(echo e; echo n; echo d) | git add -p >output &&
+	printf "%s\n" e n d | git add -p >output &&
 	grep "hunk does not apply" output
 '
 
@@ -170,7 +170,7 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'real edit works' '
-	(echo e; echo n; echo d) | git add -p &&
+	printf "%s\n" e n d | git add -p &&
 	git diff >output &&
 	diff_cmp expected output
 '
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index 83744f8c93..f32e272786 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -29,14 +29,14 @@ test_expect_success 'setup' '
 test_expect_success 'saying "n" does nothing' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state dir/foo work index &&
-	(echo n; echo n; echo n) | test_must_fail git stash save -p &&
+	printf "%s\n" n n n | test_must_fail git stash save -p &&
 	verify_state HEAD HEADfile_work HEADfile_index &&
 	verify_saved_state bar &&
 	verify_state dir/foo work index
 '
 
 test_expect_success 'git stash -p' '
-	(echo y; echo n; echo y) | git stash save -p &&
+	printf "%s\n" y n y | git stash save -p &&
 	verify_state HEAD committed HEADfile_index &&
 	verify_saved_state bar &&
 	verify_state dir/foo head index &&
@@ -51,7 +51,7 @@ test_expect_success 'git stash -p --no-keep-index' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state bar bar_work bar_index &&
 	set_state dir/foo work index &&
-	(echo y; echo n; echo y) | git stash save -p --no-keep-index &&
+	printf "%s\n" y n y | git stash save -p --no-keep-index &&
 	verify_state HEAD committed committed &&
 	verify_state bar bar_work dummy &&
 	verify_state dir/foo head head &&
@@ -66,7 +66,7 @@ test_expect_success 'git stash --no-keep-index -p' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state bar bar_work bar_index &&
 	set_state dir/foo work index &&
-	(echo y; echo n; echo y) | git stash save --no-keep-index -p &&
+	printf "%s\n" y n y | git stash save --no-keep-index -p &&
 	verify_state HEAD committed committed &&
 	verify_state dir/foo head head &&
 	verify_state bar bar_work dummy &&
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 98b7d7b969..d907b6dd18 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -19,20 +19,20 @@ test_expect_success PERL 'setup' '
 
 test_expect_success PERL 'saying "n" does nothing' '
 	set_and_save_state dir/foo work work &&
-	(echo n; echo n) | git reset -p &&
+	printf "%s\n" n n | git reset -p &&
 	verify_saved_state dir/foo &&
 	verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p' '
-	(echo n; echo y) | git reset -p >output &&
+	printf "%s\n" n y | git reset -p >output &&
 	verify_state dir/foo work head &&
 	verify_saved_state bar &&
 	test_i18ngrep "Unstage" output
 '
 
 test_expect_success PERL 'git reset -p HEAD^' '
-	(echo n; echo y) | git reset -p HEAD^ >output &&
+	printf "%s\n" n y | git reset -p HEAD^ >output &&
 	verify_state dir/foo work parent &&
 	verify_saved_state bar &&
 	test_i18ngrep "Apply" output
@@ -45,20 +45,20 @@ test_expect_success PERL 'git reset -p HEAD^' '
 
 test_expect_success PERL 'git reset -p dir' '
 	set_state dir/foo work work &&
-	(echo y; echo n) | git reset -p dir &&
+	printf "%s\n" y n | git reset -p dir &&
 	verify_state dir/foo work head &&
 	verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p -- foo (inside dir)' '
 	set_state dir/foo work work &&
-	(echo y; echo n) | (cd dir && git reset -p -- foo) &&
+	printf "%s\n" y n | (cd dir && git reset -p -- foo) &&
 	verify_state dir/foo work head &&
 	verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p HEAD^ -- dir' '
-	(echo y; echo n) | git reset -p HEAD^ -- dir &&
+	printf "%s\n" y n | git reset -p HEAD^ -- dir &&
 	verify_state dir/foo work parent &&
 	verify_saved_state bar
 '
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index 1bf9789c8a..2bc8526f59 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -107,7 +107,7 @@ test_expect_success 'git clean -id (filter all)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo f; echo "*"; echo; echo c) | \
+	printf "%s\n" f "*" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -129,7 +129,7 @@ test_expect_success 'git clean -id (filter patterns)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo f; echo "part3.* *.out"; echo; echo c) | \
+	printf "%s\n" f "part3.* *.out" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -151,7 +151,7 @@ test_expect_success 'git clean -id (filter patterns 2)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo f; echo "* !*.out"; echo; echo c) | \
+	printf "%s\n" f "* !*.out" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -173,7 +173,7 @@ test_expect_success 'git clean -id (select - all)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo "*"; echo; echo c) | \
+	printf "%s\n" s "*" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -195,7 +195,7 @@ test_expect_success 'git clean -id (select - none)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo; echo c) | \
+	printf "%s\n" s "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -217,7 +217,7 @@ test_expect_success 'git clean -id (select - number)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo 3; echo; echo c) | \
+	printf "%s\n" s 3 "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -239,7 +239,7 @@ test_expect_success 'git clean -id (select - number 2)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo 2 3; echo 5; echo; echo c) | \
+	printf "%s\n" s "2 3" 5 "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -261,7 +261,7 @@ test_expect_success 'git clean -id (select - number 3)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo 3,4 5; echo; echo c) | \
+	printf "%s\n" s "3,4 5" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -282,7 +282,7 @@ test_expect_success 'git clean -id (select - filenames)' '
 
 	mkdir -p build docs &&
 	touch a.out foo.txt bar.txt baz.txt &&
-	(echo s; echo a.out fo ba bar; echo; echo c) | \
+	printf "%s\n" s "a.out fo ba bar" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test ! -f a.out &&
@@ -298,7 +298,7 @@ test_expect_success 'git clean -id (select - range)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo 1,3-4; echo 2; echo; echo c) | \
+	printf "%s\n" s "1,3-4" 2 "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -320,7 +320,7 @@ test_expect_success 'git clean -id (select - range 2)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo 4- 1; echo; echo c) | \
+	printf "%s\n" s "4- 1" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -342,7 +342,7 @@ test_expect_success 'git clean -id (inverse select)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+	printf "%s\n" s "*" "-5- 1 -2" "" c |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -364,7 +364,7 @@ test_expect_success 'git clean -id (ask)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	printf "%s\n" a Y y no yes bad "" |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -386,7 +386,7 @@ test_expect_success 'git clean -id (ask - Ctrl+D)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(echo a; echo Y; echo no; echo yes; echo "\04") | \
+	printf "%s\n" a Y no yes "\04" |
 	git clean -id &&
 	test -f Makefile &&
 	test -f README &&
@@ -408,8 +408,8 @@ test_expect_success 'git clean -id with prefix and path (filter)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(cd build/ && \
-	 (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+	(cd build/ &&
+	 printf "%s\n" f docs "*.h" "" c |
 	 git clean -id ..) &&
 	test -f Makefile &&
 	test -f README &&
@@ -431,9 +431,8 @@ test_expect_success 'git clean -id with prefix and path (select by name)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(cd build/ && \
-	 (echo s; echo "../docs/"; echo "../src/part3.c"; \
-	  echo "../src/part4.c";  echo; echo c) | \
+	(cd build/ &&
+	 printf "%s\n" s ../docs/ ../src/part3.c ../src/part4.c "" c |
 	 git clean -id ..) &&
 	test -f Makefile &&
 	test -f README &&
@@ -455,8 +454,8 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
 	mkdir -p build docs &&
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
-	(cd build/ && \
-	 (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	(cd build/ &&
+	 printf "%s\n" a Y y no yes bad "" |
 	 git clean -id ..) &&
 	test -f Makefile &&
 	test -f README &&
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 9dbbd01fc0..589b6cea23 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -47,7 +47,7 @@ test_expect_success 'paths and -a do not mix' '
 test_expect_success PERL 'can use paths with --interactive' '
 	echo bong-o-bong >file &&
 	# 2: update, 1:st path, that is all, 7: quit
-	( echo 2; echo 1; echo; echo 7 ) |
+	printf "%s\n" 2 1 "" 7 |
 	git commit -m foo --interactive file &&
 	git reset --hard HEAD^
 '
@@ -293,7 +293,7 @@ test_expect_success PERL 'interactive add' '
 test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
 	echo zoo >file &&
 	test_must_fail git diff --exit-code >diff1 &&
-	(echo u ; echo "*" ; echo q) |
+	printf "%s\n" u "*" q |
 	(
 		EDITOR=: &&
 		export EDITOR &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 1a430b9c40..a66d038083 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -57,18 +57,18 @@ test_expect_success 'setup' '
 
 	git checkout -b delete-base branch1 &&
 	mkdir -p a/a &&
-	(echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+	printf "%s\n" one two 3 4 >a/a/file.txt &&
 	git add a/a/file.txt &&
 	git commit -m"base file" &&
 	git checkout -b move-to-b delete-base &&
 	mkdir -p b/b &&
 	git mv a/a/file.txt b/b/file.txt &&
-	(echo one; echo two; echo 4) >b/b/file.txt &&
+	printf "%s\n" one two 4 >b/b/file.txt &&
 	git commit -a -m"move to b" &&
 	git checkout -b move-to-c delete-base &&
 	mkdir -p c/c &&
 	git mv a/a/file.txt c/c/file.txt &&
-	(echo one; echo two; echo 3) >c/c/file.txt &&
+	printf "%s\n" one two 3 >c/c/file.txt &&
 	git commit -a -m"move to c" &&
 
 	git checkout -b stash1 master &&
@@ -349,7 +349,7 @@ test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
 	git checkout -b test$test_count move-to-c &&
 	test_config mergetool.keepTemporaries true &&
 	test_must_fail git merge move-to-b &&
-	! (echo a; echo n) | git mergetool a/a/file.txt &&
+	! printf "%s\n" a n | git mergetool a/a/file.txt &&
 	test -d a/a &&
 	cat >expect <<-\EOF &&
 	file_BASE_.txt
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 15/29] t: drop unnecessary terminating semicolons in subshell
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (13 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 16/29] t: use test_might_fail() instead of manipulating exit code manually Eric Sunshine
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

An upcoming change will teach --chain-lint to check the &&-chain inside
subshells. The semicolons after the final commands in these subshells
will trip up --chain-lint since they break the &&-chain. Since those
semicolons are unnecessary, just drop them.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3102-ls-tree-wildcards.sh | 2 +-
 t/t4010-diff-pathspec.sh     | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
index e804377f1c..1e16c6b8ea 100755
--- a/t/t3102-ls-tree-wildcards.sh
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -23,7 +23,7 @@ test_expect_success 'ls-tree outside prefix' '
 	cat >expect <<-EOF &&
 	100644 blob $EMPTY_BLOB	../a[a]/three
 	EOF
-	( cd aa && git ls-tree -r HEAD "../a[a]"; ) >actual &&
+	( cd aa && git ls-tree -r HEAD "../a[a]" ) >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 35b35a81c8..b7f25071cf 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -111,10 +111,10 @@ test_expect_success 'diff-tree -r with wildcard' '
 test_expect_success 'setup submodules' '
 	test_tick &&
 	git init submod &&
-	( cd submod && test_commit first; ) &&
+	( cd submod && test_commit first ) &&
 	git add submod &&
 	git commit -m first &&
-	( cd submod && test_commit second; ) &&
+	( cd submod && test_commit second ) &&
 	git add submod &&
 	git commit -m second
 '
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 16/29] t: use test_might_fail() instead of manipulating exit code manually
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (14 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 15/29] t: drop unnecessary terminating semicolons in subshell Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 17/29] t: use test_must_fail() instead of checking " Eric Sunshine
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

These tests manually coerce the exit code of invoked commands to
"success" when they don't care if the command succeeds or fails since
failure of those commands should not cause the test to fail overall.
Such manual management predates the invention of test_might_fail().

An upcoming change will teach --chain-lint to check the &&-chain inside
subshells. This sort of manual exit code manipulation will trip up
--chain-lint due to the intentional break in the &&-chain. Therefore,
replace manual exit code management with test_might_fail() and a normal
&&-chain.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t1507-rev-parse-upstream.sh | 6 +++---
 t/t1700-split-index.sh        | 2 +-
 t/t4012-diff-binary.sh        | 6 ++----
 t/t5400-send-pack.sh          | 4 ++--
 4 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 93c77eac45..349f6e10af 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -123,9 +123,9 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' '
 
 test_expect_success 'merge my-side@{u} records the correct name' '
 (
-	cd clone || exit
-	git checkout master || exit
-	git branch -D new ;# can fail but is ok
+	cd clone &&
+	git checkout master &&
+	test_might_fail git branch -D new &&
 	git branch -t new my-side@{u} &&
 	git merge -s ours new@{u} &&
 	git show -s --pretty=tformat:%s >actual &&
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 1e81b33b2e..39133bcbc8 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -435,7 +435,7 @@ test_expect_success 'writing split index with null sha1 does not write cache tre
 	commit=$(git commit-tree $tree -p HEAD <msg) &&
 	git update-ref HEAD "$commit" &&
 	GIT_ALLOW_NULL_SHA1=1 git reset --hard &&
-	(test-tool dump-cache-tree >cache-tree.out || true) &&
+	test_might_fail test-tool dump-cache-tree >cache-tree.out &&
 	test_line_count = 0 cache-tree.out
 '
 
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 0a8af76aab..6579c81216 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -102,10 +102,8 @@ test_expect_success 'apply binary patch' '
 
 test_expect_success 'diff --no-index with binary creation' '
 	echo Q | q_to_nul >binary &&
-	(: hide error code from diff, which just indicates differences
-	 git diff --binary --no-index /dev/null binary >current ||
-	 true
-	) &&
+	# hide error code from diff, which just indicates differences
+	test_might_fail git diff --binary --no-index /dev/null binary >current &&
 	rm binary &&
 	git apply --binary <current &&
 	echo Q >expected &&
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 911eae1bf7..f1932ea431 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -86,7 +86,7 @@ test_expect_success 'push can be used to delete a ref' '
 test_expect_success 'refuse deleting push with denyDeletes' '
 	(
 	    cd victim &&
-	    ( git branch -D extra || : ) &&
+	    test_might_fail git branch -D extra &&
 	    git config receive.denyDeletes true &&
 	    git branch extra master
 	) &&
@@ -119,7 +119,7 @@ test_expect_success 'override denyDeletes with git -c receive-pack' '
 test_expect_success 'denyNonFastforwards trumps --force' '
 	(
 	    cd victim &&
-	    ( git branch -D extra || : ) &&
+	    test_might_fail git branch -D extra &&
 	    git config receive.denyNonFastforwards true
 	) &&
 	victim_orig=$(cd victim && git rev-parse --verify master) &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (15 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 16/29] t: use test_might_fail() instead of manipulating exit code manually Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:59   ` Luke Diamand
  2018-06-26  8:58   ` Elijah Newren
  2018-06-26  7:29 ` [PATCH 18/29] t0000-t0999: fix broken &&-chains in subshells Eric Sunshine
                   ` (14 subsequent siblings)
  31 siblings, 2 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

These tests intentionally break the &&-chain to manually check the exit
code of invoked commands which they expect to fail, and invert that
local expected failure into a successful exit code for the test overall.
Such manual exit code manipulation predates the invention of
test_must_fail().

An upcoming change will teach --chain-lint to check the &&-chain inside
subshells. This sort of manual exit code checking will trip up
--chain-lint due to the intentional break in the &&-chain. Therefore,
replace the manual exit code management with test_must_fail() and a
normal &&-chain.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t5405-send-pack-rewind.sh |  3 +--
 t/t9814-git-p4-rename.sh    | 16 ++--------------
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 4bda18a662..235fb7686a 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '
 
 	(
 		cd another &&
-		git push .. master:master
-		test $? = 1
+		test_must_fail git push .. master:master
 	)
 
 '
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index e7e0268e98..80aac5ab16 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -12,20 +12,8 @@ test_expect_success 'start p4d' '
 test_expect_success 'p4 help unknown returns 1' '
 	(
 		cd "$cli" &&
-		(
-			p4 help client >errs 2>&1
-			echo $? >retval
-		)
-		echo 0 >expected &&
-		test_cmp expected retval &&
-		rm retval &&
-		(
-			p4 help nosuchcommand >errs 2>&1
-			echo $? >retval
-		)
-		echo 1 >expected &&
-		test_cmp expected retval &&
-		rm retval
+		p4 help client &&
+		test_must_fail p4 help nosuchcommand
 	)
 '
 
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 18/29] t0000-t0999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (16 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 17/29] t: use test_must_fail() instead of checking " Eric Sunshine
@ 2018-06-26  7:29 ` Eric Sunshine
  2018-06-26  7:29 ` [PATCH 19/29] t1000-t1999: " Eric Sunshine
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t0000-basic.sh      |  2 +-
 t/t0003-attributes.sh | 24 ++++++++++++------------
 t/t0021-conversion.sh |  4 ++--
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index af61d083b4..34859fe4a5 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -1081,7 +1081,7 @@ test_expect_success 'very long name in the index handled sanely' '
 	(
 		git ls-files -s path4 |
 		sed -e "s/	.*/	/" |
-		tr -d "\012"
+		tr -d "\012" &&
 		echo "$a"
 	) | git update-index --index-info &&
 	len=$(git ls-files "a*" | wc -c) &&
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index f19ae4f8cc..5c37c2e1f8 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -34,15 +34,15 @@ test_expect_success 'open-quoted pathname' '
 test_expect_success 'setup' '
 	mkdir -p a/b/d a/c b &&
 	(
-		echo "[attr]notest !test"
-		echo "\" d \"	test=d"
-		echo " e	test=e"
-		echo " e\"	test=e"
-		echo "f	test=f"
-		echo "a/i test=a/i"
-		echo "onoff test -test"
-		echo "offon -test test"
-		echo "no notest"
+		echo "[attr]notest !test" &&
+		echo "\" d \"	test=d" &&
+		echo " e	test=e" &&
+		echo " e\"	test=e" &&
+		echo "f	test=f" &&
+		echo "a/i test=a/i" &&
+		echo "onoff test -test" &&
+		echo "offon -test test" &&
+		echo "no notest" &&
 		echo "A/e/F test=A/e/F"
 	) >.gitattributes &&
 	(
@@ -51,7 +51,7 @@ test_expect_success 'setup' '
 	) >a/.gitattributes &&
 	(
 		echo "h test=a/b/h" &&
-		echo "d/* test=a/b/d/*"
+		echo "d/* test=a/b/d/*" &&
 		echo "d/yes notest"
 	) >a/b/.gitattributes &&
 	(
@@ -287,7 +287,7 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
 	(
 		cd bare.git &&
 		(
-			echo "f	test=f"
+			echo "f	test=f" &&
 			echo "a/i test=a/i"
 		) >.gitattributes &&
 		attr_check f unspecified &&
@@ -312,7 +312,7 @@ test_expect_success 'bare repository: test info/attributes' '
 	(
 		cd bare.git &&
 		(
-			echo "f	test=f"
+			echo "f	test=f" &&
 			echo "a/i test=a/i"
 		) >info/attributes &&
 		attr_check f f &&
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 9479a4aaab..6a213608cc 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -785,7 +785,7 @@ test_expect_success PERL 'missing file in delayed checkout' '
 		cd repo &&
 		git init &&
 		echo "*.a filter=bug" >.gitattributes &&
-		cp "$TEST_ROOT/test.o" missing-delay.a
+		cp "$TEST_ROOT/test.o" missing-delay.a &&
 		git add . &&
 		git commit -m "test commit"
 	) &&
@@ -807,7 +807,7 @@ test_expect_success PERL 'invalid file in delayed checkout' '
 		git init &&
 		echo "*.a filter=bug" >.gitattributes &&
 		cp "$TEST_ROOT/test.o" invalid-delay.a &&
-		cp "$TEST_ROOT/test.o" unfiltered
+		cp "$TEST_ROOT/test.o" unfiltered &&
 		git add . &&
 		git commit -m "test commit"
 	) &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 19/29] t1000-t1999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (17 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 18/29] t0000-t0999: fix broken &&-chains in subshells Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 20/29] t2000-t2999: " Eric Sunshine
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t1004-read-tree-m-u-wf.sh         |  8 ++++----
 t/t1005-read-tree-reset.sh          | 10 +++++-----
 t/t1008-read-tree-overlay.sh        |  2 +-
 t/t1020-subdirectory.sh             |  2 +-
 t/t1050-large.sh                    |  6 +++---
 t/t1411-reflog-show.sh              |  6 +++---
 t/t1512-rev-parse-disambiguation.sh |  6 +++---
 7 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index c7ce5d8bb5..a479549fb6 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -212,10 +212,10 @@ test_expect_success 'D/F' '
 	read_tree_u_must_succeed -m -u branch-point side-b side-a &&
 	git ls-files -u >actual &&
 	(
-		a=$(git rev-parse branch-point:subdir/file2)
-		b=$(git rev-parse side-a:subdir/file2/another)
-		echo "100644 $a 1	subdir/file2"
-		echo "100644 $a 2	subdir/file2"
+		a=$(git rev-parse branch-point:subdir/file2) &&
+		b=$(git rev-parse side-a:subdir/file2/another) &&
+		echo "100644 $a 1	subdir/file2" &&
+		echo "100644 $a 2	subdir/file2" &&
 		echo "100644 $b 3	subdir/file2/another"
 	) >expect &&
 	test_cmp expect actual
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 074568500a..83b09e1310 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -33,7 +33,7 @@ test_expect_success 'reset should remove remnants from a failed merge' '
 	git ls-files -s >expect &&
 	sha1=$(git rev-parse :new) &&
 	(
-		echo "100644 $sha1 1	old"
+		echo "100644 $sha1 1	old" &&
 		echo "100644 $sha1 3	old"
 	) | git update-index --index-info &&
 	>old &&
@@ -48,7 +48,7 @@ test_expect_success 'two-way reset should remove remnants too' '
 	git ls-files -s >expect &&
 	sha1=$(git rev-parse :new) &&
 	(
-		echo "100644 $sha1 1	old"
+		echo "100644 $sha1 1	old" &&
 		echo "100644 $sha1 3	old"
 	) | git update-index --index-info &&
 	>old &&
@@ -63,7 +63,7 @@ test_expect_success 'Porcelain reset should remove remnants too' '
 	git ls-files -s >expect &&
 	sha1=$(git rev-parse :new) &&
 	(
-		echo "100644 $sha1 1	old"
+		echo "100644 $sha1 1	old" &&
 		echo "100644 $sha1 3	old"
 	) | git update-index --index-info &&
 	>old &&
@@ -78,7 +78,7 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
 	git ls-files -s >expect &&
 	sha1=$(git rev-parse :new) &&
 	(
-		echo "100644 $sha1 1	old"
+		echo "100644 $sha1 1	old" &&
 		echo "100644 $sha1 3	old"
 	) | git update-index --index-info &&
 	>old &&
@@ -93,7 +93,7 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
 	git ls-files -s >expect &&
 	sha1=$(git rev-parse :new) &&
 	(
-		echo "100644 $sha1 1	old"
+		echo "100644 $sha1 1	old" &&
 		echo "100644 $sha1 3	old"
 	) | git update-index --index-info &&
 	>old &&
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index 4c50ed955e..e74b185b6c 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -23,7 +23,7 @@ test_expect_success setup '
 
 test_expect_success 'multi-read' '
 	read_tree_must_succeed initial master side &&
-	(echo a; echo b/c) >expect &&
+	(echo a && echo b/c) >expect &&
 	git ls-files >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index df3183ea1a..c2df75e495 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -148,7 +148,7 @@ test_expect_success 'GIT_PREFIX for built-ins' '
 	(
 		cd dir &&
 		echo "change" >two &&
-		GIT_EXTERNAL_DIFF=./diff git diff >../actual
+		GIT_EXTERNAL_DIFF=./diff git diff >../actual &&
 		git checkout -- two
 	) &&
 	test_cmp expect actual
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index f9eb143f43..1a9b21b293 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -108,7 +108,7 @@ test_expect_success 'packsize limit' '
 		test-tool genrandom "c" $(( 128 * 1024 )) >mid3 &&
 		git add mid1 mid2 mid3 &&
 
-		count=0
+		count=0 &&
 		for pi in .git/objects/pack/pack-*.idx
 		do
 			test -f "$pi" && count=$(( $count + 1 ))
@@ -116,8 +116,8 @@ test_expect_success 'packsize limit' '
 		test $count = 2 &&
 
 		(
-			git hash-object --stdin <mid1
-			git hash-object --stdin <mid2
+			git hash-object --stdin <mid1 &&
+			git hash-object --stdin <mid2 &&
 			git hash-object --stdin <mid3
 		) |
 		sort >expect &&
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 596907758d..4d62ceef9c 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -159,9 +159,9 @@ test_expect_success 'git log -g -p shows diffs vs. parents' '
 	git log -1 -p HEAD^ >log.one &&
 	git log -1 -p HEAD >log.two &&
 	(
-		cat log.one; echo
-		cat log.two; echo
-		cat log.one; echo
+		cat log.one && echo &&
+		cat log.two && echo &&
+		cat log.one && echo &&
 		cat log.two
 	) >expect &&
 	test_cmp expect actual
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 96fe3754c8..e4d5b56014 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -34,8 +34,8 @@ test_expect_success 'blob and tree' '
 		for i in 0 1 2 3 4 5 6 7 8 9
 		do
 			echo $i
-		done
-		echo
+		done &&
+		echo &&
 		echo b1rwzyc3
 	) >a0blgqsjc &&
 
@@ -222,7 +222,7 @@ test_expect_success 'more history' '
 
 	test_might_fail git rm -f a0blgqsjc &&
 	(
-		git cat-file blob $side:f5518nwu
+		git cat-file blob $side:f5518nwu &&
 		echo j3l0i9s6
 	) >ab2gs879 &&
 	git add ab2gs879 &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 20/29] t2000-t2999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (18 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 19/29] t1000-t1999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 21/29] t3000-t3999: " Eric Sunshine
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t2103-update-index-ignore-missing.sh |  2 +-
 t/t2202-add-addremove.sh               | 14 +++++++-------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh
index 332694e7d3..0114f05228 100755
--- a/t/t2103-update-index-ignore-missing.sh
+++ b/t/t2103-update-index-ignore-missing.sh
@@ -32,7 +32,7 @@ test_expect_success basics '
 		test_create_repo xyzzy &&
 		cd xyzzy &&
 		>file &&
-		git add file
+		git add file &&
 		git commit -m "sub initial"
 	) &&
 	git add xyzzy &&
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a5a3166b1..17744e8c57 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -6,12 +6,12 @@ test_description='git add --all'
 
 test_expect_success setup '
 	(
-		echo .gitignore
+		echo .gitignore &&
 		echo will-remove
 	) >expect &&
 	(
-		echo actual
-		echo expect
+		echo actual &&
+		echo expect &&
 		echo ignored
 	) >.gitignore &&
 	git --literal-pathspecs add --all &&
@@ -25,10 +25,10 @@ test_expect_success setup '
 
 test_expect_success 'git add --all' '
 	(
-		echo .gitignore
-		echo not-ignored
-		echo "M	.gitignore"
-		echo "A	not-ignored"
+		echo .gitignore &&
+		echo not-ignored &&
+		echo "M	.gitignore" &&
+		echo "A	not-ignored" &&
 		echo "D	will-remove"
 	) >expect &&
 	>ignored &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 21/29] t3000-t3999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (19 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 20/29] t2000-t2999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 22/29] t3030: " Eric Sunshine
                   ` (10 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3000-ls-files-others.sh              | 2 +-
 t/t3006-ls-files-long.sh                | 2 +-
 t/t3008-ls-files-lazy-init-name-hash.sh | 8 ++++----
 t/t3050-subprojects-fetch.sh            | 8 ++++----
 t/t3301-notes.sh                        | 8 ++++----
 t/t3400-rebase.sh                       | 8 ++++----
 t/t3402-rebase-merge.sh                 | 4 ++--
 t/t3418-rebase-continue.sh              | 4 ++--
 t/t3700-add.sh                          | 8 ++++----
 9 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index c525656b2c..afd4756134 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -84,7 +84,7 @@ test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
 	) &&
 	(
 		cd super &&
-		"$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub
+		"$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub &&
 		git ls-files --others --exclude-standard >../actual
 	) &&
 	echo sub/ >expect &&
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
index 202ad658b8..e109c3fbfb 100755
--- a/t/t3006-ls-files-long.sh
+++ b/t/t3006-ls-files-long.sh
@@ -29,7 +29,7 @@ test_expect_success 'overly-long path does not replace another by mistake' '
 	printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
 	git update-index --add --index-info &&
 	(
-		echo "$path_a"
+		echo "$path_a" &&
 		echo "$path_z"
 	) >expect &&
 	git ls-files >actual &&
diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh
index 08af596ba6..64f047332b 100755
--- a/t/t3008-ls-files-lazy-init-name-hash.sh
+++ b/t/t3008-ls-files-lazy-init-name-hash.sh
@@ -14,10 +14,10 @@ LAZY_THREAD_COST=2000
 
 test_expect_success 'no buffer overflow in lazy_init_name_hash' '
 	(
-	    test_seq $LAZY_THREAD_COST | sed "s/^/a_/"
-	    echo b/b/b
-	    test_seq $LAZY_THREAD_COST | sed "s/^/c_/"
-	    test_seq 50 | sed "s/^/d_/" | tr "\n" "/"; echo d
+	    test_seq $LAZY_THREAD_COST | sed "s/^/a_/" &&
+	    echo b/b/b &&
+	    test_seq $LAZY_THREAD_COST | sed "s/^/c_/" &&
+	    test_seq 50 | sed "s/^/d_/" | tr "\n" "/" && echo d
 	) |
 	sed "s/^/100644 $EMPTY_BLOB	/" |
 	git update-index --index-info &&
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 2f5f41a012..f1f09abdd9 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -21,10 +21,10 @@ test_expect_success setup '
 
 test_expect_success clone '
 	git clone "file://$(pwd)/.git" cloned &&
-	(git rev-parse HEAD; git ls-files -s) >expected &&
+	(git rev-parse HEAD && git ls-files -s) >expected &&
 	(
 		cd cloned &&
-		(git rev-parse HEAD; git ls-files -s) >../actual
+		(git rev-parse HEAD && git ls-files -s) >../actual
 	) &&
 	test_cmp expected actual
 '
@@ -40,11 +40,11 @@ test_expect_success advance '
 '
 
 test_expect_success fetch '
-	(git rev-parse HEAD; git ls-files -s) >expected &&
+	(git rev-parse HEAD && git ls-files -s) >expected &&
 	(
 		cd cloned &&
 		git pull &&
-		(git rev-parse HEAD; git ls-files -s) >../actual
+		(git rev-parse HEAD && git ls-files -s) >../actual
 	) &&
 	test_cmp expected actual
 '
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 2d200fdf36..ac62dc0e8f 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -914,7 +914,7 @@ test_expect_success 'git notes copy --stdin' '
 		${indent}
 		${indent}yet another note
 	EOF
-	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
 	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
 	git notes copy --stdin &&
 	git log -2 >actual &&
@@ -939,7 +939,7 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
 	EOF
 	test_commit 14th &&
 	test_commit 15th &&
-	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
 	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
 	git notes copy --for-rewrite=foo &&
 	git log -2 >actual &&
@@ -972,7 +972,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '
 	EOF
 	test_config notes.rewriteMode overwrite &&
 	test_config notes.rewriteRef "refs/notes/*" &&
-	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+	(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
 	echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
 	git notes copy --for-rewrite=foo &&
 	git log -2 >actual &&
@@ -1059,7 +1059,7 @@ test_expect_success 'git notes copy --for-rewrite (append two to one)' '
 	git notes add -f -m"append 2" HEAD^^ &&
 	test_config notes.rewriteMode concatenate &&
 	test_config notes.rewriteRef "refs/notes/*" &&
-	(echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+	(echo $(git rev-parse HEAD^) $(git rev-parse HEAD) &&
 	echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
 	git notes copy --for-rewrite=foo &&
 	git log -1 >actual &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 72d9564747..3996ee0135 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -200,10 +200,10 @@ test_expect_success 'rebase -q is quiet' '
 
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
 	(
-		echo "One"
-		echo "TwoQ"
-		echo "Three"
-		echo "FQur"
+		echo "One" &&
+		echo "TwoQ" &&
+		echo "Three" &&
+		echo "FQur" &&
 		echo "Five"
 	) | q_to_cr >CR &&
 	git add CR &&
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 488945e007..a1ec501a87 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -25,7 +25,7 @@ test_expect_success setup '
 	git commit -a -m"master updates a bit more." &&
 
 	git checkout side &&
-	(echo "0 $T" ; cat original) >renamed &&
+	(echo "0 $T" && cat original) >renamed &&
 	git add renamed &&
 	git update-index --force-remove original &&
 	git commit -a -m"side renames and edits." &&
@@ -143,7 +143,7 @@ test_expect_success 'rebase -s funny -Xopt' '
 	git checkout -b test-funny master^ &&
 	test_commit funny &&
 	(
-		PATH=./test-bin:$PATH
+		PATH=./test-bin:$PATH &&
 		git rebase -s funny -Xopt master
 	) &&
 	test -f funny.was.run
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 03bf1b8a3b..853e015839 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -60,7 +60,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
 	EOF
 	chmod +x test-bin/git-merge-funny &&
 	(
-		PATH=./test-bin:$PATH
+		PATH=./test-bin:$PATH &&
 		test_must_fail git rebase -s funny -Xopt master topic
 	) &&
 	test -f funny.was.run &&
@@ -68,7 +68,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
 	echo "Resolved" >F2 &&
 	git add F2 &&
 	(
-		PATH=./test-bin:$PATH
+		PATH=./test-bin:$PATH &&
 		git rebase --continue
 	) &&
 	test -f funny.was.run
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 07af05d7ae..618750167a 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -156,9 +156,9 @@ test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries'
 test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over stage 1' '
 	git rm --cached -f file symlink &&
 	(
-		echo "100644 $(git hash-object -w stage1) 1	file"
-		echo "100755 $(git hash-object -w stage2) 2	file"
-		echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1	symlink"
+		echo "100644 $(git hash-object -w stage1) 1	file" &&
+		echo "100755 $(git hash-object -w stage2) 2	file" &&
+		echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1	symlink" &&
 		echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2	symlink"
 	) | git update-index --index-info &&
 	git config core.filemode 0 &&
@@ -265,7 +265,7 @@ test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
 	git reset --hard &&
 	H=$(git rev-parse :1/2/a) &&
 	(
-		echo "100644 $H 1	track-this"
+		echo "100644 $H 1	track-this" &&
 		echo "100644 $H 3	track-this"
 	) | git update-index --index-info &&
 	echo track-this >>.gitignore &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 22/29] t3030: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (20 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 21/29] t3000-t3999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 23/29] t4000-t4999: " Eric Sunshine
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3030-merge-recursive.sh | 340 ++++++++++++++++++-------------------
 1 file changed, 170 insertions(+), 170 deletions(-)

diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 3563e77b37..ff641b348a 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -36,15 +36,15 @@ test_expect_success 'setup 1' '
 	test_tick &&
 	git commit -m "master modifies a and d/e" &&
 	c1=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o1	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o1	d/e"
-		echo "100644 $o1 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o1	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o1	d/e" &&
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -54,15 +54,15 @@ test_expect_success 'setup 2' '
 
 	rm -rf [abcd] &&
 	git checkout side &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -75,15 +75,15 @@ test_expect_success 'setup 2' '
 	test_tick &&
 	git commit -m "side modifies a" &&
 	c2=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o2	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o2 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o2	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o2 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -93,15 +93,15 @@ test_expect_success 'setup 3' '
 
 	rm -rf [abcd] &&
 	git checkout df-1 &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -112,15 +112,15 @@ test_expect_success 'setup 3' '
 	test_tick &&
 	git commit -m "df-1 makes b/c" &&
 	c3=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o3	b/c"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o3 0	b/c"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o3	b/c" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o3 0	b/c" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -130,15 +130,15 @@ test_expect_success 'setup 4' '
 
 	rm -rf [abcd] &&
 	git checkout df-2 &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -149,15 +149,15 @@ test_expect_success 'setup 4' '
 	test_tick &&
 	git commit -m "df-2 makes a/c" &&
 	c4=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o4	a/c"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o4 0	a/c"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o4	a/c" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o4 0	a/c" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -167,15 +167,15 @@ test_expect_success 'setup 5' '
 
 	rm -rf [abcd] &&
 	git checkout remove &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -190,13 +190,13 @@ test_expect_success 'setup 5' '
 	test_tick &&
 	git commit -m "remove removes b and modifies a" &&
 	c5=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o5	a"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o5 0	a"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o5	a" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o5 0	a" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -207,15 +207,15 @@ test_expect_success 'setup 6' '
 
 	rm -rf [abcd] &&
 	git checkout df-3 &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o0 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -226,15 +226,15 @@ test_expect_success 'setup 6' '
 	test_tick &&
 	git commit -m "df-3 makes d" &&
 	c6=$(git rev-parse --verify HEAD) &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o6	d"
-		echo "100644 $o0 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 blob $o0	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o6	d" &&
+		echo "100644 $o0 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o6 0	d"
 	) >expected &&
 	test_cmp expected actual
@@ -286,11 +286,11 @@ test_expect_success 'merge-recursive result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o0 1	a"
-		echo "100644 $o2 2	a"
-		echo "100644 $o1 3	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 $o0 1	a" &&
+		echo "100644 $o2 2	a" &&
+		echo "100644 $o1 3	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -325,10 +325,10 @@ test_expect_success 'merge-recursive remove conflict' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o0 1	a"
-		echo "100644 $o1 2	a"
-		echo "100644 $o5 3	a"
-		echo "100644 $o0 0	c"
+		echo "100644 $o0 1	a" &&
+		echo "100644 $o1 2	a" &&
+		echo "100644 $o5 3	a" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -347,9 +347,9 @@ test_expect_success 'merge-recursive result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	a"
-		echo "100644 $o3 0	b/c"
-		echo "100644 $o0 0	c"
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o3 0	b/c" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -369,11 +369,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o0 1	a"
-		echo "100644 $o1 2	a"
-		echo "100644 $o4 0	a/c"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 $o0 1	a" &&
+		echo "100644 $o1 2	a" &&
+		echo "100644 $o4 0	a/c" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -393,11 +393,11 @@ test_expect_success 'merge-recursive d/f conflict result the other way' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o0 1	a"
-		echo "100644 $o1 3	a"
-		echo "100644 $o4 0	a/c"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 $o0 1	a" &&
+		echo "100644 $o1 3	a" &&
+		echo "100644 $o4 0	a/c" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -417,11 +417,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
-		echo "100644 $o6 3	d"
-		echo "100644 $o0 1	d/e"
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
+		echo "100644 $o6 3	d" &&
+		echo "100644 $o0 1	d/e" &&
 		echo "100644 $o1 2	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -441,11 +441,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
-		echo "100644 $o6 2	d"
-		echo "100644 $o0 1	d/e"
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
+		echo "100644 $o6 2	d" &&
+		echo "100644 $o0 1	d/e" &&
 		echo "100644 $o1 3	d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -465,13 +465,13 @@ test_expect_success 'reset and bind merge' '
 	git read-tree --prefix=M/ master &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	M/a"
-		echo "100644 $o0 0	M/b"
-		echo "100644 $o0 0	M/c"
-		echo "100644 $o1 0	M/d/e"
-		echo "100644 $o1 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 $o1 0	M/a" &&
+		echo "100644 $o0 0	M/b" &&
+		echo "100644 $o0 0	M/c" &&
+		echo "100644 $o1 0	M/d/e" &&
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -479,17 +479,17 @@ test_expect_success 'reset and bind merge' '
 	git read-tree --prefix=a1/ master &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	M/a"
-		echo "100644 $o0 0	M/b"
-		echo "100644 $o0 0	M/c"
-		echo "100644 $o1 0	M/d/e"
-		echo "100644 $o1 0	a"
-		echo "100644 $o1 0	a1/a"
-		echo "100644 $o0 0	a1/b"
-		echo "100644 $o0 0	a1/c"
-		echo "100644 $o1 0	a1/d/e"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
+		echo "100644 $o1 0	M/a" &&
+		echo "100644 $o0 0	M/b" &&
+		echo "100644 $o0 0	M/c" &&
+		echo "100644 $o1 0	M/d/e" &&
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o1 0	a1/a" &&
+		echo "100644 $o0 0	a1/b" &&
+		echo "100644 $o0 0	a1/c" &&
+		echo "100644 $o1 0	a1/d/e" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
 		echo "100644 $o1 0	d/e"
 	) >expected &&
 	test_cmp expected actual &&
@@ -497,21 +497,21 @@ test_expect_success 'reset and bind merge' '
 	git read-tree --prefix=z/ master &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	M/a"
-		echo "100644 $o0 0	M/b"
-		echo "100644 $o0 0	M/c"
-		echo "100644 $o1 0	M/d/e"
-		echo "100644 $o1 0	a"
-		echo "100644 $o1 0	a1/a"
-		echo "100644 $o0 0	a1/b"
-		echo "100644 $o0 0	a1/c"
-		echo "100644 $o1 0	a1/d/e"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
-		echo "100644 $o1 0	d/e"
-		echo "100644 $o1 0	z/a"
-		echo "100644 $o0 0	z/b"
-		echo "100644 $o0 0	z/c"
+		echo "100644 $o1 0	M/a" &&
+		echo "100644 $o0 0	M/b" &&
+		echo "100644 $o0 0	M/c" &&
+		echo "100644 $o1 0	M/d/e" &&
+		echo "100644 $o1 0	a" &&
+		echo "100644 $o1 0	a1/a" &&
+		echo "100644 $o0 0	a1/b" &&
+		echo "100644 $o0 0	a1/c" &&
+		echo "100644 $o1 0	a1/d/e" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
+		echo "100644 $o1 0	d/e" &&
+		echo "100644 $o1 0	z/a" &&
+		echo "100644 $o0 0	z/b" &&
+		echo "100644 $o0 0	z/c" &&
 		echo "100644 $o1 0	z/d/e"
 	) >expected &&
 	test_cmp expected actual
@@ -589,8 +589,8 @@ test_expect_success 'merge-recursive simple w/submodule result' '
 
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o5 0	a"
-		echo "100644 $o0 0	c"
+		echo "100644 $o5 0	a" &&
+		echo "100644 $o0 0	c" &&
 		echo "160000 $c1 0	d"
 	) >expected &&
 	test_cmp expected actual
@@ -601,13 +601,13 @@ test_expect_success 'merge-recursive copy vs. rename' '
 	git merge rename &&
 	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 blob $o0	e"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
-		echo "100644 $o0 0	d/e"
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 blob $o0	e" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
+		echo "100644 $o0 0	d/e" &&
 		echo "100644 $o0 0	e"
 	) >expected &&
 	test_cmp expected actual
@@ -617,17 +617,17 @@ test_expect_failure 'merge-recursive rename vs. rename/symlink' '
 
 	git checkout -f rename &&
 	git merge rename-ln &&
-	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
-		echo "120000 blob $oln	a"
-		echo "100644 blob $o0	b"
-		echo "100644 blob $o0	c"
-		echo "100644 blob $o0	d/e"
-		echo "100644 blob $o0	e"
-		echo "120000 $oln 0	a"
-		echo "100644 $o0 0	b"
-		echo "100644 $o0 0	c"
-		echo "100644 $o0 0	d/e"
+		echo "120000 blob $oln	a" &&
+		echo "100644 blob $o0	b" &&
+		echo "100644 blob $o0	c" &&
+		echo "100644 blob $o0	d/e" &&
+		echo "100644 blob $o0	e" &&
+		echo "120000 $oln 0	a" &&
+		echo "100644 $o0 0	b" &&
+		echo "100644 $o0 0	c" &&
+		echo "100644 $o0 0	d/e" &&
 		echo "100644 $o0 0	e"
 	) >expected &&
 	test_cmp expected actual
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 23/29] t4000-t4999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (21 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 22/29] t3030: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 24/29] t5000-t5999: " Eric Sunshine
                   ` (8 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t4001-diff-rename.sh                       | 2 +-
 t/t4025-hunk-header.sh                       | 8 ++++----
 t/t4041-diff-submodule-option.sh             | 4 ++--
 t/t4060-diff-submodule-option-diff-format.sh | 2 +-
 t/t4121-apply-diffs.sh                       | 2 +-
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index bf4030371a..c16486a9d4 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -180,7 +180,7 @@ test_expect_success 'setup for many rename source candidates' '
 	git add "path??" &&
 	test_tick &&
 	git commit -m "hundred" &&
-	(cat path1; echo new) >new-path &&
+	(cat path1 && echo new) >new-path &&
 	echo old >>path1 &&
 	git add new-path path1 &&
 	git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index 7a3dbc1ea2..fa44e78869 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -12,12 +12,12 @@ NS="$N$N$N$N$N$N$N$N$N$N$N$N$N"
 test_expect_success setup '
 
 	(
-		echo "A $NS"
+		echo "A $NS" &&
 		for c in B C D E F G H I J K
 		do
 			echo "  $c"
-		done
-		echo "L  $NS"
+		done &&
+		echo "L  $NS" &&
 		for c in M N O P Q R S T U V
 		do
 			echo "  $c"
@@ -34,7 +34,7 @@ test_expect_success 'hunk header truncation with an overly long line' '
 
 	git diff | sed -n -e "s/^.*@@//p" >actual &&
 	(
-		echo " A $N$N$N$N$N$N$N$N$N2"
+		echo " A $N$N$N$N$N$N$N$N$N2" &&
 		echo " L  $N$N$N$N$N$N$N$N$N1"
 	) >expected &&
 	test_cmp actual expected
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 058ee0829d..4e3499ef84 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -498,7 +498,7 @@ test_expect_success 'given commit --submodule=short' '
 test_expect_success 'setup .git file for sm2' '
 	(cd sm2 &&
 	 REAL="$(pwd)/../.real" &&
-	 mv .git "$REAL"
+	 mv .git "$REAL" &&
 	 echo "gitdir: $REAL" >.git)
 '
 
@@ -527,7 +527,7 @@ test_expect_success 'diff --submodule with objects referenced by alternates' '
 		git commit -m "sub a"
 	) &&
 	(cd sub_alt &&
-		sha1_before=$(git rev-parse --short HEAD)
+		sha1_before=$(git rev-parse --short HEAD) &&
 		echo b >b &&
 		git add b &&
 		git commit -m b &&
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index 4b168d0ed7..0eba4620f0 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -721,7 +721,7 @@ test_expect_success 'given commit' '
 test_expect_success 'setup .git file for sm2' '
 	(cd sm2 &&
 	 REAL="$(pwd)/../.real" &&
-	 mv .git "$REAL"
+	 mv .git "$REAL" &&
 	 echo "gitdir: $REAL" >.git)
 '
 
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index aff551a1d7..66368effd5 100755
--- a/t/t4121-apply-diffs.sh
+++ b/t/t4121-apply-diffs.sh
@@ -27,6 +27,6 @@ test_expect_success 'setup' \
 
 test_expect_success \
 	'check if contextually independent diffs for the same file apply' \
-	'( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
+	'( git diff test~2 test~1 && git diff test~1 test~0 )| git apply'
 
 test_done
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 24/29] t5000-t5999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (22 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 23/29] t4000-t4999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 25/29] t6000-t6999: " Eric Sunshine
                   ` (7 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t5300-pack-object.sh         |  2 +-
 t/t5302-pack-index.sh          |  2 +-
 t/t5401-update-hooks.sh        |  4 ++--
 t/t5406-remote-rejects.sh      |  2 +-
 t/t5500-fetch-pack.sh          |  2 +-
 t/t5505-remote.sh              |  2 +-
 t/t5512-ls-remote.sh           |  4 ++--
 t/t5516-fetch-push.sh          | 10 +++++-----
 t/t5517-push-mirror.sh         | 10 +++++-----
 t/t5526-fetch-submodules.sh    |  2 +-
 t/t5531-deep-submodule-push.sh |  2 +-
 t/t5543-atomic-push.sh         |  2 +-
 t/t5601-clone.sh               |  2 +-
 t/t5605-clone-local.sh         |  2 +-
 t/t5801-remote-helpers.sh      |  2 +-
 15 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 2336d09dcc..6c620cd540 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -191,7 +191,7 @@ test_expect_success 'survive missing objects/pack directory' '
 		mkdir missing-pack &&
 		cd missing-pack &&
 		git init &&
-		GOP=.git/objects/pack
+		GOP=.git/objects/pack &&
 		rm -fr $GOP &&
 		git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
 		test -f $GOP/pack-${packname_3}.pack &&
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index bb9b8bb309..91d51b35f9 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -237,7 +237,7 @@ test_expect_success 'running index-pack in the object store' '
     rm -f .git/objects/pack/* &&
     cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
     (
-	cd .git/objects/pack
+	cd .git/objects/pack &&
 	git index-pack pack-${pack1}.pack
     ) &&
     test -f .git/objects/pack/pack-${pack1}.idx
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 7f278d8ce9..b5f886a0e2 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -82,13 +82,13 @@ test_expect_success 'hooks ran' '
 '
 
 test_expect_success 'pre-receive hook input' '
-	(echo $commit0 $commit1 refs/heads/master;
+	(echo $commit0 $commit1 refs/heads/master &&
 	 echo $commit1 $commit0 refs/heads/tofail
 	) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
-	(echo refs/heads/master $commit0 $commit1;
+	(echo refs/heads/master $commit0 $commit1 &&
 	 echo refs/heads/tofail $commit1 $commit0
 	) | test_cmp - victim.git/update.args
 '
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
index 59e80a5ea2..350d2e6ea5 100755
--- a/t/t5406-remote-rejects.sh
+++ b/t/t5406-remote-rejects.sh
@@ -6,7 +6,7 @@ test_description='remote push rejects are reported by client'
 
 test_expect_success 'setup' '
 	mkdir .git/hooks &&
-	(echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
+	(echo "#!/bin/sh" && echo "exit 1") >.git/hooks/update &&
 	chmod +x .git/hooks/update &&
 	echo 1 >file &&
 	git add file &&
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 8390c0a2d2..c394779f65 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -259,7 +259,7 @@ test_expect_success 'clone shallow object count' '
 test_expect_success 'pull in shallow repo with missing merge base' '
 	(
 		cd shallow &&
-		git fetch --depth 4 .. A
+		git fetch --depth 4 .. A &&
 		test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
 	)
 '
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 3552b51b4c..11e14a1e0f 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -844,7 +844,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)'
 		git remote rename origin origin &&
 		test_path_is_missing .git/branches/origin &&
 		test "$(git config remote.origin.url)" = "quux" &&
-		test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+		test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin" &&
 		test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
 	)
 '
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 6a949484d0..ea020040e8 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -15,7 +15,7 @@ test_expect_success setup '
 	git tag mark1.10 &&
 	git show-ref --tags -d | sed -e "s/ /	/" >expected.tag &&
 	(
-		echo "$(git rev-parse HEAD)	HEAD"
+		echo "$(git rev-parse HEAD)	HEAD" &&
 		git show-ref -d	| sed -e "s/ /	/"
 	) >expected.all &&
 
@@ -105,7 +105,7 @@ test_expect_success 'use branch.<name>.remote if possible' '
 	git clone . other.git &&
 	(
 		cd other.git &&
-		echo "$(git rev-parse HEAD)	HEAD"
+		echo "$(git rev-parse HEAD)	HEAD" &&
 		git show-ref	| sed -e "s/ /	/"
 	) >exp &&
 
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index a5077d8b7c..bd8f23e430 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -923,7 +923,7 @@ test_expect_success 'push into aliased refs (consistent)' '
 	(
 		cd child1 &&
 		git branch foo &&
-		git symbolic-ref refs/heads/bar refs/heads/foo
+		git symbolic-ref refs/heads/bar refs/heads/foo &&
 		git config receive.denyCurrentBranch false
 	) &&
 	(
@@ -945,7 +945,7 @@ test_expect_success 'push into aliased refs (inconsistent)' '
 	(
 		cd child1 &&
 		git branch foo &&
-		git symbolic-ref refs/heads/bar refs/heads/foo
+		git symbolic-ref refs/heads/bar refs/heads/foo &&
 		git config receive.denyCurrentBranch false
 	) &&
 	(
@@ -1011,7 +1011,7 @@ test_expect_success 'push --porcelain rejected' '
 	mk_empty testrepo &&
 	git push testrepo refs/heads/master:refs/remotes/origin/master &&
 	(cd testrepo &&
-		git reset --hard origin/master^
+		git reset --hard origin/master^ &&
 		git config receive.denyCurrentBranch true) &&
 
 	echo >.git/foo  "To testrepo"  &&
@@ -1025,7 +1025,7 @@ test_expect_success 'push --porcelain --dry-run rejected' '
 	mk_empty testrepo &&
 	git push testrepo refs/heads/master:refs/remotes/origin/master &&
 	(cd testrepo &&
-		git reset --hard origin/master
+		git reset --hard origin/master &&
 		git config receive.denyCurrentBranch true) &&
 
 	echo >.git/foo  "To testrepo"  &&
@@ -1333,7 +1333,7 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '
 		git commit --allow-empty -m "future commit" &&
 		git tag -m "future" future &&
 		git checkout master &&
-		git for-each-ref refs/heads/master refs/tags/tag >../expect
+		git for-each-ref refs/heads/master refs/tags/tag >../expect &&
 		git push --follow-tag ../dst master
 	) &&
 	(
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index 02f160aae0..c05a661400 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -71,7 +71,7 @@ test_expect_success 'push mirror force updates existing branches' '
 		git push --mirror up &&
 		echo two >foo && git add foo && git commit -m two &&
 		git push --mirror up &&
-		git reset --hard HEAD^
+		git reset --hard HEAD^ &&
 		git push --mirror up
 	) &&
 	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
@@ -88,7 +88,7 @@ test_expect_success 'push mirror removes branches' '
 		echo one >foo && git add foo && git commit -m one &&
 		git branch remove master &&
 		git push --mirror up &&
-		git branch -D remove
+		git branch -D remove &&
 		git push --mirror up
 	) &&
 	(
@@ -170,7 +170,7 @@ test_expect_success 'push mirror force updates existing tags' '
 		echo two >foo && git add foo && git commit -m two &&
 		git tag -f tmaster master &&
 		git push --mirror up &&
-		git reset --hard HEAD^
+		git reset --hard HEAD^ &&
 		git tag -f tmaster master &&
 		git push --mirror up
 	) &&
@@ -188,7 +188,7 @@ test_expect_success 'push mirror removes tags' '
 		echo one >foo && git add foo && git commit -m one &&
 		git tag -f tremove master &&
 		git push --mirror up &&
-		git tag -d tremove
+		git tag -d tremove &&
 		git push --mirror up
 	) &&
 	(
@@ -235,7 +235,7 @@ test_expect_success 'remote.foo.mirror adds and removes branches' '
 		git branch keep master &&
 		git branch remove master &&
 		git push up &&
-		git branch -D remove
+		git branch -D remove &&
 		git push up
 	) &&
 	(
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 9cc4b569c0..143638c8cd 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -379,7 +379,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
 			git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
 		) &&
 		git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
-		git config --unset fetch.recurseSubmodules
+		git config --unset fetch.recurseSubmodules &&
 		(
 			cd submodule &&
 			git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 39cb2c1c34..e2c37fd978 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -354,7 +354,7 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
 	git clone a a1 &&
 	(
 		cd a1 &&
-		git init b
+		git init b &&
 		(
 			cd b &&
 			>junk &&
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 3480b33007..7079bcf9a0 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -178,7 +178,7 @@ test_expect_success 'atomic push obeys update hook preventing a branch to be pus
 test_expect_success 'atomic push is not advertised if configured' '
 	mk_repo_pair &&
 	(
-		cd upstream
+		cd upstream &&
 		git config receive.advertiseatomic 0
 	) &&
 	(
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 0b62037744..ddaa96ac4f 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -618,7 +618,7 @@ hex2oct () {
 test_expect_success 'clone on case-insensitive fs' '
 	git init icasefs &&
 	(
-		cd icasefs
+		cd icasefs &&
 		o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
 		t=$(printf "100644 X\0${o}100644 x\0${o}" |
 			git hash-object -w -t tree --stdin) &&
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 3c087e907c..af23419ebf 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -94,7 +94,7 @@ test_expect_success 'clone empty repository' '
 	 git config receive.denyCurrentBranch warn) &&
 	git clone empty empty-clone &&
 	test_tick &&
-	(cd empty-clone
+	(cd empty-clone &&
 	 echo "content" >> foo &&
 	 git add foo &&
 	 git commit -m "Initial commit" &&
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 362b1581e0..ee5757966f 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -96,7 +96,7 @@ test_expect_success 'push new branch with old:new refspec' '
 
 test_expect_success 'push new branch with HEAD:new refspec' '
 	(cd local &&
-	 git checkout new-name
+	 git checkout new-name &&
 	 git push origin HEAD:new-refspec-2
 	) &&
 	compare_refs local HEAD server refs/heads/new-refspec-2
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 25/29] t6000-t6999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (23 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 24/29] t5000-t5999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 26/29] t7000-t7999: " Eric Sunshine
                   ` (6 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t6010-merge-base.sh                |  2 +-
 t/t6029-merge-subtree.sh             | 16 ++++++++--------
 t/t6036-recursive-corner-cases.sh    |  6 +++---
 t/t6042-merge-rename-corner-cases.sh |  8 ++++----
 t/t6043-merge-rename-directories.sh  |  2 +-
 5 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index aa2d360ce3..44c726ea39 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -245,7 +245,7 @@ test_expect_success 'using reflog to find the fork point' '
 			git commit --allow-empty -m "Derived #$count" &&
 			git rev-parse HEAD >derived$count &&
 			git checkout -B base $E || exit 1
-		done
+		done &&
 
 		for count in 1 2 3
 		do
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 3e692454a7..7d5bc78472 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -55,7 +55,7 @@ test_expect_success 'initial merge' '
 	git checkout -b work &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	git-gui/git-gui.sh"
+		echo "100644 $o1 0	git-gui/git-gui.sh" &&
 		echo "100644 $o2 0	git.c"
 	) >expected &&
 	test_cmp expected actual
@@ -72,7 +72,7 @@ test_expect_success 'merge update' '
 	git pull -s subtree gui master2 &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o3 0	git-gui/git-gui.sh"
+		echo "100644 $o3 0	git-gui/git-gui.sh" &&
 		echo "100644 $o2 0	git.c"
 	) >expected &&
 	test_cmp expected actual
@@ -88,8 +88,8 @@ test_expect_success 'initial ambiguous subtree' '
 	git checkout -b work2 &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	git-gui/git-gui.sh"
-		echo "100644 $o1 0	git-gui2/git-gui.sh"
+		echo "100644 $o1 0	git-gui/git-gui.sh" &&
+		echo "100644 $o1 0	git-gui2/git-gui.sh" &&
 		echo "100644 $o2 0	git.c"
 	) >expected &&
 	test_cmp expected actual
@@ -101,8 +101,8 @@ test_expect_success 'merge using explicit' '
 	git pull -Xsubtree=git-gui gui master2 &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o3 0	git-gui/git-gui.sh"
-		echo "100644 $o1 0	git-gui2/git-gui.sh"
+		echo "100644 $o3 0	git-gui/git-gui.sh" &&
+		echo "100644 $o1 0	git-gui2/git-gui.sh" &&
 		echo "100644 $o2 0	git.c"
 	) >expected &&
 	test_cmp expected actual
@@ -114,8 +114,8 @@ test_expect_success 'merge2 using explicit' '
 	git pull -Xsubtree=git-gui2 gui master2 &&
 	git ls-files -s >actual &&
 	(
-		echo "100644 $o1 0	git-gui/git-gui.sh"
-		echo "100644 $o3 0	git-gui2/git-gui.sh"
+		echo "100644 $o1 0	git-gui/git-gui.sh" &&
+		echo "100644 $o3 0	git-gui2/git-gui.sh" &&
 		echo "100644 $o2 0	git.c"
 	) >expected &&
 	test_cmp expected actual
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index b32ff8e1db..892cf08743 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -72,7 +72,7 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
 		git rev-parse   >actual     \
 			:2:three   :3:three &&
 		git hash-object >>actual    \
-			three~HEAD three~R2^0
+			three~HEAD three~R2^0 &&
 		test_cmp expect actual
 	)
 '
@@ -148,7 +148,7 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
 		git rev-parse   >actual     \
 			:2:three   :3:three &&
 		git hash-object >>actual    \
-			three~HEAD three~R2^0
+			three~HEAD three~R2^0 &&
 		test_cmp expect actual
 	)
 '
@@ -228,7 +228,7 @@ test_expect_success 'git detects differently handled merges conflict' '
 			D:new_a  E:new_a &&
 		git rev-parse   >actual     \
 			:2:new_a :3:new_a &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		git cat-file -p B:new_a >ours &&
 		git cat-file -p C:new_a >theirs &&
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
index 1cbd946fc2..661b633478 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -352,7 +352,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
 			base:file   left-conflict:newfile  right:file &&
 		git rev-parse >actual                                 \
 			:1:newfile  :2:newfile             :3:newfile &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		test_path_is_file newfile/realfile &&
 		test_path_is_file newfile~HEAD
@@ -580,7 +580,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
 			C:a   A:a   B:b   C:C &&
 		git rev-parse >actual          \
 			:3:a  :1:a  :2:b  :3:c &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		test_path_is_file a &&
 		test_path_is_file b &&
@@ -680,13 +680,13 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
 			A:a   C:b   B:b   C:c   B:c &&
 		git rev-parse >actual                \
 			:1:a  :2:b  :3:b  :2:c  :3:c &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		git rev-parse >expect               \
 			C:c     B:c     C:b     B:b &&
 		git hash-object >actual                \
 			c~HEAD  c~B\^0  b~HEAD  b~B\^0 &&
-		test_cmp expect actual
+		test_cmp expect actual &&
 
 		test_path_is_missing b &&
 		test_path_is_missing c
diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh
index 2e28f2908d..4a71f17edd 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -3583,7 +3583,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf
 		grep -q stuff z/c &&
 		test_seq 1 10 >expected &&
 		echo stuff >>expected &&
-		test_cmp expected z/c
+		test_cmp expected z/c &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 26/29] t7000-t7999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (24 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 25/29] t6000-t6999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:29 ` [PATCH 27/29] t9000-t9999: " Eric Sunshine
                   ` (5 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t7001-mv.sh                  |  2 +-
 t/t7201-co.sh                  | 40 +++++++++++++-------------
 t/t7400-submodule-basic.sh     |  2 +-
 t/t7406-submodule-update.sh    |  4 +--
 t/t7408-submodule-reference.sh |  2 +-
 t/t7501-commit.sh              | 52 +++++++++++++++++-----------------
 t/t7506-status-submodule.sh    | 10 +++----
 7 files changed, 56 insertions(+), 56 deletions(-)

diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index cc3fd2baf2..9e59e5a5dd 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -509,7 +509,7 @@ test_expect_success 'moving nested submodules' '
 		touch nested_level1 &&
 		git init &&
 		git add . &&
-		git commit -m "nested level 1"
+		git commit -m "nested level 1" &&
 		git submodule add ../sub_nested_nested &&
 		git commit -m "add nested level 2"
 	) &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 8d8a63a24b..94cb039a03 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -528,10 +528,10 @@ test_expect_success 'checkout with --merge' '
 	cat sample >filf &&
 	git checkout -m -- fild file filf &&
 	(
-		echo "<<<<<<< ours"
-		echo ourside
-		echo "======="
-		echo theirside
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "=======" &&
+		echo theirside &&
 		echo ">>>>>>> theirs"
 	) >merged &&
 	test_cmp expect fild &&
@@ -549,12 +549,12 @@ test_expect_success 'checkout with --merge, in diff3 -m style' '
 	cat sample >filf &&
 	git checkout -m -- fild file filf &&
 	(
-		echo "<<<<<<< ours"
-		echo ourside
-		echo "||||||| base"
-		echo original
-		echo "======="
-		echo theirside
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "||||||| base" &&
+		echo original &&
+		echo "=======" &&
+		echo theirside &&
 		echo ">>>>>>> theirs"
 	) >merged &&
 	test_cmp expect fild &&
@@ -572,10 +572,10 @@ test_expect_success 'checkout --conflict=merge, overriding config' '
 	cat sample >filf &&
 	git checkout --conflict=merge -- fild file filf &&
 	(
-		echo "<<<<<<< ours"
-		echo ourside
-		echo "======="
-		echo theirside
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "=======" &&
+		echo theirside &&
 		echo ">>>>>>> theirs"
 	) >merged &&
 	test_cmp expect fild &&
@@ -593,12 +593,12 @@ test_expect_success 'checkout --conflict=diff3' '
 	cat sample >filf &&
 	git checkout --conflict=diff3 -- fild file filf &&
 	(
-		echo "<<<<<<< ours"
-		echo ourside
-		echo "||||||| base"
-		echo original
-		echo "======="
-		echo theirside
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "||||||| base" &&
+		echo original &&
+		echo "=======" &&
+		echo theirside &&
 		echo ">>>>>>> theirs"
 	) >merged &&
 	test_cmp expect fild &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 401adaed32..76cf522a08 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -818,7 +818,7 @@ test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.gi
 		cp pristine-.git-config .git/config &&
 		cp pristine-.gitmodules .gitmodules &&
 		mkdir -p a/b/c &&
-		(cd a/b/c; git init) &&
+		(cd a/b/c && git init) &&
 		git config remote.origin.url ../foo/bar.git &&
 		git submodule add ../bar/a/b/c ./a/b/c &&
 		git submodule init &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 9e0d31700e..b141145fc2 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -865,9 +865,9 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
 	 (cd submodule/subsubmodule &&
 	  git log > ../../expected
 	 ) &&
-	 (cd .git/modules/submodule/modules/subsubmodule
+	 (cd .git/modules/submodule/modules/subsubmodule &&
 	  git log > ../../../../../actual
-	 )
+	 ) &&
 	 test_cmp actual expected
 	)
 '
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index 08d9add05e..34ac28c056 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -148,7 +148,7 @@ test_expect_success 'preparing second superproject with a nested submodule plus
 		cd supersuper &&
 		echo "I am super super." >file &&
 		git add file &&
-		git commit -m B-super-super-initial
+		git commit -m B-super-super-initial &&
 		git submodule add "file://$base_dir/super" subwithsub &&
 		git commit -m B-super-super-added &&
 		git submodule update --init --recursive &&
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 589b6cea23..39b14b2bdb 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -411,8 +411,8 @@ test_expect_success 'sign off (1)' '
 	git commit -s -m "thank you" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	(
-		echo thank you
-		echo
+		echo thank you &&
+		echo &&
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
@@ -430,9 +430,9 @@ test_expect_success 'sign off (2)' '
 $existing" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	(
-		echo thank you
-		echo
-		echo $existing
+		echo thank you &&
+		echo &&
+		echo $existing &&
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
@@ -450,9 +450,9 @@ test_expect_success 'signoff gap' '
 $alt" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
 	(
-		echo welcome
-		echo
-		echo $alt
+		echo welcome &&
+		echo &&
+		echo $alt &&
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
@@ -470,11 +470,11 @@ We have now
 $alt" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
 	(
-		echo welcome
-		echo
-		echo We have now
-		echo $alt
-		echo
+		echo welcome &&
+		echo &&
+		echo We have now &&
+		echo $alt &&
+		echo &&
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
@@ -491,11 +491,11 @@ non-trailer line
 Myfooter: x" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
 	(
-		echo subject
-		echo
-		echo non-trailer line
-		echo Myfooter: x
-		echo
+		echo subject &&
+		echo &&
+		echo non-trailer line &&
+		echo Myfooter: x &&
+		echo &&
 		echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 	) >expected &&
 	test_cmp expected actual &&
@@ -508,10 +508,10 @@ non-trailer line
 Myfooter: x" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
 	(
-		echo subject
-		echo
-		echo non-trailer line
-		echo Myfooter: x
+		echo subject &&
+		echo &&
+		echo non-trailer line &&
+		echo Myfooter: x &&
 		echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 	) >expected &&
 	test_cmp expected actual
@@ -524,10 +524,10 @@ test_expect_success 'multiple -m' '
 	git commit -m "one" -m "two" -m "three" &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	(
-		echo one
-		echo
-		echo two
-		echo
+		echo one &&
+		echo &&
+		echo two &&
+		echo &&
 		echo three
 	) >expected &&
 	test_cmp expected actual
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index b4b74dbe29..943708fb04 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -193,9 +193,9 @@ test_expect_success 'status with added and untracked file in modified submodule
 
 test_expect_success 'setup .git file for sub' '
 	(cd sub &&
-	 rm -f new-file
+	 rm -f new-file &&
 	 REAL="$(pwd)/../.real" &&
-	 mv .git "$REAL"
+	 mv .git "$REAL" &&
 	 echo "gitdir: $REAL" >.git) &&
 	 echo .real >>.gitignore &&
 	 git commit -m "added .real to .gitignore" .gitignore
@@ -209,12 +209,12 @@ test_expect_success 'status with added file in modified submodule with .git file
 
 test_expect_success 'status with a lot of untracked files in the submodule' '
 	(
-		cd sub
+		cd sub &&
 		i=0 &&
 		while test $i -lt 1024
 		do
-			>some-file-$i
-			i=$(( $i + 1 ))
+			>some-file-$i &&
+			i=$(( $i + 1 )) || exit 1
 		done
 	) &&
 	git status --porcelain sub 2>err.actual &&
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 27/29] t9000-t9999: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (25 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 26/29] t7000-t7999: " Eric Sunshine
@ 2018-06-26  7:29 ` " Eric Sunshine
  2018-06-26  7:30 ` [PATCH 28/29] t9119: " Eric Sunshine
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:29 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t9001-send-email.sh                           |  6 +++---
 t/t9100-git-svn-basic.sh                        |  2 +-
 t/t9101-git-svn-props.sh                        |  2 +-
 t/t9122-git-svn-author.sh                       |  6 +++---
 t/t9129-git-svn-i18n-commitencoding.sh          |  2 +-
 t/t9130-git-svn-authors-file.sh                 |  4 ++--
 t/t9134-git-svn-ignore-paths.sh                 |  6 +++---
 t/t9137-git-svn-dcommit-clobber-series.sh       |  2 +-
 t/t9138-git-svn-authors-prog.sh                 |  6 +++---
 t/t9146-git-svn-empty-dirs.sh                   | 12 ++++++------
 t/t9147-git-svn-include-paths.sh                |  6 +++---
 t/t9164-git-svn-dcommit-concurrent.sh           |  2 +-
 t/t9165-git-svn-fetch-merge-branch-of-branch.sh |  2 +-
 t/t9200-git-cvsexportcommit.sh                  |  6 +++---
 t/t9400-git-cvsserver-server.sh                 |  6 +++---
 t/t9600-cvsimport.sh                            |  2 +-
 t/t9806-git-p4-options.sh                       |  2 +-
 t/t9810-git-p4-rcs.sh                           |  2 +-
 t/t9811-git-p4-label-import.sh                  |  2 +-
 t/t9815-git-p4-submit-fail.sh                   |  2 +-
 t/t9830-git-p4-symlink-dir.sh                   |  2 +-
 t/t9831-git-p4-triggers.sh                      |  2 +-
 t/t9902-completion.sh                           |  4 ++--
 t/t9903-bash-prompt.sh                          |  2 +-
 24 files changed, 45 insertions(+), 45 deletions(-)

diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 776769fe0d..53314ff54e 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -330,7 +330,7 @@ test_expect_success $PREREQ 'Show all headers' '
 
 test_expect_success $PREREQ 'Prompting works' '
 	clean_fake_sendmail &&
-	(echo "to@example.com"
+	(echo "to@example.com" &&
 	 echo ""
 	) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
 		--smtp-server="$(pwd)/fake.sendmail" \
@@ -470,8 +470,8 @@ test_expect_success $PREREQ 'Invalid In-Reply-To' '
 
 test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
 	clean_fake_sendmail &&
-	(echo "From Example <from@example.com>"
-	 echo "To Example <to@example.com>"
+	(echo "From Example <from@example.com>" &&
+	 echo "To Example <to@example.com>" &&
 	 echo ""
 	) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
 		--smtp-server="$(pwd)/fake.sendmail" \
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index c937330a5f..9af6078844 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -31,7 +31,7 @@ test_expect_success \
 	(
 		cd import &&
 		echo foo >foo &&
-		ln -s foo foo.link
+		ln -s foo foo.link &&
 		mkdir -p dir/a/b/c/d/e &&
 		echo "deep dir" >dir/a/b/c/d/e/file &&
 		mkdir bar &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 07bfb63777..8a5c8dc1aa 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -149,7 +149,7 @@ test_expect_success 'test show-ignore' "
 		svn_cmd up &&
 		svn_cmd propset -R svn:ignore '
 no-such-file*
-' .
+' . &&
 		svn_cmd commit -m 'propset svn:ignore'
 	) &&
 	git svn show-ignore > show-ignore.got &&
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 30013b7bb9..9e8fe38e7e 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -7,8 +7,8 @@ test_expect_success 'setup svn repository' '
 	svn_cmd checkout "$svnrepo" work.svn &&
 	(
 		cd work.svn &&
-		echo >file
-		svn_cmd add file
+		echo >file &&
+		svn_cmd add file &&
 		svn_cmd commit -m "first commit" file
 	)
 '
@@ -17,7 +17,7 @@ test_expect_success 'interact with it via git svn' '
 	mkdir work.git &&
 	(
 		cd work.git &&
-		git svn init "$svnrepo"
+		git svn init "$svnrepo" &&
 		git svn fetch &&
 
 		echo modification >file &&
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 8dbd6476fa..2c213ae654 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -51,7 +51,7 @@ do
 		git add F &&
 		git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
 		E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
-		test "z$E" = "z$H"
+		test "z$E" = "z$H" &&
 		compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
 	)
 	'
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
index d8262854bb..cb764bcadc 100755
--- a/t/t9130-git-svn-authors-file.sh
+++ b/t/t9130-git-svn-authors-file.sh
@@ -25,7 +25,7 @@ test_expect_success 'start import with incomplete authors file' '
 
 test_expect_success 'imported 2 revisions successfully' '
 	(
-		cd x
+		cd x &&
 		git rev-list refs/remotes/git-svn >actual &&
 		test_line_count = 2 actual &&
 		git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
@@ -42,7 +42,7 @@ EOF
 
 test_expect_success 'continues to import once authors have been added' '
 	(
-		cd x
+		cd x &&
 		git svn fetch --authors-file=../svn-authors &&
 		git rev-list refs/remotes/git-svn >actual &&
 		test_line_count = 4 actual &&
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
index 09ff10cd9b..fff49c4100 100755
--- a/t/t9134-git-svn-ignore-paths.sh
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -82,7 +82,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
 test_expect_success 'SVN-side change inside of ignored www' '
 	(
 		cd s &&
-		echo zaq >> www/test_www.txt
+		echo zaq >> www/test_www.txt &&
 		svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
 		svn_cmd up &&
 		svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
@@ -114,8 +114,8 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
 test_expect_success 'SVN-side change in and out of ignored www' '
 	(
 		cd s &&
-		echo cvf >> www/test_www.txt
-		echo ygg >> qqq/test_qqq.txt
+		echo cvf >> www/test_www.txt &&
+		echo ygg >> qqq/test_qqq.txt &&
 		svn_cmd commit -m "SVN-side change in and out of ignored www" &&
 		svn_cmd up &&
 		svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index 5fa07a369f..067b15bad2 100755
--- a/t/t9137-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -7,7 +7,7 @@ test_description='git svn dcommit clobber series'
 test_expect_success 'initialize repo' '
 	mkdir import &&
 	(cd import &&
-	awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+	awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file &&
 	svn_cmd import -m "initial" . "$svnrepo"
 	) &&
 	git svn init "$svnrepo" &&
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 93ef44fae8..027b416720 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -38,7 +38,7 @@ test_expect_success 'import authors with prog and file' '
 
 test_expect_success 'imported 6 revisions successfully' '
 	(
-		cd x
+		cd x &&
 		git rev-list refs/remotes/git-svn >actual &&
 		test_line_count = 6 actual
 	)
@@ -46,7 +46,7 @@ test_expect_success 'imported 6 revisions successfully' '
 
 test_expect_success 'authors-prog ran correctly' '
 	(
-		cd x
+		cd x &&
 		git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual &&
 		grep "^author ee-foo <ee-foo@example\.com> " actual &&
 		git rev-list -1 --pretty=raw refs/remotes/git-svn~2 >actual &&
@@ -62,7 +62,7 @@ test_expect_success 'authors-prog ran correctly' '
 
 test_expect_success 'authors-file overrode authors-prog' '
 	(
-		cd x
+		cd x &&
 		git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
 		grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " actual
 	)
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 6d3130e618..954948812c 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -79,21 +79,21 @@ test_expect_success 'git svn mkdirs -r works' '
 		do
 			if ! test -d "$i"
 			then
-				echo >&2 "$i does not exist"
+				echo >&2 "$i does not exist" &&
 				exit 1
 			fi
-		done
+		done &&
 
 		if test -d "! !"
 		then
-			echo >&2 "$i should not exist"
+			echo >&2 "$i should not exist" &&
 			exit 1
-		fi
+		fi &&
 
 		git svn mkdirs -r8 &&
 		if ! test -d "! !"
 		then
-			echo >&2 "$i not exist"
+			echo >&2 "$i not exist" &&
 			exit 1
 		fi
 	)
@@ -115,7 +115,7 @@ test_expect_success 'empty directories in trunk exist' '
 		do
 			if ! test -d "$i"
 			then
-				echo >&2 "$i does not exist"
+				echo >&2 "$i does not exist" &&
 				exit 1
 			fi
 		done
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
index a90ff58629..d292bf9f55 100755
--- a/t/t9147-git-svn-include-paths.sh
+++ b/t/t9147-git-svn-include-paths.sh
@@ -84,7 +84,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
 test_expect_success 'SVN-side change inside of ignored www' '
 	(
 		cd s &&
-		echo zaq >> www/test_www.txt
+		echo zaq >> www/test_www.txt &&
 		svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
 		svn_cmd up &&
 		svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
@@ -116,8 +116,8 @@ test_expect_success 'update git svn-cloned repo (option include)' '
 test_expect_success 'SVN-side change in and out of included qqq' '
 	(
 		cd s &&
-		echo cvf >> www/test_www.txt
-		echo ygg >> qqq/test_qqq.txt
+		echo cvf >> www/test_www.txt &&
+		echo ygg >> qqq/test_qqq.txt &&
 		svn_cmd commit -m "SVN-side change in and out of ignored www" &&
 		svn_cmd up &&
 		svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index d8464d4218..90346ff4e9 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -12,7 +12,7 @@ test_expect_success 'setup svn repository' '
 	svn_cmd checkout "$svnrepo" work.svn &&
 	(
 		cd work.svn &&
-		echo >file && echo > auto_updated_file
+		echo >file && echo > auto_updated_file &&
 		svn_cmd add file auto_updated_file &&
 		svn_cmd commit -m "initial commit"
 	) &&
diff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
index fa3ef3b1f7..a4813c2b09 100755
--- a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
+++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
@@ -39,7 +39,7 @@ test_expect_success 'initialize source svn repo' '
 		svn_cmd commit -m trunk &&
 		svn_cmd switch "$svnrepo"/branches/branch2 &&
 		svn_cmd merge "$svnrepo"/trunk &&
-		svn_cmd commit -m "merge trunk"
+		svn_cmd commit -m "merge trunk" &&
 		svn_cmd switch "$svnrepo"/trunk &&
 		svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 &&
 		svn_cmd commit -m "merge branch2"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 1319415ba8..cd61288aa1 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -187,7 +187,7 @@ test_expect_success \
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id
+      git cvsexportcommit -c $id &&
       check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
       )'
 
@@ -245,7 +245,7 @@ test_expect_success FILEMODE \
       git add G/off &&
       git commit -a -m "Execute test" &&
       (cd "$CVSWORK" &&
-      git cvsexportcommit -c HEAD
+      git cvsexportcommit -c HEAD &&
       test -x G/on &&
       ! test -x G/off
       )'
@@ -303,7 +303,7 @@ test_expect_success 're-commit a removed filename which remains in CVS attic' '
     git add attic_gremlin &&
     git commit -m "Added attic_gremlin" &&
 	git cvsexportcommit -w "$CVSWORK" -c HEAD &&
-    (cd "$CVSWORK"; cvs -Q update -d) &&
+    (cd "$CVSWORK" && cvs -Q update -d) &&
     test -f "$CVSWORK/attic_gremlin"
 '
 
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 06742748e9..4207e9caa5 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -371,7 +371,7 @@ test_expect_success 'cvs update (merge)' \
   'echo Line 0 >expected &&
    for i in 1 2 3 4 5 6 7
    do
-     echo Line $i >>merge
+     echo Line $i >>merge &&
      echo Line $i >>expected
    done &&
    echo Line 8 >>expected &&
@@ -382,7 +382,7 @@ test_expect_success 'cvs update (merge)' \
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
    test_cmp merge ../merge &&
-   ( echo Line 0; cat merge ) >merge.tmp &&
+   ( echo Line 0 && cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    cd "$WORKDIR" &&
    echo Line 8 >>merge &&
@@ -410,7 +410,7 @@ do
 done
 
 test_expect_success 'cvs update (conflict merge)' \
-  '( echo LINE 0; cat merge ) >merge.tmp &&
+  '( echo LINE 0 && cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    git add merge &&
    git commit -q -m "Merge test (conflict)" &&
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 804ce3850f..5dfee07d9a 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -135,7 +135,7 @@ test_expect_success PERL 'second update has correct .git/cvs-revisions' '
 
 	(cd module-git &&
 	 git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
-	 git log --format="o_fortuna 1.2 %H" -1 HEAD^
+	 git log --format="o_fortuna 1.2 %H" -1 HEAD^ &&
 	 git log --format="tick 1.1 %H" -1 HEAD) > expected &&
 	test_cmp expected module-git/.git/cvs-revisions
 '
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
index 1ab76c4246..3f5291b857 100755
--- a/t/t9806-git-p4-options.sh
+++ b/t/t9806-git-p4-options.sh
@@ -134,7 +134,7 @@ test_expect_success 'clone --changesfile' '
 	(
 		cd "$git" &&
 		git log --oneline p4/master >lines &&
-		test_line_count = 2 lines
+		test_line_count = 2 lines &&
 		test_path_is_file file1 &&
 		test_path_is_missing file2 &&
 		test_path_is_file file3
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
index 8134ab439b..cc53debe19 100755
--- a/t/t9810-git-p4-rcs.sh
+++ b/t/t9810-git-p4-rcs.sh
@@ -161,7 +161,7 @@ test_expect_success 'cleanup after failure' '
 test_expect_success 'ktext expansion should not expand multi-line $File::' '
 	(
 		cd "$cli" &&
-		cat >lv.pm <<-\EOF
+		cat >lv.pm <<-\EOF &&
 		my $wanted = sub { my $f = $File::Find::name;
 				    if ( -f && $f =~ /foo/ ) {
 		EOF
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index decb66ba30..602b0a5d5c 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -133,7 +133,7 @@ test_expect_success 'export git tags to p4' '
 		p4 labels ... | grep LIGHTWEIGHT_TAG &&
 		p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
 		p4 sync ...@GIT_TAG_1 &&
-		! test -f main/f10
+		! test -f main/f10 &&
 		p4 sync ...@GIT_TAG_2 &&
 		test -f main/f10
 	)
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 37b42d03a2..eaf03a6563 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -394,7 +394,7 @@ test_expect_success 'cleanup rename after submit cancel' '
 	(
 		cd "$cli" &&
 		test_path_is_missing text2 &&
-		p4 fstat -T action text2 2>&1 | grep "no such file"
+		p4 fstat -T action text2 2>&1 | grep "no such file" &&
 		test_path_is_file text &&
 		! p4 fstat -T action text
 	)
diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh
index 3dc528bb1e..2ad1b0810d 100755
--- a/t/t9830-git-p4-symlink-dir.sh
+++ b/t/t9830-git-p4-symlink-dir.sh
@@ -30,7 +30,7 @@ test_expect_success 'symlinked directory' '
 	(
 		cd "$cli" &&
 		p4 sync &&
-		test -L some/sub/directory/subdir2
+		test -L some/sub/directory/subdir2 &&
 		test_path_is_file some/sub/directory/subdir2/file.t
 	)
 
diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh
index bbcf14c664..be44c9751a 100755
--- a/t/t9831-git-p4-triggers.sh
+++ b/t/t9831-git-p4-triggers.sh
@@ -13,7 +13,7 @@ test_expect_success 'init depot' '
 		cd "$cli" &&
 		echo file1 >file1 &&
 		p4 add file1 &&
-		p4 submit -d "change 1"
+		p4 submit -d "change 1" &&
 		echo file2 >file2 &&
 		p4 add file2 &&
 		p4 submit -d "change 2"
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a28640ce1a..69a5810555 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1067,7 +1067,7 @@ test_expect_success '__git_complete_refs - remote' '
 	master-in-other Z
 	EOF
 	(
-		cur=
+		cur= &&
 		__git_complete_refs --remote=other &&
 		print_comp
 	) &&
@@ -1086,7 +1086,7 @@ test_expect_success '__git_complete_refs - track' '
 	master-in-other Z
 	EOF
 	(
-		cur=
+		cur= &&
 		__git_complete_refs --track &&
 		print_comp
 	) &&
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index c3b89ae783..04440685a6 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -529,7 +529,7 @@ test_expect_success 'prompt - bash color pc mode - branch name' '
 	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
 	(
 		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
 		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
 	) &&
 	test_cmp expected "$actual"
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 28/29] t9119: fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (26 preceding siblings ...)
  2018-06-26  7:29 ` [PATCH 27/29] t9000-t9999: " Eric Sunshine
@ 2018-06-26  7:30 ` " Eric Sunshine
  2018-06-26  7:30 ` [PATCH 29/29] t/test-lib: teach --chain-lint to detect " Eric Sunshine
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:30 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t9119-git-svn-info.sh | 120 ++++++++++++++++++++--------------------
 1 file changed, 60 insertions(+), 60 deletions(-)

diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 88241baee3..8201c3e808 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -22,8 +22,8 @@ esac
 # same value as "svn info" (i.e. the commit timestamp that touched the
 # path most recently); do not expect that field to match.
 test_cmp_info () {
-	sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
-	sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+	sed -e '/^Text Last Updated:/d' "$1" >tmp.expect &&
+	sed -e '/^Text Last Updated:/d' "$2" >tmp.actual &&
 	test_cmp tmp.expect tmp.actual &&
 	rm -f tmp.expect tmp.actual
 }
@@ -59,24 +59,24 @@ test_expect_success 'setup repository and import' '
 	'
 
 test_expect_success 'info' "
-	(cd svnwc; svn info) > expected.info &&
-	(cd gitwc; git svn info) > actual.info &&
+	(cd svnwc && svn info) > expected.info &&
+	(cd gitwc && git svn info) > actual.info &&
 	test_cmp_info expected.info actual.info
 	"
 
 test_expect_success 'info --url' '
-	test "$(cd gitwc; git svn info --url)" = "$quoted_svnrepo"
+	test "$(cd gitwc && git svn info --url)" = "$quoted_svnrepo"
 	'
 
 test_expect_success 'info .' "
-	(cd svnwc; svn info .) > expected.info-dot &&
-	(cd gitwc; git svn info .) > actual.info-dot &&
+	(cd svnwc && svn info .) > expected.info-dot &&
+	(cd gitwc && git svn info .) > actual.info-dot &&
 	test_cmp_info expected.info-dot actual.info-dot
 	"
 
 test_expect_success 'info $(pwd)' '
-	(cd svnwc; svn info "$(pwd)") >expected.info-pwd &&
-	(cd gitwc; git svn info "$(pwd)") >actual.info-pwd &&
+	(cd svnwc && svn info "$(pwd)") >expected.info-pwd &&
+	(cd gitwc && git svn info "$(pwd)") >actual.info-pwd &&
 	grep -v ^Path: <expected.info-pwd >expected.info-np &&
 	grep -v ^Path: <actual.info-pwd >actual.info-np &&
 	test_cmp_info expected.info-np actual.info-np &&
@@ -85,8 +85,8 @@ test_expect_success 'info $(pwd)' '
 	'
 
 test_expect_success 'info $(pwd)/../___wc' '
-	(cd svnwc; svn info "$(pwd)/../svnwc") >expected.info-pwd &&
-	(cd gitwc; git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
+	(cd svnwc && svn info "$(pwd)/../svnwc") >expected.info-pwd &&
+	(cd gitwc && git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
 	grep -v ^Path: <expected.info-pwd >expected.info-np &&
 	grep -v ^Path: <actual.info-pwd >actual.info-np &&
 	test_cmp_info expected.info-np actual.info-np &&
@@ -95,8 +95,8 @@ test_expect_success 'info $(pwd)/../___wc' '
 	'
 
 test_expect_success 'info $(pwd)/../___wc//file' '
-	(cd svnwc; svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
-	(cd gitwc; git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
+	(cd svnwc && svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
+	(cd gitwc && git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
 	grep -v ^Path: <expected.info-pwd >expected.info-np &&
 	grep -v ^Path: <actual.info-pwd >actual.info-np &&
 	test_cmp_info expected.info-np actual.info-np &&
@@ -105,56 +105,56 @@ test_expect_success 'info $(pwd)/../___wc//file' '
 	'
 
 test_expect_success 'info --url .' '
-	test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
+	test "$(cd gitwc && git svn info --url .)" = "$quoted_svnrepo"
 	'
 
 test_expect_success 'info file' "
-	(cd svnwc; svn info file) > expected.info-file &&
-	(cd gitwc; git svn info file) > actual.info-file &&
+	(cd svnwc && svn info file) > expected.info-file &&
+	(cd gitwc && git svn info file) > actual.info-file &&
 	test_cmp_info expected.info-file actual.info-file
 	"
 
 test_expect_success 'info --url file' '
-	test "$(cd gitwc; git svn info --url file)" = "$quoted_svnrepo/file"
+	test "$(cd gitwc && git svn info --url file)" = "$quoted_svnrepo/file"
 	'
 
 test_expect_success 'info directory' "
-	(cd svnwc; svn info directory) > expected.info-directory &&
-	(cd gitwc; git svn info directory) > actual.info-directory &&
+	(cd svnwc && svn info directory) > expected.info-directory &&
+	(cd gitwc && git svn info directory) > actual.info-directory &&
 	test_cmp_info expected.info-directory actual.info-directory
 	"
 
 test_expect_success 'info inside directory' "
-	(cd svnwc/directory; svn info) > expected.info-inside-directory &&
-	(cd gitwc/directory; git svn info) > actual.info-inside-directory &&
+	(cd svnwc/directory && svn info) > expected.info-inside-directory &&
+	(cd gitwc/directory && git svn info) > actual.info-inside-directory &&
 	test_cmp_info expected.info-inside-directory actual.info-inside-directory
 	"
 
 test_expect_success 'info --url directory' '
-	test "$(cd gitwc; git svn info --url directory)" = "$quoted_svnrepo/directory"
+	test "$(cd gitwc && git svn info --url directory)" = "$quoted_svnrepo/directory"
 	'
 
 test_expect_success 'info symlink-file' "
-	(cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
-	(cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
+	(cd svnwc && svn info symlink-file) > expected.info-symlink-file &&
+	(cd gitwc && git svn info symlink-file) > actual.info-symlink-file &&
 	test_cmp_info expected.info-symlink-file actual.info-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file' '
-	test "$(cd gitwc; git svn info --url symlink-file)" \
+	test "$(cd gitwc && git svn info --url symlink-file)" \
 	     = "$quoted_svnrepo/symlink-file"
 	'
 
 test_expect_success 'info symlink-directory' "
-	(cd svnwc; svn info symlink-directory) \
+	(cd svnwc && svn info symlink-directory) \
 		> expected.info-symlink-directory &&
-	(cd gitwc; git svn info symlink-directory) \
+	(cd gitwc && git svn info symlink-directory) \
 		> actual.info-symlink-directory &&
 	test_cmp_info expected.info-symlink-directory actual.info-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory' '
-	test "$(cd gitwc; git svn info --url symlink-directory)" \
+	test "$(cd gitwc && git svn info --url symlink-directory)" \
 	     = "$quoted_svnrepo/symlink-directory"
 	'
 
@@ -169,13 +169,13 @@ test_expect_success 'info added-file' "
 		cd svnwc &&
 		svn_cmd add added-file > /dev/null
 	) &&
-	(cd svnwc; svn info added-file) > expected.info-added-file &&
-	(cd gitwc; git svn info added-file) > actual.info-added-file &&
+	(cd svnwc && svn info added-file) > expected.info-added-file &&
+	(cd gitwc && git svn info added-file) > actual.info-added-file &&
 	test_cmp_info expected.info-added-file actual.info-added-file
 	"
 
 test_expect_success 'info --url added-file' '
-	test "$(cd gitwc; git svn info --url added-file)" \
+	test "$(cd gitwc && git svn info --url added-file)" \
 	     = "$quoted_svnrepo/added-file"
 	'
 
@@ -190,15 +190,15 @@ test_expect_success 'info added-directory' "
 		cd gitwc &&
 		git add added-directory
 	) &&
-	(cd svnwc; svn info added-directory) \
+	(cd svnwc && svn info added-directory) \
 		> expected.info-added-directory &&
-	(cd gitwc; git svn info added-directory) \
+	(cd gitwc && git svn info added-directory) \
 		> actual.info-added-directory &&
 	test_cmp_info expected.info-added-directory actual.info-added-directory
 	"
 
 test_expect_success 'info --url added-directory' '
-	test "$(cd gitwc; git svn info --url added-directory)" \
+	test "$(cd gitwc && git svn info --url added-directory)" \
 	     = "$quoted_svnrepo/added-directory"
 	'
 
@@ -213,16 +213,16 @@ test_expect_success 'info added-symlink-file' "
 		ln -s added-file added-symlink-file &&
 		svn_cmd add added-symlink-file > /dev/null
 	) &&
-	(cd svnwc; svn info added-symlink-file) \
+	(cd svnwc && svn info added-symlink-file) \
 		> expected.info-added-symlink-file &&
-	(cd gitwc; git svn info added-symlink-file) \
+	(cd gitwc && git svn info added-symlink-file) \
 		> actual.info-added-symlink-file &&
 	test_cmp_info expected.info-added-symlink-file \
 		actual.info-added-symlink-file
 	"
 
 test_expect_success 'info --url added-symlink-file' '
-	test "$(cd gitwc; git svn info --url added-symlink-file)" \
+	test "$(cd gitwc && git svn info --url added-symlink-file)" \
 	     = "$quoted_svnrepo/added-symlink-file"
 	'
 
@@ -237,16 +237,16 @@ test_expect_success 'info added-symlink-directory' "
 		ln -s added-directory added-symlink-directory &&
 		svn_cmd add added-symlink-directory > /dev/null
 	) &&
-	(cd svnwc; svn info added-symlink-directory) \
+	(cd svnwc && svn info added-symlink-directory) \
 		> expected.info-added-symlink-directory &&
-	(cd gitwc; git svn info added-symlink-directory) \
+	(cd gitwc && git svn info added-symlink-directory) \
 		> actual.info-added-symlink-directory &&
 	test_cmp_info expected.info-added-symlink-directory \
 		actual.info-added-symlink-directory
 	"
 
 test_expect_success 'info --url added-symlink-directory' '
-	test "$(cd gitwc; git svn info --url added-symlink-directory)" \
+	test "$(cd gitwc && git svn info --url added-symlink-directory)" \
 	     = "$quoted_svnrepo/added-symlink-directory"
 	'
 
@@ -259,13 +259,13 @@ test_expect_success 'info deleted-file' "
 		cd svnwc &&
 		svn_cmd rm --force file > /dev/null
 	) &&
-	(cd svnwc; svn info file) >expected.info-deleted-file &&
-	(cd gitwc; git svn info file) >actual.info-deleted-file &&
+	(cd svnwc && svn info file) >expected.info-deleted-file &&
+	(cd gitwc && git svn info file) >actual.info-deleted-file &&
 	test_cmp_info expected.info-deleted-file actual.info-deleted-file
 	"
 
 test_expect_success 'info --url file (deleted)' '
-	test "$(cd gitwc; git svn info --url file)" \
+	test "$(cd gitwc && git svn info --url file)" \
 	     = "$quoted_svnrepo/file"
 	'
 
@@ -278,13 +278,13 @@ test_expect_success 'info deleted-directory' "
 		cd svnwc &&
 		svn_cmd rm --force directory > /dev/null
 	) &&
-	(cd svnwc; svn info directory) >expected.info-deleted-directory &&
-	(cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+	(cd svnwc && svn info directory) >expected.info-deleted-directory &&
+	(cd gitwc && git svn info directory) >actual.info-deleted-directory &&
 	test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
 	"
 
 test_expect_success 'info --url directory (deleted)' '
-	test "$(cd gitwc; git svn info --url directory)" \
+	test "$(cd gitwc && git svn info --url directory)" \
 	     = "$quoted_svnrepo/directory"
 	'
 
@@ -297,13 +297,13 @@ test_expect_success 'info deleted-symlink-file' "
 		cd svnwc &&
 		svn_cmd rm --force symlink-file > /dev/null
 	) &&
-	(cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
-	(cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+	(cd svnwc && svn info symlink-file) >expected.info-deleted-symlink-file &&
+	(cd gitwc && git svn info symlink-file) >actual.info-deleted-symlink-file &&
 	test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file (deleted)' '
-	test "$(cd gitwc; git svn info --url symlink-file)" \
+	test "$(cd gitwc && git svn info --url symlink-file)" \
 	     = "$quoted_svnrepo/symlink-file"
 	'
 
@@ -316,13 +316,13 @@ test_expect_success 'info deleted-symlink-directory' "
 		cd svnwc &&
 		svn_cmd rm --force symlink-directory > /dev/null
 	) &&
-	(cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
-	(cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+	(cd svnwc && svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+	(cd gitwc && git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
 	test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory (deleted)' '
-	test "$(cd gitwc; git svn info --url symlink-directory)" \
+	test "$(cd gitwc && git svn info --url symlink-directory)" \
 	     = "$quoted_svnrepo/symlink-directory"
 	'
 
@@ -331,27 +331,27 @@ test_expect_success 'info --url symlink-directory (deleted)' '
 
 test_expect_success 'info unknown-file' "
 	echo two > gitwc/unknown-file &&
-	(cd gitwc; test_must_fail git svn info unknown-file) \
+	(cd gitwc && test_must_fail git svn info unknown-file) \
 		 2> actual.info-unknown-file &&
 	grep unknown-file actual.info-unknown-file
 	"
 
 test_expect_success 'info --url unknown-file' '
 	echo two > gitwc/unknown-file &&
-	(cd gitwc; test_must_fail git svn info --url unknown-file) \
+	(cd gitwc && test_must_fail git svn info --url unknown-file) \
 		 2> actual.info-url-unknown-file &&
 	grep unknown-file actual.info-url-unknown-file
 	'
 
 test_expect_success 'info unknown-directory' "
 	mkdir gitwc/unknown-directory svnwc/unknown-directory &&
-	(cd gitwc; test_must_fail git svn info unknown-directory) \
+	(cd gitwc && test_must_fail git svn info unknown-directory) \
 		 2> actual.info-unknown-directory &&
 	grep unknown-directory actual.info-unknown-directory
 	"
 
 test_expect_success 'info --url unknown-directory' '
-	(cd gitwc; test_must_fail git svn info --url unknown-directory) \
+	(cd gitwc && test_must_fail git svn info --url unknown-directory) \
 		 2> actual.info-url-unknown-directory &&
 	grep unknown-directory actual.info-url-unknown-directory
 	'
@@ -361,13 +361,13 @@ test_expect_success 'info unknown-symlink-file' "
 		cd gitwc &&
 		ln -s unknown-file unknown-symlink-file
 	) &&
-	(cd gitwc; test_must_fail git svn info unknown-symlink-file) \
+	(cd gitwc && test_must_fail git svn info unknown-symlink-file) \
 		 2> actual.info-unknown-symlink-file &&
 	grep unknown-symlink-file actual.info-unknown-symlink-file
 	"
 
 test_expect_success 'info --url unknown-symlink-file' '
-	(cd gitwc; test_must_fail git svn info --url unknown-symlink-file) \
+	(cd gitwc && test_must_fail git svn info --url unknown-symlink-file) \
 		 2> actual.info-url-unknown-symlink-file &&
 	grep unknown-symlink-file actual.info-url-unknown-symlink-file
 	'
@@ -377,13 +377,13 @@ test_expect_success 'info unknown-symlink-directory' "
 		cd gitwc &&
 		ln -s unknown-directory unknown-symlink-directory
 	) &&
-	(cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
+	(cd gitwc && test_must_fail git svn info unknown-symlink-directory) \
 		 2> actual.info-unknown-symlink-directory &&
 	grep unknown-symlink-directory actual.info-unknown-symlink-directory
 	"
 
 test_expect_success 'info --url unknown-symlink-directory' '
-	(cd gitwc; test_must_fail git svn info --url unknown-symlink-directory) \
+	(cd gitwc && test_must_fail git svn info --url unknown-symlink-directory) \
 		 2> actual.info-url-unknown-symlink-directory &&
 	grep unknown-symlink-directory actual.info-url-unknown-symlink-directory
 	'
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (27 preceding siblings ...)
  2018-06-26  7:30 ` [PATCH 28/29] t9119: " Eric Sunshine
@ 2018-06-26  7:30 ` " Eric Sunshine
  2018-06-26 19:15   ` Junio C Hamano
  2018-06-26  9:20 ` [PATCH 00/29] t: detect and fix " Elijah Newren
                   ` (2 subsequent siblings)
  31 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  7:30 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, SZEDER Gábor, Stefan Beller,
	Elijah Newren, Jonathan Tan, Eric Sunshine

The --chain-lint option detects broken &&-chains by forcing the test to
exit early (as the very first step) with a sentinel value. If that
sentinel is the test's overall exit code, then the &&-chain is intact;
if not, then the chain is broken. Unfortunately, this detection does not
extend to &&-chains within subshells even when the subshell itself is
properly linked into the outer &&-chain.

Address this shortcoming by eliminating the subshell during the
"linting" phase and incorporating its body directly into the surrounding
&&-chain. To keep this transformation cheap, no attempt is made at
properly parsing shell code. Instead, the manipulations are purely
textual. For example:

    statement1 &&
    (
        statement2 &&
        statement3
    ) &&
    statement4

is transformed to:

    statement1 &&
        statement2 &&
        statement3 &&
    statement4

Notice how "statement3" gains the "&&" which dwelt originally on the
closing ") &&" line. Since this manipulation is purely textual (in fact,
line-by-line), special care is taken to ensure that the "&&" is moved to
the final _statement_ before the closing ")", not necessarily the final
_line_ of text within the subshell. For example, with a here-doc, the
"&&" needs to be added to the opening "<<EOF" line, not to the "EOF"
line which closes it.

In addition to modern subshell formatting shown above, old-style
formatting is also recognized:

    statement1 &&
    (statement2 &&
     statement3) &&
    statement4

Heuristics are employed to properly identify the extent of a subshell
formatted in the old-style since a number of legitimate constructs may
superficially appear to close the subshell even though they don't. For
instance, the ")" in "x=$(command) &&" and "case $x in *)" is specially
recognized to avoid being falsely considered the end of a subshell.

Due to the complexities of line-by-line detection (and limitations of
the tool, 'sed'), only subshells one level deep are handled, as well as
one-liner subshells one level below that. Subshells deeper than that or
multi-line subshells at level two are passed through as-is, thus
&&-chains in their bodies are not checked.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/test-lib.sh | 245 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 244 insertions(+), 1 deletion(-)

diff --git a/t/test-lib.sh b/t/test-lib.sh
index 28315706be..ade5066fff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -664,6 +664,248 @@ test_eval_ () {
 	return $test_eval_ret_
 }
 
+test_subshell_sed_='
+# incomplete line -- slurp up next line
+/[^\\]\\$/ {
+      N
+      s/\\\n//
+}
+
+# here-doc -- swallow it to avoid false hits within its body (but keep the
+# command to which it was attached)
+/<<[ 	]*[-\\]*EOF[ 	]*&&[ 	]*$/ {
+	s/<<[ 	]*[-\\]*EOF//
+	h
+	:hereslurp
+	N
+	s/.*\n//
+	/^[ 	]*EOF[ 	]*$/!bhereslurp
+	x
+	}
+
+# one-liner "(... || git ...)" or "(... || test ...)" -- short-hand for
+# "if ... then : else ...", so leave untouched; contrast with "(... || true)"
+# which ought to be replaced with "test_might_fail ..." to avoid breaking
+# &&-chain
+/^[ 	]*(..*||[ 	]*git[ 	]..*)[ 	]*&&[ 	]*$/b
+/^[ 	]*(..*||[ 	]*git[ 	]..*)[ 	]*$/b
+/^[ 	]*(..*||[ 	]*test..*)[ 	]*&&[ 	]*$/b
+/^[ 	]*(..*||[ 	]*test..*)[ 	]*$/b
+
+# one-liner "(... &) &&" backgrounder -- needs to remain in subshell to avoid
+# becoming syntactically invalid "... & &&"
+/^[ 	]*(..*&[ 	]*)[ 	]*&&[ 	]*$/b
+
+# one-liner "(...) &&" -- strip "(" and ")"
+/^[ 	]*(..*)[ 	]*&&[ 	]*$/ {
+	s/(//
+	s/)[ 	]*&&[ 	]*$/ \&\&/
+	b
+}
+
+# same as above but without trailing "&&"
+/^[ 	]*(..*)[ 	]*$/ {
+	s/(//
+	s/)[ 	]*$//
+	b
+}
+
+# one-liner "(...) >x" (or "2>x" or "<x" or "|x" or "&" -- strip "(" and ")"
+/^[ 	]*(..*)[ 	]*[0-9]*[<>|&]/ {
+	s/(//
+	s/)[ 	]*\([0-9]*[<>|&]\)/\1/
+	b
+}
+
+# multi-line "(..." -- strip "(" and pass-thru enclosed lines until ")"
+/^[ 	]*(/ {
+	# strip "(" and stash for later printing
+	s/(//
+	h
+
+	:discard
+	N
+	s/.*\n//
+
+	# loop: slurp enclosed lines
+	:slurp
+	# end-of-file
+	$beof
+	# incomplete line
+	/[^\\]\\$/bincomplete
+	# here-doc
+	/<<[ 	]*[-\\]*EOF/bheredoc
+	# comment or empty line -- discard since closing ") &&" will need to
+	# add "&&" to final non-comment, non-empty subshell line
+	/^[ 	]*#/bdiscard
+	/^[ 	]*$/bdiscard
+	# one-liner "case ... esac"
+	/^[ 	]*case[ 	]*..*esac/bpassthru
+	# multi-line "case ... esac"
+	/^[ 	]*case[ 	]..*[ 	]in/bcase
+	# nested one-liner "(...) &&"
+	/^[ 	]*(.*)[ 	]*&&[ 	]*$/boneline
+	# nested one-liner "(...)"
+	/^[ 	]*(.*)[ 	]*$/boneline
+	# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
+	/^[ 	]*(.*)[ 	]*[0-9]*[<>|]/bonelineredir
+	# nested multi-line "(...\n...)"
+	/^[ 	]*(/bnest
+	# closing ") &&" on own line
+	/^[ 	]*)[ 	]*&&[ 	]*$/bcloseamp
+	# closing ")" on own line
+	/^[ 	]*)[ 	]*$/bclose
+	# closing ") >x" (or "2>x" or "<x" or "|x") on own line
+	/^[ 	]*)[ 	]*[0-9]*[<>|]/bcloseredir
+	# "$((...))" -- not closing ")"
+	/\$(([^)][^)]*))[^)]*$/bpassthru
+	# "$(...)" -- not closing ")"
+	/\$([^)][^)]*)[^)]*$/bpassthru
+	# "=(...)" -- Bash array assignment; not closing ")"
+	/=(/bpassthru
+	# closing "...) &&"
+	/)[ 	]*&&[ 	]*$/bcloseampx
+	# closing "...)"
+	/)[ 	]*$/bclosex
+	# closing "...) >x" (or "2>x" or "<x" or "|x")
+	/)[ 	]*[<>|]/bcloseredirx
+	:passthru
+	# retrieve and print previous line
+	x
+	n
+	bslurp
+
+	# end-of-file -- must be closing "...)" line or empty line; if empty,
+	# strip ")" from previous line, else strip ")" from this line
+	:eof
+	/^[ 	]*$/bempty
+	x
+	p
+	:empty
+	x
+	/)[ 	]*[<>|]/s/[<>|]..*$//
+	s/)[ 	]*$//
+	b
+
+	# found "...\" -- slurp up next line
+	:incomplete
+	N
+	s/\\\n//
+	bslurp
+
+	# found here-doc inside subshell: when a subshell ends, we append
+	# "&&" to the final subshell line to chain it with lines outside the
+	# subshell, however, we cannot append "&&" to the ending EOF of a
+	# here-doc since "&&" belongs on the "<<EOF" opening line, so just
+	# discard the here-doc altogether (but keep the command to which it
+	# was attached)
+	:heredoc
+	s/<<[ 	]*[-\\]*EOF//
+	x
+	p
+	:hereslurpsub
+	N
+	s/.*\n//
+	/^[ 	]*EOF[ 	]*$/bdiscard
+	bhereslurpsub
+
+	# found "case ... in" -- pass-thru untouched to avoid "...)" arms
+	# being misidentified as subshell closing ")"
+	:case
+	x
+	p
+	x
+	:caseslurp
+	n
+	/^[ 	]*esac/besac
+	bcaseslurp
+	:esac
+	x
+	bdiscard
+
+	# found one-liner "(...) &&" or "(...)" -- strip "(" and ")"
+	:oneline
+	s/(//
+	s/)[ 	]*\(&&\)*[ 	]*$/ \1/
+	bpassthru
+
+	# found one-liner "(...) >x" (or "2>x" or "<x" or "|x") -- strip
+	# "(" and ")"
+	:onelineredir
+	s/(//
+	s/)[ 	]*\([0-9]*[<>|]\)/\1/
+	bpassthru
+
+	# found nested multi-line "(...\n...)" -- pass-thru untouched
+	:nest
+	x
+	p
+	x
+	:nslurp
+	n
+	# closing ")" on own line -- stop nested slurp
+	/^[ 	]*)/bnclose
+	# "$((...))" -- not closing ")"
+	/\$(([^)][^)]*))[^)]*$/bnslurp
+	# "$(...)" -- not closing ")"
+	/\$([^)][^)]*)[^)]*$/bnslurp
+	# closing "...)" -- stop nested slurp
+	/)/bnclose
+	bnslurp
+	:nclose
+	# stash ")" (or ") &&", etc.) line for later printing and drop
+	# leftover gunk from hold area
+	x
+	bdiscard
+
+	# found ") &&" -- drop ") &&" and add "&&" to final subshell line
+	:closeamp
+	x
+	s/$/ \&\&/
+	b
+
+	# found ")" -- drop it and print final subshell line
+	:close
+	x
+	b
+
+	# found ") >x" (or "2>x" or "<x" or "|x" or "|") -- replace ")" with
+	# ":" to keep ")|\n" syntactically valid, and add "&&" to final
+	# subshell line
+	:closeredir
+	x
+	s/$/ \&\&/
+	p
+	x
+	s/)/:/
+	b
+
+	# found "...) &&" -- drop ")" but keep "..."
+	:closeampx
+	x
+	p
+	x
+	s/)[ 	]*&&[ 	]*$/ \&\&/
+	b
+
+	# found "...)" -- drop ")" but keep "..."
+	:closex
+	x
+	p
+	x
+	s/)[ 	]*$//
+	b
+
+	# found "...) >x" (or "2>x" or "<x" or "|x") -- drop ")" but keep "..."
+	:closeredirx
+	x
+	p
+	x
+	s/)[ 	]*\([<>|]\)/ \&\& : \1/
+	b
+}
+'
+
 test_run_ () {
 	test_cleanup=:
 	expecting_failure=$2
@@ -675,7 +917,8 @@ test_run_ () {
 		trace=
 		# 117 is magic because it is unlikely to match the exit
 		# code of other programs
-		if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+		test_linter=$(printf '%s\n' "$1" | sed -e "$test_subshell_sed_")
+		if test "OK-117" != "$(test_eval_ "(exit 117) && ${test_linter}${LF}${LF}echo OK-\$?" 3>&1)"
 		then
 			error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
 		fi
-- 
2.18.0.419.gfe4b301394


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26  7:29 ` [PATCH 17/29] t: use test_must_fail() instead of checking " Eric Sunshine
@ 2018-06-26  7:59   ` Luke Diamand
  2018-06-26  8:58   ` Elijah Newren
  1 sibling, 0 replies; 123+ messages in thread
From: Luke Diamand @ 2018-06-26  7:59 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Users, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

On 26 June 2018 at 08:29, Eric Sunshine <sunshine@sunshineco.com> wrote:
> These tests intentionally break the &&-chain to manually check the exit
> code of invoked commands which they expect to fail, and invert that
> local expected failure into a successful exit code for the test overall.
> Such manual exit code manipulation predates the invention of
> test_must_fail().
>
> An upcoming change will teach --chain-lint to check the &&-chain inside
> subshells. This sort of manual exit code checking will trip up
> --chain-lint due to the intentional break in the &&-chain. Therefore,
> replace the manual exit code management with test_must_fail() and a
> normal &&-chain.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/t5405-send-pack-rewind.sh |  3 +--
>  t/t9814-git-p4-rename.sh    | 16 ++--------------
>  2 files changed, 3 insertions(+), 16 deletions(-)
>
> diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
> index 4bda18a662..235fb7686a 100755
> --- a/t/t5405-send-pack-rewind.sh
> +++ b/t/t5405-send-pack-rewind.sh
> @@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '
>
>         (
>                 cd another &&
> -               git push .. master:master
> -               test $? = 1
> +               test_must_fail git push .. master:master
>         )
>
>  '
> diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
> index e7e0268e98..80aac5ab16 100755
> --- a/t/t9814-git-p4-rename.sh
> +++ b/t/t9814-git-p4-rename.sh
> @@ -12,20 +12,8 @@ test_expect_success 'start p4d' '
>  test_expect_success 'p4 help unknown returns 1' '
>         (
>                 cd "$cli" &&
> -               (
> -                       p4 help client >errs 2>&1
> -                       echo $? >retval
> -               )
> -               echo 0 >expected &&
> -               test_cmp expected retval &&
> -               rm retval &&
> -               (
> -                       p4 help nosuchcommand >errs 2>&1
> -                       echo $? >retval
> -               )
> -               echo 1 >expected &&
> -               test_cmp expected retval &&
> -               rm retval
> +               p4 help client &&
> +               test_must_fail p4 help nosuchcommand

This seems a lot more sensible. It works fine in my tests.

Thanks,
Luke

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests
  2018-06-26  7:29 ` [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests Eric Sunshine
@ 2018-06-26  8:44   ` Elijah Newren
  0 siblings, 0 replies; 123+ messages in thread
From: Elijah Newren @ 2018-06-26  8:44 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> These tests reference non-existent object "c" when they really mean to
> be referencing "C", however, this error went unnoticed due to a broken
> &&-chain later in the test. Fix these errors, as well as the broken
> &&-chains behind which they hid.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/t6036-recursive-corner-cases.sh | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
> index b5621303d6..b32ff8e1db 100755
> --- a/t/t6036-recursive-corner-cases.sh
> +++ b/t/t6036-recursive-corner-cases.sh
> @@ -506,10 +506,10 @@ test_expect_success 'merge of D & E2 fails but has appropriate contents' '
>                 test_line_count = 2 out &&
>
>                 git rev-parse >expect    \
> -                       B:a   E2:a/file  c:a/file   A:ignore-me &&
> +                       B:a   E2:a/file  C:a/file   A:ignore-me &&
>                 git rev-parse   >actual   \
>                         :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
> -               test_cmp expect actual
> +               test_cmp expect actual &&
>
>                 test_path_is_file a~HEAD
>         )
> @@ -533,10 +533,10 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
>                 test_line_count = 2 out &&
>
>                 git rev-parse >expect    \
> -                       B:a   E2:a/file  c:a/file   A:ignore-me &&
> +                       B:a   E2:a/file  C:a/file   A:ignore-me &&
>                 git rev-parse   >actual   \
>                         :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
> -               test_cmp expect actual
> +               test_cmp expect actual &&
>
>                 test_path_is_file a~D^0
>         )

Eek, how did that become c:a/file when it was originally C:a/file?
Thanks for spotting the regression and fixing.

Reviewed-by: Elijah Newren <newren@gmail.com>

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26  7:29 ` [PATCH 17/29] t: use test_must_fail() instead of checking " Eric Sunshine
  2018-06-26  7:59   ` Luke Diamand
@ 2018-06-26  8:58   ` Elijah Newren
  2018-06-26  9:21     ` Eric Sunshine
  1 sibling, 1 reply; 123+ messages in thread
From: Elijah Newren @ 2018-06-26  8:58 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> These tests intentionally break the &&-chain to manually check the exit
> code of invoked commands which they expect to fail, and invert that
> local expected failure into a successful exit code for the test overall.
> Such manual exit code manipulation predates the invention of
> test_must_fail().
>
> An upcoming change will teach --chain-lint to check the &&-chain inside
> subshells. This sort of manual exit code checking will trip up
> --chain-lint due to the intentional break in the &&-chain. Therefore,
> replace the manual exit code management with test_must_fail() and a
> normal &&-chain.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/t5405-send-pack-rewind.sh |  3 +--
>  t/t9814-git-p4-rename.sh    | 16 ++--------------
>  2 files changed, 3 insertions(+), 16 deletions(-)
>
> diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
> index 4bda18a662..235fb7686a 100755
> --- a/t/t5405-send-pack-rewind.sh
> +++ b/t/t5405-send-pack-rewind.sh
> @@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '
>
>         (
>                 cd another &&
> -               git push .. master:master
> -               test $? = 1
> +               test_must_fail git push .. master:master

test_must_fail or test_expect_code 1?

>         )
>
>  '
> diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
> index e7e0268e98..80aac5ab16 100755
> --- a/t/t9814-git-p4-rename.sh
> +++ b/t/t9814-git-p4-rename.sh
> @@ -12,20 +12,8 @@ test_expect_success 'start p4d' '
>  test_expect_success 'p4 help unknown returns 1' '
>         (
>                 cd "$cli" &&
> -               (
> -                       p4 help client >errs 2>&1
> -                       echo $? >retval
> -               )
> -               echo 0 >expected &&
> -               test_cmp expected retval &&
> -               rm retval &&
> -               (
> -                       p4 help nosuchcommand >errs 2>&1
> -                       echo $? >retval
> -               )
> -               echo 1 >expected &&
> -               test_cmp expected retval &&
> -               rm retval
> +               p4 help client &&
> +               test_must_fail p4 help nosuchcommand

same question?

>         )
>  '

Overall, looks much nicer.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (28 preceding siblings ...)
  2018-06-26  7:30 ` [PATCH 29/29] t/test-lib: teach --chain-lint to detect " Eric Sunshine
@ 2018-06-26  9:20 ` " Elijah Newren
  2018-06-26  9:31   ` Eric Sunshine
  2018-06-26 19:38 ` Junio C Hamano
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
  31 siblings, 1 reply; 123+ messages in thread
From: Elijah Newren @ 2018-06-26  9:20 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> The --chain-lint[1] option detects breakage in the top-level &&-chain of
> tests. This series undertakes the more complex task of teaching it to
> also detect &&-chain breakage within subshells. See patch 29/29 for the
> gory details of how that's done.

This is awesome.  It'll replace a hacky script I had that attempted to
remind me when I messed up, and fix its false positives to boot.
Plus, I won't forget to run it.

> Aside from identifying a rather significant number of &&-chain breaks,
> repairing those broken chains uncovered genuine bugs in several tests
> which were hidden by missing &&-chain links. Those bugs are also fixed
> by this series. I would appreciate if the following people would
> double-check my fixes:
>
> Stefan Bellar - 8/29 "t7400" and (especially) 13/29 "lib-submodule-update"
> Jonathan Tan - 10/29 "t9001"
> Elijah Newren - 6/29 "t6036"

Commented on the patch in question; 6/29 looks good.

I also looked over the rest of the series.  Apart from the ones you
specifically called out as needing review by others besides me, and
the final patch which makes me feel like a sed neophyte, all but one
patch looked good to me.  I just have a small question for that
remaining patch, which I posted there.

Thanks,
Elijah

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26  8:58   ` Elijah Newren
@ 2018-06-26  9:21     ` Eric Sunshine
  2018-06-26 18:05       ` Johannes Sixt
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  9:21 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Git List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 4:58 AM Elijah Newren <newren@gmail.com> wrote:
> On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> > [...] Therefore,
> > replace the manual exit code management with test_must_fail() and a
> > normal &&-chain.
> >
> > Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> > ---
> > diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
> > @@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '> > -               git push .. master:master
> > -               test $? = 1
> > +               test_must_fail git push .. master:master
>
> test_must_fail or test_expect_code 1?

A legitimate question, and the answer is that it's a judgment call
based upon the spirit of the test.

Although test_expect_code() would make for a faithful literal
conversion, I don't think it agrees with the spirit of this test,
which wants to verify that the command correctly exits with an error
(in the general sense), as opposed to outright crashing (which it did
at one time). test_must_fail() is tailor-made for this use case.

Contrast this with patch 09/29 "t7810: use test_expect_code() instead
of hand-rolled comparison". Different exit codes from git-grep have
genuine different meanings, and that test is checking for a very
specific exit code, for which test_expect_code() is tailor-made.

> > diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
> > @@ -12,20 +12,8 @@ test_expect_success 'start p4d' '
> >  test_expect_success 'p4 help unknown returns 1' '
> >         (
> >                 cd "$cli" &&
> > -               (
> > -                       p4 help client >errs 2>&1
> > -                       echo $? >retval
> > -               )
> > -               echo 0 >expected &&
> > -               test_cmp expected retval &&
> > -               rm retval &&
> > -               (
> > -                       p4 help nosuchcommand >errs 2>&1
> > -                       echo $? >retval
> > -               )
> > -               echo 1 >expected &&
> > -               test_cmp expected retval &&
> > -               rm retval
> > +               p4 help client &&
> > +               test_must_fail p4 help nosuchcommand
>
> same question?

Same answer. Not shown in this patch, but just above the context lines
you will find this comment in the file:

    # We rely on this behavior to detect for p4 move availability.

which means that the test is really interested in being able to
reliably detect if a sub-command is or is not available. So, despite
the (somewhat) misleading test title, this test doesn't care about the
exact error code but rather cares only that "p4 help nosuchcommand"
errors out, period. Hence, test_must_fail() again agrees with the
spirit of the test.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26  9:20 ` [PATCH 00/29] t: detect and fix " Elijah Newren
@ 2018-06-26  9:31   ` Eric Sunshine
  2018-06-26 15:34     ` Elijah Newren
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26  9:31 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Git List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 5:20 AM Elijah Newren <newren@gmail.com> wrote:
> On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> > Aside from identifying a rather significant number of &&-chain breaks,
> > repairing those broken chains uncovered genuine bugs in several tests
> > which were hidden by missing &&-chain links. Those bugs are also fixed
> > by this series. I would appreciate if the following people would
> > double-check my fixes:
> >
> > Stefan Bellar - 8/29 "t7400" and (especially) 13/29 "lib-submodule-update"
> > Jonathan Tan - 10/29 "t9001"
> > Elijah Newren - 6/29 "t6036"
>
> Commented on the patch in question; 6/29 looks good.
>
> I also looked over the rest of the series.  Apart from the ones you
> specifically called out as needing review by others besides me, and
> the final patch which makes me feel like a sed neophyte, all but one
> patch looked good to me.  I just have a small question for that
> remaining patch, which I posted there.

I guess you refer to your question[1] about whether test_must_fail()
is the correct choice over test_expect_code(). I just responded[2]
with a hopefully satisfactory answer.

[1]: https://public-inbox.org/git/CABPp-BFmfN6=E+3BAKt-NH5hmU-368shgDnrnkrnMRvKnx07BQ@mail.gmail.com/
[2]: https://public-inbox.org/git/CAPig+cRTG625H3CF1Zw30vQt2W8uKf1xLxVaQni2YbJ=xAif2g@mail.gmail.com/

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26  9:31   ` Eric Sunshine
@ 2018-06-26 15:34     ` Elijah Newren
  0 siblings, 0 replies; 123+ messages in thread
From: Elijah Newren @ 2018-06-26 15:34 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Jonathan Tan

Hi Eric,

On Tue, Jun 26, 2018, 2:31 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Tue, Jun 26, 2018 at 5:20 AM Elijah Newren <newren@gmail.com> wrote:
> > On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> > > Aside from identifying a rather significant number of &&-chain breaks,
> > > repairing those broken chains uncovered genuine bugs in several tests
> > > which were hidden by missing &&-chain links. Those bugs are also fixed
> > > by this series. I would appreciate if the following people would
> > > double-check my fixes:
> > >
> > > Stefan Bellar - 8/29 "t7400" and (especially) 13/29 "lib-submodule-update"
> > > Jonathan Tan - 10/29 "t9001"
> > > Elijah Newren - 6/29 "t6036"
> >
> > Commented on the patch in question; 6/29 looks good.
> >
> > I also looked over the rest of the series.  Apart from the ones you
> > specifically called out as needing review by others besides me, and
> > the final patch which makes me feel like a sed neophyte, all but one
> > patch looked good to me.  I just have a small question for that
> > remaining patch, which I posted there.
>
> I guess you refer to your question[1] about whether test_must_fail()
> is the correct choice over test_expect_code(). I just responded[2]
> with a hopefully satisfactory answer.

Yes, it does.  Thanks!

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 10/29] t9001: fix broken "invoke hook" test
  2018-06-26  7:29 ` [PATCH 10/29] t9001: fix broken "invoke hook" test Eric Sunshine
@ 2018-06-26 17:07   ` Jonathan Tan
  0 siblings, 0 replies; 123+ messages in thread
From: Jonathan Tan @ 2018-06-26 17:07 UTC (permalink / raw)
  To: sunshine; +Cc: git, peff, jrnieder, szeder, sbeller, newren, jonathantanmy

> diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
> index e80eacbb1b..776769fe0d 100755
> --- a/t/t9001-send-email.sh
> +++ b/t/t9001-send-email.sh
> @@ -1966,11 +1966,11 @@ test_expect_success $PREREQ 'invoke hook' '
>  
>  		# Verify error message when a patch is rejected by the hook
>  		sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
> -		git send-email \
> +		test_must_fail git send-email \
>  			--from="Example <nobody@example.com>" \
>  			--to=nobody@example.com \
>  			--smtp-server="$(pwd)/../fake.sendmail" \
> -			../another.patch 2>err
> +			../another.patch 2>err &&
>  		test_i18ngrep "rejected by sendemail-validate hook" err

Thanks for catching this. Indeed, "git send-email" is supposed to fail
because the validate hook greps for the string "add master", which does
not exist in the e-mail to be sent. (Above this is a test that shows
that the same validate hook succeeds if the e-mail contains "add
master".) This looks correct to me.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26  9:21     ` Eric Sunshine
@ 2018-06-26 18:05       ` Johannes Sixt
  2018-06-26 18:14         ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Johannes Sixt @ 2018-06-26 18:05 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Elijah Newren, Git List, Jeff King, Jonathan Nieder,
	SZEDER Gábor, Stefan Beller, Jonathan Tan

Am 26.06.2018 um 11:21 schrieb Eric Sunshine:
> On Tue, Jun 26, 2018 at 4:58 AM Elijah Newren <newren@gmail.com> wrote:
>> On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>>> +               p4 help client &&
>>> +               test_must_fail p4 help nosuchcommand
>>
>> same question?
> 
> Same answer. Not shown in this patch, but just above the context lines
> you will find this comment in the file:
> 
>      # We rely on this behavior to detect for p4 move availability.
> 
> which means that the test is really interested in being able to
> reliably detect if a sub-command is or is not available. So, despite
> the (somewhat) misleading test title, this test doesn't care about the
> exact error code but rather cares only that "p4 help nosuchcommand"
> errors out, period. Hence, test_must_fail() again agrees with the
> spirit of the test.

test_must_fail ensures that only "proper" failures are diagnosed as 
expected; failures due to signals such as SEGV are not expected failures.

In the test suite we expect all programs that are not our "git" to work 
correctly; in particular, that they do not crash on anything that we ask 
them to operate on. Under this assumption, the protection given by 
test_must_fail is not needed.

Hence, these lines should actually be

		p4 help client &&
		! p4 help nosuchcommand

-- Hannes

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26 18:05       ` Johannes Sixt
@ 2018-06-26 18:14         ` Eric Sunshine
  2018-06-26 21:00           ` Johannes Sixt
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 18:14 UTC (permalink / raw)
  To: Johannes Sixt
  Cc: Elijah Newren, Git List, Jeff King, Jonathan Nieder,
	SZEDER Gábor, Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 2:06 PM Johannes Sixt <j6t@kdbg.org> wrote:
> Am 26.06.2018 um 11:21 schrieb Eric Sunshine:
> >> On Tue, Jun 26, 2018 at 12:29 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> >>> +               p4 help client &&
> >>> +               test_must_fail p4 help nosuchcommand
> > [...] So, despite
> > the (somewhat) misleading test title, this test doesn't care about the
> > exact error code but rather cares only that "p4 help nosuchcommand"
> > errors out, period. Hence, test_must_fail() again agrees with the
> > spirit of the test.
>
> test_must_fail ensures that only "proper" failures are diagnosed as
> expected; failures due to signals such as SEGV are not expected failures.
>
> In the test suite we expect all programs that are not our "git" to work
> correctly; in particular, that they do not crash on anything that we ask
> them to operate on. Under this assumption, the protection given by
> test_must_fail is not needed.
>
> Hence, these lines should actually be
>
>                 p4 help client &&
>                 ! p4 help nosuchcommand

Thanks for the comment; you're right, of course. I'll certainly make
this change if I have to re-roll for some other reason, but do you
feel that this itself is worth a re-roll?

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26  7:30 ` [PATCH 29/29] t/test-lib: teach --chain-lint to detect " Eric Sunshine
@ 2018-06-26 19:15   ` Junio C Hamano
  2018-06-26 19:52     ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-06-26 19:15 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

Eric Sunshine <sunshine@sunshineco.com> writes:

> The --chain-lint option detects broken &&-chains by forcing the test to
> exit early (as the very first step) with a sentinel value. If that
> sentinel is the test's overall exit code, then the &&-chain is intact;
> if not, then the chain is broken. Unfortunately, this detection does not
> extend to &&-chains within subshells even when the subshell itself is
> properly linked into the outer &&-chain.
>
> Address this shortcoming by eliminating the subshell during the
> "linting" phase and incorporating its body directly into the surrounding
> &&-chain. To keep this transformation cheap, no attempt is made at
> properly parsing shell code. Instead, the manipulations are purely
> textual. For example:
>
>     statement1 &&
>     (
>         statement2 &&
>         statement3
>     ) &&
>     statement4
>
> is transformed to:
>
>     statement1 &&
>         statement2 &&
>         statement3 &&
>     statement4

so, with --chain-lint, we would transform this

	mkdir -p a/b/c &&
	(
		cd a/b/c
		rm -fr ../../*
	) &&
	statement 4

into this sequence

	(exit $sentinel) &&
	mkdir -p a/b/c &&
		cd a/b/c
		rm -fr ../../* &&
	statement 4

and then rely on the non-zero exit to cancel all the remainder?

We didn't create nor cd to the t/trash$num/a/b/c thanks to the &&
chain, and end up running rm -fr ../../* from inside t/trash$num?

Hmmmmm....

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct
  2018-06-26  7:29 ` [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct Eric Sunshine
@ 2018-06-26 19:31   ` Junio C Hamano
  2018-06-26 20:06     ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-06-26 19:31 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

Eric Sunshine <sunshine@sunshineco.com> writes:

> These tests employ a noisy subshell (with missing &&-chain) to feed
> input into Git commands:
>
>     (echo a; echo b; echo c) | git some-command ...
>
> Drop the subshell in favor of a simple 'printf':
>
>     printf "%s\n" a b c | git some-command ...

That's called test_write_lines, I think.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (29 preceding siblings ...)
  2018-06-26  9:20 ` [PATCH 00/29] t: detect and fix " Elijah Newren
@ 2018-06-26 19:38 ` Junio C Hamano
  2018-06-26 21:25   ` Eric Sunshine
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
  31 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-06-26 19:38 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

Eric Sunshine <sunshine@sunshineco.com> writes:

> The --chain-lint[1] option detects breakage in the top-level &&-chain of
> tests. This series undertakes the more complex task of teaching it to
> also detect &&-chain breakage within subshells. See patch 29/29 for the
> gory details of how that's done.

I first looked at 29/29 and got heavily inclined to reject that
step, and then continued reading from 1/29 to around 15/29.  

I like these earlier changes that fix existing breakage, of course.
I also like many of the changes that simplify and/or modernise the
test scripts very much, but they are unusable as-is as long as their
justification is "chain-lint will start barfing on these constructs".

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 19:15   ` Junio C Hamano
@ 2018-06-26 19:52     ` Eric Sunshine
  2018-06-26 20:17       ` Jeff King
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 19:52 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jeff King, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 3:15 PM Junio C Hamano <gitster@pobox.com> wrote:
> so, with --chain-lint, we would transform this
>
>         mkdir -p a/b/c &&
>         (
>                 cd a/b/c
>                 rm -fr ../../*
>         ) &&
>         statement 4
>
> into this sequence
>
>         (exit $sentinel) &&
>         mkdir -p a/b/c &&
>                 cd a/b/c
>                 rm -fr ../../* &&
>         statement 4
>
> and then rely on the non-zero exit to cancel all the remainder?
>
> We didn't create nor cd to the t/trash$num/a/b/c thanks to the &&
> chain, and end up running rm -fr ../../* from inside t/trash$num?

Yes, I did take that into account and, no, I don't have a good answer
to the issue.

The existing --chain-lint already suffers the same shortcoming. Older
(or even new poorly-written) tests, even without subshells, can fall
victim already:

    (exit $sentinel) &&
    mkdir -p a/b/c &&
    cd a/b/c
    rm -fr ../../* &&
    cd ../../.. &&
    statement4

As in your example, 'mkdir' and 'cd' are skipped, but 'rm -fr ../../*' is not.

This snippet from the commit message of bb79af9d09 (t/test-lib:
introduce --chain-lint option, 2015-03-20):

    When we encounter a failure of this check, we abort the test
    script entirely. For one thing, we have no clue which subset
    of the commands in the test snippet were actually run.

suggests that the issue was considered, in some form, even then
(though, it doesn't say explicitly that Peff had the 'rm -fr' case in
mind).

So, this isn't a new problem introduced by this series, though this
series may exacerbate it.

Thanks for thinking critically about it.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct
  2018-06-26 19:31   ` Junio C Hamano
@ 2018-06-26 20:06     ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 20:06 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 3:31 PM Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
> > These tests employ a noisy subshell (with missing &&-chain) to feed
> > input into Git commands:
> >
> >     (echo a; echo b; echo c) | git some-command ...
> >
> > Drop the subshell in favor of a simple 'printf':
> >
> >     printf "%s\n" a b c | git some-command ...
>
> That's called test_write_lines, I think.

Yep, that's better. Thanks.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 19:52     ` Eric Sunshine
@ 2018-06-26 20:17       ` Jeff King
  2018-06-26 20:22         ` Jeff King
                           ` (2 more replies)
  0 siblings, 3 replies; 123+ messages in thread
From: Jeff King @ 2018-06-26 20:17 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 03:52:54PM -0400, Eric Sunshine wrote:

> The existing --chain-lint already suffers the same shortcoming. Older
> (or even new poorly-written) tests, even without subshells, can fall
> victim already:
> 
>     (exit $sentinel) &&
>     mkdir -p a/b/c &&
>     cd a/b/c
>     rm -fr ../../* &&
>     cd ../../.. &&
>     statement4
> 
> As in your example, 'mkdir' and 'cd' are skipped, but 'rm -fr ../../*' is not.
> 
> This snippet from the commit message of bb79af9d09 (t/test-lib:
> introduce --chain-lint option, 2015-03-20):
> 
>     When we encounter a failure of this check, we abort the test
>     script entirely. For one thing, we have no clue which subset
>     of the commands in the test snippet were actually run.
> 
> suggests that the issue was considered, in some form, even then
> (though, it doesn't say explicitly that Peff had the 'rm -fr' case in
> mind).
>
> So, this isn't a new problem introduced by this series, though this
> series may exacerbate it.

One way this series might be worse in practice is that we tend not to
change process state too much outside of the subshells. So if we skip
some early commands and execute a later "rm", for example, it tends to
be in the same directory (and I think as time goes on we have been
cleaning up old tests which did a "cd foo && bar && cd .." into using a
subshell).

Whereas once you start collapsing subshells into the main logic chain,
there's a very high chance that the subshell is doing a "cd", since
that's typically the main reason for the subshell in the first place.
And with the current --chain-lint logic, that subshell is either
executed or not executed as a unit.

Obviously that's a bit of a hand-waving argument. If you've fixed all of
the existing cases without accidentally deleting your home directory,
then maybe it's not so likely to be a problem after all.

I'm not sure if there's a good solution, though. Even if you retained
the subshells and instead did a chain-lint inside each subshell, like
this:

  (exit 117) &&
  one &&
  (
	(exit 117) &&
	cd foo
	two
  ) &&
  three


that doesn't really help. The fundamental issue is that we may skip the
"cd" inside the subshell. Whether it's in a subshell or not, that's
dangerous. True, we don't run "three" in this case, which is slightly
better. But it didn't expect to be in a different directory anyway. It's
running "two" that is dangerous.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:17       ` Jeff King
@ 2018-06-26 20:22         ` Jeff King
  2018-06-26 20:59           ` Eric Sunshine
  2018-06-26 21:33           ` Elijah Newren
  2018-06-26 20:46         ` Eric Sunshine
  2018-06-26 21:09         ` Junio C Hamano
  2 siblings, 2 replies; 123+ messages in thread
From: Jeff King @ 2018-06-26 20:22 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 04:17:08PM -0400, Jeff King wrote:

> I'm not sure if there's a good solution, though. Even if you retained
> the subshells and instead did a chain-lint inside each subshell, like
> this:

So obviously that means "I don't think there's a good solution with this
approach".

That whole final patch simultaneously impresses and nauseates me. Your
commit message says "no attempt is made at properly parsing shell code",
but we come pretty darn close. I almost wonder if we'd be better off
just parsing some heuristic subset and making sure (via review or
linting) that our tests conform.

Another option is to not enable this slightly-more-dangerous linting by
default. But that would probably rob it of its usefulness, since it
would just fall to some brave soul to later crank up the linting and fix
everybody else's mistakes.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:17       ` Jeff King
  2018-06-26 20:22         ` Jeff King
@ 2018-06-26 20:46         ` Eric Sunshine
  2018-06-26 21:01           ` Jeff King
  2018-06-26 21:09         ` Junio C Hamano
  2 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 20:46 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 4:17 PM Jeff King <peff@peff.net> wrote:
> On Tue, Jun 26, 2018 at 03:52:54PM -0400, Eric Sunshine wrote:
> > So, this isn't a new problem introduced by this series, though this
> > series may exacerbate it.
>
> Whereas once you start collapsing subshells into the main logic chain,
> there's a very high chance that the subshell is doing a "cd", since
> that's typically the main reason for the subshell in the first place.
> And with the current --chain-lint logic, that subshell is either
> executed or not executed as a unit.
>
> Obviously that's a bit of a hand-waving argument. If you've fixed all of
> the existing cases without accidentally deleting your home directory,
> then maybe it's not so likely to be a problem after all.

Indeed, it could be that the "rm -fr" worry is tending toward the
hypothetical. Seasoned developers tend to be pretty careful and
usually avoid indiscriminately loose "rm -fr" invocations, so I'm
somewhat less worried about them. I do share the concern, though, that
newcomers crafting or extending tests could shoot themselves in the
foot with this. However, newcomers are also the ones most likely to
use the "cd foo && bar && cd .." idiom, so they are already at risk.

(As for not blasting my home directory when fixing all the existing
tests, I did run into a few cases where one or two "foreign" files
were deposited into the "t/" directory, but nothing was deleted or
overwritten.)

> I'm not sure if there's a good solution, though. Even if you retained
> the subshells and instead did a chain-lint inside each subshell, like
> this:
>
>   (exit 117) &&
>   one &&
>   (
>         (exit 117) &&
>         cd foo
>         two
>   ) &&
>   three

I thought of that too, but the inner (exit 117) doesn't even get
invoked unless there is &&-chain breakage somewhere above that point
(for instance, if "one" lacks "&&"), so the inner (exit 117) doesn't
participate in the linting process at all.

> that doesn't really help. The fundamental issue is that we may skip the
> "cd" inside the subshell. Whether it's in a subshell or not, that's
> dangerous. True, we don't run "three" in this case, which is slightly
> better. But it didn't expect to be in a different directory anyway. It's
> running "two" that is dangerous.

Just thinking aloud...

Aside from "rm -fr", there are numerous ways to clobber files
unexpectedly when the "cd" is skipped:

    echo x >../git.c
    cp x ../git.c
    mv x ../git.c
    ln [-s] x ../git.c
    /bin/rm ../git.c
    some-cmd -o ../git.c

Some of these dangers can be de-thoothed during the linting phase by
defining do-nothing shell functions:

    cp () { :; }
    mv () { :; }
    ln () { :; }

That, at least, makes the scariest case ("rm") much less so.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:22         ` Jeff King
@ 2018-06-26 20:59           ` Eric Sunshine
  2018-06-26 21:33           ` Elijah Newren
  1 sibling, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 20:59 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 4:22 PM Jeff King <peff@peff.net> wrote:
> So obviously that means "I don't think there's a good solution with this
> approach".
>
> That whole final patch simultaneously impresses and nauseates me. Your
> commit message says "no attempt is made at properly parsing shell code",
> but we come pretty darn close. I almost wonder if we'd be better off
> just parsing some heuristic subset and making sure (via review or
> linting) that our tests conform.

I'm not sure I agree with "come pretty darn close", but your idea is
an interesting one. It would sidestep the concern with "rm -fr" and
friends (though it will probably still nauseate you). Let me cogitate
about it a bit...

> Another option is to not enable this slightly-more-dangerous linting by
> default. But that would probably rob it of its usefulness, since it
> would just fall to some brave soul to later crank up the linting and fix
> everybody else's mistakes.

I considered that, as well, and came to the same conclusion.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 17/29] t: use test_must_fail() instead of checking exit code manually
  2018-06-26 18:14         ` Eric Sunshine
@ 2018-06-26 21:00           ` Johannes Sixt
  0 siblings, 0 replies; 123+ messages in thread
From: Johannes Sixt @ 2018-06-26 21:00 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Elijah Newren, Git List, Jeff King, Jonathan Nieder,
	SZEDER Gábor, Stefan Beller, Jonathan Tan

Am 26.06.2018 um 20:14 schrieb Eric Sunshine:
> On Tue, Jun 26, 2018 at 2:06 PM Johannes Sixt <j6t@kdbg.org> wrote:
>> Hence, these lines should actually be
>>
>>                  p4 help client &&
>>                  ! p4 help nosuchcommand
> 
> Thanks for the comment; you're right, of course. I'll certainly make
> this change if I have to re-roll for some other reason, but do you
> feel that this itself is worth a re-roll?

Not worth a re-roll IMO.

-- Hannes

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:46         ` Eric Sunshine
@ 2018-06-26 21:01           ` Jeff King
  2018-06-26 21:13             ` Eric Sunshine
  2018-06-27  2:15             ` Elijah Newren
  0 siblings, 2 replies; 123+ messages in thread
From: Jeff King @ 2018-06-26 21:01 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:

> > I'm not sure if there's a good solution, though. Even if you retained
> > the subshells and instead did a chain-lint inside each subshell, like
> > this:
> >
> >   (exit 117) &&
> >   one &&
> >   (
> >         (exit 117) &&
> >         cd foo
> >         two
> >   ) &&
> >   three
> 
> I thought of that too, but the inner (exit 117) doesn't even get
> invoked unless there is &&-chain breakage somewhere above that point
> (for instance, if "one" lacks "&&"), so the inner (exit 117) doesn't
> participate in the linting process at all.

Oh, right. Not only does it not fix the problem, it's totally
unworkable. :)

> Some of these dangers can be de-thoothed during the linting phase by
> defining do-nothing shell functions:
> 
>     cp () { :; }
>     mv () { :; }
>     ln () { :; }
> 
> That, at least, makes the scariest case ("rm") much less so.

Now that's an interesting idea. We can't catch every dangerous action
(notably ">" would be hard to override), but it should be pretty cheap
to cover some obvious ones.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:17       ` Jeff King
  2018-06-26 20:22         ` Jeff King
  2018-06-26 20:46         ` Eric Sunshine
@ 2018-06-26 21:09         ` Junio C Hamano
  2 siblings, 0 replies; 123+ messages in thread
From: Junio C Hamano @ 2018-06-26 21:09 UTC (permalink / raw)
  To: Jeff King
  Cc: Eric Sunshine, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

Jeff King <peff@peff.net> writes:

> One way this series might be worse in practice is that we tend not to
> change process state too much outside of the subshells.
> ...
> Whereas once you start collapsing subshells into the main logic chain,
> there's a very high chance that the subshell is doing a "cd", since
> that's typically the main reason for the subshell in the first place.

Exactly.  I should have mentioned this when I responded to save a
round-trip.


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 21:01           ` Jeff King
@ 2018-06-26 21:13             ` Eric Sunshine
  2018-06-28 14:35               ` Jeff King
  2018-06-27  2:15             ` Elijah Newren
  1 sibling, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 21:13 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 5:01 PM Jeff King <peff@peff.net> wrote:
> On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:
> > Some of these dangers can be de-thoothed during the linting phase by
> > defining do-nothing shell functions:
> >
> >     cp () { :; }
> >     mv () { :; }
> >     ln () { :; }
> >
> > That, at least, makes the scariest case ("rm") much less so.
>
> Now that's an interesting idea. We can't catch every dangerous action
> (notably ">" would be hard to override), but it should be pretty cheap
> to cover some obvious ones.

Taking the idea a bit further, the 'sed' script could also throw away
strings of "../" inside subshells, which would help defang the more
difficult cases, like "echo x >../git.c". There are pathological
cases, of course, which it wouldn't catch:

    P=../git.c
    test_expect_success 'foo' '
        (
            cd dir &&
            echo x >$P
        )
    '

but it does help mitigate the issue for the most typical cases.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26 19:38 ` Junio C Hamano
@ 2018-06-26 21:25   ` Eric Sunshine
  2018-06-26 22:31     ` Junio C Hamano
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 21:25 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 3:38 PM Junio C Hamano <gitster@pobox.com> wrote:
> I first looked at 29/29 and got heavily inclined to reject that
> step, and then continued reading from 1/29 to around 15/29.
>
> I like these earlier changes that fix existing breakage, of course.
> I also like many of the changes that simplify and/or modernise the
> test scripts very much, but they are unusable as-is as long as their
> justification is "chain-lint will start barfing on these constructs".

Sorry, I'm having difficulty understanding.

Are you saying that you don't want patches which exist merely to
pacify --chain-lint? (For instance, 2/29 "t0001: use "{...}" block
around "||" expression rather than subshell".)

Or are you saying that you don't like how the commit messages are
worded, and that they should instead emphasize that the change is good
for its own sake, without mentioning --chain-lint?

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 20:22         ` Jeff King
  2018-06-26 20:59           ` Eric Sunshine
@ 2018-06-26 21:33           ` Elijah Newren
  2018-06-26 21:42             ` Eric Sunshine
  1 sibling, 1 reply; 123+ messages in thread
From: Elijah Newren @ 2018-06-26 21:33 UTC (permalink / raw)
  To: Jeff King
  Cc: Eric Sunshine, Junio C Hamano, Git List, Jonathan Nieder,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 1:22 PM, Jeff King <peff@peff.net> wrote:
> On Tue, Jun 26, 2018 at 04:17:08PM -0400, Jeff King wrote:
>
>> I'm not sure if there's a good solution, though. Even if you retained
>> the subshells and instead did a chain-lint inside each subshell, like
>> this:
>
> So obviously that means "I don't think there's a good solution with this
> approach".
>
> That whole final patch simultaneously impresses and nauseates me. Your
> commit message says "no attempt is made at properly parsing shell code",
> but we come pretty darn close. I almost wonder if we'd be better off
> just parsing some heuristic subset and making sure (via review or
> linting) that our tests conform.
>
> Another option is to not enable this slightly-more-dangerous linting by
> default. But that would probably rob it of its usefulness, since it
> would just fall to some brave soul to later crank up the linting and fix
> everybody else's mistakes.

This may be a dumb question, but why can't we run under errexit?  If
we could do that, we wouldn't need the &&-chaining, and bash would
parse the shell for us and exit whenever one command failed.  (Is the
reason for this documented somewhere?  I couldn't find it...)

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 21:33           ` Elijah Newren
@ 2018-06-26 21:42             ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-26 21:42 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Jeff King, Junio C Hamano, Git List, Jonathan Nieder,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 5:33 PM Elijah Newren <newren@gmail.com> wrote:
> On Tue, Jun 26, 2018 at 1:22 PM, Jeff King <peff@peff.net> wrote:
> > Another option is to not enable this slightly-more-dangerous linting by
> > default. But that would probably rob it of its usefulness, since it
> > would just fall to some brave soul to later crank up the linting and fix
> > everybody else's mistakes.
>
> This may be a dumb question, but why can't we run under errexit?  If
> we could do that, we wouldn't need the &&-chaining, and bash would
> parse the shell for us and exit whenever one command failed.  (Is the
> reason for this documented somewhere?  I couldn't find it...)

I'm not sure if it's documented anywhere, but it has been discussed.
In particular, see [1], especially [2], and [3]. Peff summed up by
saying:

    So I dunno. I think "set -e" is kind of a dangerous lure. It works
    so well _most_ of the time that you start to rely on it, but it
    really does have some funny corner cases (even on modern shells,
    and for all I know, the behavior above is mandated by POSIX).

[1]: https://public-inbox.org/git/xmqq384zha6s.fsf@gitster.dls.corp.google.com/
[2]: https://public-inbox.org/git/20150320172406.GA15172@peff.net/
[3]: https://public-inbox.org/git/xmqqoannfu84.fsf@gitster.dls.corp.google.com/

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26 21:25   ` Eric Sunshine
@ 2018-06-26 22:31     ` Junio C Hamano
  2018-06-27  0:22       ` Jonathan Nieder
  0 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-06-26 22:31 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Jeff King, Jonathan Nieder, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

Eric Sunshine <sunshine@sunshineco.com> writes:

> On Tue, Jun 26, 2018 at 3:38 PM Junio C Hamano <gitster@pobox.com> wrote:
>> I first looked at 29/29 and got heavily inclined to reject that
>> step, and then continued reading from 1/29 to around 15/29.
>>
>> I like these earlier changes that fix existing breakage, of course.
>> I also like many of the changes that simplify and/or modernise the
>> test scripts very much, but they are unusable as-is as long as their
>> justification is "chain-lint will start barfing on these constructs".
>
> Sorry, I'm having difficulty understanding.
>
> Are you saying that you don't want patches which exist merely to
> pacify --chain-lint? (For instance, 2/29 "t0001: use "{...}" block
> around "||" expression rather than subshell".)

Yes.

> Or are you saying that you don't like how the commit messages are
> worded, and that they should instead emphasize that the change is good
> for its own sake, without mentioning --chain-lint?

Yes, too.

For example, 03/29 is a good clean-up, and its value is not
diminished even if we reject the subprocess munging --chain-lint in
29/29.

As opposed to 02/29 which mostly is about appeasing the "shell
parser" in 29/29 (or you could justify it saying "one less fork and
process" if that gives us a measurable benefit).

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 00/29] t: detect and fix broken &&-chains in subshells
  2018-06-26 22:31     ` Junio C Hamano
@ 2018-06-27  0:22       ` Jonathan Nieder
  0 siblings, 0 replies; 123+ messages in thread
From: Jonathan Nieder @ 2018-06-27  0:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Eric Sunshine, Git List, Jeff King, SZEDER Gábor,
	Stefan Beller, Elijah Newren, Jonathan Tan

Jun 26, 2018 at 03:31:11PM -0700, Junio C Hamano wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
>> On Tue, Jun 26, 2018 at 3:38 PM Junio C Hamano <gitster@pobox.com> wrote:

>>> I like these earlier changes that fix existing breakage, of course.
>>> I also like many of the changes that simplify and/or modernise the
>>> test scripts very much, but they are unusable as-is as long as their
>>> justification is "chain-lint will start barfing on these constructs".
>>
>> Sorry, I'm having difficulty understanding.
>>
>> Are you saying that you don't want patches which exist merely to
>> pacify --chain-lint? (For instance, 2/29 "t0001: use "{...}" block
>> around "||" expression rather than subshell".)
>
> Yes.
>
>> Or are you saying that you don't like how the commit messages are
>> worded, and that they should instead emphasize that the change is good
>> for its own sake, without mentioning --chain-lint?
>
> Yes, too.
>
> For example, 03/29 is a good clean-up, and its value is not
> diminished even if we reject the subprocess munging --chain-lint in
> 29/29.
>
> As opposed to 02/29 which mostly is about appeasing the "shell
> parser" in 29/29 (or you could justify it saying "one less fork and
> process" if that gives us a measurable benefit).

This is a lighter-weight example of the practice described at
https://lkml.kernel.org/r/alpine.LFD.2.00.1001251002430.3574@localhost.localdomain/.
In my opinion it's good advice, often worth repeating.

Thanks,
Jonathan

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 21:01           ` Jeff King
  2018-06-26 21:13             ` Eric Sunshine
@ 2018-06-27  2:15             ` Elijah Newren
  2018-06-27  6:27               ` Johannes Sixt
  2018-06-28 14:37               ` Jeff King
  1 sibling, 2 replies; 123+ messages in thread
From: Elijah Newren @ 2018-06-27  2:15 UTC (permalink / raw)
  To: Jeff King
  Cc: Eric Sunshine, Junio C Hamano, Git List, Jonathan Nieder,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 2:01 PM, Jeff King <peff@peff.net> wrote:
> On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:

>> Some of these dangers can be de-thoothed during the linting phase by
>> defining do-nothing shell functions:
>>
>>     cp () { :; }
>>     mv () { :; }
>>     ln () { :; }
>>
>> That, at least, makes the scariest case ("rm") much less so.
>
> Now that's an interesting idea. We can't catch every dangerous action
> (notably ">" would be hard to override), but it should be pretty cheap
> to cover some obvious ones.
>
> -Peff

Crazy idea: maybe we could defang it a little more thoroughly with
something like the following (apologies in advance if gmail whitespace
damages this):

diff --git a/t/test-lib.sh b/t/test-lib.sh
index 28315706be..7fda08a90a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -675,7 +675,7 @@ test_run_ () {
                trace=
                # 117 is magic because it is unlikely to match the exit
                # code of other programs
-               if test "OK-117" != "$(test_eval_ "(exit 117) &&
$1${LF}${LF}echo OK-\$?" 3>&1)"
+               if test "OK-117" != "$(test_eval_ "cd() { return 0; }
&& PATH=/dev/null && export PATH && (exit 117) && $1${LF}${LF}echo
OK-\$?" 3>&1)"
                then
                        error "bug in the test script: broken &&-chain
or run-away HERE-DOC: $1"
                fi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-27  2:15             ` Elijah Newren
@ 2018-06-27  6:27               ` Johannes Sixt
  2018-06-27  6:48                 ` Eric Sunshine
  2018-06-28 14:37               ` Jeff King
  1 sibling, 1 reply; 123+ messages in thread
From: Johannes Sixt @ 2018-06-27  6:27 UTC (permalink / raw)
  To: Elijah Newren, Jeff King
  Cc: Eric Sunshine, Junio C Hamano, Git List, Jonathan Nieder,
	Stefan Beller, Jonathan Tan

Am 27.06.2018 um 04:15 schrieb Elijah Newren:
> On Tue, Jun 26, 2018 at 2:01 PM, Jeff King <peff@peff.net> wrote:
>> On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:
> 
>>> Some of these dangers can be de-thoothed during the linting phase by
>>> defining do-nothing shell functions:
>>>
>>>      cp () { :; }
>>>      mv () { :; }
>>>      ln () { :; }
>>>
>>> That, at least, makes the scariest case ("rm") much less so.
>>
>> Now that's an interesting idea. We can't catch every dangerous action
>> (notably ">" would be hard to override), but it should be pretty cheap
>> to cover some obvious ones.
>>
>> -Peff
> 
> Crazy idea: maybe we could defang it a little more thoroughly with
> something like the following (apologies in advance if gmail whitespace
> damages this):
> 
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 28315706be..7fda08a90a 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -675,7 +675,7 @@ test_run_ () {
>                  trace=
>                  # 117 is magic because it is unlikely to match the exit
>                  # code of other programs
> -               if test "OK-117" != "$(test_eval_ "(exit 117) &&
> $1${LF}${LF}echo OK-\$?" 3>&1)"
> +               if test "OK-117" != "$(test_eval_ "cd() { return 0; }
> && PATH=/dev/null && export PATH && (exit 117) && $1${LF}${LF}echo
> OK-\$?" 3>&1)"
>                  then
>                          error "bug in the test script: broken &&-chain
> or run-away HERE-DOC: $1"
>                  fi

I'd define all these functions as { return 1; } because we want to stop 
any && chain as early as possible (and with an exit code that is not the 
sentinel value).

-- Hannes

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-27  6:27               ` Johannes Sixt
@ 2018-06-27  6:48                 ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-27  6:48 UTC (permalink / raw)
  To: Johannes Sixt
  Cc: Elijah Newren, Jeff King, Junio C Hamano, Git List,
	Jonathan Nieder, Stefan Beller, Jonathan Tan

On Wed, Jun 27, 2018 at 2:27 AM Johannes Sixt <j6t@kdbg.org> wrote:
> Am 27.06.2018 um 04:15 schrieb Elijah Newren:
> > On Tue, Jun 26, 2018 at 2:01 PM, Jeff King <peff@peff.net> wrote:
> >> On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:
> >>> Some of these dangers can be de-thoothed during the linting phase by
> >>> defining do-nothing shell functions:
> >>>      cp () { :; }
> >>> That, at least, makes the scariest case ("rm") much less so.
> >>
> >> Now that's an interesting idea. We can't catch every dangerous action
> >> (notably ">" would be hard to override), but it should be pretty cheap
> >> to cover some obvious ones.
> >
> > Crazy idea: maybe we could defang it a little more thoroughly with
> > something like the following (apologies in advance if gmail whitespace
> > damages this):
> >
> > -               if test "OK-117" != "$(test_eval_ "(exit 117) &&
> > $1${LF}${LF}echo OK-\$?" 3>&1)"
> > +               if test "OK-117" != "$(test_eval_ "cd() { return 0; }
> > && PATH=/dev/null && export PATH && (exit 117) && $1${LF}${LF}echo
> > OK-\$?" 3>&1)"

Interesting idea (coupled with Hannes's point below)...

> I'd define all these functions as { return 1; } because we want to stop
> any && chain as early as possible (and with an exit code that is not the
> sentinel value).

A very sensible suggestion.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test
  2018-06-26  7:29 ` [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test Eric Sunshine
@ 2018-06-27 18:04   ` Stefan Beller
  0 siblings, 0 replies; 123+ messages in thread
From: Stefan Beller @ 2018-06-27 18:04 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Jeff King, Jonathan Nieder, szeder, Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 12:30 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> This test has been dysfunctional since it was added by 619acfc78c
> (submodule add: extend force flag to add existing repos, 2016-10-06),
> however, two problems early in the test went unnoticed due to a broken
> &&-chain later in the test.
>
> First, it tries configuring the submodule with repository "bogus-url",
> however, "git submodule add" insists that the repository be either an
> absolute URL or a relative pathname requiring prefix "./" or "../" (this
> is true even with --force), but "bogus-url" does not meet those
> criteria, thus the command fails.
>
> Second, it then tries configuring a submodule with a path which is
> .gitignore'd, which is disallowed. This restriction can be overridden
> with --force, but the test neglects to use that option.
>
> Fix both problems, as well as the broken &&-chain behind which they hid.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>

This patch is
Reviewed-by: Stefan Beller <sbeller@google.com>

Thanks for this whole series (I just read the cover letter) and I think
detecting broken && chains is a valuable part in the test suite.

Thanks,
Stefan

> ---
>  t/t7400-submodule-basic.sh | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
> index 812db137b8..401adaed32 100755
> --- a/t/t7400-submodule-basic.sh
> +++ b/t/t7400-submodule-basic.sh
> @@ -171,12 +171,12 @@ test_expect_success 'submodule add to .gitignored path with --force' '
>  test_expect_success 'submodule add to reconfigure existing submodule with --force' '
>         (
>                 cd addtest-ignore &&
> -               git submodule add --force bogus-url submod &&
> -               git submodule add -b initial "$submodurl" submod-branch &&
> -               test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
> -               test "bogus-url" = "$(git config submodule.submod.url)" &&
> +               git submodule add --force /bogus-url submod &&
> +               git submodule add --force -b initial "$submodurl" submod-branch &&
> +               test "/bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
> +               test "/bogus-url" = "$(git config submodule.submod.url)" &&
>                 # Restore the url
> -               git submodule add --force "$submodurl" submod
> +               git submodule add --force "$submodurl" submod &&
>                 test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" &&
>                 test "$submodurl" = "$(git config submodule.submod.url)"
>         )
> --
> 2.18.0.419.gfe4b301394
>

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH] t/lib-submodule-update: fix absorbing test
  2018-06-26  7:29 ` [PATCH 13/29] t/lib-submodule-update: fix broken "replace submodule must-fail" test Eric Sunshine
@ 2018-06-27 18:30   ` Stefan Beller
  2018-06-27 18:38     ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Stefan Beller @ 2018-06-27 18:30 UTC (permalink / raw)
  To: sunshine; +Cc: git, jonathantanmy, jrnieder, newren, peff, sbeller, szeder

From: Eric Sunshine <sunshine@sunshineco.com>

This test has been dysfunctional since it was added by 259f3ee296
(lib-submodule-update.sh: define tests for recursing into submodules,
2017-03-14), however, problems went unnoticed due to a broken &&-chain
toward the end of the test.

The test wants to verify that replacing a submodule containing a .git
directory would absorb the .git directory into the .git/modules/ of the
superproject, and then replace the working tree content with the liking of
the superproject. The check if submodule content is around is wrong as
the submodule should have been replaced by the content of the superproject.

Delete the submodule content check, which also fixes the && chain in the
test.

While at it, fix broken &&-chains in a couple neighboring tests.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---

> The test wants to verify that replacing a submodule containing a .git
> directory must fail. All other "must fail" tests in this script invoke
> the supplied command as 'test_must_fail', however, this test neglects to
> do so.

In an ideal world the commands would not fail, but absorb the git directory
of the submodule. I manually tested that it is absorbed and not data from
a git directory is lost.

I would propose to replace that patch with the patch below; I hope
the wording did not add more confusion than there is already.

Thanks,
Stefan


 t/lib-submodule-update.sh | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 1f38a85371a..e90ec790877 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -755,7 +755,7 @@ test_submodule_recursing_with_args_common() {
 			: >sub1/untrackedfile &&
 			test_must_fail $command replace_sub1_with_file &&
 			test_superproject_content origin/add_sub1 &&
-			test_submodule_content sub1 origin/add_sub1
+			test_submodule_content sub1 origin/add_sub1 &&
 			test -f sub1/untracked_file
 		)
 	'
@@ -842,7 +842,7 @@ test_submodule_switch_recursing_with_args () {
 			cd submodule_update &&
 			git branch -t add_sub1 origin/add_sub1 &&
 			: >sub1 &&
-			echo sub1 >.git/info/exclude
+			echo sub1 >.git/info/exclude &&
 			$command add_sub1 &&
 			test_superproject_content origin/add_sub1 &&
 			test_submodule_content sub1 origin/add_sub1
@@ -969,7 +969,6 @@ test_submodule_forced_switch_recursing_with_args () {
 			rm -rf .git/modules/sub1 &&
 			$command replace_sub1_with_directory &&
 			test_superproject_content origin/replace_sub1_with_directory &&
-			test_submodule_content sub1 origin/modify_sub1
 			test_git_directory_exists sub1
 		)
 	'
-- 
2.18.0.399.gad0ab374a1-goog


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH] t/lib-submodule-update: fix absorbing test
  2018-06-27 18:30   ` [PATCH] t/lib-submodule-update: fix absorbing test Stefan Beller
@ 2018-06-27 18:38     ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-06-27 18:38 UTC (permalink / raw)
  To: Stefan Beller
  Cc: Git List, Jonathan Tan, Jonathan Nieder, Elijah Newren,
	Jeff King, SZEDER Gábor

On Wed, Jun 27, 2018 at 2:31 PM Stefan Beller <sbeller@google.com> wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
>
> This test has been dysfunctional since it was added by 259f3ee296
> (lib-submodule-update.sh: define tests for recursing into submodules,
> 2017-03-14), however, problems went unnoticed due to a broken &&-chain
> toward the end of the test.
> [...]
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
> In an ideal world the commands would not fail, but absorb the git directory
> of the submodule. I manually tested that it is absorbed and not data from
> a git directory is lost.
>
> I would propose to replace that patch with the patch below; I hope
> the wording did not add more confusion than there is already.

Thanks for diagnosing the problem, Stefan. I'm not a submodule user
and was not at all confident that I had interpreted the test breakage
correctly or that my fix was appropriate, so I'm happy to have a
diagnosis and fix from the person who actually wrote the test.

I'll also add a Helped-by: when re-posting.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-26 21:13             ` Eric Sunshine
@ 2018-06-28 14:35               ` Jeff King
  0 siblings, 0 replies; 123+ messages in thread
From: Jeff King @ 2018-06-28 14:35 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Stefan Beller,
	Elijah Newren, Jonathan Tan

On Tue, Jun 26, 2018 at 05:13:05PM -0400, Eric Sunshine wrote:

> On Tue, Jun 26, 2018 at 5:01 PM Jeff King <peff@peff.net> wrote:
> > On Tue, Jun 26, 2018 at 04:46:18PM -0400, Eric Sunshine wrote:
> > > Some of these dangers can be de-thoothed during the linting phase by
> > > defining do-nothing shell functions:
> > >
> > >     cp () { :; }
> > >     mv () { :; }
> > >     ln () { :; }
> > >
> > > That, at least, makes the scariest case ("rm") much less so.
> >
> > Now that's an interesting idea. We can't catch every dangerous action
> > (notably ">" would be hard to override), but it should be pretty cheap
> > to cover some obvious ones.
> 
> Taking the idea a bit further, the 'sed' script could also throw away
> strings of "../" inside subshells, which would help defang the more
> difficult cases, like "echo x >../git.c". There are pathological
> cases, of course, which it wouldn't catch:
> 
>     P=../git.c
>     test_expect_success 'foo' '
>         (
>             cd dir &&
>             echo x >$P
>         )
>     '
> 
> but it does help mitigate the issue for the most typical cases.

It seems like the dangerous thing there is ">", not necessarily "..".
Could we just s/>/x/g ?

That "breaks" the commands in a sense, but the whole point is that these
commands shouldn't ever be run in the first place.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 29/29] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-06-27  2:15             ` Elijah Newren
  2018-06-27  6:27               ` Johannes Sixt
@ 2018-06-28 14:37               ` Jeff King
  1 sibling, 0 replies; 123+ messages in thread
From: Jeff King @ 2018-06-28 14:37 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Eric Sunshine, Junio C Hamano, Git List, Jonathan Nieder,
	Stefan Beller, Jonathan Tan

On Tue, Jun 26, 2018 at 07:15:45PM -0700, Elijah Newren wrote:

> Crazy idea: maybe we could defang it a little more thoroughly with
> something like the following (apologies in advance if gmail whitespace
> damages this):
> 
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index 28315706be..7fda08a90a 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -675,7 +675,7 @@ test_run_ () {
>                 trace=
>                 # 117 is magic because it is unlikely to match the exit
>                 # code of other programs
> -               if test "OK-117" != "$(test_eval_ "(exit 117) &&
> $1${LF}${LF}echo OK-\$?" 3>&1)"
> +               if test "OK-117" != "$(test_eval_ "cd() { return 0; }
> && PATH=/dev/null && export PATH && (exit 117) && $1${LF}${LF}echo
> OK-\$?" 3>&1)"

Clever. We'd still run shell builtins, which is why you need the cd()
above. There may be others, but at least it narrows things down. Unless
the shell is busybox or something, and implements everything as a
builtin. :)

I agree on the point elsewhere of returning non-zero (and the items
missing from PATH should do that, which is good).

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 00/10] detect broken &&-chains in subshells
  2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
                   ` (30 preceding siblings ...)
  2018-06-26 19:38 ` Junio C Hamano
@ 2018-07-11  6:46 ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
                     ` (10 more replies)
  31 siblings, 11 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

This is a re-roll of [1] which teaches --chain-lint to detect broken
&&-chains in subshells since the existing implementation[2] detects
breakage only at the top-level. It is built atop 'es/test-fixes', which
was split off of [1] and submitted separately[3], and which fixes many
broken &&-chains.

The major difference since v1 is that broken &&-chains in subshells are
now detected by pure textual inspection rather than by merging subshell
commands into the top-level &&-chain. Thus, v2 entirely sidesteps the
primary objection[4] raised during v1 review of possible unintended
side-effects of test code executing outside of the intended directory.
The pure textual detection implemented by v2 can have no such
side-effects.

A second important difference since v1 is that the "linter" itself, a
complex 'sed' script, now has its own tests to ensure correct behavior.
Not only do the tests protect against regressions, but they help to
document (for humans) expected behavior, which is important since 'sed'
scripts can seem rather inscrutable due to looking like line-noise and
due to the 'sed' "language" being stack-oriented (albeit with a very
tiny stack) which, like other stack-oriented languages (Forth,
Postscript, etc.) can be difficult to reason about.

Although the 'sed' script in v1 was already well-commented, the comments
have been improved in v2. More importantly, a high-level overview of the
script's operation has been added at the top of the file to aid
comprehension.

Thanks to Elijah, Hannes, Jonathan Nieder, Jonathan Tan, Junio, Luke,
Peff, and Stefan for comments on v1.

[1]: https://public-inbox.org/git/20180626073001.6555-1-sunshine@sunshineco.com/
[2]: https://public-inbox.org/git/20150320100429.GA17354@peff.net/
[3]: https://public-inbox.org/git/20180702002405.3042-1-sunshine@sunshineco.com/
[4]: https://public-inbox.org/git/xmqqwouljr5e.fsf@gitster-ct.c.googlers.com/

Eric Sunshine (10):
  t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  t/Makefile: add machinery to check correctness of chainlint.sed
  t/chainlint: add chainlint "basic" test cases
  t/chainlint: add chainlint "whitespace" test cases
  t/chainlint: add chainlint "one-liner" test cases
  t/chainlint: add chainlint "nested subshell" test cases
  t/chainlint: add chainlint "loop" and "conditional" test cases
  t/chainlint: add chainlint "cuddled" test cases
  t/chainlint: add chainlint "complex" test cases
  t/chainlint: add chainlint "specialized" test cases

 t/.gitignore                                  |   1 +
 t/Makefile                                    |  25 +-
 t/chainlint.sed                               | 346 ++++++++++++++++++
 t/chainlint/arithmetic-expansion.expect       |   9 +
 t/chainlint/arithmetic-expansion.test         |  11 +
 t/chainlint/bash-array.expect                 |  10 +
 t/chainlint/bash-array.test                   |  12 +
 t/chainlint/blank-line.expect                 |   4 +
 t/chainlint/blank-line.test                   |  10 +
 t/chainlint/block.expect                      |  12 +
 t/chainlint/block.test                        |  15 +
 t/chainlint/broken-chain.expect               |   6 +
 t/chainlint/broken-chain.test                 |   8 +
 t/chainlint/case.expect                       |  19 +
 t/chainlint/case.test                         |  23 ++
 .../close-nested-and-parent-together.expect   |   4 +
 .../close-nested-and-parent-together.test     |   3 +
 t/chainlint/close-subshell.expect             |  25 ++
 t/chainlint/close-subshell.test               |  27 ++
 t/chainlint/command-substitution.expect       |   9 +
 t/chainlint/command-substitution.test         |  11 +
 t/chainlint/comment.expect                    |   4 +
 t/chainlint/comment.test                      |  11 +
 t/chainlint/complex-if-in-cuddled-loop.expect |  10 +
 t/chainlint/complex-if-in-cuddled-loop.test   |  11 +
 t/chainlint/cuddled-if-then-else.expect       |   7 +
 t/chainlint/cuddled-if-then-else.test         |   7 +
 t/chainlint/cuddled-loop.expect               |   5 +
 t/chainlint/cuddled-loop.test                 |   7 +
 t/chainlint/cuddled.expect                    |  21 ++
 t/chainlint/cuddled.test                      |  23 ++
 t/chainlint/exit-loop.expect                  |  24 ++
 t/chainlint/exit-loop.test                    |  27 ++
 t/chainlint/exit-subshell.expect              |   5 +
 t/chainlint/exit-subshell.test                |   6 +
 t/chainlint/for-loop.expect                   |  11 +
 t/chainlint/for-loop.test                     |  19 +
 t/chainlint/here-doc.expect                   |   3 +
 t/chainlint/here-doc.test                     |  16 +
 t/chainlint/if-in-loop.expect                 |  12 +
 t/chainlint/if-in-loop.test                   |  15 +
 t/chainlint/if-then-else.expect               |  19 +
 t/chainlint/if-then-else.test                 |  28 ++
 t/chainlint/incomplete-line.expect            |   4 +
 t/chainlint/incomplete-line.test              |  12 +
 t/chainlint/inline-comment.expect             |   9 +
 t/chainlint/inline-comment.test               |  12 +
 t/chainlint/loop-in-if.expect                 |  12 +
 t/chainlint/loop-in-if.test                   |  15 +
 ...ti-line-nested-command-substitution.expect |   9 +
 ...ulti-line-nested-command-substitution.test |   9 +
 t/chainlint/multi-line-string.expect          |   9 +
 t/chainlint/multi-line-string.test            |  15 +
 t/chainlint/negated-one-liner.expect          |   5 +
 t/chainlint/negated-one-liner.test            |   7 +
 t/chainlint/nested-cuddled-subshell.expect    |  19 +
 t/chainlint/nested-cuddled-subshell.test      |  31 ++
 t/chainlint/nested-here-doc.expect            |   5 +
 t/chainlint/nested-here-doc.test              |  23 ++
 t/chainlint/nested-subshell-comment.expect    |  11 +
 t/chainlint/nested-subshell-comment.test      |  13 +
 t/chainlint/nested-subshell.expect            |  12 +
 t/chainlint/nested-subshell.test              |  14 +
 t/chainlint/one-liner.expect                  |   9 +
 t/chainlint/one-liner.test                    |  12 +
 t/chainlint/p4-filespec.expect                |   4 +
 t/chainlint/p4-filespec.test                  |   5 +
 t/chainlint/pipe.expect                       |   8 +
 t/chainlint/pipe.test                         |  12 +
 t/chainlint/semicolon.expect                  |  20 +
 t/chainlint/semicolon.test                    |  25 ++
 t/chainlint/subshell-here-doc.expect          |   5 +
 t/chainlint/subshell-here-doc.test            |  23 ++
 t/chainlint/subshell-one-liner.expect         |  14 +
 t/chainlint/subshell-one-liner.test           |  24 ++
 t/chainlint/while-loop.expect                 |  11 +
 t/chainlint/while-loop.test                   |  19 +
 t/test-lib.sh                                 |   3 +-
 78 files changed, 1316 insertions(+), 5 deletions(-)
 create mode 100644 t/chainlint.sed
 create mode 100644 t/chainlint/arithmetic-expansion.expect
 create mode 100644 t/chainlint/arithmetic-expansion.test
 create mode 100644 t/chainlint/bash-array.expect
 create mode 100644 t/chainlint/bash-array.test
 create mode 100644 t/chainlint/blank-line.expect
 create mode 100644 t/chainlint/blank-line.test
 create mode 100644 t/chainlint/block.expect
 create mode 100644 t/chainlint/block.test
 create mode 100644 t/chainlint/broken-chain.expect
 create mode 100644 t/chainlint/broken-chain.test
 create mode 100644 t/chainlint/case.expect
 create mode 100644 t/chainlint/case.test
 create mode 100644 t/chainlint/close-nested-and-parent-together.expect
 create mode 100644 t/chainlint/close-nested-and-parent-together.test
 create mode 100644 t/chainlint/close-subshell.expect
 create mode 100644 t/chainlint/close-subshell.test
 create mode 100644 t/chainlint/command-substitution.expect
 create mode 100644 t/chainlint/command-substitution.test
 create mode 100644 t/chainlint/comment.expect
 create mode 100644 t/chainlint/comment.test
 create mode 100644 t/chainlint/complex-if-in-cuddled-loop.expect
 create mode 100644 t/chainlint/complex-if-in-cuddled-loop.test
 create mode 100644 t/chainlint/cuddled-if-then-else.expect
 create mode 100644 t/chainlint/cuddled-if-then-else.test
 create mode 100644 t/chainlint/cuddled-loop.expect
 create mode 100644 t/chainlint/cuddled-loop.test
 create mode 100644 t/chainlint/cuddled.expect
 create mode 100644 t/chainlint/cuddled.test
 create mode 100644 t/chainlint/exit-loop.expect
 create mode 100644 t/chainlint/exit-loop.test
 create mode 100644 t/chainlint/exit-subshell.expect
 create mode 100644 t/chainlint/exit-subshell.test
 create mode 100644 t/chainlint/for-loop.expect
 create mode 100644 t/chainlint/for-loop.test
 create mode 100644 t/chainlint/here-doc.expect
 create mode 100644 t/chainlint/here-doc.test
 create mode 100644 t/chainlint/if-in-loop.expect
 create mode 100644 t/chainlint/if-in-loop.test
 create mode 100644 t/chainlint/if-then-else.expect
 create mode 100644 t/chainlint/if-then-else.test
 create mode 100644 t/chainlint/incomplete-line.expect
 create mode 100644 t/chainlint/incomplete-line.test
 create mode 100644 t/chainlint/inline-comment.expect
 create mode 100644 t/chainlint/inline-comment.test
 create mode 100644 t/chainlint/loop-in-if.expect
 create mode 100644 t/chainlint/loop-in-if.test
 create mode 100644 t/chainlint/multi-line-nested-command-substitution.expect
 create mode 100644 t/chainlint/multi-line-nested-command-substitution.test
 create mode 100644 t/chainlint/multi-line-string.expect
 create mode 100644 t/chainlint/multi-line-string.test
 create mode 100644 t/chainlint/negated-one-liner.expect
 create mode 100644 t/chainlint/negated-one-liner.test
 create mode 100644 t/chainlint/nested-cuddled-subshell.expect
 create mode 100644 t/chainlint/nested-cuddled-subshell.test
 create mode 100644 t/chainlint/nested-here-doc.expect
 create mode 100644 t/chainlint/nested-here-doc.test
 create mode 100644 t/chainlint/nested-subshell-comment.expect
 create mode 100644 t/chainlint/nested-subshell-comment.test
 create mode 100644 t/chainlint/nested-subshell.expect
 create mode 100644 t/chainlint/nested-subshell.test
 create mode 100644 t/chainlint/one-liner.expect
 create mode 100644 t/chainlint/one-liner.test
 create mode 100644 t/chainlint/p4-filespec.expect
 create mode 100644 t/chainlint/p4-filespec.test
 create mode 100644 t/chainlint/pipe.expect
 create mode 100644 t/chainlint/pipe.test
 create mode 100644 t/chainlint/semicolon.expect
 create mode 100644 t/chainlint/semicolon.test
 create mode 100644 t/chainlint/subshell-here-doc.expect
 create mode 100644 t/chainlint/subshell-here-doc.test
 create mode 100644 t/chainlint/subshell-one-liner.expect
 create mode 100644 t/chainlint/subshell-one-liner.test
 create mode 100644 t/chainlint/while-loop.expect
 create mode 100644 t/chainlint/while-loop.test

-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11 21:37     ` Junio C Hamano
                       ` (2 more replies)
  2018-07-11  6:46   ` [PATCH v2 02/10] t/Makefile: add machinery to check correctness of chainlint.sed Eric Sunshine
                     ` (9 subsequent siblings)
  10 siblings, 3 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option detects broken &&-chains by forcing the test to
exit early (as the very first step) with a sentinel value. If that
sentinel is the test's overall exit code, then the &&-chain is intact;
if not, then the chain is broken. Unfortunately, this detection does not
extend to &&-chains within subshells even when the subshell itself is
properly linked into the outer &&-chain.

Address this shortcoming by feeding the body of the test to a
lightweight "linter" which can peer inside subshells and identify broken
&&-chains by pure textual inspection. Although the linter does not
actually parse shell scripts, it has enough knowledge of shell syntax to
reliably deal with formatting style variations (as evolved over the
years) and to avoid being fooled by non-shell content (such as inside
here-docs and multi-line strings). It recognizes modern subshell
formatting:

    statement1 &&
    (
        statement2 &&
        statement3
    ) &&
    statement4

as well as old-style:

    statement1 &&
    (statement2 &&
     statement3) &&
    statement4

Heuristics are employed to properly identify the extent of a subshell
formatted in the old-style since a number of legitimate constructs may
superficially appear to close the subshell even though they don't. For
example, it understands that neither "x=$(command)" nor "case $x in *)"
end a subshell, despite the ")" at the end of line.

Due to limitations of the tool used ('sed') and its inherent
line-by-line processing, only subshells one level deep are handled, as
well as one-liner subshells one level below that. Subshells deeper than
that or multi-line subshells at level two are passed through as-is, thus
&&-chains in their bodies are not checked.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed | 346 ++++++++++++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh   |   3 +-
 2 files changed, 348 insertions(+), 1 deletion(-)
 create mode 100644 t/chainlint.sed

diff --git a/t/chainlint.sed b/t/chainlint.sed
new file mode 100644
index 0000000000..a0de8a3882
--- /dev/null
+++ b/t/chainlint.sed
@@ -0,0 +1,346 @@
+#------------------------------------------------------------------------------
+# Detect broken &&-chains in tests.
+#
+# At present, only &&-chains in subshells are examined by this linter;
+# top-level &&-chains are instead checked directly by the test framework. Like
+# the top-level &&-chain linter, the subshell linter (intentionally) does not
+# check &&-chains within {...} blocks.
+#
+# Checking for &&-chain breakage is done line-by-line by pure textual
+# inspection.
+#
+# Incomplete lines (those ending with "\") are stitched together with following
+# lines to simplify processing, particularly of "one-liner" statements.
+# Top-level here-docs are swallowed to avoid false positives within the
+# here-doc body, although the statement to which the here-doc is attached is
+# retained.
+#
+# Heuristics are used to detect end-of-subshell when the closing ")" is cuddled
+# with the final subshell statement on the same line:
+#
+#    (cd foo &&
+#        bar)
+#
+# in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
+# and "case $x in *)" as ending the subshell.
+#
+# Lines missing a final "&&" are flagged with "?!AMP?!", and lines which chain
+# commands with ";" internally rather than "&&" are flagged "?!SEMI?!". A line
+# may be flagged for both violations.
+#
+# Detection of a missing &&-link in a multi-line subshell is complicated by the
+# fact that the last statement before the closing ")" must not end with "&&".
+# Since processing is line-by-line, it is not known whether a missing "&&" is
+# legitimate or not until the _next_ line is seen. To accommodate this, within
+# multi-line subshells, each line is stored in sed's "hold" area until after
+# the next line is seen and processed. If the next line is a stand-alone ")",
+# then a missing "&&" on the previous line is legitimate; otherwise a missing
+# "&&" is a break in the &&-chain.
+#
+#    (
+#         cd foo &&
+#         bar
+#    )
+#
+# In practical terms, when "bar" is encountered, it is flagged with "?!AMP?!",
+# but when the stand-alone ")" line is seen which closes the subshell, the
+# "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
+# area) since the final statement of a subshell must not end with "&&". The
+# final line of a subshell may still break the &&-chain by using ";" internally
+# to chain commands together rather than "&&", so "?!SEMI?!" is never removed
+# from a line (even though "?!AMP?!" might be).
+#
+# Care is taken to recognize the last _statement_ of a multi-line subshell, not
+# necessarily the last textual _line_ within the subshell, since &&-chaining
+# applies to statements, not to lines. Consequently, blank lines, comment
+# lines, and here-docs are swallowed (but not the command to which the here-doc
+# is attached), leaving the last statement in the "hold" area, not the last
+# line, thus simplifying &&-link checking.
+#
+# The final statement before "done" in for- and while-loops, and before "elif",
+# "else", and "fi" in if-then-else likewise must not end with "&&", thus
+# receives similar treatment.
+#
+# To facilitate regression testing (and manual debugging), a ">" annotation is
+# applied to the line containing ")" which closes a subshell, ">>" to a line
+# closing a nested subshell, and ">>>" to a line closing both at once. This
+# makes it easy to detect whether the heuristics correctly identify
+# end-of-subshell.
+#------------------------------------------------------------------------------
+
+# incomplete line -- slurp up next line
+:squash
+/\\$/ {
+      N
+      s/\\\n//
+      bsquash
+}
+
+# here-doc -- swallow it to avoid false hits within its body (but keep the
+# command to which it was attached)
+/<<[ 	]*[-\\]*EOF[ 	]*/ {
+	s/[ 	]*<<[ 	]*[-\\]*EOF//
+	h
+	:hereslurp
+	N
+	s/.*\n//
+	/^[ 	]*EOF[ 	]*$/!bhereslurp
+	x
+}
+
+# one-liner "(...) &&"
+/^[ 	]*!*[ 	]*(..*)[ 	]*&&[ 	]*$/boneline
+
+# same as above but without trailing "&&"
+/^[ 	]*!*[ 	]*(..*)[ 	]*$/boneline
+
+# one-liner "(...) >x" (or "2>x" or "<x" or "|x" or "&"
+/^[ 	]*!*[ 	]*(..*)[ 	]*[0-9]*[<>|&]/boneline
+
+# multi-line "(...\n...)"
+/^[ 	]*(/bsubshell
+
+# innocuous line -- print it and advance to next line
+b
+
+# found one-liner "(...)" -- mark suspect if it uses ";" internally rather than
+# "&&" (but not ";" in a string)
+:oneline
+/;/{
+	/"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+}
+b
+
+:subshell
+# bare "(" line?
+/^[ 	]*([	]*$/ {
+	# stash for later printing
+	h
+	bnextline
+}
+# "(..." line -- split off and stash "(", then process "..." as its own line
+x
+s/.*/(/
+x
+s/(//
+bslurp
+
+:nextline
+N
+s/.*\n//
+
+:slurp
+# incomplete line "...\"
+/\\$/bincomplete
+# multi-line quoted string "...\n..."
+/^[^"]*"[^"]*$/bdqstring
+# multi-line quoted string '...\n...' (but not contraction in string "it's so")
+/^[^']*'[^']*$/{
+	/"[^'"]*'[^'"]*"/!bsqstring
+}
+# here-doc -- swallow it
+/<<[ 	]*[-\\]*EOF/bheredoc
+/<<[ 	]*[-\\]*EOT/bheredoc
+/<<[ 	]*[-\\]*INPUT_END/bheredoc
+# comment or empty line -- discard since final non-comment, non-empty line
+# before closing ")", "done", "elsif", "else", or "fi" will need to be
+# re-visited to drop "suspect" marking since final line of those constructs
+# legitimately lacks "&&", so "suspect" mark must be removed
+/^[ 	]*#/bnextline
+/^[ 	]*$/bnextline
+# in-line comment -- strip it (but not "#" in a string, Bash ${#...} array
+# length, or Perforce "//depot/path#42" revision in filespec)
+/[ 	]#/{
+	/"[^"]*#[^"]*"/!s/[ 	]#.*$//
+}
+# one-liner "case ... esac"
+/^[ 	]*case[ 	]*..*esac/bcheckchain
+# multi-line "case ... esac"
+/^[ 	]*case[ 	]..*[ 	]in/bcase
+# multi-line "for ... done" or "while ... done"
+/^[ 	]*for[ 	]..*[ 	]in/bcontinue
+/^[ 	]*while[ 	]/bcontinue
+/^[ 	]*do[ 	]/bcontinue
+/^[ 	]*do[ 	]*$/bcontinue
+/;[ 	]*do/bcontinue
+/^[ 	]*done[ 	]*&&[ 	]*$/bdone
+/^[ 	]*done[ 	]*$/bdone
+/^[ 	]*done[ 	]*[<>|]/bdone
+/^[ 	]*done[ 	]*)/bdone
+/||[ 	]*exit[ 	]/bcontinue
+/||[ 	]*exit[ 	]*$/bcontinue
+# multi-line "if...elsif...else...fi"
+/^[ 	]*if[ 	]/bcontinue
+/^[ 	]*then[ 	]/bcontinue
+/^[ 	]*then[ 	]*$/bcontinue
+/;[ 	]*then/bcontinue
+/^[ 	]*elif[ 	]/belse
+/^[ 	]*elif[ 	]*$/belse
+/^[ 	]*else[ 	]/belse
+/^[ 	]*else[ 	]*$/belse
+/^[ 	]*fi[ 	]*&&[ 	]*$/bdone
+/^[ 	]*fi[ 	]*$/bdone
+/^[ 	]*fi[ 	]*[<>|]/bdone
+/^[ 	]*fi[ 	]*)/bdone
+# nested one-liner "(...) &&"
+/^[ 	]*(.*)[ 	]*&&[ 	]*$/bcheckchain
+# nested one-liner "(...)"
+/^[ 	]*(.*)[ 	]*$/bcheckchain
+# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
+/^[ 	]*(.*)[ 	]*[0-9]*[<>|]/bcheckchain
+# nested multi-line "(...\n...)"
+/^[ 	]*(/bnest
+# multi-line "{...\n...}"
+/^[ 	]*{/bblock
+# closing ")" on own line -- exit subshell
+/^[ 	]*)/bclosesolo
+# "$((...))" -- arithmetic expansion; not closing ")"
+/\$(([^)][^)]*))[^)]*$/bcheckchain
+# "$(...)" -- command substitution; not closing ")"
+/\$([^)][^)]*)[^)]*$/bcheckchain
+# multi-line "$(...\n...)" -- command substitution; treat as nested subshell
+/\$([ 	     ]*$/bnest
+# "=(...)" -- Bash array assignment; not closing ")"
+/=(/bcheckchain
+# closing "...) &&"
+/)[ 	]*&&[ 	]*$/bclose
+# closing "...)"
+/)[ 	]*$/bclose
+# closing "...) >x" (or "2>x" or "<x" or "|x")
+/)[ 	]*[<>|]/bclose
+:checkchain
+# mark suspect if line uses ";" internally rather than "&&" (but not ";" in a
+# string and not ";;" in one-liner "case...esac")
+/;/{
+	/;;/!{
+		/"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+	}
+}
+# line ends with pipe "...|" -- valid; not missing "&&"
+/|[ 	]*$/bcontinue
+# missing end-of-line "&&" -- mark suspect
+/&&[ 	]*$/!s/^/?!AMP?!/
+:continue
+# retrieve and print previous line
+x
+n
+bslurp
+
+# found incomplete line "...\" -- slurp up next line
+:incomplete
+N
+s/\\\n//
+bslurp
+
+# found multi-line double-quoted string "...\n..." -- slurp until end of string
+:dqstring
+s/"//g
+N
+s/\n//
+/"/!bdqstring
+bcheckchain
+
+# found multi-line single-quoted string '...\n...' -- slurp until end of string
+:sqstring
+s/'//g
+N
+s/\n//
+/'/!bsqstring
+bcheckchain
+
+# found here-doc -- swallow it to avoid false hits within its body (but keep
+# the command to which it was attached); take care to handle here-docs nested
+# within here-docs by only recognizing closing tag matching outer here-doc
+# opening tag
+:heredoc
+/EOF/{ s/[ 	]*<<[ 	]*[-\\]*EOF//; s/^/EOF/; }
+/EOT/{ s/[ 	]*<<[ 	]*[-\\]*EOT//; s/^/EOT/; }
+/INPUT_END/{ s/[ 	]*<<[ 	]*[-\\]*INPUT_END//; s/^/INPUT_END/; }
+:hereslurpsub
+N
+/^EOF.*\n[ 	]*EOF[ 	]*$/bhereclose
+/^EOT.*\n[ 	]*EOT[ 	]*$/bhereclose
+/^INPUT_END.*\n[ 	]*INPUT_END[ 	]*$/bhereclose
+bhereslurpsub
+:hereclose
+s/^EOF//
+s/^EOT//
+s/^INPUT_END//
+s/\n.*$//
+bcheckchain
+
+# found "case ... in" -- pass through untouched
+:case
+x
+n
+/^[ 	]*esac/bslurp
+bcase
+
+# found "else" or "elif" -- drop "suspect" from final line before "else" since
+# that line legitimately lacks "&&"
+:else
+x
+s/?!AMP?!//
+x
+bcontinue
+
+# found "done" closing for-loop or while-loop, or "fi" closing if-then -- drop
+# "suspect" from final contained line since that line legitimately lacks "&&"
+:done
+x
+s/?!AMP?!//
+x
+# is 'done' or 'fi' cuddled with ")" to close subshell?
+/done.*)/bclose
+/fi.*)/bclose
+bcheckchain
+
+# found nested multi-line "(...\n...)" -- pass through untouched
+:nest
+x
+:nestslurp
+n
+# closing ")" on own line -- stop nested slurp
+/^[ 	]*)/bnestclose
+# comment -- not closing ")" if in comment
+/^[ 	]*#/bnestcontinue
+# "$((...))" -- arithmetic expansion; not closing ")"
+/\$(([^)][^)]*))[^)]*$/bnestcontinue
+# "$(...)" -- command substitution; not closing ")"
+/\$([^)][^)]*)[^)]*$/bnestcontinue
+# closing "...)" -- stop nested slurp
+/)/bnestclose
+:nestcontinue
+x
+bnestslurp
+:nestclose
+s/^/>>/
+# is it "))" which closes nested and parent subshells?
+/)[ 	]*)/bslurp
+bcheckchain
+
+# found multi-line "{...\n...}" block -- pass through untouched
+:block
+x
+n
+# closing "}" -- stop block slurp
+/}/bcheckchain
+bblock
+
+# found closing ")" on own line -- drop "suspect" from final line of subshell
+# since that line legitimately lacks "&&" and exit subshell loop
+:closesolo
+x
+s/?!AMP?!//
+p
+x
+s/^/>/
+b
+
+# found closing "...)" -- exit subshell loop
+:close
+x
+p
+x
+s/^/>/
+b
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 28315706be..78f7097746 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -675,7 +675,8 @@ test_run_ () {
 		trace=
 		# 117 is magic because it is unlikely to match the exit
 		# code of other programs
-		if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+		if $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!') ||
+			test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
 		then
 			error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
 		fi
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 02/10] t/Makefile: add machinery to check correctness of chainlint.sed
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
@ 2018-07-11  6:46   ` Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 03/10] t/chainlint: add chainlint "basic" test cases Eric Sunshine
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection.
Although the heuristics work well, they are still best-guesses and
future changes could accidentally break assumptions upon which they are
based. To protect against this possibility, tests checking correctness
of the linter itself will be added. As preparation, add a new makefile
"check-chainlint" target and associated machinery.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/.gitignore |  1 +
 t/Makefile   | 25 +++++++++++++++++++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/t/.gitignore b/t/.gitignore
index 4e731dc1e3..348715f0e4 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,3 +1,4 @@
 /trash directory*
 /test-results
 /.prove
+/chainlinttmp
diff --git a/t/Makefile b/t/Makefile
index 96317a35f4..c83fd18861 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -18,8 +18,10 @@ TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
 TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+CHAINLINTTMP = $(TEST_OUTPUT_DIRECTORY)/chainlinttmp
 else
 TEST_RESULTS_DIRECTORY = test-results
+CHAINLINTTMP = chainlinttmp
 endif
 
 # Shell quote;
@@ -27,14 +29,17 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
+CHAINLINTTMP_SQ = $(subst ','\'',$(CHAINLINTTMP))
 
 T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
 TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
 THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
+CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
+CHAINLINT = sed -f chainlint.sed
 
 all: $(DEFAULT_TEST_TARGET)
 
-test: pre-clean $(TEST_LINT)
+test: pre-clean check-chainlint $(TEST_LINT)
 	$(MAKE) aggregate-results-and-cleanup
 
 failed:
@@ -43,7 +48,7 @@ failed:
 		sed -n 's/\.counts$$/.sh/p') && \
 	test -z "$$failed" || $(MAKE) $$failed
 
-prove: pre-clean $(TEST_LINT)
+prove: pre-clean check-chainlint $(TEST_LINT)
 	@echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
 	$(MAKE) clean-except-prove-cache
 
@@ -53,13 +58,25 @@ $(T):
 pre-clean:
 	$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
-clean-except-prove-cache:
+clean-except-prove-cache: clean-chainlint
 	$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
 	$(RM) -r valgrind/bin
 
 clean: clean-except-prove-cache
 	$(RM) .prove
 
+clean-chainlint:
+	$(RM) -r '$(CHAINLINTTMP_SQ)'
+
+check-chainlint:
+	@mkdir -p '$(CHAINLINTTMP_SQ)' && \
+	err=0 && \
+	for i in $(CHAINLINTTESTS); do \
+		$(CHAINLINT) <chainlint/$$i.test | \
+		sed -e '/^# LINT: /d' >'$(CHAINLINTTMP_SQ)'/$$i.actual && \
+		diff -u chainlint/$$i.expect '$(CHAINLINTTMP_SQ)'/$$i.actual || err=1; \
+	done && exit $$err
+
 test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
 	test-lint-filenames
 
@@ -102,4 +119,4 @@ valgrind:
 perf:
 	$(MAKE) -C perf/ all
 
-.PHONY: pre-clean $(T) aggregate-results clean valgrind perf
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf check-chainlint clean-chainlint
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 03/10] t/chainlint: add chainlint "basic" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 02/10] t/Makefile: add machinery to check correctness of chainlint.sed Eric Sunshine
@ 2018-07-11  6:46   ` Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 04/10] t/chainlint: add chainlint "whitespace" " Eric Sunshine
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/arithmetic-expansion.expect |  9 +++++++++
 t/chainlint/arithmetic-expansion.test   | 11 ++++++++++
 t/chainlint/broken-chain.expect         |  6 ++++++
 t/chainlint/broken-chain.test           |  8 ++++++++
 t/chainlint/close-subshell.expect       | 25 +++++++++++++++++++++++
 t/chainlint/close-subshell.test         | 27 +++++++++++++++++++++++++
 t/chainlint/command-substitution.expect |  9 +++++++++
 t/chainlint/command-substitution.test   | 11 ++++++++++
 t/chainlint/exit-subshell.expect        |  5 +++++
 t/chainlint/exit-subshell.test          |  6 ++++++
 t/chainlint/multi-line-string.expect    |  9 +++++++++
 t/chainlint/multi-line-string.test      | 15 ++++++++++++++
 t/chainlint/pipe.expect                 |  8 ++++++++
 t/chainlint/pipe.test                   | 12 +++++++++++
 t/chainlint/semicolon.expect            | 20 ++++++++++++++++++
 t/chainlint/semicolon.test              | 25 +++++++++++++++++++++++
 16 files changed, 206 insertions(+)
 create mode 100644 t/chainlint/arithmetic-expansion.expect
 create mode 100644 t/chainlint/arithmetic-expansion.test
 create mode 100644 t/chainlint/broken-chain.expect
 create mode 100644 t/chainlint/broken-chain.test
 create mode 100644 t/chainlint/close-subshell.expect
 create mode 100644 t/chainlint/close-subshell.test
 create mode 100644 t/chainlint/command-substitution.expect
 create mode 100644 t/chainlint/command-substitution.test
 create mode 100644 t/chainlint/exit-subshell.expect
 create mode 100644 t/chainlint/exit-subshell.test
 create mode 100644 t/chainlint/multi-line-string.expect
 create mode 100644 t/chainlint/multi-line-string.test
 create mode 100644 t/chainlint/pipe.expect
 create mode 100644 t/chainlint/pipe.test
 create mode 100644 t/chainlint/semicolon.expect
 create mode 100644 t/chainlint/semicolon.test

diff --git a/t/chainlint/arithmetic-expansion.expect b/t/chainlint/arithmetic-expansion.expect
new file mode 100644
index 0000000000..09457d3196
--- /dev/null
+++ b/t/chainlint/arithmetic-expansion.expect
@@ -0,0 +1,9 @@
+(
+	foo &&
+	bar=$((42 + 1)) &&
+	baz
+>) &&
+(
+?!AMP?!	bar=$((42 + 1))
+	baz
+>)
diff --git a/t/chainlint/arithmetic-expansion.test b/t/chainlint/arithmetic-expansion.test
new file mode 100644
index 0000000000..16206960d8
--- /dev/null
+++ b/t/chainlint/arithmetic-expansion.test
@@ -0,0 +1,11 @@
+(
+	foo &&
+# LINT: closing ")" of $((...)) not misinterpreted as subshell-closing ")"
+	bar=$((42 + 1)) &&
+	baz
+) &&
+(
+# LINT: missing "&&" on $((...))
+	bar=$((42 + 1))
+	baz
+)
diff --git a/t/chainlint/broken-chain.expect b/t/chainlint/broken-chain.expect
new file mode 100644
index 0000000000..55b0f42a53
--- /dev/null
+++ b/t/chainlint/broken-chain.expect
@@ -0,0 +1,6 @@
+(
+	foo &&
+?!AMP?!	bar
+	baz &&
+	wop
+>)
diff --git a/t/chainlint/broken-chain.test b/t/chainlint/broken-chain.test
new file mode 100644
index 0000000000..3cc67b65d0
--- /dev/null
+++ b/t/chainlint/broken-chain.test
@@ -0,0 +1,8 @@
+(
+	foo &&
+# LINT: missing "&&" from 'bar'
+	bar
+	baz &&
+# LINT: final statement before closing ")" legitimately lacks "&&"
+	wop
+)
diff --git a/t/chainlint/close-subshell.expect b/t/chainlint/close-subshell.expect
new file mode 100644
index 0000000000..184688718a
--- /dev/null
+++ b/t/chainlint/close-subshell.expect
@@ -0,0 +1,25 @@
+(
+	foo
+>) &&
+(
+	bar
+>) >out &&
+(
+	baz
+>) 2>err &&
+(
+	boo
+>) <input &&
+(
+	bip
+>) | wuzzle &&
+(
+	bop
+>) | fazz 	fozz &&
+(
+	bup
+>) |
+fuzzle &&
+(
+	yop
+>)
diff --git a/t/chainlint/close-subshell.test b/t/chainlint/close-subshell.test
new file mode 100644
index 0000000000..508ca447fd
--- /dev/null
+++ b/t/chainlint/close-subshell.test
@@ -0,0 +1,27 @@
+# LINT: closing ")" with various decorations ("&&", ">", "|", etc.)
+(
+	foo
+) &&
+(
+	bar
+) >out &&
+(
+	baz
+) 2>err &&
+(
+	boo
+) <input &&
+(
+	bip
+) | wuzzle &&
+(
+	bop
+) | fazz \
+	fozz &&
+(
+	bup
+) |
+fuzzle &&
+(
+	yop
+)
diff --git a/t/chainlint/command-substitution.expect b/t/chainlint/command-substitution.expect
new file mode 100644
index 0000000000..ad4118e537
--- /dev/null
+++ b/t/chainlint/command-substitution.expect
@@ -0,0 +1,9 @@
+(
+	foo &&
+	bar=$(gobble) &&
+	baz
+>) &&
+(
+?!AMP?!	bar=$(gobble blocks)
+	baz
+>)
diff --git a/t/chainlint/command-substitution.test b/t/chainlint/command-substitution.test
new file mode 100644
index 0000000000..3bbb002a4c
--- /dev/null
+++ b/t/chainlint/command-substitution.test
@@ -0,0 +1,11 @@
+(
+	foo &&
+# LINT: closing ")" of $(...) not misinterpreted as subshell-closing ")"
+	bar=$(gobble) &&
+	baz
+) &&
+(
+# LINT: missing "&&" on $(...)
+	bar=$(gobble blocks)
+	baz
+)
diff --git a/t/chainlint/exit-subshell.expect b/t/chainlint/exit-subshell.expect
new file mode 100644
index 0000000000..bf78454f74
--- /dev/null
+++ b/t/chainlint/exit-subshell.expect
@@ -0,0 +1,5 @@
+(
+	foo || exit 1
+	bar &&
+	baz
+>)
diff --git a/t/chainlint/exit-subshell.test b/t/chainlint/exit-subshell.test
new file mode 100644
index 0000000000..4e6ab69b88
--- /dev/null
+++ b/t/chainlint/exit-subshell.test
@@ -0,0 +1,6 @@
+(
+# LINT: "|| exit {n}" valid subshell escape without hurting &&-chain
+	foo || exit 1
+	bar &&
+	baz
+)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
new file mode 100644
index 0000000000..8334c4cc8e
--- /dev/null
+++ b/t/chainlint/multi-line-string.expect
@@ -0,0 +1,9 @@
+(
+	x=line 1		line 2		line 3" &&
+?!AMP?!	y=line 1		line2'
+	foobar
+>) &&
+(
+	echo "there's nothing to see here" &&
+	exit
+>)
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
new file mode 100644
index 0000000000..14cb44d51c
--- /dev/null
+++ b/t/chainlint/multi-line-string.test
@@ -0,0 +1,15 @@
+(
+	x="line 1
+		line 2
+		line 3" &&
+# LINT: missing "&&" on assignment
+	y='line 1
+		line2'
+	foobar
+) &&
+(
+# LINT: apostrophe (in a contraction) within string not misinterpreted as
+# LINT: starting multi-line single-quoted string
+	echo "there's nothing to see here" &&
+	exit
+)
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
new file mode 100644
index 0000000000..211b901dbc
--- /dev/null
+++ b/t/chainlint/pipe.expect
@@ -0,0 +1,8 @@
+(
+	foo |
+	bar |
+	baz &&
+	fish |
+?!AMP?!	cow
+	sunder
+>)
diff --git a/t/chainlint/pipe.test b/t/chainlint/pipe.test
new file mode 100644
index 0000000000..e6af4de916
--- /dev/null
+++ b/t/chainlint/pipe.test
@@ -0,0 +1,12 @@
+(
+# LINT: no "&&" needed on line ending with "|"
+	foo |
+	bar |
+	baz &&
+
+# LINT: final line of pipe sequence ('cow') lacking "&&"
+	fish |
+	cow
+
+	sunder
+)
diff --git a/t/chainlint/semicolon.expect b/t/chainlint/semicolon.expect
new file mode 100644
index 0000000000..1d79384606
--- /dev/null
+++ b/t/chainlint/semicolon.expect
@@ -0,0 +1,20 @@
+(
+?!AMP?!?!SEMI?!	cat foo ; echo bar
+?!SEMI?!	cat foo ; echo bar
+>) &&
+(
+?!SEMI?!	cat foo ; echo bar &&
+?!SEMI?!	cat foo ; echo bar
+>) &&
+(
+	echo "foo; bar" &&
+?!SEMI?!	cat foo; echo bar
+>) &&
+(
+?!SEMI?!	foo;
+>) &&
+(
+cd foo &&
+	for i in a b c; do
+?!SEMI?!		echo;
+>	done)
diff --git a/t/chainlint/semicolon.test b/t/chainlint/semicolon.test
new file mode 100644
index 0000000000..d82c8ebbc0
--- /dev/null
+++ b/t/chainlint/semicolon.test
@@ -0,0 +1,25 @@
+(
+# LINT: missing internal "&&" and ending "&&"
+	cat foo ; echo bar
+# LINT: final statement before ")" only missing internal "&&"
+	cat foo ; echo bar
+) &&
+(
+# LINT: missing internal "&&"
+	cat foo ; echo bar &&
+	cat foo ; echo bar
+) &&
+(
+# LINT: not fooled by semicolon in string
+	echo "foo; bar" &&
+	cat foo; echo bar
+) &&
+(
+# LINT: unnecessary terminating semicolon
+	foo;
+) &&
+(cd foo &&
+	for i in a b c; do
+# LINT: unnecessary terminating semicolon
+		echo;
+	done)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 04/10] t/chainlint: add chainlint "whitespace" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (2 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 03/10] t/chainlint: add chainlint "basic" test cases Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 05/10] t/chainlint: add chainlint "one-liner" " Eric Sunshine
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/blank-line.expect        |  4 ++++
 t/chainlint/blank-line.test          | 10 ++++++++++
 t/chainlint/comment.expect           |  4 ++++
 t/chainlint/comment.test             | 11 +++++++++++
 t/chainlint/here-doc.expect          |  3 +++
 t/chainlint/here-doc.test            | 16 ++++++++++++++++
 t/chainlint/incomplete-line.expect   |  4 ++++
 t/chainlint/incomplete-line.test     | 12 ++++++++++++
 t/chainlint/inline-comment.expect    |  9 +++++++++
 t/chainlint/inline-comment.test      | 12 ++++++++++++
 t/chainlint/subshell-here-doc.expect |  5 +++++
 t/chainlint/subshell-here-doc.test   | 23 +++++++++++++++++++++++
 12 files changed, 113 insertions(+)
 create mode 100644 t/chainlint/blank-line.expect
 create mode 100644 t/chainlint/blank-line.test
 create mode 100644 t/chainlint/comment.expect
 create mode 100644 t/chainlint/comment.test
 create mode 100644 t/chainlint/here-doc.expect
 create mode 100644 t/chainlint/here-doc.test
 create mode 100644 t/chainlint/incomplete-line.expect
 create mode 100644 t/chainlint/incomplete-line.test
 create mode 100644 t/chainlint/inline-comment.expect
 create mode 100644 t/chainlint/inline-comment.test
 create mode 100644 t/chainlint/subshell-here-doc.expect
 create mode 100644 t/chainlint/subshell-here-doc.test

diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
new file mode 100644
index 0000000000..3be939ed38
--- /dev/null
+++ b/t/chainlint/blank-line.expect
@@ -0,0 +1,4 @@
+(
+	nothing &&
+	something
+>)
diff --git a/t/chainlint/blank-line.test b/t/chainlint/blank-line.test
new file mode 100644
index 0000000000..f6dd14302b
--- /dev/null
+++ b/t/chainlint/blank-line.test
@@ -0,0 +1,10 @@
+(
+
+	nothing &&
+
+	something
+# LINT: swallow blank lines since final _statement_ before subshell end is
+# LINT: significant to "&&"-check, not final _line_ (which might be blank)
+
+
+)
diff --git a/t/chainlint/comment.expect b/t/chainlint/comment.expect
new file mode 100644
index 0000000000..3be939ed38
--- /dev/null
+++ b/t/chainlint/comment.expect
@@ -0,0 +1,4 @@
+(
+	nothing &&
+	something
+>)
diff --git a/t/chainlint/comment.test b/t/chainlint/comment.test
new file mode 100644
index 0000000000..113c0c466f
--- /dev/null
+++ b/t/chainlint/comment.test
@@ -0,0 +1,11 @@
+(
+# LINT: swallow comment lines
+	# comment 1
+	nothing &&
+	# comment 2
+	something
+# LINT: swallow comment lines since final _statement_ before subshell end is
+# LINT: significant to "&&"-check, not final _line_ (which might be comment)
+	# comment 3
+	# comment 4
+)
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
new file mode 100644
index 0000000000..2328fe7753
--- /dev/null
+++ b/t/chainlint/here-doc.expect
@@ -0,0 +1,3 @@
+boodle wobba        gorgo snoot        wafta snurb &&
+
+horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
new file mode 100644
index 0000000000..bd36f6e1d3
--- /dev/null
+++ b/t/chainlint/here-doc.test
@@ -0,0 +1,16 @@
+# LINT: stitch together incomplete \-ending lines
+# LINT: swallow here-doc to avoid false positives in content
+boodle wobba \
+       gorgo snoot \
+       wafta snurb <<EOF &&
+quoth the raven,
+nevermore...
+EOF
+
+# LINT: swallow here-doc (EOF is last line of test)
+horticulture <<\EOF
+gomez
+morticia
+wednesday
+pugsly
+EOF
diff --git a/t/chainlint/incomplete-line.expect b/t/chainlint/incomplete-line.expect
new file mode 100644
index 0000000000..2f3ebabdc2
--- /dev/null
+++ b/t/chainlint/incomplete-line.expect
@@ -0,0 +1,4 @@
+line 1 line 2 line 3 line 4 &&
+(
+	line 5 	line 6 	line 7 	line 8
+>)
diff --git a/t/chainlint/incomplete-line.test b/t/chainlint/incomplete-line.test
new file mode 100644
index 0000000000..d856658083
--- /dev/null
+++ b/t/chainlint/incomplete-line.test
@@ -0,0 +1,12 @@
+# LINT: stitch together all incomplete \-ending lines
+line 1 \
+line 2 \
+line 3 \
+line 4 &&
+(
+# LINT: stitch together all incomplete \-ending lines (subshell)
+	line 5 \
+	line 6 \
+	line 7 \
+	line 8
+)
diff --git a/t/chainlint/inline-comment.expect b/t/chainlint/inline-comment.expect
new file mode 100644
index 0000000000..fc9f250ac4
--- /dev/null
+++ b/t/chainlint/inline-comment.expect
@@ -0,0 +1,9 @@
+(
+	foobar &&
+?!AMP?!	barfoo
+	flibble "not a # comment"
+>) &&
+
+(
+cd foo &&
+>	flibble "not a # comment")
diff --git a/t/chainlint/inline-comment.test b/t/chainlint/inline-comment.test
new file mode 100644
index 0000000000..8f26856e77
--- /dev/null
+++ b/t/chainlint/inline-comment.test
@@ -0,0 +1,12 @@
+(
+# LINT: swallow inline comment (leaving command intact)
+	foobar && # comment 1
+# LINT: mispositioned "&&" (correctly) swallowed with comment
+	barfoo # wrong position for &&
+# LINT: "#" in string not misinterpreted as comment
+	flibble "not a # comment"
+) &&
+
+# LINT: "#" in string in cuddled subshell not misinterpreted as comment
+(cd foo &&
+	flibble "not a # comment")
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
new file mode 100644
index 0000000000..19d5aff233
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.expect
@@ -0,0 +1,5 @@
+(
+	echo wobba 	       gorgo snoot 	       wafta snurb &&
+?!AMP?!	cat >bip
+	echo >bop
+>)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
new file mode 100644
index 0000000000..9c3564c247
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.test
@@ -0,0 +1,23 @@
+(
+# LINT: stitch together incomplete \-ending lines
+# LINT: swallow here-doc to avoid false positives in content
+	echo wobba \
+	       gorgo snoot \
+	       wafta snurb <<-EOF &&
+	quoth the raven,
+	nevermore...
+	EOF
+
+# LINT: missing "&&" on 'cat'
+	cat <<EOF >bip
+	fish fly high
+	EOF
+
+# LINT: swallow here-doc (EOF is last line of subshell)
+	echo <<-\EOF >bop
+	gomez
+	morticia
+	wednesday
+	pugsly
+	EOF
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 05/10] t/chainlint: add chainlint "one-liner" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (3 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 04/10] t/chainlint: add chainlint "whitespace" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 06/10] t/chainlint: add chainlint "nested subshell" " Eric Sunshine
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/negated-one-liner.expect  |  5 +++++
 t/chainlint/negated-one-liner.test    |  7 +++++++
 t/chainlint/one-liner.expect          |  9 +++++++++
 t/chainlint/one-liner.test            | 12 ++++++++++++
 t/chainlint/subshell-one-liner.expect | 14 ++++++++++++++
 t/chainlint/subshell-one-liner.test   | 24 ++++++++++++++++++++++++
 6 files changed, 71 insertions(+)
 create mode 100644 t/chainlint/negated-one-liner.expect
 create mode 100644 t/chainlint/negated-one-liner.test
 create mode 100644 t/chainlint/one-liner.expect
 create mode 100644 t/chainlint/one-liner.test
 create mode 100644 t/chainlint/subshell-one-liner.expect
 create mode 100644 t/chainlint/subshell-one-liner.test

diff --git a/t/chainlint/negated-one-liner.expect b/t/chainlint/negated-one-liner.expect
new file mode 100644
index 0000000000..cf18429d03
--- /dev/null
+++ b/t/chainlint/negated-one-liner.expect
@@ -0,0 +1,5 @@
+! (foo && bar) &&
+! (foo && bar) >baz &&
+
+?!SEMI?!! (foo; bar) &&
+?!SEMI?!! (foo; bar) >baz
diff --git a/t/chainlint/negated-one-liner.test b/t/chainlint/negated-one-liner.test
new file mode 100644
index 0000000000..c9598e9153
--- /dev/null
+++ b/t/chainlint/negated-one-liner.test
@@ -0,0 +1,7 @@
+# LINT: top-level one-liner subshell
+! (foo && bar) &&
+! (foo && bar) >baz &&
+
+# LINT: top-level one-liner subshell missing internal "&&"
+! (foo; bar) &&
+! (foo; bar) >baz
diff --git a/t/chainlint/one-liner.expect b/t/chainlint/one-liner.expect
new file mode 100644
index 0000000000..237f227349
--- /dev/null
+++ b/t/chainlint/one-liner.expect
@@ -0,0 +1,9 @@
+(foo && bar) &&
+(foo && bar) |
+(foo && bar) >baz &&
+
+?!SEMI?!(foo; bar) &&
+?!SEMI?!(foo; bar) |
+?!SEMI?!(foo; bar) >baz
+
+(foo "bar; baz")
diff --git a/t/chainlint/one-liner.test b/t/chainlint/one-liner.test
new file mode 100644
index 0000000000..ec9acb9825
--- /dev/null
+++ b/t/chainlint/one-liner.test
@@ -0,0 +1,12 @@
+# LINT: top-level one-liner subshell
+(foo && bar) &&
+(foo && bar) |
+(foo && bar) >baz &&
+
+# LINT: top-level one-liner subshell missing internal "&&"
+(foo; bar) &&
+(foo; bar) |
+(foo; bar) >baz
+
+# LINT: ";" in string not misinterpreted as broken &&-chain
+(foo "bar; baz")
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
new file mode 100644
index 0000000000..51162821d7
--- /dev/null
+++ b/t/chainlint/subshell-one-liner.expect
@@ -0,0 +1,14 @@
+(
+	(foo && bar) &&
+	(foo && bar) |
+	(foo && bar) >baz &&
+?!SEMI?!	(foo; bar) &&
+?!SEMI?!	(foo; bar) |
+?!SEMI?!	(foo; bar) >baz &&
+	(foo || exit 1) &&
+	(foo || exit 1) |
+	(foo || exit 1) >baz &&
+?!AMP?!	(foo && bar)
+?!AMP?!?!SEMI?!	(foo && bar; baz)
+	foobar
+>)
diff --git a/t/chainlint/subshell-one-liner.test b/t/chainlint/subshell-one-liner.test
new file mode 100644
index 0000000000..37fa643c20
--- /dev/null
+++ b/t/chainlint/subshell-one-liner.test
@@ -0,0 +1,24 @@
+(
+# LINT: nested one-liner subshell
+	(foo && bar) &&
+	(foo && bar) |
+	(foo && bar) >baz &&
+
+# LINT: nested one-liner subshell missing internal "&&"
+	(foo; bar) &&
+	(foo; bar) |
+	(foo; bar) >baz &&
+
+# LINT: nested one-liner subshell with "|| exit"
+	(foo || exit 1) &&
+	(foo || exit 1) |
+	(foo || exit 1) >baz &&
+
+# LINT: nested one-liner subshell lacking ending "&&"
+	(foo && bar)
+
+# LINT: nested one-liner subshell missing internal "&&" and lacking ending "&&"
+	(foo && bar; baz)
+
+	foobar
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 06/10] t/chainlint: add chainlint "nested subshell" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (4 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 05/10] t/chainlint: add chainlint "one-liner" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 07/10] t/chainlint: add chainlint "loop" and "conditional" " Eric Sunshine
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/block.expect                      | 12 +++++++
 t/chainlint/block.test                        | 15 +++++++++
 ...ti-line-nested-command-substitution.expect |  9 ++++++
 ...ulti-line-nested-command-substitution.test |  9 ++++++
 t/chainlint/nested-cuddled-subshell.expect    | 19 ++++++++++++
 t/chainlint/nested-cuddled-subshell.test      | 31 +++++++++++++++++++
 t/chainlint/nested-here-doc.expect            |  5 +++
 t/chainlint/nested-here-doc.test              | 23 ++++++++++++++
 t/chainlint/nested-subshell-comment.expect    | 11 +++++++
 t/chainlint/nested-subshell-comment.test      | 13 ++++++++
 t/chainlint/nested-subshell.expect            | 12 +++++++
 t/chainlint/nested-subshell.test              | 14 +++++++++
 12 files changed, 173 insertions(+)
 create mode 100644 t/chainlint/block.expect
 create mode 100644 t/chainlint/block.test
 create mode 100644 t/chainlint/multi-line-nested-command-substitution.expect
 create mode 100644 t/chainlint/multi-line-nested-command-substitution.test
 create mode 100644 t/chainlint/nested-cuddled-subshell.expect
 create mode 100644 t/chainlint/nested-cuddled-subshell.test
 create mode 100644 t/chainlint/nested-here-doc.expect
 create mode 100644 t/chainlint/nested-here-doc.test
 create mode 100644 t/chainlint/nested-subshell-comment.expect
 create mode 100644 t/chainlint/nested-subshell-comment.test
 create mode 100644 t/chainlint/nested-subshell.expect
 create mode 100644 t/chainlint/nested-subshell.test

diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
new file mode 100644
index 0000000000..fed7e89ae8
--- /dev/null
+++ b/t/chainlint/block.expect
@@ -0,0 +1,12 @@
+(
+	foo &&
+	{
+		echo a
+		echo b
+	} &&
+	bar &&
+	{
+		echo c
+?!AMP?!	}
+	baz
+>)
diff --git a/t/chainlint/block.test b/t/chainlint/block.test
new file mode 100644
index 0000000000..d859151af1
--- /dev/null
+++ b/t/chainlint/block.test
@@ -0,0 +1,15 @@
+(
+# LINT: missing "&&" in block not currently detected (for consistency with
+# LINT: --chain-lint at top level and to provide escape hatch if needed)
+	foo &&
+	{
+		echo a
+		echo b
+	} &&
+	bar &&
+# LINT: missing "&&" at closing "}"
+	{
+		echo c
+	}
+	baz
+)
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
new file mode 100644
index 0000000000..19c023b1c8
--- /dev/null
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -0,0 +1,9 @@
+(
+	foo &&
+	x=$(
+		echo bar |
+		cat
+>>	) &&
+	echo ok
+>) |
+sort
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
new file mode 100644
index 0000000000..ca0620ab6b
--- /dev/null
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -0,0 +1,9 @@
+(
+	foo &&
+	x=$(
+		echo bar |
+		cat
+	) &&
+	echo ok
+) |
+sort
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
new file mode 100644
index 0000000000..c2a59ffc33
--- /dev/null
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -0,0 +1,19 @@
+(
+	(cd foo &&
+		bar
+>>	) &&
+	(cd foo &&
+		bar
+?!AMP?!>>	)
+	(
+		cd foo &&
+>>		bar) &&
+	(
+		cd foo &&
+?!AMP?!>>		bar)
+	(cd foo &&
+>>		bar) &&
+	(cd foo &&
+?!AMP?!>>		bar)
+	foobar
+>)
diff --git a/t/chainlint/nested-cuddled-subshell.test b/t/chainlint/nested-cuddled-subshell.test
new file mode 100644
index 0000000000..8fd656c7b5
--- /dev/null
+++ b/t/chainlint/nested-cuddled-subshell.test
@@ -0,0 +1,31 @@
+(
+# LINT: opening "(" cuddled with first nested subshell statement
+	(cd foo &&
+		bar
+	) &&
+
+# LINT: same but "&&" missing
+	(cd foo &&
+		bar
+	)
+
+# LINT: closing ")" cuddled with final nested subshell statement
+	(
+		cd foo &&
+		bar) &&
+
+# LINT: same but "&&" missing
+	(
+		cd foo &&
+		bar)
+
+# LINT: "(" and ")" cuddled with first and final subshell statements
+	(cd foo &&
+		bar) &&
+
+# LINT: same but "&&" missing
+	(cd foo &&
+		bar)
+
+	foobar
+)
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
new file mode 100644
index 0000000000..559301e005
--- /dev/null
+++ b/t/chainlint/nested-here-doc.expect
@@ -0,0 +1,5 @@
+(
+	cat &&
+?!AMP?!	cat
+	foobar
+>)
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
new file mode 100644
index 0000000000..027e0bb3ff
--- /dev/null
+++ b/t/chainlint/nested-here-doc.test
@@ -0,0 +1,23 @@
+(
+# LINT: inner "EOF" not misintrepreted as closing INPUT_END here-doc
+	cat <<-\INPUT_END &&
+	fish are mice
+	but geese go slow
+	data <<EOF
+		perl is lerp
+		and nothing else
+	EOF
+	toink
+	INPUT_END
+
+# LINT: same but missing "&&"
+	cat <<-\EOT
+	text goes here
+	data <<EOF
+		data goes here
+	EOF
+	more test here
+	EOT
+
+	foobar
+)
diff --git a/t/chainlint/nested-subshell-comment.expect b/t/chainlint/nested-subshell-comment.expect
new file mode 100644
index 0000000000..15b68d4373
--- /dev/null
+++ b/t/chainlint/nested-subshell-comment.expect
@@ -0,0 +1,11 @@
+(
+	foo &&
+	(
+		bar &&
+		# bottles wobble while fiddles gobble
+		# minor numbers of cows (or do they?)
+		baz &&
+		snaff
+?!AMP?!>>	)
+	fuzzy
+>)
diff --git a/t/chainlint/nested-subshell-comment.test b/t/chainlint/nested-subshell-comment.test
new file mode 100644
index 0000000000..0ff136ab3c
--- /dev/null
+++ b/t/chainlint/nested-subshell-comment.test
@@ -0,0 +1,13 @@
+(
+	foo &&
+	(
+		bar &&
+# LINT: ")" in comment in nested subshell not misinterpreted as closing ")"
+		# bottles wobble while fiddles gobble
+		# minor numbers of cows (or do they?)
+		baz &&
+		snaff
+# LINT: missing "&&" on ')'
+	)
+	fuzzy
+)
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
new file mode 100644
index 0000000000..c8165ad19e
--- /dev/null
+++ b/t/chainlint/nested-subshell.expect
@@ -0,0 +1,12 @@
+(
+	cd foo &&
+	(
+		echo a &&
+		echo b
+>>	) >file &&
+	cd foo &&
+	(
+		echo a
+		echo b
+>>	) >file
+>)
diff --git a/t/chainlint/nested-subshell.test b/t/chainlint/nested-subshell.test
new file mode 100644
index 0000000000..998b05a47d
--- /dev/null
+++ b/t/chainlint/nested-subshell.test
@@ -0,0 +1,14 @@
+(
+	cd foo &&
+	(
+		echo a &&
+		echo b
+	) >file &&
+
+	cd foo &&
+	(
+# LINT: nested multi-line subshell not presently checked for missing "&&"
+		echo a
+		echo b
+	) >file
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 07/10] t/chainlint: add chainlint "loop" and "conditional" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (5 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 06/10] t/chainlint: add chainlint "nested subshell" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 08/10] t/chainlint: add chainlint "cuddled" " Eric Sunshine
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/case.expect         | 19 +++++++++++++++++++
 t/chainlint/case.test           | 23 +++++++++++++++++++++++
 t/chainlint/exit-loop.expect    | 24 ++++++++++++++++++++++++
 t/chainlint/exit-loop.test      | 27 +++++++++++++++++++++++++++
 t/chainlint/for-loop.expect     | 11 +++++++++++
 t/chainlint/for-loop.test       | 19 +++++++++++++++++++
 t/chainlint/if-then-else.expect | 19 +++++++++++++++++++
 t/chainlint/if-then-else.test   | 28 ++++++++++++++++++++++++++++
 t/chainlint/while-loop.expect   | 11 +++++++++++
 t/chainlint/while-loop.test     | 19 +++++++++++++++++++
 10 files changed, 200 insertions(+)
 create mode 100644 t/chainlint/case.expect
 create mode 100644 t/chainlint/case.test
 create mode 100644 t/chainlint/exit-loop.expect
 create mode 100644 t/chainlint/exit-loop.test
 create mode 100644 t/chainlint/for-loop.expect
 create mode 100644 t/chainlint/for-loop.test
 create mode 100644 t/chainlint/if-then-else.expect
 create mode 100644 t/chainlint/if-then-else.test
 create mode 100644 t/chainlint/while-loop.expect
 create mode 100644 t/chainlint/while-loop.test

diff --git a/t/chainlint/case.expect b/t/chainlint/case.expect
new file mode 100644
index 0000000000..41f121fbbf
--- /dev/null
+++ b/t/chainlint/case.expect
@@ -0,0 +1,19 @@
+(
+	case "$x" in
+	x) foo ;;
+	*) bar ;;
+	esac &&
+	foobar
+>) &&
+(
+	case "$x" in
+	x) foo ;;
+	*) bar ;;
+?!AMP?!	esac
+	foobar
+>) &&
+(
+	case "$x" in 1) true;; esac &&
+?!AMP?!	case "$y" in 2) false;; esac
+	foobar
+>)
diff --git a/t/chainlint/case.test b/t/chainlint/case.test
new file mode 100644
index 0000000000..5ef6ff7db5
--- /dev/null
+++ b/t/chainlint/case.test
@@ -0,0 +1,23 @@
+(
+# LINT: "...)" arms in 'case' not misinterpreted as subshell-closing ")"
+	case "$x" in
+	x) foo ;;
+	*) bar ;;
+	esac &&
+	foobar
+) &&
+(
+# LINT: missing "&&" on 'esac'
+	case "$x" in
+	x) foo ;;
+	*) bar ;;
+	esac
+	foobar
+) &&
+(
+# LINT: "...)" arm in one-liner 'case' not misinterpreted as closing ")"
+	case "$x" in 1) true;; esac &&
+# LINT: same but missing "&&"
+	case "$y" in 2) false;; esac
+	foobar
+)
diff --git a/t/chainlint/exit-loop.expect b/t/chainlint/exit-loop.expect
new file mode 100644
index 0000000000..84d8bdebc0
--- /dev/null
+++ b/t/chainlint/exit-loop.expect
@@ -0,0 +1,24 @@
+(
+	for i in a b c
+	do
+		foo || exit 1
+		bar &&
+		baz
+	done
+>) &&
+(
+	while true
+	do
+		foo || exit 1
+		bar &&
+		baz
+	done
+>) &&
+(
+	i=0 &&
+	while test $i -lt 10
+	do
+		echo $i || exit
+		i=$(($i + 1))
+	done
+>)
diff --git a/t/chainlint/exit-loop.test b/t/chainlint/exit-loop.test
new file mode 100644
index 0000000000..2f038207e1
--- /dev/null
+++ b/t/chainlint/exit-loop.test
@@ -0,0 +1,27 @@
+(
+	for i in a b c
+	do
+# LINT: "|| exit {n}" valid for-loop escape in subshell; no "&&" needed
+		foo || exit 1
+		bar &&
+		baz
+	done
+) &&
+(
+	while true
+	do
+# LINT: "|| exit {n}" valid while-loop escape in subshell; no "&&" needed
+		foo || exit 1
+		bar &&
+		baz
+	done
+) &&
+(
+	i=0 &&
+	while test $i -lt 10
+	do
+# LINT: "|| exit" (sans exit code) valid escape in subshell; no "&&" needed
+		echo $i || exit
+		i=$(($i + 1))
+	done
+)
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
new file mode 100644
index 0000000000..c33cf56ee7
--- /dev/null
+++ b/t/chainlint/for-loop.expect
@@ -0,0 +1,11 @@
+(
+	for i in a b c
+	do
+?!AMP?!		echo $i
+		cat
+?!AMP?!	done
+	for i in a b c; do
+		echo $i &&
+		cat $i
+	done
+>)
diff --git a/t/chainlint/for-loop.test b/t/chainlint/for-loop.test
new file mode 100644
index 0000000000..7db76262bc
--- /dev/null
+++ b/t/chainlint/for-loop.test
@@ -0,0 +1,19 @@
+(
+# LINT: 'for', 'do', 'done' do not need "&&"
+	for i in a b c
+	do
+# LINT: missing "&&" on 'echo'
+		echo $i
+# LINT: last statement of while does not need "&&"
+		cat <<-\EOF
+		bar
+		EOF
+# LINT: missing "&&" on 'done'
+	done
+
+# LINT: 'do' on same line as 'for'
+	for i in a b c; do
+		echo $i &&
+		cat $i
+	done
+)
diff --git a/t/chainlint/if-then-else.expect b/t/chainlint/if-then-else.expect
new file mode 100644
index 0000000000..5953c7bfbc
--- /dev/null
+++ b/t/chainlint/if-then-else.expect
@@ -0,0 +1,19 @@
+(
+	if test -n ""
+	then
+?!AMP?!		echo very
+		echo empty
+	elif test -z ""
+		echo foo
+	else
+		echo foo &&
+		cat
+?!AMP?!	fi
+	echo poodle
+>) &&
+(
+	if test -n ""; then
+		echo very &&
+?!AMP?!		echo empty
+	if
+>)
diff --git a/t/chainlint/if-then-else.test b/t/chainlint/if-then-else.test
new file mode 100644
index 0000000000..9bd8e9a4c6
--- /dev/null
+++ b/t/chainlint/if-then-else.test
@@ -0,0 +1,28 @@
+(
+# LINT: 'if', 'then', 'elif', 'else', 'fi' do not need "&&"
+	if test -n ""
+	then
+# LINT: missing "&&" on 'echo'
+		echo very
+# LINT: last statement before 'elif' does not need "&&"
+		echo empty
+	elif test -z ""
+# LINT: last statement before 'else' does not need "&&"
+		echo foo
+	else
+		echo foo &&
+# LINT: last statement before 'fi' does not need "&&"
+		cat <<-\EOF
+		bar
+		EOF
+# LINT: missing "&&" on 'fi'
+	fi
+	echo poodle
+) &&
+(
+# LINT: 'then' on same line as 'if'
+	if test -n ""; then
+		echo very &&
+		echo empty
+	if
+)
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
new file mode 100644
index 0000000000..13cff2c0a5
--- /dev/null
+++ b/t/chainlint/while-loop.expect
@@ -0,0 +1,11 @@
+(
+	while true
+	do
+?!AMP?!		echo foo
+		cat
+?!AMP?!	done
+	while true; do
+		echo foo &&
+		cat bar
+	done
+>)
diff --git a/t/chainlint/while-loop.test b/t/chainlint/while-loop.test
new file mode 100644
index 0000000000..f1df085bf0
--- /dev/null
+++ b/t/chainlint/while-loop.test
@@ -0,0 +1,19 @@
+(
+# LINT: 'while, 'do', 'done' do not need "&&"
+	while true
+	do
+# LINT: missing "&&" on 'echo'
+		echo foo
+# LINT: last statement of while does not need "&&"
+		cat <<-\EOF
+		bar
+		EOF
+# LINT: missing "&&" on 'done'
+	done
+
+# LINT: 'do' on same line as 'while'
+	while true; do
+		echo foo &&
+		cat bar
+	done
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 08/10] t/chainlint: add chainlint "cuddled" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (6 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 07/10] t/chainlint: add chainlint "loop" and "conditional" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 09/10] t/chainlint: add chainlint "complex" " Eric Sunshine
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/cuddled-if-then-else.expect |  7 +++++++
 t/chainlint/cuddled-if-then-else.test   |  7 +++++++
 t/chainlint/cuddled-loop.expect         |  5 +++++
 t/chainlint/cuddled-loop.test           |  7 +++++++
 t/chainlint/cuddled.expect              | 21 +++++++++++++++++++++
 t/chainlint/cuddled.test                | 23 +++++++++++++++++++++++
 6 files changed, 70 insertions(+)
 create mode 100644 t/chainlint/cuddled-if-then-else.expect
 create mode 100644 t/chainlint/cuddled-if-then-else.test
 create mode 100644 t/chainlint/cuddled-loop.expect
 create mode 100644 t/chainlint/cuddled-loop.test
 create mode 100644 t/chainlint/cuddled.expect
 create mode 100644 t/chainlint/cuddled.test

diff --git a/t/chainlint/cuddled-if-then-else.expect b/t/chainlint/cuddled-if-then-else.expect
new file mode 100644
index 0000000000..ab2a026fbc
--- /dev/null
+++ b/t/chainlint/cuddled-if-then-else.expect
@@ -0,0 +1,7 @@
+(
+if test -z ""; then
+    echo empty
+ else
+    echo bizzy
+> fi) &&
+echo foobar
diff --git a/t/chainlint/cuddled-if-then-else.test b/t/chainlint/cuddled-if-then-else.test
new file mode 100644
index 0000000000..eed774a9d6
--- /dev/null
+++ b/t/chainlint/cuddled-if-then-else.test
@@ -0,0 +1,7 @@
+# LINT: 'if' cuddled with "(" and ")"; indented with spaces, not tabs
+(if test -z ""; then
+    echo empty
+ else
+    echo bizzy
+ fi) &&
+echo foobar
diff --git a/t/chainlint/cuddled-loop.expect b/t/chainlint/cuddled-loop.expect
new file mode 100644
index 0000000000..8c0260d7f1
--- /dev/null
+++ b/t/chainlint/cuddled-loop.expect
@@ -0,0 +1,5 @@
+(
+ while read x
+  do foobar bop || exit 1
+>  done <file ) &&
+outside subshell
diff --git a/t/chainlint/cuddled-loop.test b/t/chainlint/cuddled-loop.test
new file mode 100644
index 0000000000..a841d781f0
--- /dev/null
+++ b/t/chainlint/cuddled-loop.test
@@ -0,0 +1,7 @@
+# LINT: 'while' loop cuddled with "(" and ")", with embedded (allowed)
+# LINT: "|| exit {n}" to exit loop early, and using redirection "<" to feed
+# LINT: loop; indented with spaces, not tabs
+( while read x
+  do foobar bop || exit 1
+  done <file ) &&
+outside subshell
diff --git a/t/chainlint/cuddled.expect b/t/chainlint/cuddled.expect
new file mode 100644
index 0000000000..b506d46221
--- /dev/null
+++ b/t/chainlint/cuddled.expect
@@ -0,0 +1,21 @@
+(
+cd foo &&
+	bar
+>) &&
+
+(
+?!AMP?!cd foo
+	bar
+>) &&
+
+(
+	cd foo &&
+>	bar) &&
+
+(
+cd foo &&
+>	bar) &&
+
+(
+?!AMP?!cd foo
+>	bar)
diff --git a/t/chainlint/cuddled.test b/t/chainlint/cuddled.test
new file mode 100644
index 0000000000..0499fa4180
--- /dev/null
+++ b/t/chainlint/cuddled.test
@@ -0,0 +1,23 @@
+# LINT: first subshell statement cuddled with opening "("; for implementation
+# LINT: simplicity, "(..." is split into two lines, "(" and "..."
+(cd foo &&
+	bar
+) &&
+
+# LINT: same with missing "&&"
+(cd foo
+	bar
+) &&
+
+# LINT: closing ")" cuddled with final subshell statement
+(
+	cd foo &&
+	bar) &&
+
+# LINT: "(" and ")" cuddled with first and final subshell statements
+(cd foo &&
+	bar) &&
+
+# LINT: same with missing "&&"
+(cd foo
+	bar)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 09/10] t/chainlint: add chainlint "complex" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (7 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 08/10] t/chainlint: add chainlint "cuddled" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-07-11  6:46   ` [PATCH v2 10/10] t/chainlint: add chainlint "specialized" " Eric Sunshine
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 .../close-nested-and-parent-together.expect       |  4 ++++
 t/chainlint/close-nested-and-parent-together.test |  3 +++
 t/chainlint/complex-if-in-cuddled-loop.expect     | 10 ++++++++++
 t/chainlint/complex-if-in-cuddled-loop.test       | 11 +++++++++++
 t/chainlint/if-in-loop.expect                     | 12 ++++++++++++
 t/chainlint/if-in-loop.test                       | 15 +++++++++++++++
 t/chainlint/loop-in-if.expect                     | 12 ++++++++++++
 t/chainlint/loop-in-if.test                       | 15 +++++++++++++++
 8 files changed, 82 insertions(+)
 create mode 100644 t/chainlint/close-nested-and-parent-together.expect
 create mode 100644 t/chainlint/close-nested-and-parent-together.test
 create mode 100644 t/chainlint/complex-if-in-cuddled-loop.expect
 create mode 100644 t/chainlint/complex-if-in-cuddled-loop.test
 create mode 100644 t/chainlint/if-in-loop.expect
 create mode 100644 t/chainlint/if-in-loop.test
 create mode 100644 t/chainlint/loop-in-if.expect
 create mode 100644 t/chainlint/loop-in-if.test

diff --git a/t/chainlint/close-nested-and-parent-together.expect b/t/chainlint/close-nested-and-parent-together.expect
new file mode 100644
index 0000000000..2a910f9d66
--- /dev/null
+++ b/t/chainlint/close-nested-and-parent-together.expect
@@ -0,0 +1,4 @@
+(
+cd foo &&
+	(bar &&
+>>>		baz))
diff --git a/t/chainlint/close-nested-and-parent-together.test b/t/chainlint/close-nested-and-parent-together.test
new file mode 100644
index 0000000000..72d482f76d
--- /dev/null
+++ b/t/chainlint/close-nested-and-parent-together.test
@@ -0,0 +1,3 @@
+(cd foo &&
+	(bar &&
+		baz))
diff --git a/t/chainlint/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
new file mode 100644
index 0000000000..9674b88cf2
--- /dev/null
+++ b/t/chainlint/complex-if-in-cuddled-loop.expect
@@ -0,0 +1,10 @@
+(
+for i in a b c; do
+   if test "$(echo $(waffle bat))" = "eleventeen" &&
+     test "$x" = "$y"; then
+     :
+   else
+     echo >file
+   fi
+> done) &&
+test ! -f file
diff --git a/t/chainlint/complex-if-in-cuddled-loop.test b/t/chainlint/complex-if-in-cuddled-loop.test
new file mode 100644
index 0000000000..571bbd85cd
--- /dev/null
+++ b/t/chainlint/complex-if-in-cuddled-loop.test
@@ -0,0 +1,11 @@
+# LINT: 'for' loop cuddled with "(" and ")" and nested 'if' with complex
+# LINT: multi-line condition; indented with spaces, not tabs
+(for i in a b c; do
+   if test "$(echo $(waffle bat))" = "eleventeen" &&
+     test "$x" = "$y"; then
+     :
+   else
+     echo >file
+   fi
+ done) &&
+test ! -f file
diff --git a/t/chainlint/if-in-loop.expect b/t/chainlint/if-in-loop.expect
new file mode 100644
index 0000000000..03d3ceb22d
--- /dev/null
+++ b/t/chainlint/if-in-loop.expect
@@ -0,0 +1,12 @@
+(
+	for i in a b c
+	do
+		if false
+		then
+?!AMP?!			echo "err"
+			exit 1
+?!AMP?!		fi
+		foo
+?!AMP?!	done
+	bar
+>)
diff --git a/t/chainlint/if-in-loop.test b/t/chainlint/if-in-loop.test
new file mode 100644
index 0000000000..daf22da164
--- /dev/null
+++ b/t/chainlint/if-in-loop.test
@@ -0,0 +1,15 @@
+(
+	for i in a b c
+	do
+		if false
+		then
+# LINT: missing "&&" on 'echo'
+			echo "err"
+			exit 1
+# LINT: missing "&&" on 'fi'
+		fi
+		foo
+# LINT: missing "&&" on 'done'
+	done
+	bar
+)
diff --git a/t/chainlint/loop-in-if.expect b/t/chainlint/loop-in-if.expect
new file mode 100644
index 0000000000..088e622c31
--- /dev/null
+++ b/t/chainlint/loop-in-if.expect
@@ -0,0 +1,12 @@
+(
+	if true
+	then
+		while true
+		do
+?!AMP?!			echo "pop"
+			echo "glup"
+?!AMP?!		done
+		foo
+?!AMP?!	fi
+	bar
+>)
diff --git a/t/chainlint/loop-in-if.test b/t/chainlint/loop-in-if.test
new file mode 100644
index 0000000000..93e8ba8e4d
--- /dev/null
+++ b/t/chainlint/loop-in-if.test
@@ -0,0 +1,15 @@
+(
+	if true
+	then
+		while true
+		do
+# LINT: missing "&&" on 'echo'
+			echo "pop"
+			echo "glup"
+# LINT: missing "&&" on 'done'
+		done
+		foo
+# LINT: missing "&&" on 'fi'
+	fi
+	bar
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 10/10] t/chainlint: add chainlint "specialized" test cases
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (8 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 09/10] t/chainlint: add chainlint "complex" " Eric Sunshine
@ 2018-07-11  6:46   ` " Eric Sunshine
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  10 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-07-11  6:46 UTC (permalink / raw)
  To: git
  Cc: Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King,
	Eric Sunshine

The --chain-lint option uses heuristics and knowledge of shell syntax to
detect broken &&-chains in subshells by pure textual inspection. The
heuristics handle a range of stylistic variations in existing tests
(evolved over the years), however, they are still best-guesses. As such,
it is possible for future changes to accidentally break assumptions upon
which the heuristics are based. Protect against this possibility by
adding tests which check the linter itself for correctness.

In addition to protecting against regressions, these tests help document
(for humans) expected behavior, which is important since the linter's
implementation language ('sed') does not necessarily lend itself to easy
comprehension.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/bash-array.expect  | 10 ++++++++++
 t/chainlint/bash-array.test    | 12 ++++++++++++
 t/chainlint/p4-filespec.expect |  4 ++++
 t/chainlint/p4-filespec.test   |  5 +++++
 4 files changed, 31 insertions(+)
 create mode 100644 t/chainlint/bash-array.expect
 create mode 100644 t/chainlint/bash-array.test
 create mode 100644 t/chainlint/p4-filespec.expect
 create mode 100644 t/chainlint/p4-filespec.test

diff --git a/t/chainlint/bash-array.expect b/t/chainlint/bash-array.expect
new file mode 100644
index 0000000000..c4a830d1c1
--- /dev/null
+++ b/t/chainlint/bash-array.expect
@@ -0,0 +1,10 @@
+(
+	foo &&
+	bar=(gumbo stumbo wumbo) &&
+	baz
+>) &&
+(
+	foo &&
+	bar=${#bar[@]} &&
+	baz
+>)
diff --git a/t/chainlint/bash-array.test b/t/chainlint/bash-array.test
new file mode 100644
index 0000000000..92bbb777b8
--- /dev/null
+++ b/t/chainlint/bash-array.test
@@ -0,0 +1,12 @@
+(
+	foo &&
+# LINT: ")" in Bash array assignment not misinterpreted as subshell-closing ")"
+	bar=(gumbo stumbo wumbo) &&
+	baz
+) &&
+(
+	foo &&
+# LINT: Bash array length operator not misinterpreted as comment
+	bar=${#bar[@]} &&
+	baz
+)
diff --git a/t/chainlint/p4-filespec.expect b/t/chainlint/p4-filespec.expect
new file mode 100644
index 0000000000..98b3d881fd
--- /dev/null
+++ b/t/chainlint/p4-filespec.expect
@@ -0,0 +1,4 @@
+(
+	p4 print -1 //depot/fiddle#42 >file &&
+	foobar
+>)
diff --git a/t/chainlint/p4-filespec.test b/t/chainlint/p4-filespec.test
new file mode 100644
index 0000000000..4fd2d6e2b8
--- /dev/null
+++ b/t/chainlint/p4-filespec.test
@@ -0,0 +1,5 @@
+(
+# LINT: Perforce revspec in filespec not misinterpreted as in-line comment
+	p4 print -1 //depot/fiddle#42 >file &&
+	foobar
+)
-- 
2.18.0.203.gfac676dfb9


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
@ 2018-07-11 21:37     ` Junio C Hamano
  2018-07-12 10:50       ` Eric Sunshine
  2018-07-30 18:13     ` Jonathan Nieder
  2018-08-23 18:02     ` Ævar Arnfjörð Bjarmason
  2 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-07-11 21:37 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Luke Diamand, Jeff King

Eric Sunshine <sunshine@sunshineco.com> writes:

> The --chain-lint option detects broken &&-chains by forcing the test to
> exit early (as the very first step) with a sentinel value. If that
> sentinel is the test's overall exit code, then the &&-chain is intact;
> if not, then the chain is broken. Unfortunately, this detection does not
> extend to &&-chains within subshells even when the subshell itself is
> properly linked into the outer &&-chain.
>
> Address this shortcoming by feeding the body of the test to a
> lightweight "linter" which can peer inside subshells and identify broken
> &&-chains by pure textual inspection. Although the linter does not
> ...
> Heuristics are employed to properly identify the extent of a subshell
> formatted in the old-style since a number of legitimate constructs may
> superficially appear to close the subshell even though they don't. For
> example, it understands that neither "x=$(command)" nor "case $x in *)"
> end a subshell, despite the ")" at the end of line.
>
> Due to limitations of the tool used ('sed') and its inherent
> line-by-line processing, only subshells one level deep are handled, as
> well as one-liner subshells one level below that. Subshells deeper than
> that or multi-line subshells at level two are passed through as-is, thus
> &&-chains in their bodies are not checked.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---

As with the previous "transform the script and feed the result to
shell" approach, this risks to force us into writing our tests in a
subset of valid shell language, which is the primary reason why I
was not enthused when I saw the previous round.  The worst part of
it is that the subset is not strictly defined based on the shell
language syntax or features (e.g. we allow this and that feature but
not that other feature) but "whatever that does not cause the linter
script to trigger false positives".

So I dunno.  I haven't spent enough time to carefully look at the
actual scripts to access how serious the "problem" I perceive
actually is with this series to form a firm opinion yet.  Let me
come back to the topic after doing so.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-11 21:37     ` Junio C Hamano
@ 2018-07-12 10:50       ` Eric Sunshine
  2018-07-12 16:56         ` Jeff King
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-07-12 10:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Elijah Newren, Johannes Sixt, Jonathan Nieder,
	Jonathan Tan, Stefan Beller, Luke Diamand, Jeff King

On Wed, Jul 11, 2018 at 5:37 PM Junio C Hamano <gitster@pobox.com> wrote:
> As with the previous "transform the script and feed the result to
> shell" approach, this risks to force us into writing our tests in a
> subset of valid shell language, which is the primary reason why I
> was not enthused when I saw the previous round.  The worst part of
> it is that the subset is not strictly defined based on the shell
> language syntax or features (e.g. we allow this and that feature but
> not that other feature) but "whatever that does not cause the linter
> script to trigger false positives".

Some observations perhaps worth considering:

The linter is happy (no false positives) with the 13000+ existing
tests (though, of course, not all of them use subshells). Those tests,
written over many years, vary quite wildly in style and implementation
approach, so the "subset" of shell language accepted by the linter is
quite broad.

The original --chain-lint series (jk/test-chain-lint) had to make some
changes, such as wrapping code in a {...} block[1], merely to pacify
the linter. v2 of the subshell linter required no such changes.

The subshell linter was crafted to be on par with the existing
--chain-lint in terms of strictness (and looseness), so the subshell
linter is not more strict than the existing implementation. (For
instance, one can escape the strict &&-chain requirement in the
existing --chain-lint by wrapping code in a {...} block. The subshell
linter intentionally allows that escape, as well.)

And, perhaps most important: We're not tied indefinitely to the
"subset" implemented by the current linter. If it is indeed found to
be too strict or limiting, it can always be loosened or retired
altogether.

Thanks for the feedback.

[1]: bfe998fc9b (t0050: appease --chain-lint, 2015-03-20)

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-12 10:50       ` Eric Sunshine
@ 2018-07-12 16:56         ` Jeff King
  2018-07-12 19:32           ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Jeff King @ 2018-07-12 16:56 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Junio C Hamano, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Nieder, Jonathan Tan, Stefan Beller, Luke Diamand

On Thu, Jul 12, 2018 at 06:50:20AM -0400, Eric Sunshine wrote:

> And, perhaps most important: We're not tied indefinitely to the
> "subset" implemented by the current linter. If it is indeed found to
> be too strict or limiting, it can always be loosened or retired
> altogether.

Yeah, I agree this is the key point.

Like Junio, I'm a little nervous that this is going to end up being a
maintenance burden. People may hit false positives and then be
confronted with this horrible mass of sed to try to figure out what went
wrong (which isn't to bust on your sed in particular; I think you made a
heroic effort in commenting).

But I came around to thinking:

  - this found and fixed real problems in the test suite, with minimal
    false positives across the existing code

  - it's being done by a long-time contributor, not somebody who is
    going to dump sed on us and leave

  - worst case is that relief is only a "git revert" away

So I'm OK with merging it, and even running it by default.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-12 16:56         ` Jeff King
@ 2018-07-12 19:32           ` Eric Sunshine
  2018-07-12 19:54             ` Junio C Hamano
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-07-12 19:32 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Nieder, Jonathan Tan, Stefan Beller, Luke Diamand

On Thu, Jul 12, 2018 at 12:56 PM Jeff King <peff@peff.net> wrote:
> Like Junio, I'm a little nervous that this is going to end up being a
> maintenance burden. People may hit false positives and then be
> confronted with this horrible mass of sed to try to figure out what went
> wrong [...]

A very valid concern.

> But I came around to thinking:
>   - this found and fixed real problems in the test suite, with minimal
>     false positives across the existing code

The counterargument (and arguing against my own case) is that, while
it found 3 or 4 genuine test bugs hidden by &&-breakage, they were
just that: bugs in the tests; they weren't hiding any bugs in Git
itself, which is pretty measly return for the effort invested in the
linter.

However, existing tests aside, the more important goal is detecting
problems in new or updated tests hiding genuine bugs in changes to Git
itself, so it may have some value.

>   - worst case is that relief is only a "git revert" away

Right. It's just a developer aid, not a user-facing feature which has
to be maintained in perpetuity, so retiring it is easy.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-12 19:32           ` Eric Sunshine
@ 2018-07-12 19:54             ` Junio C Hamano
  0 siblings, 0 replies; 123+ messages in thread
From: Junio C Hamano @ 2018-07-12 19:54 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Jeff King, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Nieder, Jonathan Tan, Stefan Beller, Luke Diamand

Eric Sunshine <sunshine@sunshineco.com> writes:

> However, existing tests aside, the more important goal is detecting
> problems in new or updated tests hiding genuine bugs in changes to Git
> itself, so it may have some value.

Yes, indeed.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
  2018-07-11 21:37     ` Junio C Hamano
@ 2018-07-30 18:13     ` Jonathan Nieder
  2018-07-30 19:06       ` [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells) Jonathan Nieder
  2018-07-30 20:25       ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells Eric Sunshine
  2018-08-23 18:02     ` Ævar Arnfjörð Bjarmason
  2 siblings, 2 replies; 123+ messages in thread
From: Jonathan Nieder @ 2018-07-30 18:13 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Elijah Newren, Johannes Sixt, Jonathan Tan, Stefan Beller,
	Junio C Hamano, Luke Diamand, Jeff King

Hi,

Eric Sunshine wrote:

> The --chain-lint option detects broken &&-chains by forcing the test to
> exit early (as the very first step) with a sentinel value. If that
> sentinel is the test's overall exit code, then the &&-chain is intact;
> if not, then the chain is broken. Unfortunately, this detection does not
> extend to &&-chains within subshells even when the subshell itself is
> properly linked into the outer &&-chain.
>
> Address this shortcoming by feeding the body of the test to a
> lightweight "linter" which can peer inside subshells and identify broken
> &&-chains by pure textual inspection.

Interesting.

>                                        Although the linter does not
> actually parse shell scripts, it has enough knowledge of shell syntax to
> reliably deal with formatting style variations (as evolved over the
> years) and to avoid being fooled by non-shell content (such as inside
> here-docs and multi-line strings).

This is causing contrib/subtree tests to fail for me: running "make -C
contrib/subtree test" produces

[...]
	*** t7900-subtree.sh ***
	ok 1 - no merge from non-existent subtree
	ok 2 - no pull from non-existent subtree
	ok 3 - add subproj as subtree into sub dir/ with --prefix
	ok 4 - add subproj as subtree into sub dir/ with --prefix and --message
	ok 5 - add subproj as subtree into sub dir/ with --prefix as -P and --message as -m
	ok 6 - add subproj as subtree into sub dir/ with --squash and --prefix and --message
	ok 7 - merge new subproj history into sub dir/ with --prefix
	ok 8 - merge new subproj history into sub dir/ with --prefix and --message
	ok 9 - merge new subproj history into sub dir/ with --squash and --prefix and --message
	ok 10 - merge the added subproj again, should do nothing
	ok 11 - merge new subproj history into subdir/ with a slash appended to the argument of --prefix
	ok 12 - split requires option --prefix
	ok 13 - split requires path given by option --prefix must exist
	ok 14 - split sub dir/ with --rejoin
	ok 15 - split sub dir/ with --rejoin from scratch
	ok 16 - split sub dir/ with --rejoin and --message
	ok 17 - split "sub dir"/ with --branch
	ok 18 - check hash of split
	ok 19 - split "sub dir"/ with --branch for an existing branch
	ok 20 - split "sub dir"/ with --branch for an incompatible branch
	error: bug in the test script: broken &&-chain or run-away HERE-DOC: 
		subtree_test_create_repo "$subtree_test_count" &&
[...]
		)

	Makefile:44: recipe for target 't7900-subtree.sh' failed

The problematic test code looks like this:

	(
		cd "$subtree_test_count/sub proj" &&
		git fetch .. subproj-br &&
		git merge FETCH_HEAD &&

		chks="sub1
sub2
sub3
sub4" &&
		chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
$chks
TXT
) &&
		chkms="main-sub1
main-sub2
main-sub3
main-sub4" &&
		chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
$chkms
TXT
) &&

		subfiles=$(git ls-files) &&
		check_equal "$subfiles" "$chkms
$chks"
	)

Ugly quoting, useless use of "cat", etc, aside, I don't think it's
missing any &&.  Hints?

Thanks,
Jonathan

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells)
  2018-07-30 18:13     ` Jonathan Nieder
@ 2018-07-30 19:06       ` Jonathan Nieder
  2018-07-30 19:07         ` [PATCH 1/2] subtree test: add missing && to &&-chain Jonathan Nieder
  2018-07-30 19:07         ` [PATCH 2/2] subtree test: simplify preparation of expected results Jonathan Nieder
  2018-07-30 20:25       ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells Eric Sunshine
  1 sibling, 2 replies; 123+ messages in thread
From: Jonathan Nieder @ 2018-07-30 19:06 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Ævar Arnfjörð Bjarmason, David Aguilar

(resetting cc list)
Jonathan Nieder wrote:

> This is causing contrib/subtree tests to fail for me: running "make -C
> contrib/subtree test" produces
[...]
> 	error: bug in the test script: broken &&-chain or run-away HERE-DOC:
[...]
> Ugly quoting, useless use of "cat", etc, aside, I don't think it's
> missing any &&.  Hints?

Turns out it was missing a && too. :)

These patches are against "master".  Ideally this would have come
before es/chain-lint-in-subshell.  Since this is contrib/, I'm okay
with losing bisectability and having it come after, though.

Thoughts of all kinds welcome.

Jonathan Nieder (2):
  subtree: add missing && to &&-chain
  subtree: simplify preparation of expected results

 contrib/subtree/t/t7900-subtree.sh | 121 ++++++++---------------------
 1 file changed, 31 insertions(+), 90 deletions(-)

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 1/2] subtree test: add missing && to &&-chain
  2018-07-30 19:06       ` [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells) Jonathan Nieder
@ 2018-07-30 19:07         ` Jonathan Nieder
  2018-07-30 19:07         ` [PATCH 2/2] subtree test: simplify preparation of expected results Jonathan Nieder
  1 sibling, 0 replies; 123+ messages in thread
From: Jonathan Nieder @ 2018-07-30 19:07 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Ævar Arnfjörð Bjarmason, David Aguilar

Detected using t/chainlint.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 contrib/subtree/t/t7900-subtree.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index d05c613c97..e6a28f2c3e 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -708,7 +708,7 @@ test_expect_success 'make sure each filename changed exactly once in the entire
 	test_create_commit "$subtree_test_count/sub proj" sub1 &&
 	(
 		cd "$subtree_test_count" &&
-		git config log.date relative
+		git config log.date relative &&
 		git fetch ./"sub proj" master &&
 		git subtree add --prefix="sub dir" FETCH_HEAD
 	) &&
-- 
2.18.0.345.g5c9ce644c3-goog


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 2/2] subtree test: simplify preparation of expected results
  2018-07-30 19:06       ` [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells) Jonathan Nieder
  2018-07-30 19:07         ` [PATCH 1/2] subtree test: add missing && to &&-chain Jonathan Nieder
@ 2018-07-30 19:07         ` Jonathan Nieder
  1 sibling, 0 replies; 123+ messages in thread
From: Jonathan Nieder @ 2018-07-30 19:07 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Ævar Arnfjörð Bjarmason, David Aguilar

This mixture of quoting, pipes, and here-docs to produce expected
results in shell variables is difficult to follow.  Simplify by using
simpler constructs that write output to files instead.

Noticed because without this patch, t/chainlint is not able to
understand the script in order to validate that its subshells use an
unbroken &&-chain, causing "make -C contrib/subtree test" to fail with

	error: bug in the test script: broken &&-chain or run-away HERE-DOC:

in t7900.21.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
That's the end of the series.  Thanks for reading.

Thanks,
Jonathan

 contrib/subtree/t/t7900-subtree.sh | 119 ++++++++---------------------
 1 file changed, 30 insertions(+), 89 deletions(-)

diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index e6a28f2c3e..57ff4b25c1 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -540,26 +540,10 @@ test_expect_success 'make sure exactly the right set of files ends up in the sub
 		git fetch .. subproj-br &&
 		git merge FETCH_HEAD &&
 
-		chks="sub1
-sub2
-sub3
-sub4" &&
-		chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chks
-TXT
-) &&
-		chkms="main-sub1
-main-sub2
-main-sub3
-main-sub4" &&
-		chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chkms
-TXT
-) &&
-
-		subfiles=$(git ls-files) &&
-		check_equal "$subfiles" "$chkms
-$chks"
+		test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \
+			sub1 sub2 sub3 sub4 >expect &&
+		git ls-files >actual &&
+		test_cmp expect actual
 	)
 '
 
@@ -606,25 +590,11 @@ test_expect_success 'make sure the subproj *only* contains commits that affect t
 		git fetch .. subproj-br &&
 		git merge FETCH_HEAD &&
 
-		chks="sub1
-sub2
-sub3
-sub4" &&
-		chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chks
-TXT
-) &&
-		chkms="main-sub1
-main-sub2
-main-sub3
-main-sub4" &&
-		chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chkms
-TXT
-) &&
-		allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") &&
-		check_equal "$allchanges" "$chkms
-$chks"
+		test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \
+			sub1 sub2 sub3 sub4 >expect &&
+		git log --name-only --pretty=format:"" >log &&
+		sort <log | sed "/^\$/ d" >actual &&
+		test_cmp expect actual
 	)
 '
 
@@ -675,29 +645,16 @@ test_expect_success 'make sure exactly the right set of files ends up in the mai
 		cd "$subtree_test_count" &&
 		git subtree pull --prefix="sub dir" ./"sub proj" master &&
 
-		chkm="main1
-main2" &&
-		chks="sub1
-sub2
-sub3
-sub4" &&
-		chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chks
-TXT
-) &&
-		chkms="main-sub1
-main-sub2
-main-sub3
-main-sub4" &&
-		chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chkms
-TXT
-) &&
-		mainfiles=$(git ls-files) &&
-		check_equal "$mainfiles" "$chkm
-$chkms_sub
-$chks_sub"
-)
+		test_write_lines main1 main2 >chkm &&
+		test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms &&
+		sed "s,^,sub dir/," chkms >chkms_sub &&
+		test_write_lines sub1 sub2 sub3 sub4 >chks &&
+		sed "s,^,sub dir/," chks >chks_sub &&
+
+		cat chkm chkms_sub chks_sub >expect &&
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
 '
 
 next_test
@@ -748,37 +705,21 @@ test_expect_success 'make sure each filename changed exactly once in the entire
 		cd "$subtree_test_count" &&
 		git subtree pull --prefix="sub dir" ./"sub proj" master &&
 
-		chkm="main1
-main2" &&
-		chks="sub1
-sub2
-sub3
-sub4" &&
-		chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chks
-TXT
-) &&
-		chkms="main-sub1
-main-sub2
-main-sub3
-main-sub4" &&
-		chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
-$chkms
-TXT
-) &&
+		test_write_lines main1 main2 >chkm &&
+		test_write_lines sub1 sub2 sub3 sub4 >chks &&
+		test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms &&
+		sed "s,^,sub dir/," chkms >chkms_sub &&
 
 		# main-sub?? and /"sub dir"/main-sub?? both change, because those are the
 		# changes that were split into their own history.  And "sub dir"/sub?? never
 		# change, since they were *only* changed in the subtree branch.
-		allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") &&
-		expected=''"$(cat <<TXT | sort
-$chkms
-$chkm
-$chks
-$chkms_sub
-TXT
-)"'' &&
-		check_equal "$allchanges" "$expected"
+		git log --name-only --pretty=format:"" >log &&
+		sort <log >sorted-log &&
+		sed "/^$/ d" sorted-log >actual &&
+
+		cat chkms chkm chks chkms_sub >expect-unsorted &&
+		sort expect-unsorted >expect &&
+		test_cmp expect actual
 	)
 '
 
-- 
2.18.0.345.g5c9ce644c3


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-30 18:13     ` Jonathan Nieder
  2018-07-30 19:06       ` [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells) Jonathan Nieder
@ 2018-07-30 20:25       ` Eric Sunshine
  2018-07-30 20:59         ` Jonathan Nieder
  1 sibling, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-07-30 20:25 UTC (permalink / raw)
  To: Jonathan Nieder
  Cc: Git List, Elijah Newren, Johannes Sixt, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King

On Mon, Jul 30, 2018 at 2:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
> Eric Sunshine wrote:
> > Address this shortcoming by feeding the body of the test to a
> > lightweight "linter" which can peer inside subshells and identify broken
> > &&-chains by pure textual inspection.
>
> This is causing contrib/subtree tests to fail for me: running "make -C
> contrib/subtree test" produces

Thanks, I forgot that some of 'contrib' had bundled tests. (In fact, I
just checked the other 'contrib' tests and found that a MediaWiki test
has a broken top-level &&-chain.)

> The problematic test code looks like this:
>
>         (
>                 chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
> $chks
> TXT
> ) &&
>
> Ugly quoting, useless use of "cat", etc, aside, I don't think it's
> missing any &&.  Hints?

Yes, it's a false positive.

The subshell linter would normally fold out the here-doc content, but
'sed' isn't a proper programming language, so the linter can't
recognize arbitrary here-doc tags. Instead it has hard-coded knowledge
of the tags commonly used in the Git tests, specifically EOF, EOT, and
INPUT_END.

The linter also deals with multi-line $(...) expressions, however, it
currently only recognizes them when the $( is on its own line.

Had this test used one of the common here-doc tags _or_ had it
formatted the $(...) as described, then it wouldn't have misfired.

I could try to update the linter to not trip over this sort of input,
however, this test code is indeed ugly and difficult to understand,
and your rewrite[1] of it makes it far easier to grok, so I'm not sure
the effort would be worthwhile. What do you think?

[1]: https://public-inbox.org/git/20180730190738.GD156463@aiede.svl.corp.google.com/

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-30 20:25       ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells Eric Sunshine
@ 2018-07-30 20:59         ` Jonathan Nieder
  2018-07-30 21:38           ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Jonathan Nieder @ 2018-07-30 20:59 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Elijah Newren, Johannes Sixt, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King

Eric Sunshine wrote:
> On Mon, Jul 30, 2018 at 2:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:

>>         (
>>                 chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
>> $chks
>> TXT
>> ) &&
>>
>> Ugly quoting, useless use of "cat", etc, aside, I don't think it's
>> missing any &&.  Hints?
>
> Yes, it's a false positive.
>
> The subshell linter would normally fold out the here-doc content, but
> 'sed' isn't a proper programming language, so the linter can't
> recognize arbitrary here-doc tags. Instead it has hard-coded knowledge
> of the tags commonly used in the Git tests, specifically EOF, EOT, and
> INPUT_END.

Oh, hmm.  I also see some others (outside subshells, though):

	EXPECT_END
	FRONTEND_END
	END_PART1
	SETUP_END
	EOF2
	EXPECTED
	END_OF_LOG
	INPUT_END
	END_EXPECT

I wonder if it should look for something like [A-Z][A-Z_]* to catch
all of these.

> The linter also deals with multi-line $(...) expressions, however, it
> currently only recognizes them when the $( is on its own line.

That's reasonable, especially if "on its own line" means "at end of
line".

What would help most is if the error message could explain what is
going on, but I understand that that can be hard to do in a sed
script.

[...]
> I could try to update the linter to not trip over this sort of input,
> however, this test code is indeed ugly and difficult to understand,
> and your rewrite[1] of it makes it far easier to grok, so I'm not sure
> the effort would be worthwhile. What do you think?

I'd be happy to look over a change that handles more here-doc
delimiters or produces a clearer message for tests in poor style, but
I agree with you that it's not too important.

Thanks for looking it over.

Sincerely,
Jonathan

> [1]: https://public-inbox.org/git/20180730190738.GD156463@aiede.svl.corp.google.com/

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-30 20:59         ` Jonathan Nieder
@ 2018-07-30 21:38           ` Eric Sunshine
  2018-07-31 12:50             ` Jeff King
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-07-30 21:38 UTC (permalink / raw)
  To: Jonathan Nieder
  Cc: Git List, Elijah Newren, Johannes Sixt, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King

On Mon, Jul 30, 2018 at 4:59 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
> Eric Sunshine wrote:
> > The subshell linter would normally fold out the here-doc content, but
> > 'sed' isn't a proper programming language, so the linter can't
> > recognize arbitrary here-doc tags. Instead it has hard-coded knowledge
> > of the tags commonly used in the Git tests, specifically EOF, EOT, and
> > INPUT_END.
>
> Oh, hmm.  I also see some others (outside subshells, though):
>
>         EXPECT_END
>         [...]

Correct. The linter does fold-out top-level EOF here-docs to hedge
against the body of a here-doc containing something that might look
like the start of a subshell (which would activate the more
strict/expensive &&-chain validation). It special-cases top-level EOF
because it's such a common tag name, thus an easy way to avoid
false-positives, in general. I didn't bother trying to recognize
_every_ possible tag since those other here-docs don't trigger any
problems. (It's heuristic-based, after all.)

> I wonder if it should look for something like [A-Z][A-Z_]* to catch
> all of these.

I considered that, but it doesn't handle nested here-docs, which we
actually have in the test suite. For instance, from t9300-fast-import:

    cat >input <<-INPUT_END &&
    mark :2
    data <<EOF
    $file2_data
    EOF
    ...
    INPUT_END

Nesting could be handled easily enough either by stashing away the
opening tag and matching against it later _or_ by doing recursive
here-doc folding, however, 'sed' isn't a proper programming language
and can't be coerced into doing either of those. (And, it was tricky
enough just getting it to handle the nested case with a limited set of
recognized tag names, without having to explicitly handle every
combination of those names nested inside one another.)

> > The linter also deals with multi-line $(...) expressions, however, it
> > currently only recognizes them when the $( is on its own line.
>
> That's reasonable, especially if "on its own line" means "at end of
> line".

It does; sorry for not being clear.

> What would help most is if the error message could explain what is
> going on, but I understand that that can be hard to do in a sed
> script.

Right, unfortunately, it can't be too helpful, but, when fixing all
those broken chains, I did find it useful to dump the result after
'sed' processing in order to identify what it was actually complaining
about. I did so by changing the $1 in this line from test-lib.sh:

error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"

to print the 'sed' output instead. The linter prepends "??AMP??" and
"??SEMI??" to suspect lines. It also prepends lines with ">" to
indicate where it thinks a subshell ends. This information was quite
helpful when figuring out what was broken in the test (or, for false
positives, where a heuristic had gone wrong).

I considered enabling this output by default instead of $1 but decided
against it since it is only helpful for broken &&-chains in subshells,
thus doesn't aid in the more common case of top-level &&-breakage,
thus might be confusing.

> > I could try to update the linter to not trip over this sort of input,
> > however, this test code is indeed ugly and difficult to understand,
> > and your rewrite[1] of it makes it far easier to grok, so I'm not sure
> > the effort would be worthwhile. What do you think?
>
> I'd be happy to look over a change that handles more here-doc
> delimiters or produces a clearer message for tests in poor style, but
> I agree with you that it's not too important.

I am, for a couple reasons, somewhat hesitant to tweak the heuristic.

First, each tweak has the potential of causing more false-positives or
(perhaps worse) false-negatives. The linter's own test-suite is
supposed to protect against that, but test suite coverage is never
perfect.

Second, ideally, the linter should protect against new broken
&&-chains from entering the codebase, so poorly coded historic tests
such as these aren't necessarily good motivation for tweaking, _and_
it is (hopefully) unlikely that we would allow this sort of ugly shell
code to enter the codebase going forward. (The counterargument is that
this false-positive doesn't help someone coding up a new test who
hasn't yet submitted the patch to the mailing list where more seasoned
eyes would suggest better coding style.)

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-30 21:38           ` Eric Sunshine
@ 2018-07-31 12:50             ` Jeff King
  2018-07-31 18:55               ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Jeff King @ 2018-07-31 12:50 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Jonathan Nieder, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Tan, Stefan Beller, Junio C Hamano, Luke Diamand

On Mon, Jul 30, 2018 at 05:38:06PM -0400, Eric Sunshine wrote:

> > I wonder if it should look for something like [A-Z][A-Z_]* to catch
> > all of these.
> 
> I considered that, but it doesn't handle nested here-docs, which we
> actually have in the test suite. For instance, from t9300-fast-import:
> 
>     cat >input <<-INPUT_END &&
>     mark :2
>     data <<EOF
>     $file2_data
>     EOF
>     ...
>     INPUT_END
> 
> Nesting could be handled easily enough either by stashing away the
> opening tag and matching against it later _or_ by doing recursive
> here-doc folding, however, 'sed' isn't a proper programming language
> and can't be coerced into doing either of those. (And, it was tricky
> enough just getting it to handle the nested case with a limited set of
> recognized tag names, without having to explicitly handle every
> combination of those names nested inside one another.)

I hesitate to make any suggestion here, as I think we may have passed
a point of useful cost/benefit in sinking more time into this script.
But...is switching to awk or perl an option? Our test suite already
depends on having a vanilla perl, so I don't think it would be a new
dependency. And it would give you actual data structures.

But like I said, it may not be worth it. I'd be OK just adjusting the
false positive and moving on.

> I am, for a couple reasons, somewhat hesitant to tweak the heuristic.
> 
> First, each tweak has the potential of causing more false-positives or
> (perhaps worse) false-negatives. The linter's own test-suite is
> supposed to protect against that, but test suite coverage is never
> perfect.
> 
> Second, ideally, the linter should protect against new broken
> &&-chains from entering the codebase, so poorly coded historic tests
> such as these aren't necessarily good motivation for tweaking, _and_
> it is (hopefully) unlikely that we would allow this sort of ugly shell
> code to enter the codebase going forward. (The counterargument is that
> this false-positive doesn't help someone coding up a new test who
> hasn't yet submitted the patch to the mailing list where more seasoned
> eyes would suggest better coding style.)

Right, I think the real cost is somebody who adds "<<CUSTOM_END_TAG"
later and is confused when they see the breakage. I.e., I don't mind
saying "we have a couple of style rules that you must follow to appease
the linter". But if the error message is not clear, it can send somebody
down the wrong rabbit hole trying to figure out what is going on.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-31 12:50             ` Jeff King
@ 2018-07-31 18:55               ` Eric Sunshine
  2018-07-31 19:08                 ` Jeff King
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-07-31 18:55 UTC (permalink / raw)
  To: Jeff King
  Cc: Jonathan Nieder, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Tan, Stefan Beller, Junio C Hamano, Luke Diamand

On Tue, Jul 31, 2018 at 8:50 AM Jeff King <peff@peff.net> wrote:
> On Mon, Jul 30, 2018 at 05:38:06PM -0400, Eric Sunshine wrote:
> > I considered that, but it doesn't handle nested here-docs, which we
> > actually have in the test suite. For instance, from t9300-fast-import:
> > [...]
> > Nesting could be handled easily enough either by stashing away the
> > opening tag and matching against it later _or_ by doing recursive
> > here-doc folding, however, 'sed' isn't a proper programming language
> > and can't be coerced into doing either of those. (And, it was tricky
> > enough just getting it to handle the nested case with a limited set of
> > recognized tag names, without having to explicitly handle every
> > combination of those names nested inside one another.)
>
> I hesitate to make any suggestion here, as I think we may have passed
> a point of useful cost/benefit in sinking more time into this script.
> But...is switching to awk or perl an option? Our test suite already
> depends on having a vanilla perl, so I don't think it would be a new
> dependency. And it would give you actual data structures.

It would, and I did consider it, however, I was very concerned about
startup cost (launch time) with heavyweight perl considering that it
would have to be run for _every_ test. With 13000+ tests, that cost
was a very real concern, especially for Windows users, but even for
MacOS users (such as myself, for which the full test suite already
takes probably close to 30 minutes to run, even on a ram drive). So, I
wanted something very lightweight (and deliberately used that word in
the commit message), and 'sed' seemed the lightest-weight of the
bunch.

'awk' might be about as lightweight as 'sed', and it may even be
possible to coerce it into handling the task (since the linter's job
is primarily just a bunch of regex matching with very little
"manipulating"). v1 of the linter was somewhat simpler and didn't deal
with these more complex cases, such as nested here-docs. v1 also did
rather more "manipulating" of the script since the result was meant to
be run by the shell. When it came time to implement v2, which detects
broken &&-chains itself by textual inspection, most of the
functionality (coming from v1) was already implemented in 'sed', so
'awk' never really came up as a candidate since rewriting the script
from scratch in 'awk' didn't seem like a good idea. (And, at the time
v2 was started, I didn't know that these more complex cases would
arise.) So, 'awk' might be a viable alternative, and perhaps I'll take
a stab at it for fun at some point (or not), but I don't think there's
a pressing need right now.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-31 18:55               ` Eric Sunshine
@ 2018-07-31 19:08                 ` Jeff King
  0 siblings, 0 replies; 123+ messages in thread
From: Jeff King @ 2018-07-31 19:08 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Jonathan Nieder, Git List, Elijah Newren, Johannes Sixt,
	Jonathan Tan, Stefan Beller, Junio C Hamano, Luke Diamand

On Tue, Jul 31, 2018 at 02:55:51PM -0400, Eric Sunshine wrote:

> > I hesitate to make any suggestion here, as I think we may have passed
> > a point of useful cost/benefit in sinking more time into this script.
> > But...is switching to awk or perl an option? Our test suite already
> > depends on having a vanilla perl, so I don't think it would be a new
> > dependency. And it would give you actual data structures.
> 
> It would, and I did consider it, however, I was very concerned about
> startup cost (launch time) with heavyweight perl considering that it
> would have to be run for _every_ test. With 13000+ tests, that cost
> was a very real concern, especially for Windows users, but even for
> MacOS users (such as myself, for which the full test suite already
> takes probably close to 30 minutes to run, even on a ram drive). So, I
> wanted something very lightweight (and deliberately used that word in
> the commit message), and 'sed' seemed the lightest-weight of the
> bunch.

Both perl and sed seem about the same on my system (sometimes one is
faster than the other, and sometimes vice versa). However, I expect for
Windows the problem is not how big the child executable is, but running
a child process at all. I might be wrong, though.

> 'awk' might be about as lightweight as 'sed', and it may even be
> possible to coerce it into handling the task (since the linter's job
> is primarily just a bunch of regex matching with very little
> "manipulating"). v1 of the linter was somewhat simpler and didn't deal
> with these more complex cases, such as nested here-docs. v1 also did
> rather more "manipulating" of the script since the result was meant to
> be run by the shell. When it came time to implement v2, which detects
> broken &&-chains itself by textual inspection, most of the
> functionality (coming from v1) was already implemented in 'sed', so
> 'awk' never really came up as a candidate since rewriting the script
> from scratch in 'awk' didn't seem like a good idea. (And, at the time
> v2 was started, I didn't know that these more complex cases would
> arise.) So, 'awk' might be a viable alternative, and perhaps I'll take
> a stab at it for fun at some point (or not), but I don't think there's
> a pressing need right now.

Yeah, I agree with that.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding
  2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
                     ` (9 preceding siblings ...)
  2018-07-11  6:46   ` [PATCH v2 10/10] t/chainlint: add chainlint "specialized" " Eric Sunshine
@ 2018-08-07  8:21   ` Eric Sunshine
  2018-08-07  8:21     ` [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
                       ` (6 more replies)
  10 siblings, 7 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

This series improves chainlint's robustness when faced with the sort of
unusual shell coding in contrib/subtree/t7900 which triggered a
false-positive, as reported by Jonathan[1]. Jonathan has already
rewritten[2] that code to be cleaner and more easily understood (and,
consequently, to avoid triggering the false-positive), thus the
improvements in this series are not strictly necessary.

Nevertheless, it seems prudent to make chainlint more robust against
such unusual coding as an aid to future less-experienced test writers,
making it less likely for them to trigger a false-positive and waste
time trying to decipher a non-existent problem (in their code).

In [3], I said that 'sed' couldn't "be coerced" into dealing with nested
here-docs with arbitrary tag names (explaining why it recognized only a
"blessed" set of hard-coded names). However, I put a bit of thought into
it and figured out how to do it. Patch 1/5 is the result.

This applies atop 'master'.

[1]: https://public-inbox.org/git/20180730181356.GA156463@aiede.svl.corp.google.com/
[2]: https://public-inbox.org/git/20180730190738.GD156463@aiede.svl.corp.google.com/
[3]: https://public-inbox.org/git/CAPig+cRTgh6DStUdmXqvhbL_7sQY6wu21h27rjq_i=kZ_d+LAw@mail.gmail.com/

Eric Sunshine (5):
  chainlint: match arbitrary here-docs tags rather than hard-coded names
  chainlint: recognize multi-line $(...) when command cuddled with "$("
  chainlint: let here-doc and multi-line string commence on same line
  chainlint: recognize multi-line quoted strings more robustly
  chainlint: add test of pathological case which triggered false
    positive

 t/chainlint.sed                               | 98 ++++++++++++-------
 t/chainlint/here-doc-close-subshell.expect    |  2 +
 t/chainlint/here-doc-close-subshell.test      |  5 +
 .../here-doc-multi-line-command-subst.expect  |  5 +
 .../here-doc-multi-line-command-subst.test    |  9 ++
 t/chainlint/here-doc-multi-line-string.expect |  4 +
 t/chainlint/here-doc-multi-line-string.test   |  8 ++
 t/chainlint/here-doc.expect                   |  2 +
 t/chainlint/here-doc.test                     |  7 ++
 ...ti-line-nested-command-substitution.expect | 11 ++-
 ...ulti-line-nested-command-substitution.test | 11 ++-
 t/chainlint/multi-line-string.expect          | 10 +-
 t/chainlint/multi-line-string.test            | 12 +++
 t/chainlint/nested-here-doc.expect            |  2 +
 t/chainlint/nested-here-doc.test              | 10 ++
 t/chainlint/subshell-here-doc.expect          |  4 +
 t/chainlint/subshell-here-doc.test            |  8 ++
 t/chainlint/t7900-subtree.expect              | 10 ++
 t/chainlint/t7900-subtree.test                | 22 +++++
 19 files changed, 199 insertions(+), 41 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
@ 2018-08-07  8:21     ` Eric Sunshine
  2018-08-08 22:50       ` Jeff King
  2018-08-07  8:21     ` [PATCH 2/5] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
                       ` (5 subsequent siblings)
  6 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.

At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.

In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.

To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                      | 57 +++++++++++++++++-----------
 t/chainlint/here-doc.expect          |  2 +
 t/chainlint/here-doc.test            |  7 ++++
 t/chainlint/nested-here-doc.expect   |  2 +
 t/chainlint/nested-here-doc.test     | 10 +++++
 t/chainlint/subshell-here-doc.expect |  4 ++
 t/chainlint/subshell-here-doc.test   |  8 ++++
 7 files changed, 67 insertions(+), 23 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 5f0882cb38..bd76c5d181 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -61,6 +61,22 @@
 # "else", and "fi" in if-then-else likewise must not end with "&&", thus
 # receives similar treatment.
 #
+# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
+# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
+# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# As each subsequent line is read, it is appended to the target line and a
+# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
+# the content inside "<...>" matches the entirety of the newly-read line. For
+# instance, if the next line read is "some data", when concatenated with the
+# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
+# to see if "EOF" matches "some data". Since it doesn't, the next line is
+# attempted. When a line consisting of only "EOF" (and possible whitespace) is
+# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
+# in which case the "EOF" inside "<...>" does match the text following the
+# newline, thus the closing here-doc tag has been found. The closing tag line
+# and the "<...>" prefix on the target line are then discarded, leaving just
+# the target line "cat >out".
+#
 # To facilitate regression testing (and manual debugging), a ">" annotation is
 # applied to the line containing ")" which closes a subshell, ">>" to a line
 # closing a nested subshell, and ">>>" to a line closing both at once. This
@@ -78,14 +94,17 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\]*EOF[ 	]*/ {
-	s/[ 	]*<<[ 	]*[-\\]*EOF//
-	h
+/<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ {
+	s/^\(.*\)<<[ 	]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
+	s/[ 	]*<<//
 	:hereslurp
 	N
-	s/.*\n//
-	/^[ 	]*EOF[ 	]*$/!bhereslurp
-	x
+	/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+		s/\n.*$//
+		bhereslurp
+	}
+	s/^<[^>]*>//
+	s/\n.*$//
 }
 
 # one-liner "(...) &&"
@@ -139,9 +158,7 @@ s/.*\n//
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 # here-doc -- swallow it
-/<<[ 	]*[-\\]*EOF/bheredoc
-/<<[ 	]*[-\\]*EOT/bheredoc
-/<<[ 	]*[-\\]*INPUT_END/bheredoc
+/<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -249,23 +266,17 @@ s/\n//
 bcheckchain
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
-# the command to which it was attached); take care to handle here-docs nested
-# within here-docs by only recognizing closing tag matching outer here-doc
-# opening tag
+# the command to which it was attached)
 :heredoc
-/EOF/{ s/[ 	]*<<[ 	]*[-\\]*EOF//; s/^/EOF/; }
-/EOT/{ s/[ 	]*<<[ 	]*[-\\]*EOT//; s/^/EOT/; }
-/INPUT_END/{ s/[ 	]*<<[ 	]*[-\\]*INPUT_END//; s/^/INPUT_END/; }
+s/^\(.*\)<<[ 	]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
+s/[ 	]*<<//
 :hereslurpsub
 N
-/^EOF.*\n[ 	]*EOF[ 	]*$/bhereclose
-/^EOT.*\n[ 	]*EOT[ 	]*$/bhereclose
-/^INPUT_END.*\n[ 	]*INPUT_END[ 	]*$/bhereclose
-bhereslurpsub
-:hereclose
-s/^EOF//
-s/^EOT//
-s/^INPUT_END//
+/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+	s/\n.*$//
+	bhereslurpsub
+}
+s/^<[^>]*>//
 s/\n.*$//
 bcheckchain
 
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 2328fe7753..33bc3cc0b4 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,3 +1,5 @@
 boodle wobba        gorgo snoot        wafta snurb &&
 
+cat >foo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index bd36f6e1d3..3736fd2cb4 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -7,6 +7,13 @@ quoth the raven,
 nevermore...
 EOF
 
+# LINT: swallow here-doc with arbitrary tag
+cat <<-ARBITRARY >foo &&
+snoz
+boz
+woz
+ARBITRARY
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index 559301e005..0c9ef1cfc6 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,3 +1,5 @@
+cat >foop &&
+
 (
 	cat &&
 ?!AMP?!	cat
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
index 027e0bb3ff..f35404bf0f 100644
--- a/t/chainlint/nested-here-doc.test
+++ b/t/chainlint/nested-here-doc.test
@@ -1,3 +1,13 @@
+# LINT: inner "EOF" not misintrepreted as closing ARBITRARY here-doc
+cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+	nozzle
+	noodle
+EOF
+formp
+ARBITRARY
+
 (
 # LINT: inner "EOF" not misintrepreted as closing INPUT_END here-doc
 	cat <<-\INPUT_END &&
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 19d5aff233..7c2da63bc7 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -2,4 +2,8 @@
 	echo wobba 	       gorgo snoot 	       wafta snurb &&
 ?!AMP?!	cat >bip
 	echo >bop
+>) &&
+(
+	cat >bup &&
+	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index 9c3564c247..05139af0b5 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -20,4 +20,12 @@
 	wednesday
 	pugsly
 	EOF
+) &&
+(
+# LINT: swallow here-doc with arbitrary tag
+	cat <<-\ARBITRARY >bup &&
+	glink
+	FIZZ
+	ARBITRARY
+	meep
 )
-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 2/5] chainlint: recognize multi-line $(...) when command cuddled with "$("
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  2018-08-07  8:21     ` [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
@ 2018-08-07  8:21     ` Eric Sunshine
  2018-08-07  8:21     ` [PATCH 3/5] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

For multi-line $(...) expressions nested within subshells, chainlint.sed
only recognizes:

    x=$(
        echo foo &&
        ...

but it is not unlikely that test authors may also cuddle the command
with the opening "$(", so support that style, as well:

    x=$(echo foo &&
        ...

The closing ")" is already correctly recognized when cuddled or not.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                       |  2 +-
 .../multi-line-nested-command-substitution.expect     | 11 ++++++++++-
 .../multi-line-nested-command-substitution.test       | 11 ++++++++++-
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index bd76c5d181..a0726d3e7d 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -216,7 +216,7 @@ s/.*\n//
 # "$(...)" -- command substitution; not closing ")"
 /\$([^)][^)]*)[^)]*$/bcheckchain
 # multi-line "$(...\n...)" -- command substitution; treat as nested subshell
-/\$([ 	]*$/bnest
+/\$([^)]*$/bnest
 # "=(...)" -- Bash array assignment; not closing ")"
 /=(/bcheckchain
 # closing "...) &&"
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
index 19c023b1c8..59b6c8b850 100644
--- a/t/chainlint/multi-line-nested-command-substitution.expect
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -6,4 +6,13 @@
 >>	) &&
 	echo ok
 >) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+>>	) &&
+	y=$(echo baz |
+>>		fip) &&
+	echo fail
+>)
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
index ca0620ab6b..300058341b 100644
--- a/t/chainlint/multi-line-nested-command-substitution.test
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -6,4 +6,13 @@
 	) &&
 	echo ok
 ) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+	) &&
+	y=$(echo baz |
+		fip) &&
+	echo fail
+)
-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 3/5] chainlint: let here-doc and multi-line string commence on same line
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  2018-08-07  8:21     ` [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
  2018-08-07  8:21     ` [PATCH 2/5] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
@ 2018-08-07  8:21     ` Eric Sunshine
  2018-08-07  8:21     ` [PATCH 4/5] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

After swallowing a here-doc, chainlint.sed assumes that no other
processing needs to be done on the line aside from checking for &&-chain
breakage; likewise, after folding a multi-line quoted string. However,
it's conceivable (even if unlikely in practice) that both a here-doc and
a multi-line quoted string might commence on the same line:

    cat <<\EOF && echo "foo
    bar"
    data
    EOF

Support this case by sending the line (after swallowing and folding)
through the normal processing sequence rather than jumping directly to
the check for broken &&-chain.

This change also allows other somewhat pathological cases to be handled,
such as closing a subshell on the same line starting a here-doc:

    (
        cat <<-\INPUT)
        data
        INPUT

or, for instance, opening a multi-line $(...) expression on the same
line starting a here-doc:

    x=$(cat <<-\END &&
        data
        END
        echo "x")

among others.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                      | 7 ++++---
 t/chainlint/here-doc-close-subshell.expect           | 2 ++
 t/chainlint/here-doc-close-subshell.test             | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.expect | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.test   | 9 +++++++++
 t/chainlint/here-doc-multi-line-string.expect        | 4 ++++
 t/chainlint/here-doc-multi-line-string.test          | 8 ++++++++
 7 files changed, 37 insertions(+), 3 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test

diff --git a/t/chainlint.sed b/t/chainlint.sed
index a0726d3e7d..6c891bf383 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -157,6 +157,7 @@ s/.*\n//
 /^[^']*'[^']*$/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
+:folded
 # here-doc -- swallow it
 /<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
@@ -255,7 +256,7 @@ s/"//g
 N
 s/\n//
 /"/!bdqstring
-bcheckchain
+bfolded
 
 # found multi-line single-quoted string '...\n...' -- slurp until end of string
 :sqstring
@@ -263,7 +264,7 @@ s/'//g
 N
 s/\n//
 /'/!bsqstring
-bcheckchain
+bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
@@ -278,7 +279,7 @@ N
 }
 s/^<[^>]*>//
 s/\n.*$//
-bcheckchain
+bfolded
 
 # found "case ... in" -- pass through untouched
 :case
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
new file mode 100644
index 0000000000..f011e335e5
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -0,0 +1,2 @@
+(
+>	cat)
diff --git a/t/chainlint/here-doc-close-subshell.test b/t/chainlint/here-doc-close-subshell.test
new file mode 100644
index 0000000000..b857ff5467
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.test
@@ -0,0 +1,5 @@
+(
+# LINT: line contains here-doc and closes nested subshell
+	cat <<-\INPUT)
+	fizz
+	INPUT
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
new file mode 100644
index 0000000000..e5fb752d2f
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -0,0 +1,5 @@
+(
+	x=$(bobble &&
+?!AMP?!>>		wiffle)
+	echo $x
+>)
diff --git a/t/chainlint/here-doc-multi-line-command-subst.test b/t/chainlint/here-doc-multi-line-command-subst.test
new file mode 100644
index 0000000000..899bc5de8b
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.test
@@ -0,0 +1,9 @@
+(
+# LINT: line contains here-doc and opens multi-line $(...)
+	x=$(bobble <<-\END &&
+		fossil
+		vegetable
+		END
+		wiffle)
+	echo $x
+)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
new file mode 100644
index 0000000000..1e5b724b9d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -0,0 +1,4 @@
+(
+?!AMP?!	cat && echo multi-line	string"
+	bap
+>)
diff --git a/t/chainlint/here-doc-multi-line-string.test b/t/chainlint/here-doc-multi-line-string.test
new file mode 100644
index 0000000000..a53edbcc8d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.test
@@ -0,0 +1,8 @@
+(
+# LINT: line contains here-doc and opens multi-line string
+	cat <<-\TXT && echo "multi-line
+	string"
+	fizzle
+	TXT
+	bap
+)
-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 4/5] chainlint: recognize multi-line quoted strings more robustly
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                       ` (2 preceding siblings ...)
  2018-08-07  8:21     ` [PATCH 3/5] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
@ 2018-08-07  8:21     ` Eric Sunshine
  2018-08-07  8:21     ` [PATCH 5/5] chainlint: add test of pathological case which triggered false positive Eric Sunshine
                       ` (2 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

chainlint.sed recognizes multi-line quoted strings within subshells:

    echo "abc
        def" >out &&

so it can avoid incorrectly classifying lines internal to the string as
breaking the &&-chain. To identify the first line of a multi-line
string, it checks if the line contains a single quote. However, this is
fragile and can be easily fooled by a line containing multiple strings:

    echo "xyz" "abc
        def" >out &&

Make detection more robust by checking for an odd number of quotes
rather than only a single one.

(Escaped quotes are not handled, but support may be added later.)

The original multi-line string recognizer rather cavalierly threw away
all but the final quote, whereas the new one is careful to retain all
quotes, so the "expected" output of a couple existing chainlint tests is
updated to account for this new behavior.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                               | 32 +++++++++++++------
 t/chainlint/here-doc-multi-line-string.expect |  2 +-
 t/chainlint/multi-line-string.expect          | 10 ++++--
 t/chainlint/multi-line-string.test            | 12 +++++++
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 6c891bf383..338163c2f5 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -151,10 +151,10 @@ s/.*\n//
 :slurp
 # incomplete line "...\"
 /\\$/bincomplete
-# multi-line quoted string "...\n..."
-/^[^"]*"[^"]*$/bdqstring
-# multi-line quoted string '...\n...' (but not contraction in string "it's so")
-/^[^']*'[^']*$/{
+# multi-line quoted string "...\n..."?
+/"/bdqstring
+# multi-line quoted string '...\n...'? (but not contraction in string "it's")
+/'/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 :folded
@@ -250,20 +250,32 @@ N
 s/\\\n//
 bslurp
 
-# found multi-line double-quoted string "...\n..." -- slurp until end of string
+# check for multi-line double-quoted string "...\n..." -- fold to one line
 :dqstring
-s/"//g
+# remove all quote pairs
+s/"\([^"]*\)"/@!\1@!/g
+# done if no dangling quote
+/"/!bdqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/"/!bdqstring
+bdqstring
+:dqdone
+s/@!/"/g
 bfolded
 
-# found multi-line single-quoted string '...\n...' -- slurp until end of string
+# check for multi-line single-quoted string '...\n...' -- fold to one line
 :sqstring
-s/'//g
+# remove all quote pairs
+s/'\([^']*\)'/@!\1@!/g
+# done if no dangling quote
+/'/!bsqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/'/!bsqstring
+bsqstring
+:sqdone
+s/@!/'/g
 bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 1e5b724b9d..32038a070c 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,4 @@
 (
-?!AMP?!	cat && echo multi-line	string"
+?!AMP?!	cat && echo "multi-line	string"
 	bap
 >)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index 8334c4cc8e..170cb59993 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,9 +1,15 @@
 (
-	x=line 1		line 2		line 3" &&
-?!AMP?!	y=line 1		line2'
+	x="line 1		line 2		line 3" &&
+?!AMP?!	y='line 1		line2'
 	foobar
 >) &&
 (
 	echo "there's nothing to see here" &&
 	exit
+>) &&
+(
+	echo "xyz" "abc		def		ghi" &&
+	echo 'xyz' 'abc		def		ghi' &&
+	echo 'xyz' "abc		def		ghi" &&
+	barfoo
 >)
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
index 14cb44d51c..287ab89705 100644
--- a/t/chainlint/multi-line-string.test
+++ b/t/chainlint/multi-line-string.test
@@ -12,4 +12,16 @@
 # LINT: starting multi-line single-quoted string
 	echo "there's nothing to see here" &&
 	exit
+) &&
+(
+	echo "xyz" "abc
+		def
+		ghi" &&
+	echo 'xyz' 'abc
+		def
+		ghi' &&
+	echo 'xyz' "abc
+		def
+		ghi" &&
+	barfoo
 )
-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 5/5] chainlint: add test of pathological case which triggered false positive
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                       ` (3 preceding siblings ...)
  2018-08-07  8:21     ` [PATCH 4/5] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
@ 2018-08-07  8:21     ` Eric Sunshine
  2018-08-08 22:53     ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Jeff King
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-07  8:21 UTC (permalink / raw)
  To: git; +Cc: Jonathan Nieder, Jeff King, Eric Sunshine

This extract from contrib/subtree/t7900 triggered a false positive due
to three chainlint limitations:

* recognizing only a "blessed" set of here-doc tag names in a subshell
  ("EOF", "EOT", "INPUT_END"), of which "TXT" is not a member

* inability to recognize multi-line $(...) when the first statement of
  the body is cuddled with the opening "$("

* inability to recognize multiple constructs on a single line, such as
  opening a multi-line $(...) and starting a here-doc

Now that all of these shortcomings have been addressed, turn this rather
pathological bit of shell coding into a chainlint test case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/t7900-subtree.expect | 10 ++++++++++
 t/chainlint/t7900-subtree.test   | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
new file mode 100644
index 0000000000..c9913429e6
--- /dev/null
+++ b/t/chainlint/t7900-subtree.expect
@@ -0,0 +1,10 @@
+(
+	chks="sub1sub2sub3sub4" &&
+	chks_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	chkms="main-sub1main-sub2main-sub3main-sub4" &&
+	chkms_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms$chks"
+>)
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
new file mode 100644
index 0000000000..277d8358df
--- /dev/null
+++ b/t/chainlint/t7900-subtree.test
@@ -0,0 +1,22 @@
+(
+	chks="sub1
+sub2
+sub3
+sub4" &&
+	chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+) &&
+	chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+	chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+) &&
+
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms
+$chks"
+)
-- 
2.18.0.758.g1932418f46


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-07  8:21     ` [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
@ 2018-08-08 22:50       ` Jeff King
  2018-08-09  5:58         ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Jeff King @ 2018-08-08 22:50 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Jonathan Nieder

On Tue, Aug 07, 2018 at 04:21:31AM -0400, Eric Sunshine wrote:

> diff --git a/t/chainlint.sed b/t/chainlint.sed
> index 5f0882cb38..bd76c5d181 100644
> --- a/t/chainlint.sed
> +++ b/t/chainlint.sed
> @@ -61,6 +61,22 @@
>  # "else", and "fi" in if-then-else likewise must not end with "&&", thus
>  # receives similar treatment.
>  #
> +# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
> +# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
> +# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
> +# As each subsequent line is read, it is appended to the target line and a
> +# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
> +# the content inside "<...>" matches the entirety of the newly-read line. For
> +# instance, if the next line read is "some data", when concatenated with the
> +# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
> +# to see if "EOF" matches "some data". Since it doesn't, the next line is
> +# attempted. When a line consisting of only "EOF" (and possible whitespace) is
> +# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
> +# in which case the "EOF" inside "<...>" does match the text following the
> +# newline, thus the closing here-doc tag has been found. The closing tag line
> +# and the "<...>" prefix on the target line are then discarded, leaving just
> +# the target line "cat >out".

Gross, but OK, as long as we would not get confused by a line that
actually started with <EOF> at the start.

> +/<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ {
> +	s/^\(.*\)<<[ 	]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
> +	s/[ 	]*<<//

Here-docs can use lowercase, too, though I'd personally frown on that
from a style perspective.

It looks like this doesn't catch:

  cat <<'EOF'
  EOF

either. I think we prefer the backslash style, but there are quite a few
<<-'EOF' hits. Is it covered somewhere else?

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                       ` (4 preceding siblings ...)
  2018-08-07  8:21     ` [PATCH 5/5] chainlint: add test of pathological case which triggered false positive Eric Sunshine
@ 2018-08-08 22:53     ` Jeff King
  2018-08-09  0:44       ` Junio C Hamano
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
  6 siblings, 1 reply; 123+ messages in thread
From: Jeff King @ 2018-08-08 22:53 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Jonathan Nieder

On Tue, Aug 07, 2018 at 04:21:30AM -0400, Eric Sunshine wrote:

> This series improves chainlint's robustness when faced with the sort of
> unusual shell coding in contrib/subtree/t7900 which triggered a
> false-positive, as reported by Jonathan[1]. Jonathan has already
> rewritten[2] that code to be cleaner and more easily understood (and,
> consequently, to avoid triggering the false-positive), thus the
> improvements in this series are not strictly necessary.
> 
> Nevertheless, it seems prudent to make chainlint more robust against
> such unusual coding as an aid to future less-experienced test writers,
> making it less likely for them to trigger a false-positive and waste
> time trying to decipher a non-existent problem (in their code).
> 
> In [3], I said that 'sed' couldn't "be coerced" into dealing with nested
> here-docs with arbitrary tag names (explaining why it recognized only a
> "blessed" set of hard-coded names). However, I put a bit of thought into
> it and figured out how to do it. Patch 1/5 is the result.
> 
> This applies atop 'master'.

I had two minor comments on the first patch. I'll admit my eyes glazed
over looking at the rest of them, and to make any kind of intelligent
review I'd need to spend an hour understanding how the sed script works.
Which frankly, I'm not sure is worth it. Given the empirical results
(both on the real code base and the new tests you add) and the low-risk
nature (it's linting our tests, after all, not code users run), I'd be
inclined to say it's not making anything worse, and probably making
things better. We can find out about any further short-comings in the
wild.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding
  2018-08-08 22:53     ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Jeff King
@ 2018-08-09  0:44       ` Junio C Hamano
  0 siblings, 0 replies; 123+ messages in thread
From: Junio C Hamano @ 2018-08-09  0:44 UTC (permalink / raw)
  To: Jeff King; +Cc: Eric Sunshine, git, Jonathan Nieder

Jeff King <peff@peff.net> writes:

> I had two minor comments on the first patch. I'll admit my eyes glazed
> over looking at the rest of them, and to make any kind of intelligent
> review I'd need to spend an hour understanding how the sed script works.
> Which frankly, I'm not sure is worth it.

Didn't I make this prediction when we started the "text inspection"
approach that it quickly go downhill resulting in unmaintainable
mess rather quickly ;-)?

> Given the empirical results
> (both on the real code base and the new tests you add) and the low-risk
> nature (it's linting our tests, after all, not code users run), I'd be
> inclined to say it's not making anything worse, and probably making
> things better. We can find out about any further short-comings in the
> wild.

Amen to that.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-08 22:50       ` Jeff King
@ 2018-08-09  5:58         ` Eric Sunshine
  2018-08-09 14:26           ` Jeff King
  0 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-08-09  5:58 UTC (permalink / raw)
  To: Jeff King; +Cc: Git List, Jonathan Nieder

On Wed, Aug 8, 2018 at 6:50 PM Jeff King <peff@peff.net> wrote:
> On Tue, Aug 07, 2018 at 04:21:31AM -0400, Eric Sunshine wrote:
> > +# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
> > +# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
> > +# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
>
> Gross, but OK, as long as we would not get confused by a line that
> actually started with <EOF> at the start.

It can't get confused by such a line. There here-doc swallower
prepends that when it starts the swallowing process and removes it add
the end. Even if a line actually started with that, it would become
"<EOF><EOF>cmd" while swallowing the here-doc, and be restored to
"<EOF>cmd" at the end. Stripping the "<EOF>" is done non-greedily, so
it wouldn't remove both of them. Likewise, non-greedy matching is used
for pulling the "EOF" out of the "<...>" when trying to match against
the terminating "EOF" line, so there can be no confusion.

> > +/<<[         ]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ {
> > +     s/^\(.*\)<<[    ]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
> > +     s/[     ]*<<//
>
> Here-docs can use lowercase, too, though I'd personally frown on that
> from a style perspective.

Yeah, I was going with the tighter uppercase-only which Jonathan
suggested[1], but I guess it wouldn't hurt to re-roll to allow
lowercase too.

[1]: https://public-inbox.org/git/20180730205914.GE156463@aiede.svl.corp.google.com/

> It looks like this doesn't catch:
>
>   cat <<'EOF'
>   EOF
>
> either. I think we prefer the backslash style, but there are quite a few
> <<-'EOF' hits. Is it covered somewhere else?

No. I've gotten so used to \EOF in this codebase that it didn't occur
to me to even think about 'EOF', but a re-roll could add that, as
well.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-09  5:58         ` Eric Sunshine
@ 2018-08-09 14:26           ` Jeff King
  0 siblings, 0 replies; 123+ messages in thread
From: Jeff King @ 2018-08-09 14:26 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List, Jonathan Nieder

On Thu, Aug 09, 2018 at 01:58:05AM -0400, Eric Sunshine wrote:

> On Wed, Aug 8, 2018 at 6:50 PM Jeff King <peff@peff.net> wrote:
> > On Tue, Aug 07, 2018 at 04:21:31AM -0400, Eric Sunshine wrote:
> > > +# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
> > > +# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
> > > +# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
> >
> > Gross, but OK, as long as we would not get confused by a line that
> > actually started with <EOF> at the start.
> 
> It can't get confused by such a line. There here-doc swallower
> prepends that when it starts the swallowing process and removes it add
> the end. Even if a line actually started with that, it would become
> "<EOF><EOF>cmd" while swallowing the here-doc, and be restored to
> "<EOF>cmd" at the end. Stripping the "<EOF>" is done non-greedily, so
> it wouldn't remove both of them. Likewise, non-greedy matching is used
> for pulling the "EOF" out of the "<...>" when trying to match against
> the terminating "EOF" line, so there can be no confusion.

Thanks. I figured you probably had thought of that, but it seemed easier
to ask than to wade through the sed code (I do feel like a bad person to
give that answer, because IMHO one of the key things that makes open
source work is a willingness to dig in yourself rather than asking; but
I am making an exception for this sed code).

> Yeah, I was going with the tighter uppercase-only which Jonathan
> suggested[1], but I guess it wouldn't hurt to re-roll to allow
> lowercase too.
>
> [...]
>
> No. I've gotten so used to \EOF in this codebase that it didn't occur
> to me to even think about 'EOF', but a re-roll could add that, as
> well.

Thanks. I could take or leave such fixes, since I think our style
discourages both, so I'll leave it up to you whether you want to pursue
them.

-Peff

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 0/6] chainlint: improve robustness against "unusual" shell coding
  2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                       ` (5 preceding siblings ...)
  2018-08-08 22:53     ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Jeff King
@ 2018-08-13  8:47     ` " Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
                         ` (6 more replies)
  6 siblings, 7 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

This is a re-roll of [1] which improves chainlint's robustness in the
face of unusual shell coding such as in contrib/subtree/t7900 which
triggered a false-positive[2].

Changes since v1:

* recognize lowercase in here-doc tag names (in addition to uppercase)
* recognize 'quoted' here-doc tag names (in addition to \escaped)

Patch 2/6 is new. Range-diff below.

[1]: https://public-inbox.org/git/20180807082135.60913-1-sunshine@sunshineco.com/
[2]: https://public-inbox.org/git/20180730181356.GA156463@aiede.svl.corp.google.com/

Eric Sunshine (6):
  chainlint: match arbitrary here-docs tags rather than hard-coded names
  chainlint: match 'quoted' here-doc tags
  chainlint: recognize multi-line $(...) when command cuddled with "$("
  chainlint: let here-doc and multi-line string commence on same line
  chainlint: recognize multi-line quoted strings more robustly
  chainlint: add test of pathological case which triggered false
    positive

 t/chainlint.sed                               | 98 ++++++++++++-------
 t/chainlint/here-doc-close-subshell.expect    |  2 +
 t/chainlint/here-doc-close-subshell.test      |  5 +
 .../here-doc-multi-line-command-subst.expect  |  5 +
 .../here-doc-multi-line-command-subst.test    |  9 ++
 t/chainlint/here-doc-multi-line-string.expect |  4 +
 t/chainlint/here-doc-multi-line-string.test   |  8 ++
 t/chainlint/here-doc.expect                   |  4 +
 t/chainlint/here-doc.test                     | 14 +++
 ...ti-line-nested-command-substitution.expect | 11 ++-
 ...ulti-line-nested-command-substitution.test | 11 ++-
 t/chainlint/multi-line-string.expect          | 10 +-
 t/chainlint/multi-line-string.test            | 12 +++
 t/chainlint/nested-here-doc.expect            |  2 +
 t/chainlint/nested-here-doc.test              | 10 ++
 t/chainlint/subshell-here-doc.expect          |  5 +
 t/chainlint/subshell-here-doc.test            | 12 +++
 t/chainlint/t7900-subtree.expect              | 10 ++
 t/chainlint/t7900-subtree.test                | 22 +++++
 19 files changed, 213 insertions(+), 41 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

Range-diff against v1:
1:  d9ed356b97 ! 1:  f4c606cb7a chainlint: match arbitrary here-docs tags rather than hard-coded names
    @@ -64,8 +64,8 @@
     -/<<[ 	]*[-\\]*EOF[ 	]*/ {
     -	s/[ 	]*<<[ 	]*[-\\]*EOF//
     -	h
    -+/<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ {
    -+	s/^\(.*\)<<[ 	]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
    ++/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
    ++	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
     +	s/[ 	]*<<//
      	:hereslurp
      	N
    @@ -88,7 +88,7 @@
     -/<<[ 	]*[-\\]*EOF/bheredoc
     -/<<[ 	]*[-\\]*EOT/bheredoc
     -/<<[ 	]*[-\\]*INPUT_END/bheredoc
    -+/<<[ 	]*[-\\]*[A-Z0-9_][A-Z0-9_]*/bheredoc
    ++/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
      # comment or empty line -- discard since final non-comment, non-empty line
      # before closing ")", "done", "elsif", "else", or "fi" will need to be
      # re-visited to drop "suspect" marking since final line of those constructs
    @@ -104,7 +104,7 @@
     -/EOF/{ s/[ 	]*<<[ 	]*[-\\]*EOF//; s/^/EOF/; }
     -/EOT/{ s/[ 	]*<<[ 	]*[-\\]*EOT//; s/^/EOT/; }
     -/INPUT_END/{ s/[ 	]*<<[ 	]*[-\\]*INPUT_END//; s/^/INPUT_END/; }
    -+s/^\(.*\)<<[ 	]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1<</
    ++s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
     +s/[ 	]*<<//
      :hereslurpsub
      N
    @@ -143,11 +143,11 @@
      EOF
      
     +# LINT: swallow here-doc with arbitrary tag
    -+cat <<-ARBITRARY >foo &&
    ++cat <<-Arbitrary_Tag_42 >foo &&
     +snoz
     +boz
     +woz
    -+ARBITRARY
    ++Arbitrary_Tag_42
     +
      # LINT: swallow here-doc (EOF is last line of test)
      horticulture <<\EOF
-:  ---------- > 2:  61c0d9c979 chainlint: match 'quoted' here-doc tags
2:  d63920cdd5 = 3:  b97a05aa9c chainlint: recognize multi-line $(...) when command cuddled with "$("
3:  a5078923ef = 4:  9d4d2b6c4d chainlint: let here-doc and multi-line string commence on same line
4:  4841ca6ac9 = 5:  0a7f533889 chainlint: recognize multi-line quoted strings more robustly
5:  e9eb45a2dc = 6:  d42bad0323 chainlint: add test of pathological case which triggered false positive
-- 
2.18.0.267.gbc8be36ecb

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags Eric Sunshine
                         ` (5 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.

At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.

In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.

To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                      | 57 +++++++++++++++++-----------
 t/chainlint/here-doc.expect          |  2 +
 t/chainlint/here-doc.test            |  7 ++++
 t/chainlint/nested-here-doc.expect   |  2 +
 t/chainlint/nested-here-doc.test     | 10 +++++
 t/chainlint/subshell-here-doc.expect |  4 ++
 t/chainlint/subshell-here-doc.test   |  8 ++++
 7 files changed, 67 insertions(+), 23 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 5f0882cb38..2af1a687f8 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -61,6 +61,22 @@
 # "else", and "fi" in if-then-else likewise must not end with "&&", thus
 # receives similar treatment.
 #
+# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
+# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
+# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# As each subsequent line is read, it is appended to the target line and a
+# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
+# the content inside "<...>" matches the entirety of the newly-read line. For
+# instance, if the next line read is "some data", when concatenated with the
+# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
+# to see if "EOF" matches "some data". Since it doesn't, the next line is
+# attempted. When a line consisting of only "EOF" (and possible whitespace) is
+# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
+# in which case the "EOF" inside "<...>" does match the text following the
+# newline, thus the closing here-doc tag has been found. The closing tag line
+# and the "<...>" prefix on the target line are then discarded, leaving just
+# the target line "cat >out".
+#
 # To facilitate regression testing (and manual debugging), a ">" annotation is
 # applied to the line containing ")" which closes a subshell, ">>" to a line
 # closing a nested subshell, and ">>>" to a line closing both at once. This
@@ -78,14 +94,17 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\]*EOF[ 	]*/ {
-	s/[ 	]*<<[ 	]*[-\\]*EOF//
-	h
+/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+	s/[ 	]*<<//
 	:hereslurp
 	N
-	s/.*\n//
-	/^[ 	]*EOF[ 	]*$/!bhereslurp
-	x
+	/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+		s/\n.*$//
+		bhereslurp
+	}
+	s/^<[^>]*>//
+	s/\n.*$//
 }
 
 # one-liner "(...) &&"
@@ -139,9 +158,7 @@ s/.*\n//
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 # here-doc -- swallow it
-/<<[ 	]*[-\\]*EOF/bheredoc
-/<<[ 	]*[-\\]*EOT/bheredoc
-/<<[ 	]*[-\\]*INPUT_END/bheredoc
+/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -249,23 +266,17 @@ s/\n//
 bcheckchain
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
-# the command to which it was attached); take care to handle here-docs nested
-# within here-docs by only recognizing closing tag matching outer here-doc
-# opening tag
+# the command to which it was attached)
 :heredoc
-/EOF/{ s/[ 	]*<<[ 	]*[-\\]*EOF//; s/^/EOF/; }
-/EOT/{ s/[ 	]*<<[ 	]*[-\\]*EOT//; s/^/EOT/; }
-/INPUT_END/{ s/[ 	]*<<[ 	]*[-\\]*INPUT_END//; s/^/INPUT_END/; }
+s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+s/[ 	]*<<//
 :hereslurpsub
 N
-/^EOF.*\n[ 	]*EOF[ 	]*$/bhereclose
-/^EOT.*\n[ 	]*EOT[ 	]*$/bhereclose
-/^INPUT_END.*\n[ 	]*INPUT_END[ 	]*$/bhereclose
-bhereslurpsub
-:hereclose
-s/^EOF//
-s/^EOT//
-s/^INPUT_END//
+/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+	s/\n.*$//
+	bhereslurpsub
+}
+s/^<[^>]*>//
 s/\n.*$//
 bcheckchain
 
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 2328fe7753..33bc3cc0b4 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,3 +1,5 @@
 boodle wobba        gorgo snoot        wafta snurb &&
 
+cat >foo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index bd36f6e1d3..8986eefe74 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -7,6 +7,13 @@ quoth the raven,
 nevermore...
 EOF
 
+# LINT: swallow here-doc with arbitrary tag
+cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index 559301e005..0c9ef1cfc6 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,3 +1,5 @@
+cat >foop &&
+
 (
 	cat &&
 ?!AMP?!	cat
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
index 027e0bb3ff..f35404bf0f 100644
--- a/t/chainlint/nested-here-doc.test
+++ b/t/chainlint/nested-here-doc.test
@@ -1,3 +1,13 @@
+# LINT: inner "EOF" not misintrepreted as closing ARBITRARY here-doc
+cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+	nozzle
+	noodle
+EOF
+formp
+ARBITRARY
+
 (
 # LINT: inner "EOF" not misintrepreted as closing INPUT_END here-doc
 	cat <<-\INPUT_END &&
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 19d5aff233..7c2da63bc7 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -2,4 +2,8 @@
 	echo wobba 	       gorgo snoot 	       wafta snurb &&
 ?!AMP?!	cat >bip
 	echo >bop
+>) &&
+(
+	cat >bup &&
+	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index 9c3564c247..05139af0b5 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -20,4 +20,12 @@
 	wednesday
 	pugsly
 	EOF
+) &&
+(
+# LINT: swallow here-doc with arbitrary tag
+	cat <<-\ARBITRARY >bup &&
+	glink
+	FIZZ
+	ARBITRARY
+	meep
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-13 19:27         ` Junio C Hamano
  2018-08-13  8:47       ` [PATCH v2 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

A here-doc tag can be quoted ('EOF') or escaped (\EOF) to suppress
interpolation within the body. Although, chainlint recognizes escaped
tags, it does not know about quoted tags. For completeness, teach it to
recognize quoted tags, as well.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                      | 8 ++++----
 t/chainlint/here-doc.expect          | 2 ++
 t/chainlint/here-doc.test            | 7 +++++++
 t/chainlint/subshell-here-doc.expect | 1 +
 t/chainlint/subshell-here-doc.test   | 4 ++++
 5 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 2af1a687f8..2901494e8a 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -94,8 +94,8 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
-	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+/<<[ 	]*[-\\']*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
 	s/[ 	]*<<//
 	:hereslurp
 	N
@@ -158,7 +158,7 @@ s/.*\n//
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 # here-doc -- swallow it
-/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
+/<<[ 	]*[-\\']*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -268,7 +268,7 @@ bcheckchain
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
 :heredoc
-s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
 s/[ 	]*<<//
 :hereslurpsub
 N
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 33bc3cc0b4..aff6568716 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -2,4 +2,6 @@ boodle wobba        gorgo snoot        wafta snurb &&
 
 cat >foo &&
 
+cat >bar &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index 8986eefe74..f2bb14b693 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -14,6 +14,13 @@ boz
 woz
 Arbitrary_Tag_42
 
+# LINT: swallow 'quoted' here-doc
+cat <<'FUMP' >bar &&
+snoz
+boz
+woz
+FUMP
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 7c2da63bc7..7663ea7fc4 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -5,5 +5,6 @@
 >) &&
 (
 	cat >bup &&
+	cat >bup2 &&
 	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index 05139af0b5..b6b5a9b33a 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -27,5 +27,9 @@
 	glink
 	FIZZ
 	ARBITRARY
+	cat <<-'ARBITRARY2' >bup2 &&
+	glink
+	FIZZ
+	ARBITRARY2
 	meep
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$("
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
                         ` (3 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

For multi-line $(...) expressions nested within subshells, chainlint.sed
only recognizes:

    x=$(
        echo foo &&
        ...

but it is not unlikely that test authors may also cuddle the command
with the opening "$(", so support that style, as well:

    x=$(echo foo &&
        ...

The closing ")" is already correctly recognized when cuddled or not.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                       |  2 +-
 .../multi-line-nested-command-substitution.expect     | 11 ++++++++++-
 .../multi-line-nested-command-substitution.test       | 11 ++++++++++-
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 2901494e8a..afb2443a9c 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -216,7 +216,7 @@ s/.*\n//
 # "$(...)" -- command substitution; not closing ")"
 /\$([^)][^)]*)[^)]*$/bcheckchain
 # multi-line "$(...\n...)" -- command substitution; treat as nested subshell
-/\$([ 	]*$/bnest
+/\$([^)]*$/bnest
 # "=(...)" -- Bash array assignment; not closing ")"
 /=(/bcheckchain
 # closing "...) &&"
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
index 19c023b1c8..59b6c8b850 100644
--- a/t/chainlint/multi-line-nested-command-substitution.expect
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -6,4 +6,13 @@
 >>	) &&
 	echo ok
 >) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+>>	) &&
+	y=$(echo baz |
+>>		fip) &&
+	echo fail
+>)
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
index ca0620ab6b..300058341b 100644
--- a/t/chainlint/multi-line-nested-command-substitution.test
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -6,4 +6,13 @@
 	) &&
 	echo ok
 ) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+	) &&
+	y=$(echo baz |
+		fip) &&
+	echo fail
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 4/6] chainlint: let here-doc and multi-line string commence on same line
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
                         ` (2 preceding siblings ...)
  2018-08-13  8:47       ` [PATCH v2 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
                         ` (2 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

After swallowing a here-doc, chainlint.sed assumes that no other
processing needs to be done on the line aside from checking for &&-chain
breakage; likewise, after folding a multi-line quoted string. However,
it's conceivable (even if unlikely in practice) that both a here-doc and
a multi-line quoted string might commence on the same line:

    cat <<\EOF && echo "foo
    bar"
    data
    EOF

Support this case by sending the line (after swallowing and folding)
through the normal processing sequence rather than jumping directly to
the check for broken &&-chain.

This change also allows other somewhat pathological cases to be handled,
such as closing a subshell on the same line starting a here-doc:

    (
        cat <<-\INPUT)
        data
        INPUT

or, for instance, opening a multi-line $(...) expression on the same
line starting a here-doc:

    x=$(cat <<-\END &&
        data
        END
        echo "x")

among others.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                      | 7 ++++---
 t/chainlint/here-doc-close-subshell.expect           | 2 ++
 t/chainlint/here-doc-close-subshell.test             | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.expect | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.test   | 9 +++++++++
 t/chainlint/here-doc-multi-line-string.expect        | 4 ++++
 t/chainlint/here-doc-multi-line-string.test          | 8 ++++++++
 7 files changed, 37 insertions(+), 3 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test

diff --git a/t/chainlint.sed b/t/chainlint.sed
index afb2443a9c..6661e21f20 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -157,6 +157,7 @@ s/.*\n//
 /^[^']*'[^']*$/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
+:folded
 # here-doc -- swallow it
 /<<[ 	]*[-\\']*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
@@ -255,7 +256,7 @@ s/"//g
 N
 s/\n//
 /"/!bdqstring
-bcheckchain
+bfolded
 
 # found multi-line single-quoted string '...\n...' -- slurp until end of string
 :sqstring
@@ -263,7 +264,7 @@ s/'//g
 N
 s/\n//
 /'/!bsqstring
-bcheckchain
+bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
@@ -278,7 +279,7 @@ N
 }
 s/^<[^>]*>//
 s/\n.*$//
-bcheckchain
+bfolded
 
 # found "case ... in" -- pass through untouched
 :case
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
new file mode 100644
index 0000000000..f011e335e5
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -0,0 +1,2 @@
+(
+>	cat)
diff --git a/t/chainlint/here-doc-close-subshell.test b/t/chainlint/here-doc-close-subshell.test
new file mode 100644
index 0000000000..b857ff5467
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.test
@@ -0,0 +1,5 @@
+(
+# LINT: line contains here-doc and closes nested subshell
+	cat <<-\INPUT)
+	fizz
+	INPUT
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
new file mode 100644
index 0000000000..e5fb752d2f
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -0,0 +1,5 @@
+(
+	x=$(bobble &&
+?!AMP?!>>		wiffle)
+	echo $x
+>)
diff --git a/t/chainlint/here-doc-multi-line-command-subst.test b/t/chainlint/here-doc-multi-line-command-subst.test
new file mode 100644
index 0000000000..899bc5de8b
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.test
@@ -0,0 +1,9 @@
+(
+# LINT: line contains here-doc and opens multi-line $(...)
+	x=$(bobble <<-\END &&
+		fossil
+		vegetable
+		END
+		wiffle)
+	echo $x
+)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
new file mode 100644
index 0000000000..1e5b724b9d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -0,0 +1,4 @@
+(
+?!AMP?!	cat && echo multi-line	string"
+	bap
+>)
diff --git a/t/chainlint/here-doc-multi-line-string.test b/t/chainlint/here-doc-multi-line-string.test
new file mode 100644
index 0000000000..a53edbcc8d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.test
@@ -0,0 +1,8 @@
+(
+# LINT: line contains here-doc and opens multi-line string
+	cat <<-\TXT && echo "multi-line
+	string"
+	fizzle
+	TXT
+	bap
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 5/6] chainlint: recognize multi-line quoted strings more robustly
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
                         ` (3 preceding siblings ...)
  2018-08-13  8:47       ` [PATCH v2 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-13  8:47       ` [PATCH v2 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

chainlint.sed recognizes multi-line quoted strings within subshells:

    echo "abc
        def" >out &&

so it can avoid incorrectly classifying lines internal to the string as
breaking the &&-chain. To identify the first line of a multi-line
string, it checks if the line contains a single quote. However, this is
fragile and can be easily fooled by a line containing multiple strings:

    echo "xyz" "abc
        def" >out &&

Make detection more robust by checking for an odd number of quotes
rather than only a single one.

(Escaped quotes are not handled, but support may be added later.)

The original multi-line string recognizer rather cavalierly threw away
all but the final quote, whereas the new one is careful to retain all
quotes, so the "expected" output of a couple existing chainlint tests is
updated to account for this new behavior.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                               | 32 +++++++++++++------
 t/chainlint/here-doc-multi-line-string.expect |  2 +-
 t/chainlint/multi-line-string.expect          | 10 ++++--
 t/chainlint/multi-line-string.test            | 12 +++++++
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 6661e21f20..8544df38df 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -151,10 +151,10 @@ s/.*\n//
 :slurp
 # incomplete line "...\"
 /\\$/bincomplete
-# multi-line quoted string "...\n..."
-/^[^"]*"[^"]*$/bdqstring
-# multi-line quoted string '...\n...' (but not contraction in string "it's so")
-/^[^']*'[^']*$/{
+# multi-line quoted string "...\n..."?
+/"/bdqstring
+# multi-line quoted string '...\n...'? (but not contraction in string "it's")
+/'/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 :folded
@@ -250,20 +250,32 @@ N
 s/\\\n//
 bslurp
 
-# found multi-line double-quoted string "...\n..." -- slurp until end of string
+# check for multi-line double-quoted string "...\n..." -- fold to one line
 :dqstring
-s/"//g
+# remove all quote pairs
+s/"\([^"]*\)"/@!\1@!/g
+# done if no dangling quote
+/"/!bdqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/"/!bdqstring
+bdqstring
+:dqdone
+s/@!/"/g
 bfolded
 
-# found multi-line single-quoted string '...\n...' -- slurp until end of string
+# check for multi-line single-quoted string '...\n...' -- fold to one line
 :sqstring
-s/'//g
+# remove all quote pairs
+s/'\([^']*\)'/@!\1@!/g
+# done if no dangling quote
+/'/!bsqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/'/!bsqstring
+bsqstring
+:sqdone
+s/@!/'/g
 bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 1e5b724b9d..32038a070c 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,4 @@
 (
-?!AMP?!	cat && echo multi-line	string"
+?!AMP?!	cat && echo "multi-line	string"
 	bap
 >)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index 8334c4cc8e..170cb59993 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,9 +1,15 @@
 (
-	x=line 1		line 2		line 3" &&
-?!AMP?!	y=line 1		line2'
+	x="line 1		line 2		line 3" &&
+?!AMP?!	y='line 1		line2'
 	foobar
 >) &&
 (
 	echo "there's nothing to see here" &&
 	exit
+>) &&
+(
+	echo "xyz" "abc		def		ghi" &&
+	echo 'xyz' 'abc		def		ghi' &&
+	echo 'xyz' "abc		def		ghi" &&
+	barfoo
 >)
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
index 14cb44d51c..287ab89705 100644
--- a/t/chainlint/multi-line-string.test
+++ b/t/chainlint/multi-line-string.test
@@ -12,4 +12,16 @@
 # LINT: starting multi-line single-quoted string
 	echo "there's nothing to see here" &&
 	exit
+) &&
+(
+	echo "xyz" "abc
+		def
+		ghi" &&
+	echo 'xyz' 'abc
+		def
+		ghi' &&
+	echo 'xyz' "abc
+		def
+		ghi" &&
+	barfoo
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v2 6/6] chainlint: add test of pathological case which triggered false positive
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
                         ` (4 preceding siblings ...)
  2018-08-13  8:47       ` [PATCH v2 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
@ 2018-08-13  8:47       ` Eric Sunshine
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13  8:47 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

This extract from contrib/subtree/t7900 triggered a false positive due
to three chainlint limitations:

* recognizing only a "blessed" set of here-doc tag names in a subshell
  ("EOF", "EOT", "INPUT_END"), of which "TXT" is not a member

* inability to recognize multi-line $(...) when the first statement of
  the body is cuddled with the opening "$("

* inability to recognize multiple constructs on a single line, such as
  opening a multi-line $(...) and starting a here-doc

Now that all of these shortcomings have been addressed, turn this rather
pathological bit of shell coding into a chainlint test case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/t7900-subtree.expect | 10 ++++++++++
 t/chainlint/t7900-subtree.test   | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
new file mode 100644
index 0000000000..c9913429e6
--- /dev/null
+++ b/t/chainlint/t7900-subtree.expect
@@ -0,0 +1,10 @@
+(
+	chks="sub1sub2sub3sub4" &&
+	chks_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	chkms="main-sub1main-sub2main-sub3main-sub4" &&
+	chkms_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms$chks"
+>)
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
new file mode 100644
index 0000000000..277d8358df
--- /dev/null
+++ b/t/chainlint/t7900-subtree.test
@@ -0,0 +1,22 @@
+(
+	chks="sub1
+sub2
+sub3
+sub4" &&
+	chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+) &&
+	chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+	chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+) &&
+
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms
+$chks"
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags
  2018-08-13  8:47       ` [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags Eric Sunshine
@ 2018-08-13 19:27         ` Junio C Hamano
  2018-08-13 20:12           ` Eric Sunshine
  0 siblings, 1 reply; 123+ messages in thread
From: Junio C Hamano @ 2018-08-13 19:27 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Jeff King, Jonathan Nieder

Eric Sunshine <sunshine@sunshineco.com> writes:

> A here-doc tag can be quoted ('EOF') or escaped (\EOF) to suppress
> interpolation within the body. Although, chainlint recognizes escaped
> tags, it does not know about quoted tags. For completeness, teach it to
> recognize quoted tags, as well.

Is this step merely completeness and future-proofing, or do we have
existing instances that the linter would be confused without this
patch?  I am primarily wondering what the reason is that <<"EOF" is
not looked for while we are at it (the answer could be that we
have tests that use <<'EOF' but not <<"EOF").

> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/chainlint.sed                      | 8 ++++----
>  t/chainlint/here-doc.expect          | 2 ++
>  t/chainlint/here-doc.test            | 7 +++++++
>  t/chainlint/subshell-here-doc.expect | 1 +
>  t/chainlint/subshell-here-doc.test   | 4 ++++
>  5 files changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/t/chainlint.sed b/t/chainlint.sed
> index 2af1a687f8..2901494e8a 100644
> --- a/t/chainlint.sed
> +++ b/t/chainlint.sed
> @@ -94,8 +94,8 @@
>  
>  # here-doc -- swallow it to avoid false hits within its body (but keep the
>  # command to which it was attached)
> -/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
> -	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
> +/<<[ 	]*[-\\']*[A-Za-z0-9_]/ {
> +	s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
>  	s/[ 	]*<<//
>  	:hereslurp
>  	N
> @@ -158,7 +158,7 @@ s/.*\n//
>  	/"[^'"]*'[^'"]*"/!bsqstring
>  }
>  # here-doc -- swallow it
> -/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
> +/<<[ 	]*[-\\']*[A-Za-z0-9_]/bheredoc
>  # comment or empty line -- discard since final non-comment, non-empty line
>  # before closing ")", "done", "elsif", "else", or "fi" will need to be
>  # re-visited to drop "suspect" marking since final line of those constructs
> @@ -268,7 +268,7 @@ bcheckchain
>  # found here-doc -- swallow it to avoid false hits within its body (but keep
>  # the command to which it was attached)
>  :heredoc
> -s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
> +s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
>  s/[ 	]*<<//
>  :hereslurpsub
>  N
> diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
> index 33bc3cc0b4..aff6568716 100644
> --- a/t/chainlint/here-doc.expect
> +++ b/t/chainlint/here-doc.expect
> @@ -2,4 +2,6 @@ boodle wobba        gorgo snoot        wafta snurb &&
>  
>  cat >foo &&
>  
> +cat >bar &&
> +
>  horticulture
> diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
> index 8986eefe74..f2bb14b693 100644
> --- a/t/chainlint/here-doc.test
> +++ b/t/chainlint/here-doc.test
> @@ -14,6 +14,13 @@ boz
>  woz
>  Arbitrary_Tag_42
>  
> +# LINT: swallow 'quoted' here-doc
> +cat <<'FUMP' >bar &&
> +snoz
> +boz
> +woz
> +FUMP
> +
>  # LINT: swallow here-doc (EOF is last line of test)
>  horticulture <<\EOF
>  gomez
> diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
> index 7c2da63bc7..7663ea7fc4 100644
> --- a/t/chainlint/subshell-here-doc.expect
> +++ b/t/chainlint/subshell-here-doc.expect
> @@ -5,5 +5,6 @@
>  >) &&
>  (
>  	cat >bup &&
> +	cat >bup2 &&
>  	meep
>  >)
> diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
> index 05139af0b5..b6b5a9b33a 100644
> --- a/t/chainlint/subshell-here-doc.test
> +++ b/t/chainlint/subshell-here-doc.test
> @@ -27,5 +27,9 @@
>  	glink
>  	FIZZ
>  	ARBITRARY
> +	cat <<-'ARBITRARY2' >bup2 &&
> +	glink
> +	FIZZ
> +	ARBITRARY2
>  	meep
>  )

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags
  2018-08-13 19:27         ` Junio C Hamano
@ 2018-08-13 20:12           ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-13 20:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git List, Jeff King, Jonathan Nieder

On Mon, Aug 13, 2018 at 3:27 PM Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
>
> > A here-doc tag can be quoted ('EOF') or escaped (\EOF) to suppress
> > interpolation within the body. Although, chainlint recognizes escaped
> > tags, it does not know about quoted tags. For completeness, teach it to
> > recognize quoted tags, as well.
>
> Is this step merely completeness and future-proofing, or do we have
> existing instances that the linter would be confused without this
> patch?

We do have quite a few instances of single-quoted 'EOF' in the tests
already, though none of them confuse the linter, so this change is for
completeness and future-proofing.

> I am primarily wondering what the reason is that <<"EOF" is
> not looked for while we are at it (the answer could be that we
> have tests that use <<'EOF' but not <<"EOF").

It's an oversight on my part. I've gotten so used to \EOF (and before
working on Git, 'EOF'), that "EOF" just slipped my mind[1].

I do think it's worth a re-roll. Thanks for noticing.

[1]: What did not slip my mind is that quoting _any_ part of the tag
suppresses interpolation, so 'E'OF, E'O'F, and even E''OF are all
valid, however, that's so ugly that I really didn't want to support
it.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding
  2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
                         ` (5 preceding siblings ...)
  2018-08-13  8:47       ` [PATCH v2 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
@ 2018-08-15 18:45       ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
                           ` (6 more replies)
  6 siblings, 7 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

This is a re-roll of [1] which improves chainlint's robustness in the
face of unusual shell coding such as in contrib/subtree/t7900 which
triggered a false-positive[2].

Changes since v2:

* recognize "quoted" here-doc tag names (in addition to 'quoted'
  and \escaped)

Interdiff below.

[1]: https://public-inbox.org/git/20180813084739.16134-1-sunshine@sunshineco.com/
[2]: https://public-inbox.org/git/20180730181356.GA156463@aiede.svl.corp.google.com/

Eric Sunshine (6):
  chainlint: match arbitrary here-docs tags rather than hard-coded names
  chainlint: match quoted here-doc tags
  chainlint: recognize multi-line $(...) when command cuddled with "$("
  chainlint: let here-doc and multi-line string commence on same line
  chainlint: recognize multi-line quoted strings more robustly
  chainlint: add test of pathological case which triggered false
    positive

 t/chainlint.sed                               | 98 ++++++++++++-------
 t/chainlint/here-doc-close-subshell.expect    |  2 +
 t/chainlint/here-doc-close-subshell.test      |  5 +
 .../here-doc-multi-line-command-subst.expect  |  5 +
 .../here-doc-multi-line-command-subst.test    |  9 ++
 t/chainlint/here-doc-multi-line-string.expect |  4 +
 t/chainlint/here-doc-multi-line-string.test   |  8 ++
 t/chainlint/here-doc.expect                   |  6 ++
 t/chainlint/here-doc.test                     | 21 ++++
 ...ti-line-nested-command-substitution.expect | 11 ++-
 ...ulti-line-nested-command-substitution.test | 11 ++-
 t/chainlint/multi-line-string.expect          | 10 +-
 t/chainlint/multi-line-string.test            | 12 +++
 t/chainlint/nested-here-doc.expect            |  2 +
 t/chainlint/nested-here-doc.test              | 10 ++
 t/chainlint/subshell-here-doc.expect          |  6 ++
 t/chainlint/subshell-here-doc.test            | 16 +++
 t/chainlint/t7900-subtree.expect              | 10 ++
 t/chainlint/t7900-subtree.test                | 22 +++++
 19 files changed, 227 insertions(+), 41 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

Interdiff against v2:
diff --git a/t/chainlint.sed b/t/chainlint.sed
index 8544df38df..1da58b554b 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -94,8 +94,8 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\']*[A-Za-z0-9_]/ {
-	s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 	s/[ 	]*<<//
 	:hereslurp
 	N
@@ -159,7 +159,7 @@ s/.*\n//
 }
 :folded
 # here-doc -- swallow it
-/<<[ 	]*[-\\']*[A-Za-z0-9_]/bheredoc
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -281,7 +281,7 @@ bfolded
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
 :heredoc
-s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
+s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 s/[ 	]*<<//
 :hereslurpsub
 N
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index aff6568716..534b065e38 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -4,4 +4,6 @@ cat >foo &&
 
 cat >bar &&
 
+cat >boo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index f2bb14b693..ad4ce8afd9 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -21,6 +21,13 @@ boz
 woz
 FUMP
 
+# LINT: swallow "quoted" here-doc
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 7663ea7fc4..74723e7340 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -6,5 +6,6 @@
 (
 	cat >bup &&
 	cat >bup2 &&
+	cat >bup3 &&
 	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index b6b5a9b33a..f6b3ba4214 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -31,6 +31,9 @@
 	glink
 	FIZZ
 	ARBITRARY2
+	cat <<-"ARBITRARY3" >bup3 &&
+	glink
+	FIZZ
+	ARBITRARY3
 	meep
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 2/6] chainlint: match quoted here-doc tags Eric Sunshine
                           ` (5 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.

At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.

In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.

To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                      | 57 +++++++++++++++++-----------
 t/chainlint/here-doc.expect          |  2 +
 t/chainlint/here-doc.test            |  7 ++++
 t/chainlint/nested-here-doc.expect   |  2 +
 t/chainlint/nested-here-doc.test     | 10 +++++
 t/chainlint/subshell-here-doc.expect |  4 ++
 t/chainlint/subshell-here-doc.test   |  8 ++++
 7 files changed, 67 insertions(+), 23 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 5f0882cb38..2af1a687f8 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -61,6 +61,22 @@
 # "else", and "fi" in if-then-else likewise must not end with "&&", thus
 # receives similar treatment.
 #
+# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
+# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
+# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# As each subsequent line is read, it is appended to the target line and a
+# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
+# the content inside "<...>" matches the entirety of the newly-read line. For
+# instance, if the next line read is "some data", when concatenated with the
+# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
+# to see if "EOF" matches "some data". Since it doesn't, the next line is
+# attempted. When a line consisting of only "EOF" (and possible whitespace) is
+# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
+# in which case the "EOF" inside "<...>" does match the text following the
+# newline, thus the closing here-doc tag has been found. The closing tag line
+# and the "<...>" prefix on the target line are then discarded, leaving just
+# the target line "cat >out".
+#
 # To facilitate regression testing (and manual debugging), a ">" annotation is
 # applied to the line containing ")" which closes a subshell, ">>" to a line
 # closing a nested subshell, and ">>>" to a line closing both at once. This
@@ -78,14 +94,17 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\]*EOF[ 	]*/ {
-	s/[ 	]*<<[ 	]*[-\\]*EOF//
-	h
+/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+	s/[ 	]*<<//
 	:hereslurp
 	N
-	s/.*\n//
-	/^[ 	]*EOF[ 	]*$/!bhereslurp
-	x
+	/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+		s/\n.*$//
+		bhereslurp
+	}
+	s/^<[^>]*>//
+	s/\n.*$//
 }
 
 # one-liner "(...) &&"
@@ -139,9 +158,7 @@ s/.*\n//
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 # here-doc -- swallow it
-/<<[ 	]*[-\\]*EOF/bheredoc
-/<<[ 	]*[-\\]*EOT/bheredoc
-/<<[ 	]*[-\\]*INPUT_END/bheredoc
+/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -249,23 +266,17 @@ s/\n//
 bcheckchain
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
-# the command to which it was attached); take care to handle here-docs nested
-# within here-docs by only recognizing closing tag matching outer here-doc
-# opening tag
+# the command to which it was attached)
 :heredoc
-/EOF/{ s/[ 	]*<<[ 	]*[-\\]*EOF//; s/^/EOF/; }
-/EOT/{ s/[ 	]*<<[ 	]*[-\\]*EOT//; s/^/EOT/; }
-/INPUT_END/{ s/[ 	]*<<[ 	]*[-\\]*INPUT_END//; s/^/INPUT_END/; }
+s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+s/[ 	]*<<//
 :hereslurpsub
 N
-/^EOF.*\n[ 	]*EOF[ 	]*$/bhereclose
-/^EOT.*\n[ 	]*EOT[ 	]*$/bhereclose
-/^INPUT_END.*\n[ 	]*INPUT_END[ 	]*$/bhereclose
-bhereslurpsub
-:hereclose
-s/^EOF//
-s/^EOT//
-s/^INPUT_END//
+/^<\([^>]*\)>.*\n[ 	]*\1[ 	]*$/!{
+	s/\n.*$//
+	bhereslurpsub
+}
+s/^<[^>]*>//
 s/\n.*$//
 bcheckchain
 
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 2328fe7753..33bc3cc0b4 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,3 +1,5 @@
 boodle wobba        gorgo snoot        wafta snurb &&
 
+cat >foo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index bd36f6e1d3..8986eefe74 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -7,6 +7,13 @@ quoth the raven,
 nevermore...
 EOF
 
+# LINT: swallow here-doc with arbitrary tag
+cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index 559301e005..0c9ef1cfc6 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,3 +1,5 @@
+cat >foop &&
+
 (
 	cat &&
 ?!AMP?!	cat
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
index 027e0bb3ff..f35404bf0f 100644
--- a/t/chainlint/nested-here-doc.test
+++ b/t/chainlint/nested-here-doc.test
@@ -1,3 +1,13 @@
+# LINT: inner "EOF" not misintrepreted as closing ARBITRARY here-doc
+cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+	nozzle
+	noodle
+EOF
+formp
+ARBITRARY
+
 (
 # LINT: inner "EOF" not misintrepreted as closing INPUT_END here-doc
 	cat <<-\INPUT_END &&
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 19d5aff233..7c2da63bc7 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -2,4 +2,8 @@
 	echo wobba 	       gorgo snoot 	       wafta snurb &&
 ?!AMP?!	cat >bip
 	echo >bop
+>) &&
+(
+	cat >bup &&
+	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index 9c3564c247..05139af0b5 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -20,4 +20,12 @@
 	wednesday
 	pugsly
 	EOF
+) &&
+(
+# LINT: swallow here-doc with arbitrary tag
+	cat <<-\ARBITRARY >bup &&
+	glink
+	FIZZ
+	ARBITRARY
+	meep
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 2/6] chainlint: match quoted here-doc tags
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
                           ` (4 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

A here-doc tag can be quoted ('EOF'/"EOF") or escaped (\EOF) to suppress
interpolation within the body. Although, chainlint recognizes escaped
tags, it does not know about quoted tags. For completeness, teach it to
recognize quoted tags, as well.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                      |  8 ++++----
 t/chainlint/here-doc.expect          |  4 ++++
 t/chainlint/here-doc.test            | 14 ++++++++++++++
 t/chainlint/subshell-here-doc.expect |  2 ++
 t/chainlint/subshell-here-doc.test   |  8 ++++++++
 5 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 2af1a687f8..07c624fe09 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -94,8 +94,8 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\]*[A-Za-z0-9_]/ {
-	s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 	s/[ 	]*<<//
 	:hereslurp
 	N
@@ -158,7 +158,7 @@ s/.*\n//
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 # here-doc -- swallow it
-/<<[ 	]*[-\\]*[A-Za-z0-9_]/bheredoc
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -268,7 +268,7 @@ bcheckchain
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
 :heredoc
-s/^\(.*\)<<[ 	]*[-\\]*\([A-Za-z0-9_][A-Za-z0-9_]*\)/<\2>\1<</
+s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 s/[ 	]*<<//
 :hereslurpsub
 N
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 33bc3cc0b4..534b065e38 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -2,4 +2,8 @@ boodle wobba        gorgo snoot        wafta snurb &&
 
 cat >foo &&
 
+cat >bar &&
+
+cat >boo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index 8986eefe74..ad4ce8afd9 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -14,6 +14,20 @@ boz
 woz
 Arbitrary_Tag_42
 
+# LINT: swallow 'quoted' here-doc
+cat <<'FUMP' >bar &&
+snoz
+boz
+woz
+FUMP
+
+# LINT: swallow "quoted" here-doc
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 7c2da63bc7..74723e7340 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -5,5 +5,7 @@
 >) &&
 (
 	cat >bup &&
+	cat >bup2 &&
+	cat >bup3 &&
 	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index 05139af0b5..f6b3ba4214 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -27,5 +27,13 @@
 	glink
 	FIZZ
 	ARBITRARY
+	cat <<-'ARBITRARY2' >bup2 &&
+	glink
+	FIZZ
+	ARBITRARY2
+	cat <<-"ARBITRARY3" >bup3 &&
+	glink
+	FIZZ
+	ARBITRARY3
 	meep
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$("
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 2/6] chainlint: match quoted here-doc tags Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
                           ` (3 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

For multi-line $(...) expressions nested within subshells, chainlint.sed
only recognizes:

    x=$(
        echo foo &&
        ...

but it is not unlikely that test authors may also cuddle the command
with the opening "$(", so support that style, as well:

    x=$(echo foo &&
        ...

The closing ")" is already correctly recognized when cuddled or not.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                       |  2 +-
 .../multi-line-nested-command-substitution.expect     | 11 ++++++++++-
 .../multi-line-nested-command-substitution.test       | 11 ++++++++++-
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 07c624fe09..a21c4b4d71 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -216,7 +216,7 @@ s/.*\n//
 # "$(...)" -- command substitution; not closing ")"
 /\$([^)][^)]*)[^)]*$/bcheckchain
 # multi-line "$(...\n...)" -- command substitution; treat as nested subshell
-/\$([ 	]*$/bnest
+/\$([^)]*$/bnest
 # "=(...)" -- Bash array assignment; not closing ")"
 /=(/bcheckchain
 # closing "...) &&"
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
index 19c023b1c8..59b6c8b850 100644
--- a/t/chainlint/multi-line-nested-command-substitution.expect
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -6,4 +6,13 @@
 >>	) &&
 	echo ok
 >) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+>>	) &&
+	y=$(echo baz |
+>>		fip) &&
+	echo fail
+>)
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
index ca0620ab6b..300058341b 100644
--- a/t/chainlint/multi-line-nested-command-substitution.test
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -6,4 +6,13 @@
 	) &&
 	echo ok
 ) |
-sort
+sort &&
+(
+	bar &&
+	x=$(echo bar |
+		cat
+	) &&
+	y=$(echo baz |
+		fip) &&
+	echo fail
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 4/6] chainlint: let here-doc and multi-line string commence on same line
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                           ` (2 preceding siblings ...)
  2018-08-15 18:45         ` [PATCH v3 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
                           ` (2 subsequent siblings)
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

After swallowing a here-doc, chainlint.sed assumes that no other
processing needs to be done on the line aside from checking for &&-chain
breakage; likewise, after folding a multi-line quoted string. However,
it's conceivable (even if unlikely in practice) that both a here-doc and
a multi-line quoted string might commence on the same line:

    cat <<\EOF && echo "foo
    bar"
    data
    EOF

Support this case by sending the line (after swallowing and folding)
through the normal processing sequence rather than jumping directly to
the check for broken &&-chain.

This change also allows other somewhat pathological cases to be handled,
such as closing a subshell on the same line starting a here-doc:

    (
        cat <<-\INPUT)
        data
        INPUT

or, for instance, opening a multi-line $(...) expression on the same
line starting a here-doc:

    x=$(cat <<-\END &&
        data
        END
        echo "x")

among others.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                                      | 7 ++++---
 t/chainlint/here-doc-close-subshell.expect           | 2 ++
 t/chainlint/here-doc-close-subshell.test             | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.expect | 5 +++++
 t/chainlint/here-doc-multi-line-command-subst.test   | 9 +++++++++
 t/chainlint/here-doc-multi-line-string.expect        | 4 ++++
 t/chainlint/here-doc-multi-line-string.test          | 8 ++++++++
 7 files changed, 37 insertions(+), 3 deletions(-)
 create mode 100644 t/chainlint/here-doc-close-subshell.expect
 create mode 100644 t/chainlint/here-doc-close-subshell.test
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.expect
 create mode 100644 t/chainlint/here-doc-multi-line-command-subst.test
 create mode 100644 t/chainlint/here-doc-multi-line-string.expect
 create mode 100644 t/chainlint/here-doc-multi-line-string.test

diff --git a/t/chainlint.sed b/t/chainlint.sed
index a21c4b4d71..41cb6ef865 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -157,6 +157,7 @@ s/.*\n//
 /^[^']*'[^']*$/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
+:folded
 # here-doc -- swallow it
 /<<[ 	]*[-\\'"]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
@@ -255,7 +256,7 @@ s/"//g
 N
 s/\n//
 /"/!bdqstring
-bcheckchain
+bfolded
 
 # found multi-line single-quoted string '...\n...' -- slurp until end of string
 :sqstring
@@ -263,7 +264,7 @@ s/'//g
 N
 s/\n//
 /'/!bsqstring
-bcheckchain
+bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
@@ -278,7 +279,7 @@ N
 }
 s/^<[^>]*>//
 s/\n.*$//
-bcheckchain
+bfolded
 
 # found "case ... in" -- pass through untouched
 :case
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
new file mode 100644
index 0000000000..f011e335e5
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -0,0 +1,2 @@
+(
+>	cat)
diff --git a/t/chainlint/here-doc-close-subshell.test b/t/chainlint/here-doc-close-subshell.test
new file mode 100644
index 0000000000..b857ff5467
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.test
@@ -0,0 +1,5 @@
+(
+# LINT: line contains here-doc and closes nested subshell
+	cat <<-\INPUT)
+	fizz
+	INPUT
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
new file mode 100644
index 0000000000..e5fb752d2f
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -0,0 +1,5 @@
+(
+	x=$(bobble &&
+?!AMP?!>>		wiffle)
+	echo $x
+>)
diff --git a/t/chainlint/here-doc-multi-line-command-subst.test b/t/chainlint/here-doc-multi-line-command-subst.test
new file mode 100644
index 0000000000..899bc5de8b
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.test
@@ -0,0 +1,9 @@
+(
+# LINT: line contains here-doc and opens multi-line $(...)
+	x=$(bobble <<-\END &&
+		fossil
+		vegetable
+		END
+		wiffle)
+	echo $x
+)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
new file mode 100644
index 0000000000..1e5b724b9d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -0,0 +1,4 @@
+(
+?!AMP?!	cat && echo multi-line	string"
+	bap
+>)
diff --git a/t/chainlint/here-doc-multi-line-string.test b/t/chainlint/here-doc-multi-line-string.test
new file mode 100644
index 0000000000..a53edbcc8d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.test
@@ -0,0 +1,8 @@
+(
+# LINT: line contains here-doc and opens multi-line string
+	cat <<-\TXT && echo "multi-line
+	string"
+	fizzle
+	TXT
+	bap
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 5/6] chainlint: recognize multi-line quoted strings more robustly
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                           ` (3 preceding siblings ...)
  2018-08-15 18:45         ` [PATCH v3 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-15 18:45         ` [PATCH v3 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
  2018-08-29  9:45         ` [PATCH] chainlint: match "quoted" here-doc tags Eric Sunshine
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

chainlint.sed recognizes multi-line quoted strings within subshells:

    echo "abc
        def" >out &&

so it can avoid incorrectly classifying lines internal to the string as
breaking the &&-chain. To identify the first line of a multi-line
string, it checks if the line contains a single quote. However, this is
fragile and can be easily fooled by a line containing multiple strings:

    echo "xyz" "abc
        def" >out &&

Make detection more robust by checking for an odd number of quotes
rather than only a single one.

(Escaped quotes are not handled, but support may be added later.)

The original multi-line string recognizer rather cavalierly threw away
all but the final quote, whereas the new one is careful to retain all
quotes, so the "expected" output of a couple existing chainlint tests is
updated to account for this new behavior.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint.sed                               | 32 +++++++++++++------
 t/chainlint/here-doc-multi-line-string.expect |  2 +-
 t/chainlint/multi-line-string.expect          | 10 ++++--
 t/chainlint/multi-line-string.test            | 12 +++++++
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 41cb6ef865..1da58b554b 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -151,10 +151,10 @@ s/.*\n//
 :slurp
 # incomplete line "...\"
 /\\$/bincomplete
-# multi-line quoted string "...\n..."
-/^[^"]*"[^"]*$/bdqstring
-# multi-line quoted string '...\n...' (but not contraction in string "it's so")
-/^[^']*'[^']*$/{
+# multi-line quoted string "...\n..."?
+/"/bdqstring
+# multi-line quoted string '...\n...'? (but not contraction in string "it's")
+/'/{
 	/"[^'"]*'[^'"]*"/!bsqstring
 }
 :folded
@@ -250,20 +250,32 @@ N
 s/\\\n//
 bslurp
 
-# found multi-line double-quoted string "...\n..." -- slurp until end of string
+# check for multi-line double-quoted string "...\n..." -- fold to one line
 :dqstring
-s/"//g
+# remove all quote pairs
+s/"\([^"]*\)"/@!\1@!/g
+# done if no dangling quote
+/"/!bdqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/"/!bdqstring
+bdqstring
+:dqdone
+s/@!/"/g
 bfolded
 
-# found multi-line single-quoted string '...\n...' -- slurp until end of string
+# check for multi-line single-quoted string '...\n...' -- fold to one line
 :sqstring
-s/'//g
+# remove all quote pairs
+s/'\([^']*\)'/@!\1@!/g
+# done if no dangling quote
+/'/!bsqdone
+# otherwise, slurp next line and try again
 N
 s/\n//
-/'/!bsqstring
+bsqstring
+:sqdone
+s/@!/'/g
 bfolded
 
 # found here-doc -- swallow it to avoid false hits within its body (but keep
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 1e5b724b9d..32038a070c 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,4 @@
 (
-?!AMP?!	cat && echo multi-line	string"
+?!AMP?!	cat && echo "multi-line	string"
 	bap
 >)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index 8334c4cc8e..170cb59993 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,9 +1,15 @@
 (
-	x=line 1		line 2		line 3" &&
-?!AMP?!	y=line 1		line2'
+	x="line 1		line 2		line 3" &&
+?!AMP?!	y='line 1		line2'
 	foobar
 >) &&
 (
 	echo "there's nothing to see here" &&
 	exit
+>) &&
+(
+	echo "xyz" "abc		def		ghi" &&
+	echo 'xyz' 'abc		def		ghi' &&
+	echo 'xyz' "abc		def		ghi" &&
+	barfoo
 >)
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
index 14cb44d51c..287ab89705 100644
--- a/t/chainlint/multi-line-string.test
+++ b/t/chainlint/multi-line-string.test
@@ -12,4 +12,16 @@
 # LINT: starting multi-line single-quoted string
 	echo "there's nothing to see here" &&
 	exit
+) &&
+(
+	echo "xyz" "abc
+		def
+		ghi" &&
+	echo 'xyz' 'abc
+		def
+		ghi' &&
+	echo 'xyz' "abc
+		def
+		ghi" &&
+	barfoo
 )
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH v3 6/6] chainlint: add test of pathological case which triggered false positive
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                           ` (4 preceding siblings ...)
  2018-08-15 18:45         ` [PATCH v3 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
@ 2018-08-15 18:45         ` Eric Sunshine
  2018-08-29  9:45         ` [PATCH] chainlint: match "quoted" here-doc tags Eric Sunshine
  6 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-15 18:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Jonathan Nieder, Junio C Hamano, Eric Sunshine

This extract from contrib/subtree/t7900 triggered a false positive due
to three chainlint limitations:

* recognizing only a "blessed" set of here-doc tag names in a subshell
  ("EOF", "EOT", "INPUT_END"), of which "TXT" is not a member

* inability to recognize multi-line $(...) when the first statement of
  the body is cuddled with the opening "$("

* inability to recognize multiple constructs on a single line, such as
  opening a multi-line $(...) and starting a here-doc

Now that all of these shortcomings have been addressed, turn this rather
pathological bit of shell coding into a chainlint test case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/chainlint/t7900-subtree.expect | 10 ++++++++++
 t/chainlint/t7900-subtree.test   | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 t/chainlint/t7900-subtree.expect
 create mode 100644 t/chainlint/t7900-subtree.test

diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
new file mode 100644
index 0000000000..c9913429e6
--- /dev/null
+++ b/t/chainlint/t7900-subtree.expect
@@ -0,0 +1,10 @@
+(
+	chks="sub1sub2sub3sub4" &&
+	chks_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	chkms="main-sub1main-sub2main-sub3main-sub4" &&
+	chkms_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms$chks"
+>)
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
new file mode 100644
index 0000000000..277d8358df
--- /dev/null
+++ b/t/chainlint/t7900-subtree.test
@@ -0,0 +1,22 @@
+(
+	chks="sub1
+sub2
+sub3
+sub4" &&
+	chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+) &&
+	chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+	chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+) &&
+
+	subfiles=$(git ls-files) &&
+	check_equal "$subfiles" "$chkms
+$chks"
+)
-- 
2.18.0.267.gbc8be36ecb


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
  2018-07-11 21:37     ` Junio C Hamano
  2018-07-30 18:13     ` Jonathan Nieder
@ 2018-08-23 18:02     ` Ævar Arnfjörð Bjarmason
  2018-08-23 18:27       ` Eric Sunshine
  2 siblings, 1 reply; 123+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2018-08-23 18:02 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Elijah Newren, Johannes Sixt, Jonathan Nieder, Jonathan Tan,
	Stefan Beller, Junio C Hamano, Luke Diamand, Jeff King


On Wed, Jul 11 2018, Eric Sunshine wrote:

Found in some 2.19 testing on AIX:

> +# here-doc -- swallow it to avoid false hits within its body (but keep the
> +# command to which it was attached)
> +/<<[ 	]*[-\\]*EOF[ 	]*/ {
> +	s/[ 	]*<<[ 	]*[-\\]*EOF//
> +	h
> +	:hereslurp
> +	N
> +	s/.*\n//
> +	/^[ 	]*EOF[ 	]*$/!bhereslurp
> +	x
> +}

AIX sed doesn't like this, and will yell:

    :hereslurp is greater than eight characters

This on top fixes it:

    diff --git a/t/chainlint.sed b/t/chainlint.sed
    index 8544df38df..2333705b27 100644
    --- a/t/chainlint.sed
    +++ b/t/chainlint.sed
    @@ -100 +100 @@
    -       :hereslurp
    +       :hered
    @@ -104 +104 @@
    -               bhereslurp
    +               bhered
    @@ -286 +286 @@ s/[     ]*<<//
    -:hereslurpsub
    +:heredsub
    @@ -290 +290 @@ N
    -       bhereslurpsub
    +       bheredsub

> +:subshell
> +# bare "(" line?
> +/^[ 	]*([	]*$/ {
> +	# stash for later printing
> +	h
> +	bnextline
> +}
> +# "(..." line -- split off and stash "(", then process "..." as its own line

AIX sed doesn't like this either, and prints:

    sed:    # stash for later printing is not a recognized function.

I have no idea what the fix is for that one.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
  2018-08-23 18:02     ` Ævar Arnfjörð Bjarmason
@ 2018-08-23 18:27       ` Eric Sunshine
  0 siblings, 0 replies; 123+ messages in thread
From: Eric Sunshine @ 2018-08-23 18:27 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Elijah Newren, Johannes Sixt, Jonathan Nieder,
	Jonathan Tan, Stefan Beller, Junio C Hamano, Luke Diamand,
	Jeff King

On Thu, Aug 23, 2018 at 2:02 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> Found in some 2.19 testing on AIX:

Thanks for reporting these issues.

> > +     /^[     ]*EOF[  ]*$/!bhereslurp
>
> AIX sed doesn't like this, and will yell:
>     :hereslurp is greater than eight characters
> This on top fixes it:

Fix make sense, and checking POSIX[1] , I see that it says that
behavior is unspecified for labels longer than 8 bytes.

[1]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html

> > +# bare "(" line?
> > +/^[  ]*([    ]*$/ {
> > +     # stash for later printing
>
> AIX sed doesn't like this either, and prints:
>     sed:    # stash for later printing is not a recognized function.
> I have no idea what the fix is for that one.

That's the only indented comment in the entire script, so it's almost
certainly that. How about we move the indented comment up to the
comment for the enclosing block, so it reads:

    # bare "(" line? -- stash for later printing
    /^[  ]*([    ]*$/ {
        h
        bnextline
    }

I could prepare such a patch, although perhaps it would be better for
you to do so since you are in a position to test it (and because you
discovered the problems)?

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH] chainlint: match "quoted" here-doc tags
  2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
                           ` (5 preceding siblings ...)
  2018-08-15 18:45         ` [PATCH v3 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
@ 2018-08-29  9:45         ` Eric Sunshine
  2018-08-29 17:57           ` Junio C Hamano
  6 siblings, 1 reply; 123+ messages in thread
From: Eric Sunshine @ 2018-08-29  9:45 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Jonathan Nieder, Junio C Hamano,
	Ævar Arnfjörð Bjarmason, Eric Sunshine

A here-doc tag can be quoted ('EOF'/"EOF") or escaped (\EOF) to suppress
interpolation within the body. chainlint recognizes single-quoted and
escaped tags, but does not know about double-quoted tags. For
completeness, teach it to recognize double-quoted tags, as well.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---

This is an extract from v3 of es/chain-lint-more[1] which never got
picked up. Instead, v2 of that series[2] got merged to 'next' and later
'master'. The only change between v2 and v3 was to make 'chainlint' also
recognize double-quoted here-doc tags. This solo patch makes that change
incrementally atop v2.

This is built on 'master'. With the default "recursive" strategy, it
merges cleanly with Ævar's ab/portable[3] changes to 'chainlint' for AIX
compatibility, currently in 'next'.

[1]: https://public-inbox.org/git/20180815184552.8418-1-sunshine@sunshineco.com/
[2]: https://public-inbox.org/git/20180813084739.16134-1-sunshine@sunshineco.com/
[3]: https://public-inbox.org/git/20180824152016.20286-1-avarab@gmail.com/

 t/chainlint.sed                      | 8 ++++----
 t/chainlint/here-doc.expect          | 2 ++
 t/chainlint/here-doc.test            | 7 +++++++
 t/chainlint/subshell-here-doc.expect | 1 +
 t/chainlint/subshell-here-doc.test   | 4 ++++
 5 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/t/chainlint.sed b/t/chainlint.sed
index 8544df38df..1da58b554b 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -94,8 +94,8 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[ 	]*[-\\']*[A-Za-z0-9_]/ {
-	s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/ {
+	s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 	s/[ 	]*<<//
 	:hereslurp
 	N
@@ -159,7 +159,7 @@ s/.*\n//
 }
 :folded
 # here-doc -- swallow it
-/<<[ 	]*[-\\']*[A-Za-z0-9_]/bheredoc
+/<<[ 	]*[-\\'"]*[A-Za-z0-9_]/bheredoc
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -281,7 +281,7 @@ bfolded
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
 :heredoc
-s/^\(.*\)<<[ 	]*[-\\']*\([A-Za-z0-9_][A-Za-z0-9_]*\)'*/<\2>\1<</
+s/^\(.*\)<<[ 	]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
 s/[ 	]*<<//
 :hereslurpsub
 N
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index aff6568716..534b065e38 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -4,4 +4,6 @@ cat >foo &&
 
 cat >bar &&
 
+cat >boo &&
+
 horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index f2bb14b693..ad4ce8afd9 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -21,6 +21,13 @@ boz
 woz
 FUMP
 
+# LINT: swallow "quoted" here-doc
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
+
 # LINT: swallow here-doc (EOF is last line of test)
 horticulture <<\EOF
 gomez
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 7663ea7fc4..74723e7340 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -6,5 +6,6 @@
 (
 	cat >bup &&
 	cat >bup2 &&
+	cat >bup3 &&
 	meep
 >)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index b6b5a9b33a..f6b3ba4214 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -31,5 +31,9 @@
 	glink
 	FIZZ
 	ARBITRARY2
+	cat <<-"ARBITRARY3" >bup3 &&
+	glink
+	FIZZ
+	ARBITRARY3
 	meep
 )
-- 
2.19.0.rc1.352.gb1634b371d


^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH] chainlint: match "quoted" here-doc tags
  2018-08-29  9:45         ` [PATCH] chainlint: match "quoted" here-doc tags Eric Sunshine
@ 2018-08-29 17:57           ` Junio C Hamano
  0 siblings, 0 replies; 123+ messages in thread
From: Junio C Hamano @ 2018-08-29 17:57 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Jeff King, Jonathan Nieder, Ævar Arnfjörð Bjarmason

Eric Sunshine <sunshine@sunshineco.com> writes:

> This is an extract from v3 of es/chain-lint-more[1] which never got
> picked up. Instead, v2 of that series[2] got merged to 'next' and later
> 'master'. The only change between v2 and v3 was to make 'chainlint' also
> recognize double-quoted here-doc tags. This solo patch makes that change
> incrementally atop v2.

Thanks.

^ permalink raw reply	[flat|nested] 123+ messages in thread

end of thread, back to index

Thread overview: 123+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-26  7:29 [PATCH 00/29] t: detect and fix broken &&-chains in subshells Eric Sunshine
2018-06-26  7:29 ` [PATCH 01/29] t7508: use test_when_finished() instead of managing exit code manually Eric Sunshine
2018-06-26  7:29 ` [PATCH 02/29] t0001: use "{...}" block around "||" expression rather than subshell Eric Sunshine
2018-06-26  7:29 ` [PATCH 03/29] t1300: use sane_unset() to avoid breaking &&-chain Eric Sunshine
2018-06-26  7:29 ` [PATCH 04/29] t3303: use standard here-doc tag "EOF" to avoid fooling --chain-lint Eric Sunshine
2018-06-26  7:29 ` [PATCH 05/29] t5505: modernize and simplify hard-to-digest test Eric Sunshine
2018-06-26  7:29 ` [PATCH 06/29] t6036: fix broken "merge fails but has appropriate contents" tests Eric Sunshine
2018-06-26  8:44   ` Elijah Newren
2018-06-26  7:29 ` [PATCH 07/29] t7201: drop pointless "exit 0" at end of subshell Eric Sunshine
2018-06-26  7:29 ` [PATCH 08/29] t7400: fix broken "submodule add/reconfigure --force" test Eric Sunshine
2018-06-27 18:04   ` Stefan Beller
2018-06-26  7:29 ` [PATCH 09/29] t7810: use test_expect_code() instead of hand-rolled comparison Eric Sunshine
2018-06-26  7:29 ` [PATCH 10/29] t9001: fix broken "invoke hook" test Eric Sunshine
2018-06-26 17:07   ` Jonathan Tan
2018-06-26  7:29 ` [PATCH 11/29] t9104: use "{...}" block around "||" expression rather than subshell Eric Sunshine
2018-06-26  7:29 ` [PATCH 12/29] t9401: drop unnecessary nested subshell Eric Sunshine
2018-06-26  7:29 ` [PATCH 13/29] t/lib-submodule-update: fix broken "replace submodule must-fail" test Eric Sunshine
2018-06-27 18:30   ` [PATCH] t/lib-submodule-update: fix absorbing test Stefan Beller
2018-06-27 18:38     ` Eric Sunshine
2018-06-26  7:29 ` [PATCH 14/29] t: drop subshell with missing &&-chain in favor of simpler construct Eric Sunshine
2018-06-26 19:31   ` Junio C Hamano
2018-06-26 20:06     ` Eric Sunshine
2018-06-26  7:29 ` [PATCH 15/29] t: drop unnecessary terminating semicolons in subshell Eric Sunshine
2018-06-26  7:29 ` [PATCH 16/29] t: use test_might_fail() instead of manipulating exit code manually Eric Sunshine
2018-06-26  7:29 ` [PATCH 17/29] t: use test_must_fail() instead of checking " Eric Sunshine
2018-06-26  7:59   ` Luke Diamand
2018-06-26  8:58   ` Elijah Newren
2018-06-26  9:21     ` Eric Sunshine
2018-06-26 18:05       ` Johannes Sixt
2018-06-26 18:14         ` Eric Sunshine
2018-06-26 21:00           ` Johannes Sixt
2018-06-26  7:29 ` [PATCH 18/29] t0000-t0999: fix broken &&-chains in subshells Eric Sunshine
2018-06-26  7:29 ` [PATCH 19/29] t1000-t1999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 20/29] t2000-t2999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 21/29] t3000-t3999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 22/29] t3030: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 23/29] t4000-t4999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 24/29] t5000-t5999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 25/29] t6000-t6999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 26/29] t7000-t7999: " Eric Sunshine
2018-06-26  7:29 ` [PATCH 27/29] t9000-t9999: " Eric Sunshine
2018-06-26  7:30 ` [PATCH 28/29] t9119: " Eric Sunshine
2018-06-26  7:30 ` [PATCH 29/29] t/test-lib: teach --chain-lint to detect " Eric Sunshine
2018-06-26 19:15   ` Junio C Hamano
2018-06-26 19:52     ` Eric Sunshine
2018-06-26 20:17       ` Jeff King
2018-06-26 20:22         ` Jeff King
2018-06-26 20:59           ` Eric Sunshine
2018-06-26 21:33           ` Elijah Newren
2018-06-26 21:42             ` Eric Sunshine
2018-06-26 20:46         ` Eric Sunshine
2018-06-26 21:01           ` Jeff King
2018-06-26 21:13             ` Eric Sunshine
2018-06-28 14:35               ` Jeff King
2018-06-27  2:15             ` Elijah Newren
2018-06-27  6:27               ` Johannes Sixt
2018-06-27  6:48                 ` Eric Sunshine
2018-06-28 14:37               ` Jeff King
2018-06-26 21:09         ` Junio C Hamano
2018-06-26  9:20 ` [PATCH 00/29] t: detect and fix " Elijah Newren
2018-06-26  9:31   ` Eric Sunshine
2018-06-26 15:34     ` Elijah Newren
2018-06-26 19:38 ` Junio C Hamano
2018-06-26 21:25   ` Eric Sunshine
2018-06-26 22:31     ` Junio C Hamano
2018-06-27  0:22       ` Jonathan Nieder
2018-07-11  6:46 ` [PATCH v2 00/10] detect " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to " Eric Sunshine
2018-07-11 21:37     ` Junio C Hamano
2018-07-12 10:50       ` Eric Sunshine
2018-07-12 16:56         ` Jeff King
2018-07-12 19:32           ` Eric Sunshine
2018-07-12 19:54             ` Junio C Hamano
2018-07-30 18:13     ` Jonathan Nieder
2018-07-30 19:06       ` [PATCH 0/2] subtree: fix &&-chain and simplify tests (Re: [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells) Jonathan Nieder
2018-07-30 19:07         ` [PATCH 1/2] subtree test: add missing && to &&-chain Jonathan Nieder
2018-07-30 19:07         ` [PATCH 2/2] subtree test: simplify preparation of expected results Jonathan Nieder
2018-07-30 20:25       ` [PATCH v2 01/10] t/test-lib: teach --chain-lint to detect broken &&-chains in subshells Eric Sunshine
2018-07-30 20:59         ` Jonathan Nieder
2018-07-30 21:38           ` Eric Sunshine
2018-07-31 12:50             ` Jeff King
2018-07-31 18:55               ` Eric Sunshine
2018-07-31 19:08                 ` Jeff King
2018-08-23 18:02     ` Ævar Arnfjörð Bjarmason
2018-08-23 18:27       ` Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 02/10] t/Makefile: add machinery to check correctness of chainlint.sed Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 03/10] t/chainlint: add chainlint "basic" test cases Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 04/10] t/chainlint: add chainlint "whitespace" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 05/10] t/chainlint: add chainlint "one-liner" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 06/10] t/chainlint: add chainlint "nested subshell" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 07/10] t/chainlint: add chainlint "loop" and "conditional" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 08/10] t/chainlint: add chainlint "cuddled" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 09/10] t/chainlint: add chainlint "complex" " Eric Sunshine
2018-07-11  6:46   ` [PATCH v2 10/10] t/chainlint: add chainlint "specialized" " Eric Sunshine
2018-08-07  8:21   ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
2018-08-07  8:21     ` [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
2018-08-08 22:50       ` Jeff King
2018-08-09  5:58         ` Eric Sunshine
2018-08-09 14:26           ` Jeff King
2018-08-07  8:21     ` [PATCH 2/5] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
2018-08-07  8:21     ` [PATCH 3/5] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
2018-08-07  8:21     ` [PATCH 4/5] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
2018-08-07  8:21     ` [PATCH 5/5] chainlint: add test of pathological case which triggered false positive Eric Sunshine
2018-08-08 22:53     ` [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding Jeff King
2018-08-09  0:44       ` Junio C Hamano
2018-08-13  8:47     ` [PATCH v2 0/6] " Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 2/6] chainlint: match 'quoted' here-doc tags Eric Sunshine
2018-08-13 19:27         ` Junio C Hamano
2018-08-13 20:12           ` Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
2018-08-13  8:47       ` [PATCH v2 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
2018-08-15 18:45       ` [PATCH v3 0/6] chainlint: improve robustness against "unusual" shell coding Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 1/6] chainlint: match arbitrary here-docs tags rather than hard-coded names Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 2/6] chainlint: match quoted here-doc tags Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 3/6] chainlint: recognize multi-line $(...) when command cuddled with "$(" Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 4/6] chainlint: let here-doc and multi-line string commence on same line Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 5/6] chainlint: recognize multi-line quoted strings more robustly Eric Sunshine
2018-08-15 18:45         ` [PATCH v3 6/6] chainlint: add test of pathological case which triggered false positive Eric Sunshine
2018-08-29  9:45         ` [PATCH] chainlint: match "quoted" here-doc tags Eric Sunshine
2018-08-29 17:57           ` Junio C Hamano

git@vger.kernel.org mailing list mirror (one of many)

Archives are clonable:
	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.org/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/
       or Tor2web: https://www.tor2web.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox