{"thread":{"id":"51278","subject":"[PATCH 0/3] Reduce number of processes spawned by git-mergetool","startedAt":"2019-06-10T08:59:30Z","lastAt":"2019-06-12T16:34:20Z","messageCount":18,"participants":["Johannes Sixt","SZEDER Gábor","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"376912","messageId":"cover.1560152205.git.j6t@kdbg.org","threadId":"51278","inReplyTo":null,"subject":"[PATCH 0/3] Reduce number of processes spawned by git-mergetool","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T08:58:57Z","receivedAt":"2019-06-10T08:59:30Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git, but there are\nalso some that can be replaced with shell builtins.\n\nI've measured the number of processes with\n\n  ps; ./t7610-mergetool.sh >/dev/null; ps\n\non a quiet system and the number of non-git commands invoked with\n\n  strace -e execve -f -o /tmp/git.strace ./t7610-mergetool.sh\n  grep 'execve(' /tmp/git.strace | grep -v 'execve(\".home' | wc -l\n\nbaseline:\n15628 pts/1    00:00:00 ps\n29413 pts/1    00:00:00 ps\n\n=> 13785 processes\n=> 3082 non-git commands\n\nt7610-mergetool: do not place pipelines headed by `yes` in subshells\n12620 pts/1    00:00:00 ps\n26348 pts/1    00:00:00 ps\n\n=> 13728 processes\n=> 3082 non-git commands\n\nmergetool: dissect strings with shell variable magic instead of `expr`\n 8766 pts/1    00:00:00 ps\n21913 pts/1    00:00:00 ps\n\n=> 13147 processes\n=> 2521 non-git commands\n\nmergetool: use shell variable magic instead of `awk`\n 2081 pts/1    00:00:00 ps\n14393 pts/1    00:00:00 ps\n\n=> 12312 processes\n=> 2007 non-git commands\n\nThe reduction of processes is not as dramatic as I hoped, but still\nmore than 10%.\n\nJohannes Sixt (3):\n  t7610-mergetool: do not place pipelines headed by `yes` in subshells\n  mergetool: dissect strings with shell variable magic instead of `expr`\n  mergetool: use shell variable magic instead of `awk`\n\n git-mergetool.sh     |  45 ++++++++----\n t/t7610-mergetool.sh | 170 +++++++++++++++++++++----------------------\n 2 files changed, 116 insertions(+), 99 deletions(-)\n\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"376913","messageId":"75c812bd4838f6f35b6f42b97ae396ebb28d8b95.1560152205.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560152205.git.j6t@kdbg.org","subject":"[PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T08:58:58Z","receivedAt":"2019-06-10T08:59:42Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Subshells for pipelines are not required. This can save a number of\nprocesses (if the shell does not optimize it away anyway).\n\nThe patch was generated with the command\n\n   sed -i 's/( *\\(yes.*[^ ]\\) *) *\\&\\&/\\1 \\&\\&/' t7610-mergetool.sh\n\nwith a manual fixup of the case having no && at the end.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n t/t7610-mergetool.sh | 170 +++++++++++++++++++++----------------------\n 1 file changed, 85 insertions(+), 85 deletions(-)\n\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex 5b61c10a9c..b67440882b 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -131,13 +131,13 @@ test_expect_success 'custom mergetool' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"\" | git mergetool file1 file1 ) &&\n-\t( yes \"\" | git mergetool file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"\" | git mergetool file1 file1 &&\n+\tyes \"\" | git mergetool file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool subdir/file3 &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat file1)\" = \"master updated\" &&\n \ttest \"$(cat file2)\" = \"master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n@@ -153,13 +153,13 @@ test_expect_success 'gui mergetool' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool --gui both ) &&\n-\t( yes \"\" | git mergetool -g file1 file1 ) &&\n-\t( yes \"\" | git mergetool --gui file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool --gui subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool --gui file11 ) &&\n-\t( yes \"d\" | git mergetool --gui file12 ) &&\n-\t( yes \"l\" | git mergetool --gui submod ) &&\n+\tyes \"\" | git mergetool --gui both &&\n+\tyes \"\" | git mergetool -g file1 file1 &&\n+\tyes \"\" | git mergetool --gui file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool --gui subdir/file3 &&\n+\tyes \"d\" | git mergetool --gui file11 &&\n+\tyes \"d\" | git mergetool --gui file12 &&\n+\tyes \"l\" | git mergetool --gui submod &&\n \ttest \"$(cat file1)\" = \"gui master updated\" &&\n \ttest \"$(cat file2)\" = \"gui master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"gui master new sub\" &&\n@@ -172,13 +172,13 @@ test_expect_success 'gui mergetool without merge.guitool set falls back to merge\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool --gui both ) &&\n-\t( yes \"\" | git mergetool -g file1 file1 ) &&\n-\t( yes \"\" | git mergetool --gui file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool --gui subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool --gui file11 ) &&\n-\t( yes \"d\" | git mergetool --gui file12 ) &&\n-\t( yes \"l\" | git mergetool --gui submod ) &&\n+\tyes \"\" | git mergetool --gui both &&\n+\tyes \"\" | git mergetool -g file1 file1 &&\n+\tyes \"\" | git mergetool --gui file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool --gui subdir/file3 &&\n+\tyes \"d\" | git mergetool --gui file11 &&\n+\tyes \"d\" | git mergetool --gui file12 &&\n+\tyes \"l\" | git mergetool --gui submod &&\n \ttest \"$(cat file1)\" = \"master updated\" &&\n \ttest \"$(cat file2)\" = \"master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n@@ -195,14 +195,14 @@ test_expect_success 'mergetool crlf' '\n \ttest_config core.autocrlf true &&\n \tgit checkout -b test$test_count branch1 &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool file1 ) &&\n-\t( yes \"\" | git mergetool file2 ) &&\n-\t( yes \"\" | git mergetool \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"\" | git mergetool subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 &&\n+\tyes \"\" | git mergetool file2 &&\n+\tyes \"\" | git mergetool \"spaced name\" &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"\" | git mergetool subdir/file3 &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest \"$(printf x | cat file1 -)\" = \"$(printf \"master updated\\r\\nx\")\" &&\n \ttest \"$(printf x | cat file2 -)\" = \"$(printf \"master new\\r\\nx\")\" &&\n \ttest \"$(printf x | cat subdir/file3 -)\" = \"$(printf \"master new sub\\r\\nx\")\" &&\n@@ -218,7 +218,7 @@ test_expect_success 'mergetool in subdir' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"\" | git mergetool file3 ) &&\n+\t\tyes \"\" | git mergetool file3 &&\n \t\ttest \"$(cat file3)\" = \"master new sub\"\n \t)\n '\n@@ -230,13 +230,13 @@ test_expect_success 'mergetool on file in parent dir' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"\" | git mergetool file3 ) &&\n-\t\t( yes \"\" | git mergetool ../file1 ) &&\n-\t\t( yes \"\" | git mergetool ../file2 ../spaced\\ name ) &&\n-\t\t( yes \"\" | git mergetool ../both ) &&\n-\t\t( yes \"d\" | git mergetool ../file11 ) &&\n-\t\t( yes \"d\" | git mergetool ../file12 ) &&\n-\t\t( yes \"l\" | git mergetool ../submod ) &&\n+\t\tyes \"\" | git mergetool file3 &&\n+\t\tyes \"\" | git mergetool ../file1 &&\n+\t\tyes \"\" | git mergetool ../file2 ../spaced\\ name &&\n+\t\tyes \"\" | git mergetool ../both &&\n+\t\tyes \"d\" | git mergetool ../file11 &&\n+\t\tyes \"d\" | git mergetool ../file12 &&\n+\t\tyes \"l\" | git mergetool ../submod &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat ../submod/bar)\" = \"branch1 submodule\" &&\n@@ -250,9 +250,9 @@ test_expect_success 'mergetool skips autoresolved' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\"\n '\n@@ -264,8 +264,8 @@ test_expect_success 'mergetool merges all from subdir (rerere disabled)' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"r\" | git mergetool ../submod ) &&\n-\t\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\t\tyes \"r\" | git mergetool ../submod &&\n+\t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat file3)\" = \"master new sub\" &&\n@@ -283,8 +283,8 @@ test_expect_success 'mergetool merges all from subdir (rerere enabled)' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"r\" | git mergetool ../submod ) &&\n-\t\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\t\tyes \"r\" | git mergetool ../submod &&\n+\t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat file3)\" = \"master new sub\" &&\n@@ -301,8 +301,8 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"l\" | git mergetool --no-prompt submod ) &&\n-\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\tyes \"l\" | git mergetool --no-prompt submod &&\n+\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \tgit submodule update -N &&\n \toutput=\"$(yes \"n\" | git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\"\n@@ -343,7 +343,7 @@ test_expect_success 'mergetool takes partial path' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \n-\t( yes \"\" | git mergetool subdir ) &&\n+\tyes \"\" | git mergetool subdir &&\n \n \ttest \"$(cat subdir/file3)\" = \"master new sub\"\n '\n@@ -410,10 +410,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit checkout -b test$test_count.a test$test_count &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n \ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n \tgit submodule update -N &&\n@@ -427,10 +427,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest ! -e submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n@@ -441,10 +441,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest ! -e submod &&\n \ttest -d submod.orig &&\n \tgit submodule update -N &&\n@@ -457,10 +457,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n@@ -481,10 +481,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit checkout -b test$test_count.a branch1 &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n \ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n \tgit submodule update -N &&\n@@ -497,10 +497,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit checkout -b test$test_count.b test$test_count &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod)\" = \"not a submodule\" &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n@@ -513,10 +513,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod)\" = \"not a submodule\" &&\n@@ -529,10 +529,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n@@ -587,7 +587,7 @@ test_expect_success 'submodule in subdirectory' '\n \ttest_must_fail git merge test$test_count.a &&\n \t(\n \t\tcd subdir &&\n-\t\t( yes \"l\" | git mergetool subdir_module )\n+\t\tyes \"l\" | git mergetool subdir_module\n \t) &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n \tgit submodule update -N &&\n@@ -596,7 +596,7 @@ test_expect_success 'submodule in subdirectory' '\n \tgit submodule update -N &&\n \n \ttest_must_fail git merge test$test_count.a &&\n-\t( yes \"r\" | git mergetool subdir/subdir_module ) &&\n+\tyes \"r\" | git mergetool subdir/subdir_module &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.a\" &&\n@@ -615,7 +615,7 @@ test_expect_success 'directory vs modified submodule' '\n \n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n \trm -rf submod.orig &&\n \n@@ -623,7 +623,7 @@ test_expect_success 'directory vs modified submodule' '\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n \ttest ! -e submod.orig &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n \ttest \"$(cat submod.orig/file16)\" = \"not a submodule\" &&\n \trm -r submod.orig &&\n@@ -638,7 +638,7 @@ test_expect_success 'directory vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \n@@ -647,7 +647,7 @@ test_expect_success 'directory vs modified submodule' '\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n \ttest ! -e submod.orig &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n \n \tgit reset --hard master &&\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"376914","messageId":"2a33ca20af41d68a5bb4e2cf1e5ae32fddf2796c.1560152205.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560152205.git.j6t@kdbg.org","subject":"[PATCH 2/3] mergetool: dissect strings with shell variable magic instead of `expr`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T08:58:59Z","receivedAt":"2019-06-10T08:59:42Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git, but there are\nalso some that can be replaced with shell builtins. Do so with `expr`.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n git-mergetool.sh | 20 +++++++++++---------\n 1 file changed, 11 insertions(+), 9 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 88fa6a914a..8a937f680f 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -228,9 +228,8 @@ stage_submodule () {\n }\n \n checkout_staged_file () {\n-\ttmpfile=$(expr \\\n-\t\t\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" \\\n-\t\t: '\\([^\t]*\\)\t')\n+\ttmpfile=\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" &&\n+\ttmpfile=${tmpfile%%'\t'*}\n \n \tif test $? -eq 0 && test -n \"$tmpfile\"\n \tthen\n@@ -255,13 +254,16 @@ merge_file () {\n \t\treturn 1\n \tfi\n \n-\tif BASE=$(expr \"$MERGED\" : '\\(.*\\)\\.[^/]*$')\n-\tthen\n-\t\text=$(expr \"$MERGED\" : '.*\\(\\.[^/]*\\)$')\n-\telse\n+\t# extract file extension from the last path component\n+\tcase \"${MERGED##*/}\" in\n+\t*.*)\n+\t\text=.${MERGED##*.}\n+\t\tBASE=${MERGED%\"$ext\"}\n+\t\t;;\n+\t*)\n \t\tBASE=$MERGED\n \t\text=\n-\tfi\n+\tesac\n \n \tmergetool_tmpdir_init\n \n@@ -406,7 +408,7 @@ main () {\n \t\t-t|--tool*)\n \t\t\tcase \"$#,$1\" in\n \t\t\t*,*=*)\n-\t\t\t\tmerge_tool=$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)')\n+\t\t\t\tmerge_tool=${1#*=}\n \t\t\t\t;;\n \t\t\t1,*)\n \t\t\t\tusage ;;\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"376915","messageId":"d6675b02918364736ec74db937481127ab7d8990.1560152205.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560152205.git.j6t@kdbg.org","subject":"[PATCH 3/3] mergetool: use shell variable magic instead of `awk`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T08:59:00Z","receivedAt":"2019-06-10T08:59:46Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git, but there are\nalso some that can be replaced with shell builtins. Avoid repeated\ncalls of `git ls-files` and `awk`.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n git-mergetool.sh | 25 ++++++++++++++++++++-----\n 1 file changed, 20 insertions(+), 5 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 8a937f680f..e3f6d543fb 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -279,15 +279,30 @@ merge_file () {\n \tREMOTE=\"$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext\"\n \tBASE=\"$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext\"\n \n-\tbase_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n-\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n-\tremote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n+\tbase_mode= local_mode= remote_mode=\n+\n+\t# here, $IFS is just a LF\n+\tfor line in $f\n+\tdo\n+\t\tmode=${line%% *}\t\t# 1st word\n+\t\tsha1=${line#\"$mode \"}\n+\t\tsha1=${sha1%% *}\t\t# 2nd word\n+\t\tcase \"${line#$mode $sha1 }\" in\t# remainder\n+\t\t'1\t'*)\n+\t\t\tbase_mode=$mode\n+\t\t\t;;\n+\t\t'2\t'*)\n+\t\t\tlocal_mode=$mode local_sha1=$sha1\n+\t\t\t;;\n+\t\t'3\t'*)\n+\t\t\tremote_mode=$mode remote_sha1=$sha1\n+\t\t\t;;\n+\t\tesac\n+\tdone\n \n \tif is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"\n \tthen\n \t\techo \"Submodule merge conflict for '$MERGED':\"\n-\t\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n-\t\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n \t\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n \t\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n \t\tresolve_submodule_merge\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"376917","messageId":"20190610095942.GA19035@szeder.dev","threadId":"51278","inReplyTo":"75c812bd4838f6f35b6f42b97ae396ebb28d8b95.1560152205.git.j6t@kdbg.org","subject":"Re: [PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-06-10T09:59:42Z","receivedAt":"2019-06-10T09:59:51Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Mon, Jun 10, 2019 at 10:58:58AM +0200, Johannes Sixt wrote:\n> Subshells for pipelines are not required. This can save a number of\n> processes (if the shell does not optimize it away anyway).\n> \n> The patch was generated with the command\n> \n>    sed -i 's/( *\\(yes.*[^ ]\\) *) *\\&\\&/\\1 \\&\\&/' t7610-mergetool.sh\n> \n> with a manual fixup of the case having no && at the end.\n\nI think it would be great to include the corresponding numbers from\nthe cover letter in each of the commit messages.\n\n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  t/t7610-mergetool.sh | 170 +++++++++++++++++++++----------------------\n>  1 file changed, 85 insertions(+), 85 deletions(-)\n> \n> diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\n> index 5b61c10a9c..b67440882b 100755\n> --- a/t/t7610-mergetool.sh\n> +++ b/t/t7610-mergetool.sh\n> @@ -131,13 +131,13 @@ test_expect_success 'custom mergetool' '\n>  \tgit checkout -b test$test_count branch1 &&\n>  \tgit submodule update -N &&\n>  \ttest_must_fail git merge master &&\n> -\t( yes \"\" | git mergetool both ) &&\n> -\t( yes \"\" | git mergetool file1 file1 ) &&\n> -\t( yes \"\" | git mergetool file2 \"spaced name\" ) &&\n> -\t( yes \"\" | git mergetool subdir/file3 ) &&\n> -\t( yes \"d\" | git mergetool file11 ) &&\n> -\t( yes \"d\" | git mergetool file12 ) &&\n> -\t( yes \"l\" | git mergetool submod ) &&\n> +\tyes \"\" | git mergetool both &&\n> +\tyes \"\" | git mergetool file1 file1 &&\n> +\tyes \"\" | git mergetool file2 \"spaced name\" &&\n> +\tyes \"\" | git mergetool subdir/file3 &&\n> +\tyes \"d\" | git mergetool file11 &&\n> +\tyes \"d\" | git mergetool file12 &&\n> +\tyes \"l\" | git mergetool submod &&\n>  \ttest \"$(cat file1)\" = \"master updated\" &&\n>  \ttest \"$(cat file2)\" = \"master new\" &&\n>  \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n\nAnother possibility for eliminating a few more subshells might be to\nturn these\n\n  test \"$(cat file1)\" = \"that\"'\n\nchecks into\n\n  echo that >expect &&\n  test_cmp expect file1\n\nbecause 'test_cmp' on Windows first compares the two files in shell\nand runs 'diff' only when there is a difference to report.\n\n"},{"id":"376950","messageId":"xmqqblz5nyve.fsf@gitster-ct.c.googlers.com","threadId":"51278","inReplyTo":"2a33ca20af41d68a5bb4e2cf1e5ae32fddf2796c.1560152205.git.j6t@kdbg.org","subject":"Re: [PATCH 2/3] mergetool: dissect strings with shell variable magic instead of `expr`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T17:17:41Z","receivedAt":"2019-06-10T17:17:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> git-mergetool spawns an enormous amount of processes. For this reason,\n> the test script, t7610, is exceptionally slow, in particular, on\n> Windows. Most of the processes are invocations of git, but there are\n> also some that can be replaced with shell builtins. Do so with `expr`.\n\nI see these as improvements independent of whatever test may or may\nnot be slow ;-)  s/^.*/but there are/There are/.  Thanks for working\non it.\n\n>  checkout_staged_file () {\n> -\ttmpfile=$(expr \\\n> -\t\t\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" \\\n> -\t\t: '\\([^\t]*\\)\t')\n\nSo this wants to grab leading non-HT substring that comes before an\nHT; we are trying to grab the name of the temorary picked by the\ncheckout-index command, the output is \".merge_file_XXXXXX\" followed\nby HT followed by the original filename \"$2\".\n\n> +\ttmpfile=\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" &&\n> +\ttmpfile=${tmpfile%%'\t'*}\n\nAnd this obviously is an equivalent, at least in the successful\ncase.  The \".merge_file_XXXXXX\" temporary filename never has HT in\nit, and we are stripping everything after the first HT.\n\nAnd this rewrite makes the error behaviour much better.  In the\noriginal, the exit code checked in the next \"if test $? -eq 0\" is\nthat of \"expr\" (i.e. does the pattern match?); with this version, we\nare looking at the exit status of the checkout-index command.\n\nGood.\n\n> @@ -255,13 +254,16 @@ merge_file () {\n>  \t\treturn 1\n>  \tfi\n>  \n> -\tif BASE=$(expr \"$MERGED\" : '\\(.*\\)\\.[^/]*$')\n> -\tthen\n> -\t\text=$(expr \"$MERGED\" : '.*\\(\\.[^/]*\\)$')\n> -\telse\n> +\t# extract file extension from the last path component\n> +\tcase \"${MERGED##*/}\" in\n> +\t*.*)\n> +\t\text=.${MERGED##*.}\n> +\t\tBASE=${MERGED%\"$ext\"}\n\nThis rewrite can potentially change the behaviour, when $ext has\nglob metacharacters.  Wouldn't BASE=${MERGED%.*} be more faithful\nconversion?\n\n> +\t\t;;\n> +\t*)\n>  \t\tBASE=$MERGED\n>  \t\text=\n> -\tfi\n> +\tesac\n> @@ -406,7 +408,7 @@ main () {\n>  \t\t-t|--tool*)\n>  \t\t\tcase \"$#,$1\" in\n>  \t\t\t*,*=*)\n> -\t\t\t\tmerge_tool=$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)')\n> +\t\t\t\tmerge_tool=${1#*=}\n\nOK, we strip leading substring before the first '=' out of \"$1\" and\nthe case/esac ensures that there is such an equal '=' sign in \"$1\",\nso the rewrite is correct.\n\nLooks good.  Thanks.\n\n\n"},{"id":"376951","messageId":"xmqq7e9tnyp0.fsf@gitster-ct.c.googlers.com","threadId":"51278","inReplyTo":"d6675b02918364736ec74db937481127ab7d8990.1560152205.git.j6t@kdbg.org","subject":"Re: [PATCH 3/3] mergetool: use shell variable magic instead of `awk`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T17:21:31Z","receivedAt":"2019-06-10T17:21:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> +\t# here, $IFS is just a LF\n> +\tfor line in $f\n> +\tdo\n> +\t\tmode=${line%% *}\t\t# 1st word\n> +\t\tsha1=${line#\"$mode \"}\n> +\t\tsha1=${sha1%% *}\t\t# 2nd word\n> +\t\tcase \"${line#$mode $sha1 }\" in\t# remainder\n> +\t\t'1\t'*)\n> +\t\t\tbase_mode=$mode\n> +\t\t\t;;\n> +\t\t'2\t'*)\n> +\t\t\tlocal_mode=$mode local_sha1=$sha1\n> +\t\t\t;;\n> +\t\t'3\t'*)\n> +\t\t\tremote_mode=$mode remote_sha1=$sha1\n> +\t\t\t;;\n> +\t\tesac\n> +\tdone\n\nOK.  $mode won't have any glob metacharacter, and there is only one\ninvocation of \"ls-files -u\", which is now two fewer processes ;-)\n\n>  \n>  \tif is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"\n>  \tthen\n>  \t\techo \"Submodule merge conflict for '$MERGED':\"\n> -\t\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n> -\t\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n>  \t\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n>  \t\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n>  \t\tresolve_submodule_merge\n"},{"id":"376952","messageId":"xmqq36khnyky.fsf@gitster-ct.c.googlers.com","threadId":"51278","inReplyTo":"20190610095942.GA19035@szeder.dev","subject":"Re: [PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T17:23:57Z","receivedAt":"2019-06-10T17:24:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n> turn these\n>\n>   test \"$(cat file1)\" = \"that\"'\n>\n> checks into\n>\n>   echo that >expect &&\n>   test_cmp expect file1\n>\n> because 'test_cmp' on Windows first compares the two files in shell\n> and runs 'diff' only when there is a difference to report.\n\nNeeds measuring.  Is two extra file I/Os that inexpensive?\n"},{"id":"376957","messageId":"20190610175642.GB4012@szeder.dev","threadId":"51278","inReplyTo":"xmqq36khnyky.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-06-10T17:56:42Z","receivedAt":"2019-06-10T17:56:49Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Mon, Jun 10, 2019 at 10:23:57AM -0700, Junio C Hamano wrote:\n> SZEDER Gábor <szeder.dev@gmail.com> writes:\n> \n> > turn these\n> >\n> >   test \"$(cat file1)\" = \"that\"'\n> >\n> > checks into\n> >\n> >   echo that >expect &&\n> >   test_cmp expect file1\n> >\n> > because 'test_cmp' on Windows first compares the two files in shell\n> > and runs 'diff' only when there is a difference to report.\n> \n> Needs measuring.  Is two extra file I/Os that inexpensive?\n\nCompared to subshells and external processes, yes.\n\nI run this on Linux:\n\n  --- >8 ---\n\n#!/bin/sh\n\ntest_description='test'\n\n. ./lib-bash.sh\n\ntest_expect_success 'test' '\n\techo \"master updated\" >file1 &&\n\n\techo \"original:\" &&\n\ttime for i in $(seq 1000)\n\tdo\n\t\ttest \"$(cat file1)\" = \"master updated\"\n\tdone &&\n\n\techo \"using test_cmp:\" &&\n\ttime for i in $(seq 1000)\n\tdo\n\t\techo \"master updated\" >expect &&\n\t\ttest_cmp expect file1\n\tdone &&\n\n\techo \"using mingw_test_cmp:\" &&\n\ttime for i in $(seq 1000)\n\tdo\n\t\techo \"master updated\" >expect &&\n\t\tmingw_test_cmp expect file1\n\tdone\n'\n\ntest_done\n\n  --- >8 ---\n\nAnd it produced:\n\n  original:\n  \n  real    0m1.888s\n  user    0m1.491s\n  sys     0m0.532s\n  using test_cmp:\n  \n  real    0m1.233s\n  user    0m0.877s\n  sys     0m0.432s\n  using mingw_test_cmp:\n  \n  real    0m0.344s\n  user    0m0.298s\n  sys     0m0.026s\n  ok 1 - test\n\nIt's faster even on Linux, so it should be much faster on Windows,\nwhere both external commands and subshells have much higher overhead.\n\nHowever, there are only about 50 \"$(cat ...)\" in t7610 that can be\neliminated this way, which is not all that much compared to the\nseveral thousand processes shown in the cover letter...\n\nAnyway, there is still the better developer experience in case of one\nof these checks were to fail without '-x' tracing... :)\n\n"},{"id":"376960","messageId":"nycvar.QRO.7.76.6.1906102026030.789@QRFXGBC-DHN364S.ybpnyqbznva","threadId":"51278","inReplyTo":"20190610095942.GA19035@szeder.dev","subject":"Re: [PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-06-10T18:29:56Z","receivedAt":"2019-06-10T18:29:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 10 Jun 2019, SZEDER Gábor wrote:\n\n> > diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\n> > index 5b61c10a9c..b67440882b 100755\n> > --- a/t/t7610-mergetool.sh\n> > +++ b/t/t7610-mergetool.sh\n> > @@ -131,13 +131,13 @@ test_expect_success 'custom mergetool' '\n> >  \tgit checkout -b test$test_count branch1 &&\n> >  \tgit submodule update -N &&\n> >  \ttest_must_fail git merge master &&\n> > -\t( yes \"\" | git mergetool both ) &&\n> > -\t( yes \"\" | git mergetool file1 file1 ) &&\n> > -\t( yes \"\" | git mergetool file2 \"spaced name\" ) &&\n> > -\t( yes \"\" | git mergetool subdir/file3 ) &&\n> > -\t( yes \"d\" | git mergetool file11 ) &&\n> > -\t( yes \"d\" | git mergetool file12 ) &&\n> > -\t( yes \"l\" | git mergetool submod ) &&\n> > +\tyes \"\" | git mergetool both &&\n> > +\tyes \"\" | git mergetool file1 file1 &&\n> > +\tyes \"\" | git mergetool file2 \"spaced name\" &&\n> > +\tyes \"\" | git mergetool subdir/file3 &&\n> > +\tyes \"d\" | git mergetool file11 &&\n> > +\tyes \"d\" | git mergetool file12 &&\n> > +\tyes \"l\" | git mergetool submod &&\n> >  \ttest \"$(cat file1)\" = \"master updated\" &&\n> >  \ttest \"$(cat file2)\" = \"master new\" &&\n> >  \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n>\n> Another possibility for eliminating a few more subshells might be to\n> turn these\n>\n>   test \"$(cat file1)\" = \"that\"'\n>\n> checks into\n>\n>   echo that >expect &&\n>   test_cmp expect file1\n>\n> because 'test_cmp' on Windows first compares the two files in shell\n> and runs 'diff' only when there is a difference to report.\n\nWhen you remember that spawning processes is much more expensive on\nWindows, still, than I/O, you will realize that this adds even more\nexpense. Instead of a spawn & read, you are suggesting essentially a\nwrite, spawn, read & read, and that is only the best case.\n\nIn the worst case, it would be a write, spawn, read & read, spawn, read &\nread.\n\n(Even if the first spawn is an MSYS2 spawn on Windows, which is more\nexpensive than the MINGW spawn for the `git diff`, if that is a `git diff`\nrather than `diff`, didn't check...)\n\nSo I am rather negative about this suggestion ;-)\n\nCiao,\nDscho\n"},{"id":"376963","messageId":"20190610185708.GC4012@szeder.dev","threadId":"51278","inReplyTo":"nycvar.QRO.7.76.6.1906102026030.789@QRFXGBC-DHN364S.ybpnyqbznva","subject":"Re: [PATCH 1/3] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-06-10T18:57:08Z","receivedAt":"2019-06-10T18:57:22Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Mon, Jun 10, 2019 at 08:29:56PM +0200, Johannes Schindelin wrote:\n> Hi,\n> \n> On Mon, 10 Jun 2019, SZEDER Gábor wrote:\n> \n> > > diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\n> > > index 5b61c10a9c..b67440882b 100755\n> > > --- a/t/t7610-mergetool.sh\n> > > +++ b/t/t7610-mergetool.sh\n> > > @@ -131,13 +131,13 @@ test_expect_success 'custom mergetool' '\n> > >  \tgit checkout -b test$test_count branch1 &&\n> > >  \tgit submodule update -N &&\n> > >  \ttest_must_fail git merge master &&\n> > > -\t( yes \"\" | git mergetool both ) &&\n> > > -\t( yes \"\" | git mergetool file1 file1 ) &&\n> > > -\t( yes \"\" | git mergetool file2 \"spaced name\" ) &&\n> > > -\t( yes \"\" | git mergetool subdir/file3 ) &&\n> > > -\t( yes \"d\" | git mergetool file11 ) &&\n> > > -\t( yes \"d\" | git mergetool file12 ) &&\n> > > -\t( yes \"l\" | git mergetool submod ) &&\n> > > +\tyes \"\" | git mergetool both &&\n> > > +\tyes \"\" | git mergetool file1 file1 &&\n> > > +\tyes \"\" | git mergetool file2 \"spaced name\" &&\n> > > +\tyes \"\" | git mergetool subdir/file3 &&\n> > > +\tyes \"d\" | git mergetool file11 &&\n> > > +\tyes \"d\" | git mergetool file12 &&\n> > > +\tyes \"l\" | git mergetool submod &&\n> > >  \ttest \"$(cat file1)\" = \"master updated\" &&\n> > >  \ttest \"$(cat file2)\" = \"master new\" &&\n> > >  \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n> >\n> > Another possibility for eliminating a few more subshells might be to\n> > turn these\n> >\n> >   test \"$(cat file1)\" = \"that\"'\n> >\n> > checks into\n> >\n> >   echo that >expect &&\n> >   test_cmp expect file1\n> >\n> > because 'test_cmp' on Windows first compares the two files in shell\n> > and runs 'diff' only when there is a difference to report.\n> \n> When you remember that spawning processes is much more expensive on\n> Windows, still, than I/O, you will realize that this adds even more\n> expense. Instead of a spawn & read, you are suggesting essentially a\n> write, spawn, read & read, and that is only the best case.\n\nNo, instead of a spawn (the subshell of the command substitution), spawn\n('cat'), read, I suggest a write, read, read (no subshell or external\nprocess in 'mingw_test_cmp's main code path).\n\n> In the worst case, it would be a write, spawn, read & read, spawn, read &\n> read.\n\nIt would be a write, read, read, only one spawn (diff), read read, but\nI assume that the tests succeed, so I ignore this worst case.\n\n\n> (Even if the first spawn is an MSYS2 spawn on Windows, which is more\n> expensive than the MINGW spawn for the `git diff`, if that is a `git diff`\n> rather than `diff`, didn't check...)\n> \n> So I am rather negative about this suggestion ;-)\n> \n> Ciao,\n> Dscho\n\n"},{"id":"376971","messageId":"0a350943-9264-1a40-6452-91a990ec790d@kdbg.org","threadId":"51278","inReplyTo":"xmqqblz5nyve.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 2/3] mergetool: dissect strings with shell variable magic instead of `expr`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T21:34:47Z","receivedAt":"2019-06-10T21:34:51Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 10.06.19 um 19:17 schrieb Junio C Hamano:\n> Johannes Sixt <j6t@kdbg.org> writes:\n>> git-mergetool spawns an enormous amount of processes. For this reason,\n>> the test script, t7610, is exceptionally slow, in particular, on\n>> Windows. Most of the processes are invocations of git, but there are\n>> also some that can be replaced with shell builtins. Do so with `expr`.\n> \n> I see these as improvements independent of whatever test may or may\n> not be slow ;-)  s/^.*/but there are/There are/.  Thanks for working\n> on it.\n\nNoted.\n\n>> @@ -255,13 +254,16 @@ merge_file () {\n>>  \t\treturn 1\n>>  \tfi\n>>  \n>> -\tif BASE=$(expr \"$MERGED\" : '\\(.*\\)\\.[^/]*$')\n>> -\tthen\n>> -\t\text=$(expr \"$MERGED\" : '.*\\(\\.[^/]*\\)$')\n>> -\telse\n>> +\t# extract file extension from the last path component\n>> +\tcase \"${MERGED##*/}\" in\n>> +\t*.*)\n>> +\t\text=.${MERGED##*.}\n>> +\t\tBASE=${MERGED%\"$ext\"}\n> \n> This rewrite can potentially change the behaviour, when $ext has\n> glob metacharacters.  Wouldn't BASE=${MERGED%.*} be more faithful\n> conversion?\n\nSince \"$ext\" is quoted inside the braces of the parameter expansion, the\npattern counts as quoted, so all glob characters in $ext lose their\nspecial meaning. At least that's how I read the spec.\n\nI do see the symmetry in your proposed version. Nevertheless, I have a\nslight preference for my version because it specifies exactly what is to\nbe removed from the end of value.\n\n-- Hannes\n"},{"id":"376974","messageId":"a80b9f58-5aa1-f7fa-7f64-f9cc1024c248@kdbg.org","threadId":"51278","inReplyTo":"xmqq7e9tnyp0.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 3/3] mergetool: use shell variable magic instead of `awk`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-10T22:01:03Z","receivedAt":"2019-06-10T22:01:07Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 10.06.19 um 19:21 schrieb Junio C Hamano:\n> Johannes Sixt <j6t@kdbg.org> writes:\n> \n>> +\t# here, $IFS is just a LF\n>> +\tfor line in $f\n>> +\tdo\n>> +\t\tmode=${line%% *}\t\t# 1st word\n>> +\t\tsha1=${line#\"$mode \"}\n>> +\t\tsha1=${sha1%% *}\t\t# 2nd word\n>> +\t\tcase \"${line#$mode $sha1 }\" in\t# remainder\n>> +\t\t'1\t'*)\n>> +\t\t\tbase_mode=$mode\n>> +\t\t\t;;\n>> +\t\t'2\t'*)\n>> +\t\t\tlocal_mode=$mode local_sha1=$sha1\n>> +\t\t\t;;\n>> +\t\t'3\t'*)\n>> +\t\t\tremote_mode=$mode remote_sha1=$sha1\n>> +\t\t\t;;\n>> +\t\tesac\n>> +\tdone\n> \n> OK.  $mode won't have any glob metacharacter, and there is only one\n> invocation of \"ls-files -u\", which is now two fewer processes ;-)\n\nI think it's better than that: there're about 9 fewer from the context\nthat you didn't quote:\n\n-\tbase_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n-\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n-\tremote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n\nand sometimes the 6 from below on top of that. But I won't insist in\nhaving counted correctly ;-)\n\n>>  \n>>  \tif is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"\n>>  \tthen\n>>  \t\techo \"Submodule merge conflict for '$MERGED':\"\n>> -\t\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n>> -\t\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n>>  \t\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n>>  \t\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n>>  \t\tresolve_submodule_merge\n"},{"id":"377084","messageId":"cover.1560356675.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560152205.git.j6t@kdbg.org","subject":"[PATCH v2 0/4] Reduce number of processes spawned by git-mergetool","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-12T16:33:44Z","receivedAt":"2019-06-12T16:34:06Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git. There are\nalso some that can be replaced with shell builtins.\n\nI've measured the number of processes and the number of\nnon-git commands invoked with\n\n  strace -e execve,clone -f -o /tmp/git.strace ./t7610-mergetool.sh\n  # processes:\n  grep 'stack=NULL' /tmp/git.strace | wc -l\n  # non-git commands:\n  grep 'execve(' /tmp/git.strace | grep -v 'execve(\".home' | wc -l\n\n          non-git\nprocesses commands\n13775     3082    baseline\n13690     3082    t7610-mergetool: do not place pipelines headed by `yes` in subshells\n13645     3082    t7610-mergetool: use test_cmp instead of test $(cat file) = $txt\n13084     2521    mergetool: dissect strings with shell variable magic instead of `expr`\n11542     2007    mergetool: use shell variable magic instead of `awk`\n\nNotes on v2:\n- Followed Gábor's suggestion to transform test ($cat ..) = txt\n  (new patch 2).  It does not help a lot, but neither does\n  patch 1, the pipe-in-subshell conversion.  Both are foremost\n  clean-ups.\n- Commit message tweaks; see below.\n- Did not include measurement results in the commit messages, because\n  I would have to repeat the measurement method every time.\n\nJohannes Sixt (4):\n  t7610-mergetool: do not place pipelines headed by `yes` in subshells\n  t7610-mergetool: use test_cmp instead of test $(cat file) = $txt\n  mergetool: dissect strings with shell variable magic instead of `expr`\n  mergetool: use shell variable magic instead of `awk`\n\n git-mergetool.sh     |  45 +++++--\n t/t7610-mergetool.sh | 309 +++++++++++++++++++++++++------------------\n 2 files changed, 208 insertions(+), 146 deletions(-)\n\nRange-diff against v1:\n1:  75c812bd48 = 1:  75c812bd48 t7610-mergetool: do not place pipelines headed by `yes` in subshells\n-:  ---------- > 2:  82bafc90de t7610-mergetool: use test_cmp instead of test $(cat file) = $txt\n2:  2a33ca20af ! 3:  ad34a9a7ab mergetool: dissect strings with shell variable magic instead of `expr`\n    @@ -4,7 +4,7 @@\n     \n         git-mergetool spawns an enormous amount of processes. For this reason,\n         the test script, t7610, is exceptionally slow, in particular, on\n    -    Windows. Most of the processes are invocations of git, but there are\n    +    Windows. Most of the processes are invocations of git. There are\n         also some that can be replaced with shell builtins. Do so with `expr`.\n     \n         Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n3:  d6675b0291 ! 4:  99a6a89339 mergetool: use shell variable magic instead of `awk`\n    @@ -4,7 +4,7 @@\n     \n         git-mergetool spawns an enormous amount of processes. For this reason,\n         the test script, t7610, is exceptionally slow, in particular, on\n    -    Windows. Most of the processes are invocations of git, but there are\n    +    Windows. Most of the processes are invocations of git. There are\n         also some that can be replaced with shell builtins. Avoid repeated\n         calls of `git ls-files` and `awk`.\n     \n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"377085","messageId":"ad34a9a7ab7bcfb20274d404031ff875aab797ae.1560356675.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560356675.git.j6t@kdbg.org","subject":"[PATCH v2 3/4] mergetool: dissect strings with shell variable magic instead of `expr`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-12T16:33:47Z","receivedAt":"2019-06-12T16:34:15Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git. There are\nalso some that can be replaced with shell builtins. Do so with `expr`.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n git-mergetool.sh | 20 +++++++++++---------\n 1 file changed, 11 insertions(+), 9 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 88fa6a914a..8a937f680f 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -228,9 +228,8 @@ stage_submodule () {\n }\n \n checkout_staged_file () {\n-\ttmpfile=$(expr \\\n-\t\t\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" \\\n-\t\t: '\\([^\t]*\\)\t')\n+\ttmpfile=\"$(git checkout-index --temp --stage=\"$1\" \"$2\" 2>/dev/null)\" &&\n+\ttmpfile=${tmpfile%%'\t'*}\n \n \tif test $? -eq 0 && test -n \"$tmpfile\"\n \tthen\n@@ -255,13 +254,16 @@ merge_file () {\n \t\treturn 1\n \tfi\n \n-\tif BASE=$(expr \"$MERGED\" : '\\(.*\\)\\.[^/]*$')\n-\tthen\n-\t\text=$(expr \"$MERGED\" : '.*\\(\\.[^/]*\\)$')\n-\telse\n+\t# extract file extension from the last path component\n+\tcase \"${MERGED##*/}\" in\n+\t*.*)\n+\t\text=.${MERGED##*.}\n+\t\tBASE=${MERGED%\"$ext\"}\n+\t\t;;\n+\t*)\n \t\tBASE=$MERGED\n \t\text=\n-\tfi\n+\tesac\n \n \tmergetool_tmpdir_init\n \n@@ -406,7 +408,7 @@ main () {\n \t\t-t|--tool*)\n \t\t\tcase \"$#,$1\" in\n \t\t\t*,*=*)\n-\t\t\t\tmerge_tool=$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)')\n+\t\t\t\tmerge_tool=${1#*=}\n \t\t\t\t;;\n \t\t\t1,*)\n \t\t\t\tusage ;;\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"377086","messageId":"99a6a893395a8b6bada170172c821550d84d6983.1560356675.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560356675.git.j6t@kdbg.org","subject":"[PATCH v2 4/4] mergetool: use shell variable magic instead of `awk`","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-12T16:33:48Z","receivedAt":"2019-06-12T16:34:16Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"git-mergetool spawns an enormous amount of processes. For this reason,\nthe test script, t7610, is exceptionally slow, in particular, on\nWindows. Most of the processes are invocations of git. There are\nalso some that can be replaced with shell builtins. Avoid repeated\ncalls of `git ls-files` and `awk`.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n git-mergetool.sh | 25 ++++++++++++++++++++-----\n 1 file changed, 20 insertions(+), 5 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 8a937f680f..e3f6d543fb 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -279,15 +279,30 @@ merge_file () {\n \tREMOTE=\"$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext\"\n \tBASE=\"$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext\"\n \n-\tbase_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n-\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n-\tremote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n+\tbase_mode= local_mode= remote_mode=\n+\n+\t# here, $IFS is just a LF\n+\tfor line in $f\n+\tdo\n+\t\tmode=${line%% *}\t\t# 1st word\n+\t\tsha1=${line#\"$mode \"}\n+\t\tsha1=${sha1%% *}\t\t# 2nd word\n+\t\tcase \"${line#$mode $sha1 }\" in\t# remainder\n+\t\t'1\t'*)\n+\t\t\tbase_mode=$mode\n+\t\t\t;;\n+\t\t'2\t'*)\n+\t\t\tlocal_mode=$mode local_sha1=$sha1\n+\t\t\t;;\n+\t\t'3\t'*)\n+\t\t\tremote_mode=$mode remote_sha1=$sha1\n+\t\t\t;;\n+\t\tesac\n+\tdone\n \n \tif is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"\n \tthen\n \t\techo \"Submodule merge conflict for '$MERGED':\"\n-\t\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n-\t\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n \t\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n \t\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n \t\tresolve_submodule_merge\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"377087","messageId":"75c812bd4838f6f35b6f42b97ae396ebb28d8b95.1560356675.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560356675.git.j6t@kdbg.org","subject":"[PATCH v2 1/4] t7610-mergetool: do not place pipelines headed by `yes` in subshells","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-12T16:33:45Z","receivedAt":"2019-06-12T16:34:19Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Subshells for pipelines are not required. This can save a number of\nprocesses (if the shell does not optimize it away anyway).\n\nThe patch was generated with the command\n\n   sed -i 's/( *\\(yes.*[^ ]\\) *) *\\&\\&/\\1 \\&\\&/' t7610-mergetool.sh\n\nwith a manual fixup of the case having no && at the end.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n t/t7610-mergetool.sh | 170 +++++++++++++++++++++----------------------\n 1 file changed, 85 insertions(+), 85 deletions(-)\n\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex 5b61c10a9c..b67440882b 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -131,13 +131,13 @@ test_expect_success 'custom mergetool' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"\" | git mergetool file1 file1 ) &&\n-\t( yes \"\" | git mergetool file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"\" | git mergetool file1 file1 &&\n+\tyes \"\" | git mergetool file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool subdir/file3 &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat file1)\" = \"master updated\" &&\n \ttest \"$(cat file2)\" = \"master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n@@ -153,13 +153,13 @@ test_expect_success 'gui mergetool' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool --gui both ) &&\n-\t( yes \"\" | git mergetool -g file1 file1 ) &&\n-\t( yes \"\" | git mergetool --gui file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool --gui subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool --gui file11 ) &&\n-\t( yes \"d\" | git mergetool --gui file12 ) &&\n-\t( yes \"l\" | git mergetool --gui submod ) &&\n+\tyes \"\" | git mergetool --gui both &&\n+\tyes \"\" | git mergetool -g file1 file1 &&\n+\tyes \"\" | git mergetool --gui file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool --gui subdir/file3 &&\n+\tyes \"d\" | git mergetool --gui file11 &&\n+\tyes \"d\" | git mergetool --gui file12 &&\n+\tyes \"l\" | git mergetool --gui submod &&\n \ttest \"$(cat file1)\" = \"gui master updated\" &&\n \ttest \"$(cat file2)\" = \"gui master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"gui master new sub\" &&\n@@ -172,13 +172,13 @@ test_expect_success 'gui mergetool without merge.guitool set falls back to merge\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool --gui both ) &&\n-\t( yes \"\" | git mergetool -g file1 file1 ) &&\n-\t( yes \"\" | git mergetool --gui file2 \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool --gui subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool --gui file11 ) &&\n-\t( yes \"d\" | git mergetool --gui file12 ) &&\n-\t( yes \"l\" | git mergetool --gui submod ) &&\n+\tyes \"\" | git mergetool --gui both &&\n+\tyes \"\" | git mergetool -g file1 file1 &&\n+\tyes \"\" | git mergetool --gui file2 \"spaced name\" &&\n+\tyes \"\" | git mergetool --gui subdir/file3 &&\n+\tyes \"d\" | git mergetool --gui file11 &&\n+\tyes \"d\" | git mergetool --gui file12 &&\n+\tyes \"l\" | git mergetool --gui submod &&\n \ttest \"$(cat file1)\" = \"master updated\" &&\n \ttest \"$(cat file2)\" = \"master new\" &&\n \ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n@@ -195,14 +195,14 @@ test_expect_success 'mergetool crlf' '\n \ttest_config core.autocrlf true &&\n \tgit checkout -b test$test_count branch1 &&\n \ttest_must_fail git merge master &&\n-\t( yes \"\" | git mergetool file1 ) &&\n-\t( yes \"\" | git mergetool file2 ) &&\n-\t( yes \"\" | git mergetool \"spaced name\" ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"\" | git mergetool subdir/file3 ) &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 &&\n+\tyes \"\" | git mergetool file2 &&\n+\tyes \"\" | git mergetool \"spaced name\" &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"\" | git mergetool subdir/file3 &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest \"$(printf x | cat file1 -)\" = \"$(printf \"master updated\\r\\nx\")\" &&\n \ttest \"$(printf x | cat file2 -)\" = \"$(printf \"master new\\r\\nx\")\" &&\n \ttest \"$(printf x | cat subdir/file3 -)\" = \"$(printf \"master new sub\\r\\nx\")\" &&\n@@ -218,7 +218,7 @@ test_expect_success 'mergetool in subdir' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"\" | git mergetool file3 ) &&\n+\t\tyes \"\" | git mergetool file3 &&\n \t\ttest \"$(cat file3)\" = \"master new sub\"\n \t)\n '\n@@ -230,13 +230,13 @@ test_expect_success 'mergetool on file in parent dir' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"\" | git mergetool file3 ) &&\n-\t\t( yes \"\" | git mergetool ../file1 ) &&\n-\t\t( yes \"\" | git mergetool ../file2 ../spaced\\ name ) &&\n-\t\t( yes \"\" | git mergetool ../both ) &&\n-\t\t( yes \"d\" | git mergetool ../file11 ) &&\n-\t\t( yes \"d\" | git mergetool ../file12 ) &&\n-\t\t( yes \"l\" | git mergetool ../submod ) &&\n+\t\tyes \"\" | git mergetool file3 &&\n+\t\tyes \"\" | git mergetool ../file1 &&\n+\t\tyes \"\" | git mergetool ../file2 ../spaced\\ name &&\n+\t\tyes \"\" | git mergetool ../both &&\n+\t\tyes \"d\" | git mergetool ../file11 &&\n+\t\tyes \"d\" | git mergetool ../file12 &&\n+\t\tyes \"l\" | git mergetool ../submod &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat ../submod/bar)\" = \"branch1 submodule\" &&\n@@ -250,9 +250,9 @@ test_expect_success 'mergetool skips autoresolved' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"d\" | git mergetool file11 ) &&\n-\t( yes \"d\" | git mergetool file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"d\" | git mergetool file11 &&\n+\tyes \"d\" | git mergetool file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\"\n '\n@@ -264,8 +264,8 @@ test_expect_success 'mergetool merges all from subdir (rerere disabled)' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"r\" | git mergetool ../submod ) &&\n-\t\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\t\tyes \"r\" | git mergetool ../submod &&\n+\t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat file3)\" = \"master new sub\" &&\n@@ -283,8 +283,8 @@ test_expect_success 'mergetool merges all from subdir (rerere enabled)' '\n \t(\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n-\t\t( yes \"r\" | git mergetool ../submod ) &&\n-\t\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\t\tyes \"r\" | git mergetool ../submod &&\n+\t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n \t\ttest \"$(cat ../file2)\" = \"master new\" &&\n \t\ttest \"$(cat file3)\" = \"master new sub\" &&\n@@ -301,8 +301,8 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '\n \tgit checkout -b test$test_count branch1 &&\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n-\t( yes \"l\" | git mergetool --no-prompt submod ) &&\n-\t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n+\tyes \"l\" | git mergetool --no-prompt submod &&\n+\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n \tgit submodule update -N &&\n \toutput=\"$(yes \"n\" | git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\"\n@@ -343,7 +343,7 @@ test_expect_success 'mergetool takes partial path' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \n-\t( yes \"\" | git mergetool subdir ) &&\n+\tyes \"\" | git mergetool subdir &&\n \n \ttest \"$(cat subdir/file3)\" = \"master new sub\"\n '\n@@ -410,10 +410,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit checkout -b test$test_count.a test$test_count &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n \ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n \tgit submodule update -N &&\n@@ -427,10 +427,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest ! -e submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n@@ -441,10 +441,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest ! -e submod &&\n \ttest -d submod.orig &&\n \tgit submodule update -N &&\n@@ -457,10 +457,10 @@ test_expect_success 'deleted vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n@@ -481,10 +481,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit checkout -b test$test_count.a branch1 &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n \ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n \tgit submodule update -N &&\n@@ -497,10 +497,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit checkout -b test$test_count.b test$test_count &&\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod)\" = \"not a submodule\" &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n@@ -513,10 +513,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod)\" = \"not a submodule\" &&\n@@ -529,10 +529,10 @@ test_expect_success 'file vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 ) &&\n-\t( yes \"\" | git mergetool both ) &&\n-\t( yes \"d\" | git mergetool file11 file12 ) &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"\" | git mergetool file1 file2 spaced\\ name subdir/file3 &&\n+\tyes \"\" | git mergetool both &&\n+\tyes \"d\" | git mergetool file11 file12 &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n@@ -587,7 +587,7 @@ test_expect_success 'submodule in subdirectory' '\n \ttest_must_fail git merge test$test_count.a &&\n \t(\n \t\tcd subdir &&\n-\t\t( yes \"l\" | git mergetool subdir_module )\n+\t\tyes \"l\" | git mergetool subdir_module\n \t) &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n \tgit submodule update -N &&\n@@ -596,7 +596,7 @@ test_expect_success 'submodule in subdirectory' '\n \tgit submodule update -N &&\n \n \ttest_must_fail git merge test$test_count.a &&\n-\t( yes \"r\" | git mergetool subdir/subdir_module ) &&\n+\tyes \"r\" | git mergetool subdir/subdir_module &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n \tgit submodule update -N &&\n \ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.a\" &&\n@@ -615,7 +615,7 @@ test_expect_success 'directory vs modified submodule' '\n \n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"l\" | git mergetool submod &&\n \ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n \trm -rf submod.orig &&\n \n@@ -623,7 +623,7 @@ test_expect_success 'directory vs modified submodule' '\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n \ttest ! -e submod.orig &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n \ttest \"$(cat submod.orig/file16)\" = \"not a submodule\" &&\n \trm -r submod.orig &&\n@@ -638,7 +638,7 @@ test_expect_success 'directory vs modified submodule' '\n \tgit submodule update -N &&\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n-\t( yes \"l\" | git mergetool submod ) &&\n+\tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n \ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n \n@@ -647,7 +647,7 @@ test_expect_success 'directory vs modified submodule' '\n \ttest_must_fail git merge test$test_count &&\n \ttest -n \"$(git ls-files -u)\" &&\n \ttest ! -e submod.orig &&\n-\t( yes \"r\" | git mergetool submod ) &&\n+\tyes \"r\" | git mergetool submod &&\n \ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n \n \tgit reset --hard master &&\n-- \n2.21.0.285.gc38d92e052\n\n"},{"id":"377088","messageId":"82bafc90de5892d95b17c95f8193660a2fcd3b9c.1560356675.git.j6t@kdbg.org","threadId":"51278","inReplyTo":"cover.1560356675.git.j6t@kdbg.org","subject":"[PATCH v2 2/4] t7610-mergetool: use test_cmp instead of test $(cat file) = $txt","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2019-06-12T16:33:46Z","receivedAt":"2019-06-12T16:34:20Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Fix that anti-pattern by a sequence of echo and test_cmp.\n\nThe patch was generated with this command:\n\n   sed -i -e '/test.*(cat/s/^\\(\\t*\\)test \"..cat \\(.*\\))\" = \\(\".*\"\\)\\(.*\\)/\\1echo \\3 >expect \\&\\&\\n\\1test_cmp expect \\2\\4/' t7610-mergetool.sh\n\nThis helps on Windows, where test_cmp avoids spawning a process when\nthere is no difference.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n t/t7610-mergetool.sh | 135 ++++++++++++++++++++++++++++---------------\n 1 file changed, 90 insertions(+), 45 deletions(-)\n\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex b67440882b..ad288ddc69 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -138,10 +138,14 @@ test_expect_success 'custom mergetool' '\n \tyes \"d\" | git mergetool file11 &&\n \tyes \"d\" | git mergetool file12 &&\n \tyes \"l\" | git mergetool submod &&\n-\ttest \"$(cat file1)\" = \"master updated\" &&\n-\ttest \"$(cat file2)\" = \"master new\" &&\n-\ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n-\ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+\techo \"master updated\" >expect &&\n+\ttest_cmp expect file1 &&\n+\techo \"master new\" >expect &&\n+\ttest_cmp expect file2 &&\n+\techo \"master new sub\" >expect &&\n+\ttest_cmp expect subdir/file3 &&\n+\techo \"branch1 submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -160,10 +164,14 @@ test_expect_success 'gui mergetool' '\n \tyes \"d\" | git mergetool --gui file11 &&\n \tyes \"d\" | git mergetool --gui file12 &&\n \tyes \"l\" | git mergetool --gui submod &&\n-\ttest \"$(cat file1)\" = \"gui master updated\" &&\n-\ttest \"$(cat file2)\" = \"gui master new\" &&\n-\ttest \"$(cat subdir/file3)\" = \"gui master new sub\" &&\n-\ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+\techo \"gui master updated\" >expect &&\n+\ttest_cmp expect file1 &&\n+\techo \"gui master new\" >expect &&\n+\ttest_cmp expect file2 &&\n+\techo \"gui master new sub\" >expect &&\n+\ttest_cmp expect subdir/file3 &&\n+\techo \"branch1 submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -179,10 +187,14 @@ test_expect_success 'gui mergetool without merge.guitool set falls back to merge\n \tyes \"d\" | git mergetool --gui file11 &&\n \tyes \"d\" | git mergetool --gui file12 &&\n \tyes \"l\" | git mergetool --gui submod &&\n-\ttest \"$(cat file1)\" = \"master updated\" &&\n-\ttest \"$(cat file2)\" = \"master new\" &&\n-\ttest \"$(cat subdir/file3)\" = \"master new sub\" &&\n-\ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+\techo \"master updated\" >expect &&\n+\ttest_cmp expect file1 &&\n+\techo \"master new\" >expect &&\n+\ttest_cmp expect file2 &&\n+\techo \"master new sub\" >expect &&\n+\ttest_cmp expect subdir/file3 &&\n+\techo \"branch1 submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -207,7 +219,8 @@ test_expect_success 'mergetool crlf' '\n \ttest \"$(printf x | cat file2 -)\" = \"$(printf \"master new\\r\\nx\")\" &&\n \ttest \"$(printf x | cat subdir/file3 -)\" = \"$(printf \"master new sub\\r\\nx\")\" &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit commit -m \"branch1 resolved with mergetool - autocrlf\"\n '\n \n@@ -219,7 +232,8 @@ test_expect_success 'mergetool in subdir' '\n \t\tcd subdir &&\n \t\ttest_must_fail git merge master &&\n \t\tyes \"\" | git mergetool file3 &&\n-\t\ttest \"$(cat file3)\" = \"master new sub\"\n+\t\techo \"master new sub\" >expect &&\n+\t\ttest_cmp expect file3\n \t)\n '\n \n@@ -237,9 +251,12 @@ test_expect_success 'mergetool on file in parent dir' '\n \t\tyes \"d\" | git mergetool ../file11 &&\n \t\tyes \"d\" | git mergetool ../file12 &&\n \t\tyes \"l\" | git mergetool ../submod &&\n-\t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n-\t\ttest \"$(cat ../file2)\" = \"master new\" &&\n-\t\ttest \"$(cat ../submod/bar)\" = \"branch1 submodule\" &&\n+\t\techo \"master updated\" >expect &&\n+\t\ttest_cmp expect ../file1 &&\n+\t\techo \"master new\" >expect &&\n+\t\ttest_cmp expect ../file2 &&\n+\t\techo \"branch1 submodule\" >expect &&\n+\t\ttest_cmp expect ../submod/bar &&\n \t\tgit commit -m \"branch1 resolved with mergetool - subdir\"\n \t)\n '\n@@ -266,11 +283,15 @@ test_expect_success 'mergetool merges all from subdir (rerere disabled)' '\n \t\ttest_must_fail git merge master &&\n \t\tyes \"r\" | git mergetool ../submod &&\n \t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n-\t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n-\t\ttest \"$(cat ../file2)\" = \"master new\" &&\n-\t\ttest \"$(cat file3)\" = \"master new sub\" &&\n+\t\techo \"master updated\" >expect &&\n+\t\ttest_cmp expect ../file1 &&\n+\t\techo \"master new\" >expect &&\n+\t\ttest_cmp expect ../file2 &&\n+\t\techo \"master new sub\" >expect &&\n+\t\ttest_cmp expect file3 &&\n \t\t( cd .. && git submodule update -N ) &&\n-\t\ttest \"$(cat ../submod/bar)\" = \"master submodule\" &&\n+\t\techo \"master submodule\" >expect &&\n+\t\ttest_cmp expect ../submod/bar &&\n \t\tgit commit -m \"branch2 resolved by mergetool from subdir\"\n \t)\n '\n@@ -285,11 +306,15 @@ test_expect_success 'mergetool merges all from subdir (rerere enabled)' '\n \t\ttest_must_fail git merge master &&\n \t\tyes \"r\" | git mergetool ../submod &&\n \t\tyes \"d\" \"d\" | git mergetool --no-prompt &&\n-\t\ttest \"$(cat ../file1)\" = \"master updated\" &&\n-\t\ttest \"$(cat ../file2)\" = \"master new\" &&\n-\t\ttest \"$(cat file3)\" = \"master new sub\" &&\n+\t\techo \"master updated\" >expect &&\n+\t\ttest_cmp expect ../file1 &&\n+\t\techo \"master new\" >expect &&\n+\t\ttest_cmp expect ../file2 &&\n+\t\techo \"master new sub\" >expect &&\n+\t\ttest_cmp expect file3 &&\n \t\t( cd .. && git submodule update -N ) &&\n-\t\ttest \"$(cat ../submod/bar)\" = \"master submodule\" &&\n+\t\techo \"master submodule\" >expect &&\n+\t\ttest_cmp expect ../submod/bar &&\n \t\tgit commit -m \"branch2 resolved by mergetool from subdir\"\n \t)\n '\n@@ -345,7 +370,8 @@ test_expect_success 'mergetool takes partial path' '\n \n \tyes \"\" | git mergetool subdir &&\n \n-\ttest \"$(cat subdir/file3)\" = \"master new sub\"\n+\techo \"master new sub\" >expect &&\n+\ttest_cmp expect subdir/file3\n '\n \n test_expect_success 'mergetool delete/delete conflict' '\n@@ -415,9 +441,11 @@ test_expect_success 'deleted vs modified submodule' '\n \tyes \"d\" | git mergetool file11 file12 &&\n \tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n-\ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+\techo \"branch1 submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping module\" &&\n@@ -461,9 +489,11 @@ test_expect_success 'deleted vs modified submodule' '\n \tyes \"\" | git mergetool both &&\n \tyes \"d\" | git mergetool file11 file12 &&\n \tyes \"l\" | git mergetool submod &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping module\"\n@@ -486,9 +516,11 @@ test_expect_success 'file vs modified submodule' '\n \tyes \"d\" | git mergetool file11 file12 &&\n \tyes \"r\" | git mergetool submod &&\n \trmdir submod && mv submod-movedaside submod &&\n-\ttest \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+\techo \"branch1 submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping module\" &&\n@@ -502,7 +534,8 @@ test_expect_success 'file vs modified submodule' '\n \tyes \"d\" | git mergetool file11 file12 &&\n \tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod)\" = \"not a submodule\" &&\n+\techo \"not a submodule\" >expect &&\n+\ttest_cmp expect submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping file\" &&\n@@ -519,7 +552,8 @@ test_expect_success 'file vs modified submodule' '\n \tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod)\" = \"not a submodule\" &&\n+\techo \"not a submodule\" >expect &&\n+\ttest_cmp expect submod &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping file\" &&\n@@ -533,9 +567,11 @@ test_expect_success 'file vs modified submodule' '\n \tyes \"\" | git mergetool both &&\n \tyes \"d\" | git mergetool file11 file12 &&\n \tyes \"l\" | git mergetool submod &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \toutput=\"$(git mergetool --no-prompt)\" &&\n \ttest \"$output\" = \"No files need merging\" &&\n \tgit commit -m \"Merge resolved by keeping module\"\n@@ -589,17 +625,21 @@ test_expect_success 'submodule in subdirectory' '\n \t\tcd subdir &&\n \t\tyes \"l\" | git mergetool subdir_module\n \t) &&\n-\ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n+\techo \"test$test_count.b\" >expect &&\n+\ttest_cmp expect subdir/subdir_module/file15 &&\n \tgit submodule update -N &&\n-\ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n+\techo \"test$test_count.b\" >expect &&\n+\ttest_cmp expect subdir/subdir_module/file15 &&\n \tgit reset --hard &&\n \tgit submodule update -N &&\n \n \ttest_must_fail git merge test$test_count.a &&\n \tyes \"r\" | git mergetool subdir/subdir_module &&\n-\ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.b\" &&\n+\techo \"test$test_count.b\" >expect &&\n+\ttest_cmp expect subdir/subdir_module/file15 &&\n \tgit submodule update -N &&\n-\ttest \"$(cat subdir/subdir_module/file15)\" = \"test$test_count.a\" &&\n+\techo \"test$test_count.a\" >expect &&\n+\ttest_cmp expect subdir/subdir_module/file15 &&\n \tgit commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -616,7 +656,8 @@ test_expect_success 'directory vs modified submodule' '\n \ttest_must_fail git merge master &&\n \ttest -n \"$(git ls-files -u)\" &&\n \tyes \"l\" | git mergetool submod &&\n-\ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n+\techo \"not a submodule\" >expect &&\n+\ttest_cmp expect submod/file16 &&\n \trm -rf submod.orig &&\n \n \tgit reset --hard &&\n@@ -625,12 +666,14 @@ test_expect_success 'directory vs modified submodule' '\n \ttest ! -e submod.orig &&\n \tyes \"r\" | git mergetool submod &&\n \ttest -d submod.orig &&\n-\ttest \"$(cat submod.orig/file16)\" = \"not a submodule\" &&\n+\techo \"not a submodule\" >expect &&\n+\ttest_cmp expect submod.orig/file16 &&\n \trm -r submod.orig &&\n \tmv submod-movedaside/.git submod &&\n \t( cd submod && git clean -f && git reset --hard ) &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \tgit reset --hard &&\n \trm -rf submod-movedaside &&\n \n@@ -640,7 +683,8 @@ test_expect_success 'directory vs modified submodule' '\n \ttest -n \"$(git ls-files -u)\" &&\n \tyes \"l\" | git mergetool submod &&\n \tgit submodule update -N &&\n-\ttest \"$(cat submod/bar)\" = \"master submodule\" &&\n+\techo \"master submodule\" >expect &&\n+\ttest_cmp expect submod/bar &&\n \n \tgit reset --hard &&\n \tgit submodule update -N &&\n@@ -648,7 +692,8 @@ test_expect_success 'directory vs modified submodule' '\n \ttest -n \"$(git ls-files -u)\" &&\n \ttest ! -e submod.orig &&\n \tyes \"r\" | git mergetool submod &&\n-\ttest \"$(cat submod/file16)\" = \"not a submodule\" &&\n+\techo \"not a submodule\" >expect &&\n+\ttest_cmp expect submod/file16 &&\n \n \tgit reset --hard master &&\n \t( cd submod && git clean -f && git reset --hard ) &&\n-- \n2.21.0.285.gc38d92e052\n\n"}]}