threads / patch / 52895

patch, 5 partsTestcase cleanups (merge related)

Subject: [PATCH 0/5] Testcase cleanups (merge related)

## tl;dr

16 messages between Feb 27, 2020 and Mar 13, 2020. Diffs are folded; open one to read it.

replies: 15people: 2as markdown or json

Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · lore
This series has a few testcase cleanups, and a new testcase. In more detail:
 * The first three patches are just various forms of testcase modernization
 * The fourth patch updates some tests to check intended behavior instead of
   a proxy thereof
 * The fifth patch adds a new simple testcase that there is no other
   merge/rebase/cherry-pick test for in our testsuite, just for added
   defensiveness.

As a side note: t60[01]* is used by rev-list, and then t60[234].sh is kinda taken by merge-recursive except that t6030 and t6041 are for bisect and t6040 for remote tracking stuff. Because t60[234].sh got filled up, rev-list spilled over to t61[01]* as well. It's all kind of a mess. I'm curious if moving the merge stuff over to t64* would make sense, since that'd keep the merge stuff together, and would also free up space for rev-list, bisect, remote-tracking and whatnot. Would that just be too much churn?

Elijah Newren (5):
  t602[1236], t6034: modernize test formatting
  t6020, t6022, t6035: update merge tests to use test helper functions
  t3035: prefer test_must_fail to bash negation for git commands
  t6022, t6046: test expected behavior instead of testing a proxy for it
  t6020: new test with interleaved lexicographic ordering of directories
 t/t3035-merge-sparse.sh                |   4 +-
 t/t6020-merge-df.sh                    |  55 ++-
 t/t6021-merge-criss-cross.sh           | 135 +++---
 t/t6022-merge-rename.sh                | 382 +++++++++--------
 t/t6023-merge-file.sh                  | 568 +++++++++++++------------
 t/t6026-merge-attr.sh                  |  46 +-
 t/t6034-merge-rename-nocruft.sh        | 122 +++---
 t/t6035-merge-dir-to-symlink.sh        |  28 +-
 t/t6046-merge-skip-unneeded-updates.sh |  89 ++--
 9 files changed, 752 insertions(+), 677 deletions(-)
base-commit: 2d2118b814c11f509e1aa76cb07110f7231668dc
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-716%2Fnewren%2Ftestcase-cleanups-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-716/newren/testcase-cleanups-v1
Pull-Request: https://github.com/git/git/pull/716
-- 
gitgitgadget
Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · re: Elijah Newren via GitGitGadget · lore

[PATCH 3/5] t3035: prefer test_must_fail to bash negation for git commands

From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
 t/t3035-merge-sparse.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Show changes to t/t3035-merge-sparse.sh +2 −2
diff --git a/t/t3035-merge-sparse.sh b/t/t3035-merge-sparse.sh
index c4b4a94324a..74562e12356 100755
--- a/t/t3035-merge-sparse.sh
+++ b/t/t3035-merge-sparse.sh
@@ -28,7 +28,7 @@ test_expect_success 'setup' '
 	git config core.sparseCheckout true &&
 	echo "/checked-out" >.git/info/sparse-checkout &&
 	git reset --hard &&
-	! git merge theirs
+	test_must_fail git merge theirs
 '
 
 test_expect_success 'reset --hard works after the conflict' '
@@ -42,7 +42,7 @@ test_expect_success 'is reset properly' '
 '
 
 test_expect_success 'setup: conflict back' '
-	! git merge theirs
+	test_must_fail git merge theirs
 '
 
 test_expect_success 'Merge abort works after the conflict' '
-- 
gitgitgadget
Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · re: Elijah Newren via GitGitGadget · lore

[PATCH 2/5] t6020, t6022, t6035: update merge tests to use test helper functions

From: Elijah Newren <newren@gmail.com>

Make use of test_path_is_file, test_write_lines, and similar helpers in these old test files.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 t/t6020-merge-df.sh             | 12 ++++----
 t/t6022-merge-rename.sh         | 54 ++++++++++++++++-----------------
 t/t6035-merge-dir-to-symlink.sh | 28 ++++++++---------
 3 files changed, 47 insertions(+), 47 deletions(-)
Show changes to 3 files +47 −47

t/t6020-merge-df.sh, t/t6022-merge-rename.sh, t/t6035-merge-dir-to-symlink.sh

diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index 46b506b3b7b..cf662068da1 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -83,9 +83,9 @@ test_expect_success 'modify/delete + directory/file conflict' '
 	test 4 -eq $(git ls-files -u | wc -l) &&
 	test 1 -eq $(git ls-files -o | wc -l) &&
 
-	test -f letters/file &&
-	test -f letters.txt &&
-	test -f letters~modify
+	test_path_is_file letters/file &&
+	test_path_is_file letters.txt &&
+	test_path_is_file letters~modify
 '
 
 test_expect_success 'modify/delete + directory/file conflict; other way' '
@@ -99,9 +99,9 @@ test_expect_success 'modify/delete + directory/file conflict; other way' '
 	test 4 -eq $(git ls-files -u | wc -l) &&
 	test 1 -eq $(git ls-files -o | wc -l) &&
 
-	test -f letters/file &&
-	test -f letters.txt &&
-	test -f letters~HEAD
+	test_path_is_file letters/file &&
+	test_path_is_file letters.txt &&
+	test_path_is_file letters~HEAD
 '
 
 test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 1e34e1f48bb..6f196aaf276 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -295,8 +295,8 @@ test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way'
 	git merge --strategy=recursive dir-not-in-way &&
 
 	git diff --quiet &&
-	test -f dir &&
-	printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected &&
+	test_path_is_file dir &&
+	test_write_lines 1 2 3 4 5555 6 7 8 9 10 11 >expected &&
 	test_cmp expected dir
 '
 
@@ -316,8 +316,8 @@ test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
 
-	test -f dir/file-in-the-way &&
-	test -f dir~HEAD &&
+	test_path_is_file dir/file-in-the-way &&
+	test_path_is_file dir~HEAD &&
 	test_cmp expected dir~HEAD
 '
 
@@ -338,8 +338,8 @@ test_expect_success 'Same as previous, but merged other way' '
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
 
-	test -f dir/file-in-the-way &&
-	test -f dir~renamed-file-has-no-conflicts &&
+	test_path_is_file dir/file-in-the-way &&
+	test_path_is_file dir~renamed-file-has-no-conflicts &&
 	test_cmp expected dir~renamed-file-has-no-conflicts
 '
 
@@ -355,7 +355,7 @@ test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
 
-	test -f dir &&
+	test_path_is_file dir &&
 	cat >expected <<-\EOF &&
 	1
 	2
@@ -391,8 +391,8 @@ test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in t
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
 
-	test -f dir/file-in-the-way &&
-	test -f dir~HEAD &&
+	test_path_is_file dir/file-in-the-way &&
+	test_path_is_file dir~HEAD &&
 	test_cmp expected dir~HEAD
 '
 
@@ -409,8 +409,8 @@ test_expect_success 'Same as previous, but merged other way' '
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
 
-	test -f dir/file-in-the-way &&
-	test -f dir~renamed-file-has-conflicts &&
+	test_path_is_file dir/file-in-the-way &&
+	test_path_is_file dir~renamed-file-has-conflicts &&
 	cat >expected <<-\EOF &&
 	1
 	2
@@ -463,9 +463,9 @@ test_expect_success 'both rename source and destination involved in D/F conflict
 
 	test_must_fail git diff --quiet &&
 
-	test -f destdir/foo &&
-	test -f one &&
-	test -f destdir~HEAD &&
+	test_path_is_file destdir/foo &&
+	test_path_is_file one &&
+	test_path_is_file destdir~HEAD &&
 	test "stuff" = "$(cat destdir~HEAD)"
 '
 
@@ -506,9 +506,9 @@ test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked
 
 	test 4 -eq $(find . | grep -v .git | wc -l) &&
 
-	test -d one &&
-	test -f one~rename-two &&
-	test -f two &&
+	test_path_is_dir one &&
+	test_path_is_file one~rename-two &&
+	test_path_is_file two &&
 	test "other" = $(cat one~rename-two) &&
 	test "stuff" = $(cat two)
 '
@@ -526,8 +526,8 @@ test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean sta
 
 	test 3 -eq $(find . | grep -v .git | wc -l) &&
 
-	test -f one &&
-	test -f two &&
+	test_path_is_file one &&
+	test_path_is_file two &&
 	test "other" = $(cat one) &&
 	test "stuff" = $(cat two)
 '
@@ -567,11 +567,11 @@ test_expect_success 'check handling of differently renamed file with D/F conflic
 	test 1 -eq "$(git ls-files -u original | wc -l)" &&
 	test 2 -eq "$(git ls-files -o | wc -l)" &&
 
-	test -f one/file &&
-	test -f two/file &&
-	test -f one~HEAD &&
-	test -f two~second-rename &&
-	! test -f original
+	test_path_is_file one/file &&
+	test_path_is_file two/file &&
+	test_path_is_file one~HEAD &&
+	test_path_is_file two~second-rename &&
+	test_path_is_missing original
 '
 
 test_expect_success 'setup rename one file to two; directories moving out of the way' '
@@ -606,9 +606,9 @@ test_expect_success 'check handling of differently renamed file with D/F conflic
 	test 1 -eq "$(git ls-files -u original | wc -l)" &&
 	test 0 -eq "$(git ls-files -o | wc -l)" &&
 
-	test -f one &&
-	test -f two &&
-	! test -f original
+	test_path_is_file one &&
+	test_path_is_file two &&
+	test_path_is_missing original
 '
 
 test_expect_success 'setup avoid unnecessary update, normal rename' '
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
index 9324ea44162..2eddcc7664e 100755
--- a/t/t6035-merge-dir-to-symlink.sh
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -31,19 +31,19 @@ test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
 	git rm --cached a/b &&
 	git commit -m "untracked symlink remains" &&
 	git checkout -f start^0 &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success 'checkout should not have deleted a/b-2/c/d' '
 	git checkout HEAD^0 &&
 	git reset --hard master &&
 	 git checkout start^0 &&
-	 test -f a/b-2/c/d
+	 test_path_is_file a/b-2/c/d
 '
 
 test_expect_success 'setup for merge test' '
 	git reset --hard &&
-	test -f a/b-2/c/d &&
+	test_path_is_file a/b-2/c/d &&
 	echo x > a/x &&
 	git add a/x &&
 	git commit -m x &&
@@ -54,7 +54,7 @@ test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolv
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s resolve master &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success SYMLINKS 'a/b was resolved as symlink' '
@@ -65,7 +65,7 @@ test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recurs
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s recursive master &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success SYMLINKS 'a/b was resolved as symlink' '
@@ -76,7 +76,7 @@ test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolv
 	git reset --hard &&
 	git checkout master^0 &&
 	git merge -s resolve baseline^0 &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success SYMLINKS 'a/b was resolved as symlink' '
@@ -87,7 +87,7 @@ test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recurs
 	git reset --hard &&
 	git checkout master^0 &&
 	git merge -s recursive baseline^0 &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success SYMLINKS 'a/b was resolved as symlink' '
@@ -99,8 +99,8 @@ test_expect_failure 'do not lose untracked in merge (resolve)' '
 	git checkout baseline^0 &&
 	>a/b/c/e &&
 	test_must_fail git merge -s resolve master &&
-	test -f a/b/c/e &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b/c/e &&
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success 'do not lose untracked in merge (recursive)' '
@@ -108,8 +108,8 @@ test_expect_success 'do not lose untracked in merge (recursive)' '
 	git checkout baseline^0 &&
 	>a/b/c/e &&
 	test_must_fail git merge -s recursive master &&
-	test -f a/b/c/e &&
-	test -f a/b-2/c/d
+	test_path_is_file a/b/c/e &&
+	test_path_is_file a/b-2/c/d
 '
 
 test_expect_success 'do not lose modifications in merge (resolve)' '
@@ -140,7 +140,7 @@ test_expect_success 'merge should not have D/F conflicts (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s resolve test2 &&
-	test -f a/b/c/d
+	test_path_is_file a/b/c/d
 '
 
 test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
@@ -151,7 +151,7 @@ test_expect_success 'merge should not have D/F conflicts (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s recursive test2 &&
-	test -f a/b/c/d
+	test_path_is_file a/b/c/d
 '
 
 test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
@@ -162,7 +162,7 @@ test_expect_success 'merge should not have F/D conflicts (recursive)' '
 	git reset --hard &&
 	git checkout -b foo test2 &&
 	git merge -s recursive baseline^0 &&
-	test -f a/b/c/d
+	test_path_is_file a/b/c/d
 '
 
 test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
-- 
gitgitgadget
Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · re: Elijah Newren via GitGitGadget · lore

[PATCH 5/5] t6020: new test with interleaved lexicographic ordering of directories

From: Elijah Newren <newren@gmail.com>
If a repository has two files:
    foo/bar/baz
    foo/bar-2/baz
then a simple lexicographic ordering of files and directories shows
    ...
    foo/bar
    foo/bar-2
    foo/bar/baz
    ...
and the appearance of foo/bar-2 between foo/bar and foo/bar/baz can trip
up some codepaths.  Add a test to catch such cases.

t6020 might be a slight misfit since this testcase does not test any kind of file/directory conflict. However, it is similar in spirit to some tests (4-6) already in t6020 that check cases where a *file* sorted between a directory and the files underneath that directory. This testcase differs in that now there is a *directory* that sorts in the middle.

Although merge-recursive currently has no problems with this simple testcase, I discovered that it's very possible to accidentally mess it up. Further, we have no other merge or cherry-pick or rebase testcases in the entire testsuite that cover such a case, so I felt like it would be a worthwhile addition to the testsuite.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 t/t6020-merge-df.sh | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
Show changes to t/t6020-merge-df.sh +43 −0
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index cf662068da1..400a4cd1392 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -104,4 +104,47 @@ test_expect_success 'modify/delete + directory/file conflict; other way' '
 	test_path_is_file letters~HEAD
 '
 
+test_expect_success 'Simple merge in repo with interesting pathnames' '
+	# Simple lexicographic ordering of files and directories would be:
+	#     foo
+	#     foo/bar
+	#     foo/bar-2
+	#     foo/bar/baz
+	#     foo/bar-2/baz
+	# The fact that foo/bar-2 appears between foo/bar and foo/bar/baz
+	# can trip up some codepaths, and is the point of this test.
+	test_create_repo name-ordering &&
+	(
+		cd name-ordering &&
+
+		mkdir -p foo/bar &&
+		mkdir -p foo/bar-2 &&
+		>foo/bar/baz &&
+		>foo/bar-2/baz &&
+		git add . &&
+		git commit -m initial &&
+
+		git branch main &&
+		git branch other &&
+
+		git checkout other &&
+		echo other >foo/bar-2/baz &&
+		git add -u &&
+		git commit -m other &&
+
+		git checkout main &&
+		echo main >foo/bar/baz &&
+		git add -u &&
+		git commit -m main &&
+
+		git merge other &&
+		git ls-files -s >out &&
+		test_line_count = 2 out &&
+		git rev-parse :0:foo/bar/baz :0:foo/bar-2/baz >actual &&
+		git rev-parse HEAD~1:foo/bar/baz other:foo/bar-2/baz >expect &&
+		test_cmp expect actual
+	)
+
+'
+
 test_done
-- 
gitgitgadget
Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · re: Elijah Newren via GitGitGadget · lore

[PATCH 1/5] t602[1236], t6034: modernize test formatting

From: Elijah Newren <newren@gmail.com>
Indent code, and include it inside test_expect* blocks.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
 t/t6021-merge-criss-cross.sh    | 135 +++-----
 t/t6022-merge-rename.sh         | 315 +++++++++---------
 t/t6023-merge-file.sh           | 568 +++++++++++++++++---------------
 t/t6026-merge-attr.sh           |  46 +--
 t/t6034-merge-rename-nocruft.sh | 122 ++++---
 5 files changed, 591 insertions(+), 595 deletions(-)
Show changes to 5 files +591 −595

t/t6021-merge-criss-cross.sh, t/t6022-merge-rename.sh, t/t6023-merge-file.sh, t/t6026-merge-attr.sh, t/t6034-merge-rename-nocruft.sh

diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index d254e020b6d..9d5e992878f 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -10,87 +10,58 @@
 test_description='Test criss-cross merge'
 . ./test-lib.sh
 
-test_expect_success 'prepare repository' \
-'echo "1
-2
-3
-4
-5
-6
-7
-8
-9" > file &&
-git add file &&
-git commit -m "Initial commit" file &&
-git branch A &&
-git branch B &&
-git checkout A &&
-echo "1
-2
-3
-4
-5
-6
-7
-8 changed in B8, branch A
-9" > file &&
-git commit -m "B8" file &&
-git checkout B &&
-echo "1
-2
-3 changed in C3, branch B
-4
-5
-6
-7
-8
-9
-" > file &&
-git commit -m "C3" file &&
-git branch C3 &&
-git merge -m "pre E3 merge" A &&
-echo "1
-2
-3 changed in E3, branch B. New file size
-4
-5
-6
-7
-8 changed in B8, branch A
-9
-" > file &&
-git commit -m "E3" file &&
-git checkout A &&
-git merge -m "pre D8 merge" C3 &&
-echo "1
-2
-3 changed in C3, branch B
-4
-5
-6
-7
-8 changed in D8, branch A. New file size 2
-9" > file &&
-git commit -m D8 file'
-
-test_expect_success 'Criss-cross merge' 'git merge -m "final merge" B'
-
-cat > file-expect <<EOF
-1
-2
-3 changed in E3, branch B. New file size
-4
-5
-6
-7
-8 changed in D8, branch A. New file size 2
-9
-EOF
-
-test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
-
-test_expect_success 'Criss-cross merge fails (-s resolve)' \
-'git reset --hard A^ &&
-test_must_fail git merge -s resolve -m "final merge" B'
+test_expect_success 'prepare repository' '
+	test_write_lines 1 2 3 4 5 6 7 8 9 >file &&
+	git add file &&
+	git commit -m "Initial commit" file &&
+
+	git branch A &&
+	git branch B &&
+	git checkout A &&
+
+	test_write_lines 1 2 3 4 5 6 7 "8 changed in B8, branch A" 9 >file &&
+	git commit -m "B8" file &&
+	git checkout B &&
+
+	test_write_lines 1 2 "3 changed in C3, branch B" 4 5 6 7 8 9 >file &&
+	git commit -m "C3" file &&
+	git branch C3 &&
+
+	git merge -m "pre E3 merge" A &&
+
+	test_write_lines 1 2 "3 changed in E3, branch B. New file size" 4 5 6 7 "8 changed in B8, branch A" 9 >file &&
+	git commit -m "E3" file &&
+
+	git checkout A &&
+	git merge -m "pre D8 merge" C3 &&
+	test_write_lines 1 2 "3 changed in C3, branch B" 4 5 6 7 "8 changed in D8, branch A. New file size 2" 9 >file &&
+
+	git commit -m D8 file
+'
+
+test_expect_success 'Criss-cross merge' '
+	git merge -m "final merge" B
+'
+
+test_expect_success 'Criss-cross merge result' '
+	cat <<-\EOF >file-expect &&
+	1
+	2
+	3 changed in E3, branch B. New file size
+	4
+	5
+	6
+	7
+	8 changed in D8, branch A. New file size 2
+	9
+	EOF
+
+	test_cmp file-expect file
+'
+
+test_expect_success 'Criss-cross merge fails (-s resolve)' '
+	git reset --hard A^ &&
+	test_must_fail git merge -s resolve -m "final merge" B
+'
 
 test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 53cc9b2ffbd..1e34e1f48bb 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -8,94 +8,94 @@ modify () {
 	mv "$2.x" "$2"
 }
 
-test_expect_success setup \
-'
-cat >A <<\EOF &&
-a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-c cccccccccccccccccccccccccccccccccccccccccccccccc
-d dddddddddddddddddddddddddddddddddddddddddddddddd
-e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
-f ffffffffffffffffffffffffffffffffffffffffffffffff
-g gggggggggggggggggggggggggggggggggggggggggggggggg
-h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
-i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
-j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
-k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
-l llllllllllllllllllllllllllllllllllllllllllllllll
-m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
-n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
-o oooooooooooooooooooooooooooooooooooooooooooooooo
-EOF
-
-cat >M <<\EOF &&
-A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
-E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
-F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
-M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-EOF
-
-git add A M &&
-git commit -m "initial has A and M" &&
-git branch white &&
-git branch red &&
-git branch blue &&
-git branch yellow &&
-git branch change &&
-git branch change+rename &&
-
-sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
-mv A+ A &&
-git commit -a -m "master updates A" &&
-
-git checkout yellow &&
-rm -f M &&
-git commit -a -m "yellow removes M" &&
-
-git checkout white &&
-sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
-sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
-rm -f A M &&
-git update-index --add --remove A B M N &&
-git commit -m "white renames A->B, M->N" &&
-
-git checkout red &&
-sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
-sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
-rm -f A M &&
-git update-index --add --remove A B M N &&
-git commit -m "red renames A->B, M->N" &&
-
-git checkout blue &&
-sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
-sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
-rm -f A M &&
-git update-index --add --remove A C M N &&
-git commit -m "blue renames A->C, M->N" &&
-
-git checkout change &&
-sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
-mv A+ A &&
-git commit -q -a -m "changed" &&
-
-git checkout change+rename &&
-sed -e "/^g /s/.*/g : changed line/" <A >B &&
-rm A &&
-git update-index --add B &&
-git commit -q -a -m "changed and renamed" &&
-
-git checkout master'
+test_expect_success 'setup' '
+	cat >A <<-\EOF &&
+	a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+	c cccccccccccccccccccccccccccccccccccccccccccccccc
+	d dddddddddddddddddddddddddddddddddddddddddddddddd
+	e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+	f ffffffffffffffffffffffffffffffffffffffffffffffff
+	g gggggggggggggggggggggggggggggggggggggggggggggggg
+	h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+	i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+	j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+	k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+	l llllllllllllllllllllllllllllllllllllllllllllllll
+	m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+	n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+	o oooooooooooooooooooooooooooooooooooooooooooooooo
+	EOF
+
+	cat >M <<-\EOF &&
+	A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+	C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+	D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+	E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+	F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+	G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+	H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+	I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+	J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+	K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+	L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+	M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+	N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+	O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+	EOF
+
+	git add A M &&
+	git commit -m "initial has A and M" &&
+	git branch white &&
+	git branch red &&
+	git branch blue &&
+	git branch yellow &&
+	git branch change &&
+	git branch change+rename &&
+
+	sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
+	mv A+ A &&
+	git commit -a -m "master updates A" &&
+
+	git checkout yellow &&
+	rm -f M &&
+	git commit -a -m "yellow removes M" &&
+
+	git checkout white &&
+	sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+	sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+	rm -f A M &&
+	git update-index --add --remove A B M N &&
+	git commit -m "white renames A->B, M->N" &&
+
+	git checkout red &&
+	sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
+	sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+	rm -f A M &&
+	git update-index --add --remove A B M N &&
+	git commit -m "red renames A->B, M->N" &&
+
+	git checkout blue &&
+	sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
+	sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+	rm -f A M &&
+	git update-index --add --remove A C M N &&
+	git commit -m "blue renames A->C, M->N" &&
+
+	git checkout change &&
+	sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
+	mv A+ A &&
+	git commit -q -a -m "changed" &&
+
+	git checkout change+rename &&
+	sed -e "/^g /s/.*/g : changed line/" <A >B &&
+	rm A &&
+	git update-index --add B &&
+	git commit -q -a -m "changed and renamed" &&
+
+	git checkout master
+'
 
 test_expect_success 'pull renaming branch into unrenaming one' \
 '
@@ -288,14 +288,15 @@ test_expect_success 'setup for rename + d/f conflicts' '
 	git commit -m "Conflicting change"
 '
 
-printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
-
 test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
 	git reset --hard &&
 	git checkout -q renamed-file-has-no-conflicts^0 &&
+
 	git merge --strategy=recursive dir-not-in-way &&
+
 	git diff --quiet &&
 	test -f dir &&
+	printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected &&
 	test_cmp expected dir
 '
 
@@ -342,24 +343,6 @@ test_expect_success 'Same as previous, but merged other way' '
 	test_cmp expected dir~renamed-file-has-no-conflicts
 '
 
-cat >expected <<\EOF &&
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-<<<<<<< HEAD:dir
-12
-=======
-11
->>>>>>> dir-not-in-way:sub/file
-EOF
-
 test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
 	git reset --hard &&
 	rm -rf dir~* &&
@@ -373,6 +356,23 @@ test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in
 	test_must_fail git diff --cached --quiet &&
 
 	test -f dir &&
+	cat >expected <<-\EOF &&
+	1
+	2
+	3
+	4
+	5
+	6
+	7
+	8
+	9
+	10
+	<<<<<<< HEAD:dir
+	12
+	=======
+	11
+	>>>>>>> dir-not-in-way:sub/file
+	EOF
 	test_cmp expected dir
 '
 
@@ -396,24 +396,6 @@ test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in t
 	test_cmp expected dir~HEAD
 '
 
-cat >expected <<\EOF &&
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-<<<<<<< HEAD:sub/file
-11
-=======
-12
->>>>>>> renamed-file-has-conflicts:dir
-EOF
-
 test_expect_success 'Same as previous, but merged other way' '
 	git reset --hard &&
 	rm -rf dir~* &&
@@ -429,6 +411,23 @@ test_expect_success 'Same as previous, but merged other way' '
 
 	test -f dir/file-in-the-way &&
 	test -f dir~renamed-file-has-conflicts &&
+	cat >expected <<-\EOF &&
+	1
+	2
+	3
+	4
+	5
+	6
+	7
+	8
+	9
+	10
+	<<<<<<< HEAD:sub/file
+	11
+	=======
+	12
+	>>>>>>> renamed-file-has-conflicts:dir
+	EOF
 	test_cmp expected dir~renamed-file-has-conflicts
 '
 
@@ -810,48 +809,48 @@ test_expect_success 'setup for use of extended merge markers' '
 	git commit -mC
 '
 
-cat >expected <<\EOF &&
-1
-2
-3
-4
-5
-6
-7
-8
-<<<<<<< HEAD:renamed_file
-9
-=======
-8.5
->>>>>>> master^0:original_file
-EOF
-
 test_expect_success 'merge master into rename has correct extended markers' '
 	git checkout rename^0 &&
 	test_must_fail git merge -s recursive master^0 &&
+
+	cat >expected <<-\EOF &&
+	1
+	2
+	3
+	4
+	5
+	6
+	7
+	8
+	<<<<<<< HEAD:renamed_file
+	9
+	=======
+	8.5
+	>>>>>>> master^0:original_file
+	EOF
 	test_cmp expected renamed_file
 '
 
-cat >expected <<\EOF &&
-1
-2
-3
-4
-5
-6
-7
-8
-<<<<<<< HEAD:original_file
-8.5
-=======
-9
->>>>>>> rename^0:renamed_file
-EOF
-
 test_expect_success 'merge rename into master has correct extended markers' '
 	git reset --hard &&
 	git checkout master^0 &&
 	test_must_fail git merge -s recursive rename^0 &&
+
+	cat >expected <<-\EOF &&
+	1
+	2
+	3
+	4
+	5
+	6
+	7
+	8
+	<<<<<<< HEAD:original_file
+	8.5
+	=======
+	9
+	>>>>>>> rename^0:renamed_file
+	EOF
 	test_cmp expected renamed_file
 '
 
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 51ee887a776..2f421d967ab 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -3,56 +3,59 @@
 test_description='RCS merge replacement: merge-file'
 . ./test-lib.sh
 
-cat > orig.txt << EOF
-Dominus regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-EOF
-
-cat > new1.txt << EOF
-Dominus regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-
-cat > new2.txt << EOF
-Dominus regit me, et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-EOF
-
-cat > new3.txt << EOF
-DOMINUS regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-EOF
-
-cat > new4.txt << EOF
-Dominus regit me, et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-EOF
-printf "propter nomen suum." >> new4.txt
+test_expect_success 'setup' '
+	cat >orig.txt <<-\EOF &&
+	Dominus regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	EOF
+
+	cat >new1.txt <<-\EOF &&
+	Dominus regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	cat >new2.txt <<-\EOF &&
+	Dominus regit me, et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	EOF
+
+	cat >new3.txt <<-\EOF &&
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	EOF
+
+	cat >new4.txt <<-\EOF &&
+	Dominus regit me, et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	EOF
+
+	printf "propter nomen suum." >>new4.txt
+'
 
 test_expect_success 'merge with no changes' '
 	cp orig.txt test.txt &&
@@ -60,9 +63,10 @@ test_expect_success 'merge with no changes' '
 	test_cmp test.txt orig.txt
 '
 
-cp new1.txt test.txt
-test_expect_success "merge without conflict" \
-	"git merge-file test.txt orig.txt new2.txt"
+test_expect_success "merge without conflict" '
+	cp new1.txt test.txt &&
+	git merge-file test.txt orig.txt new2.txt
+'
 
 test_expect_success 'works in subdirectory' '
 	mkdir dir &&
@@ -73,151 +77,176 @@ test_expect_success 'works in subdirectory' '
 	test_path_is_missing a.txt
 '
 
-cp new1.txt test.txt
-test_expect_success "merge without conflict (--quiet)" \
-	"git merge-file --quiet test.txt orig.txt new2.txt"
-
-cp new1.txt test2.txt
-test_expect_failure "merge without conflict (missing LF at EOF)" \
-	"git merge-file test2.txt orig.txt new4.txt"
-
-test_expect_failure "merge result added missing LF" \
-	"test_cmp test.txt test2.txt"
-
-cp new4.txt test3.txt
-test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
-	"git merge-file --quiet test3.txt new2.txt new3.txt"
-
-cat > expect.txt << EOF
-DOMINUS regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-EOF
-printf "propter nomen suum." >> expect.txt
-
-test_expect_success "merge does not add LF away of change" \
-	"test_cmp expect.txt test3.txt"
-
-cp test.txt backup.txt
-test_expect_success "merge with conflicts" \
-	"test_must_fail git merge-file test.txt orig.txt new3.txt"
-
-cat > expect.txt << EOF
-<<<<<<< test.txt
-Dominus regit me, et nihil mihi deerit.
-=======
-DOMINUS regit me,
-et nihil mihi deerit.
->>>>>>> new3.txt
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-
-test_expect_success "expected conflict markers" "test_cmp expect.txt test.txt"
-
-cp backup.txt test.txt
-
-cat > expect.txt << EOF
-Dominus regit me, et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-test_expect_success "merge conflicting with --ours" \
-	"git merge-file --ours test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
-cp backup.txt test.txt
-
-cat > expect.txt << EOF
-DOMINUS regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-test_expect_success "merge conflicting with --theirs" \
-	"git merge-file --theirs test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
-cp backup.txt test.txt
-
-cat > expect.txt << EOF
-Dominus regit me, et nihil mihi deerit.
-DOMINUS regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-test_expect_success "merge conflicting with --union" \
-	"git merge-file --union test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
-cp backup.txt test.txt
-
-test_expect_success "merge with conflicts, using -L" \
-	"test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
-
-cat > expect.txt << EOF
-<<<<<<< 1
-Dominus regit me, et nihil mihi deerit.
-=======
-DOMINUS regit me,
-et nihil mihi deerit.
->>>>>>> new3.txt
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam tu mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
-
-test_expect_success "expected conflict markers, with -L" \
-	"test_cmp expect.txt test.txt"
-
-sed "s/ tu / TU /" < new1.txt > new5.txt
-test_expect_success "conflict in removed tail" \
-	"test_must_fail git merge-file -p orig.txt new1.txt new5.txt > out"
-
-cat > expect << EOF
-Dominus regit me,
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-<<<<<<< orig.txt
-=======
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam TU mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
->>>>>>> new5.txt
-EOF
-
-test_expect_success "expected conflict markers" "test_cmp expect out"
+test_expect_success "merge without conflict (--quiet)" '
+	cp new1.txt test.txt &&
+	git merge-file --quiet test.txt orig.txt new2.txt
+'
+
+test_expect_failure "merge without conflict (missing LF at EOF)" '
+	cp new1.txt test2.txt &&
+	git merge-file test2.txt orig.txt new4.txt
+'
+
+test_expect_failure "merge result added missing LF" '
+	test_cmp test.txt test2.txt
+'
+
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" '
+	cp new4.txt test3.txt &&
+	git merge-file --quiet test3.txt new2.txt new3.txt
+'
+
+test_expect_success "merge does not add LF away of change" '
+	cat >expect.txt <<-\EOF &&
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	EOF
+	printf "propter nomen suum." >>expect.txt &&
+
+	test_cmp expect.txt test3.txt
+'
+
+test_expect_success "merge with conflicts" '
+	cp test.txt backup.txt &&
+	test_must_fail git merge-file test.txt orig.txt new3.txt
+'
+
+test_expect_success "expected conflict markers" '
+	cat >expect.txt <<-\EOF &&
+	<<<<<<< test.txt
+	Dominus regit me, et nihil mihi deerit.
+	=======
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	>>>>>>> new3.txt
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	test_cmp expect.txt test.txt
+'
+
+test_expect_success "merge conflicting with --ours" '
+	cp backup.txt test.txt &&
+
+	cat >expect.txt <<-\EOF &&
+	Dominus regit me, et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	git merge-file --ours test.txt orig.txt new3.txt &&
+	test_cmp expect.txt test.txt
+'
+
+test_expect_success "merge conflicting with --theirs" '
+	cp backup.txt test.txt &&
+
+	cat >expect.txt <<-\EOF &&
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	git merge-file --theirs test.txt orig.txt new3.txt &&
+	test_cmp expect.txt test.txt
+'
+
+test_expect_success "merge conflicting with --union" '
+	cp backup.txt test.txt &&
+
+	cat >expect.txt <<-\EOF &&
+	Dominus regit me, et nihil mihi deerit.
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	git merge-file --union test.txt orig.txt new3.txt &&
+	test_cmp expect.txt test.txt
+'
+
+test_expect_success "merge with conflicts, using -L" '
+	cp backup.txt test.txt &&
+
+	test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt
+'
+
+test_expect_success "expected conflict markers, with -L" '
+	cat >expect.txt <<-\EOF &&
+	<<<<<<< 1
+	Dominus regit me, et nihil mihi deerit.
+	=======
+	DOMINUS regit me,
+	et nihil mihi deerit.
+	>>>>>>> new3.txt
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam tu mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
+
+	test_cmp expect.txt test.txt
+'
+
+test_expect_success "conflict in removed tail" '
+	sed "s/ tu / TU /" <new1.txt >new5.txt &&
+	test_must_fail git merge-file -p orig.txt new1.txt new5.txt >out
+'
+
+test_expect_success "expected conflict markers" '
+	cat >expect <<-\EOF &&
+	Dominus regit me,
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	<<<<<<< orig.txt
+	=======
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam TU mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	>>>>>>> new5.txt
+	EOF
+
+	test_cmp expect out
+'
 
 test_expect_success 'binary files cannot be merged' '
 	test_must_fail git merge-file -p \
@@ -225,59 +254,55 @@ test_expect_success 'binary files cannot be merged' '
 	grep "Cannot merge binary files" merge.err
 '
 
-sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" < new5.txt > new6.txt
-sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" < new5.txt > new7.txt
-
 test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
+	sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" <new5.txt >new6.txt &&
+	sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" <new5.txt >new7.txt &&
 
 	test_must_fail git merge-file -p new6.txt new5.txt new7.txt > output &&
-	test 1 = $(grep ======= < output | wc -l)
-
+	test 1 = $(grep ======= <output | wc -l)
 '
 
-sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit;/"< new6.txt | tr '%' '\012' > new8.txt
-sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit --/" < new7.txt | tr '%' '\012' > new9.txt
-
 test_expect_success 'ZEALOUS_ALNUM' '
+	sed -e "s/deerit./&%%%%/" -e "s/locavit,/locavit;/" <new6.txt | tr % "\012" >new8.txt &&
+	sed -e "s/deerit./&%%%%/" -e "s/locavit,/locavit --/" <new7.txt | tr % "\012" >new9.txt &&
 
 	test_must_fail git merge-file -p \
-		new8.txt new5.txt new9.txt > merge.out &&
-	test 1 = $(grep ======= < merge.out | wc -l)
-
+		new8.txt new5.txt new9.txt >merge.out &&
+	test 1 = $(grep ======= <merge.out | wc -l)
 '
 
-cat >expect <<\EOF
-Dominus regit me,
-<<<<<<< new8.txt
-et nihil mihi deerit;
+test_expect_success '"diff3 -m" style output (1)' '
+	cat >expect <<-\EOF &&
+	Dominus regit me,
+	<<<<<<< new8.txt
+	et nihil mihi deerit;
 
 
 
 
-In loco pascuae ibi me collocavit;
-super aquam refectionis educavit me.
-||||||| new5.txt
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-=======
-et nihil mihi deerit,
+	In loco pascuae ibi me collocavit;
+	super aquam refectionis educavit me.
+	||||||| new5.txt
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	=======
+	et nihil mihi deerit,
 
 
 
 
-In loco pascuae ibi me collocavit --
-super aquam refectionis educavit me,
->>>>>>> new9.txt
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam TU mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
+	In loco pascuae ibi me collocavit --
+	super aquam refectionis educavit me,
+	>>>>>>> new9.txt
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam TU mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
 
-test_expect_success '"diff3 -m" style output (1)' '
 	test_must_fail git merge-file -p --diff3 \
 		new8.txt new5.txt new9.txt >actual &&
 	test_cmp expect actual
@@ -290,61 +315,64 @@ test_expect_success '"diff3 -m" style output (2)' '
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Dominus regit me,
-<<<<<<<<<< new8.txt
-et nihil mihi deerit;
+test_expect_success 'marker size' '
+	cat >expect <<-\EOF &&
+	Dominus regit me,
+	<<<<<<<<<< new8.txt
+	et nihil mihi deerit;
 
 
 
 
-In loco pascuae ibi me collocavit;
-super aquam refectionis educavit me.
-|||||||||| new5.txt
-et nihil mihi deerit.
-In loco pascuae ibi me collocavit,
-super aquam refectionis educavit me;
-==========
-et nihil mihi deerit,
+	In loco pascuae ibi me collocavit;
+	super aquam refectionis educavit me.
+	|||||||||| new5.txt
+	et nihil mihi deerit.
+	In loco pascuae ibi me collocavit,
+	super aquam refectionis educavit me;
+	==========
+	et nihil mihi deerit,
 
 
 
 
-In loco pascuae ibi me collocavit --
-super aquam refectionis educavit me,
->>>>>>>>>> new9.txt
-animam meam convertit,
-deduxit me super semitas jusitiae,
-propter nomen suum.
-Nam et si ambulavero in medio umbrae mortis,
-non timebo mala, quoniam TU mecum es:
-virga tua et baculus tuus ipsa me consolata sunt.
-EOF
+	In loco pascuae ibi me collocavit --
+	super aquam refectionis educavit me,
+	>>>>>>>>>> new9.txt
+	animam meam convertit,
+	deduxit me super semitas jusitiae,
+	propter nomen suum.
+	Nam et si ambulavero in medio umbrae mortis,
+	non timebo mala, quoniam TU mecum es:
+	virga tua et baculus tuus ipsa me consolata sunt.
+	EOF
 
-test_expect_success 'marker size' '
 	test_must_fail git merge-file -p --marker-size=10 \
 		new8.txt new5.txt new9.txt >actual &&
 	test_cmp expect actual
 '
 
-printf "line1\nline2\nline3" >nolf-orig.txt
-printf "line1\nline2\nline3x" >nolf-diff1.txt
-printf "line1\nline2\nline3y" >nolf-diff2.txt
+test_expect_success 'conflict at EOF without LF resolved by --ours' '
+	printf "line1\nline2\nline3" >nolf-orig.txt &&
+	printf "line1\nline2\nline3x" >nolf-diff1.txt &&
+	printf "line1\nline2\nline3y" >nolf-diff2.txt &&
 
-test_expect_success 'conflict at EOF without LF resolved by --ours' \
-	'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
-	 printf "line1\nline2\nline3x" >expect.txt &&
-	 test_cmp expect.txt output.txt'
+	git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+	printf "line1\nline2\nline3x" >expect.txt &&
+	test_cmp expect.txt output.txt
+'
 
-test_expect_success 'conflict at EOF without LF resolved by --theirs' \
-	'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
-	 printf "line1\nline2\nline3y" >expect.txt &&
-	 test_cmp expect.txt output.txt'
+test_expect_success 'conflict at EOF without LF resolved by --theirs' '
+	git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+	printf "line1\nline2\nline3y" >expect.txt &&
+	test_cmp expect.txt output.txt
+'
 
-test_expect_success 'conflict at EOF without LF resolved by --union' \
-	'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
-	 printf "line1\nline2\nline3x\nline3y" >expect.txt &&
-	 test_cmp expect.txt output.txt'
+test_expect_success 'conflict at EOF without LF resolved by --union' '
+	git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+	printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+	test_cmp expect.txt output.txt
+'
 
 test_expect_success 'conflict sections match existing line endings' '
 	printf "1\\r\\n2\\r\\n3" >crlf-orig.txt &&
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 8f9b48a4937..5900358ce9c 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -32,7 +32,29 @@ test_expect_success setup '
 	test_tick &&
 	git commit -m Side &&
 
-	git tag anchor
+	git tag anchor &&
+
+	cat >./custom-merge <<-\EOF &&
+	#!/bin/sh
+
+	orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
+	(
+		echo "orig is $orig"
+		echo "ours is $ours"
+		echo "theirs is $theirs"
+		echo "path is $path"
+		echo "=== orig ==="
+		cat "$orig"
+		echo "=== ours ==="
+		cat "$ours"
+		echo "=== theirs ==="
+		cat "$theirs"
+	) >"$ours+"
+	cat "$ours+" >"$ours"
+	rm -f "$ours+"
+	exit "$exit"
+	EOF
+	chmod +x ./custom-merge
 '
 
 test_expect_success merge '
@@ -82,28 +104,6 @@ test_expect_success 'retry the merge with longer context' '
 	grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual
 '
 
-cat >./custom-merge <<\EOF
-#!/bin/sh
-
-orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
-(
-	echo "orig is $orig"
-	echo "ours is $ours"
-	echo "theirs is $theirs"
-	echo "path is $path"
-	echo "=== orig ==="
-	cat "$orig"
-	echo "=== ours ==="
-	cat "$ours"
-	echo "=== theirs ==="
-	cat "$theirs"
-) >"$ours+"
-cat "$ours+" >"$ours"
-rm -f "$ours+"
-exit "$exit"
-EOF
-chmod +x ./custom-merge
-
 test_expect_success 'custom merge backend' '
 
 	echo "* merge=union" >.gitattributes &&
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
index 89871aa5b04..a25e7304604 100755
--- a/t/t6034-merge-rename-nocruft.sh
+++ b/t/t6034-merge-rename-nocruft.sh
@@ -3,74 +3,73 @@
 test_description='Merge-recursive merging renames'
 . ./test-lib.sh
 
-test_expect_success setup \
-'
-cat >A <<\EOF &&
-a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-c cccccccccccccccccccccccccccccccccccccccccccccccc
-d dddddddddddddddddddddddddddddddddddddddddddddddd
-e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
-f ffffffffffffffffffffffffffffffffffffffffffffffff
-g gggggggggggggggggggggggggggggggggggggggggggggggg
-h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
-i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
-j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
-k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
-l llllllllllllllllllllllllllllllllllllllllllllllll
-m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
-n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
-o oooooooooooooooooooooooooooooooooooooooooooooooo
-EOF
+test_expect_success 'setup' '
+	cat >A <<-\EOF &&
+	a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+	c cccccccccccccccccccccccccccccccccccccccccccccccc
+	d dddddddddddddddddddddddddddddddddddddddddddddddd
+	e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+	f ffffffffffffffffffffffffffffffffffffffffffffffff
+	g gggggggggggggggggggggggggggggggggggggggggggggggg
+	h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+	i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+	j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+	k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+	l llllllllllllllllllllllllllllllllllllllllllllllll
+	m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+	n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+	o oooooooooooooooooooooooooooooooooooooooooooooooo
+	EOF
 
-cat >M <<\EOF &&
-A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
-E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
-F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
-M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-EOF
+	cat >M <<-\EOF &&
+	A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+	C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+	D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+	E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+	F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+	G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+	H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+	I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+	J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+	K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+	L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+	M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+	N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+	O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+	EOF
 
-git add A M &&
-git commit -m "initial has A and M" &&
-git branch white &&
-git branch red &&
-git branch blue &&
+	git add A M &&
+	git commit -m "initial has A and M" &&
+	git branch white &&
+	git branch red &&
+	git branch blue &&
 
-git checkout white &&
-sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
-sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
-rm -f A M &&
-git update-index --add --remove A B M N &&
-git commit -m "white renames A->B, M->N" &&
+	git checkout white &&
+	sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+	sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+	rm -f A M &&
+	git update-index --add --remove A B M N &&
+	git commit -m "white renames A->B, M->N" &&
 
-git checkout red &&
-echo created by red >R &&
-git update-index --add R &&
-git commit -m "red creates R" &&
+	git checkout red &&
+	echo created by red >R &&
+	git update-index --add R &&
+	git commit -m "red creates R" &&
 
-git checkout blue &&
-sed -e "/^o /s/.*/g : blue changes a line/" <A >B &&
-rm -f A &&
-mv B A &&
-git update-index A &&
-git commit -m "blue modify A" &&
+	git checkout blue &&
+	sed -e "/^o /s/.*/g : blue changes a line/" <A >B &&
+	rm -f A &&
+	mv B A &&
+	git update-index A &&
+	git commit -m "blue modify A" &&
 
-git checkout master'
+	git checkout master
+'
 
 # This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
-test_expect_success 'merge white into red (A->B,M->N)' \
-'
+test_expect_success 'merge white into red (A->B,M->N)' '
 	git checkout -b red-white red &&
 	git merge white &&
 	git write-tree &&
@@ -82,8 +81,7 @@ test_expect_success 'merge white into red (A->B,M->N)' \
 '
 
 # This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
-test_expect_success 'merge blue into white (A->B, mod A, A untracked)' \
-'
+test_expect_success 'merge blue into white (A->B, mod A, A untracked)' '
 	git checkout -b white-blue white &&
 	echo dirty >A &&
 	git merge blue &&
-- 
gitgitgadget
Elijah Newren via GitGitGadget· Feb 27, 2020, 00:14 UTC · re: Elijah Newren via GitGitGadget · lore

[PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

From: Elijah Newren <newren@gmail.com>

In t6022, we were testing for file being overwritten (or not) based on an output message instead of checking for the file being overwritten. Since we can check for the file being overwritten via mtime updates, check that instead.

In t6046, we were largely checking for both the expected behavior and a proxy for it, which is unnecessary. The calls to test-tool also were a bit cryptic. Make them a little clearer.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 t/t6022-merge-rename.sh                | 15 ++++-
 t/t6046-merge-skip-unneeded-updates.sh | 89 +++++++++++++++++---------
 2 files changed, 70 insertions(+), 34 deletions(-)
Show changes to 2 files +70 −34

t/t6022-merge-rename.sh, t/t6046-merge-skip-unneeded-updates.sh

diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 6f196aaf276..d97cf48495b 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
 	rm -f A M N &&
 	git reset --hard &&
 	git checkout change+rename &&
+
+	test-tool chmtime =31337 B &&
+	test-tool chmtime --get B >old-mtime &&
 	GIT_MERGE_VERBOSITY=3 git merge change >out &&
-	test_i18ngrep "^Skipped B" out &&
+
+	test-tool chmtime --get B >new-mtime &&
+	test_cmp old-mtime new-mtime &&
+
 	git reset --hard HEAD^ &&
 	git checkout change &&
+
+	test-tool chmtime =-1 M &&
+	test-tool chmtime --get M >old-mtime &&
 	GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
-	test_i18ngrep ! "^Skipped B" out
+
+	test-tool chmtime --get B >new-mtime &&
+	test $(cat old-mtime) -lt $(cat new-mtime)
 '
 
 test_expect_success 'setup for rename + d/f conflicts' '
diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh
index b7e46698321..962030ecdb6 100755
--- a/t/t6046-merge-skip-unneeded-updates.sh
+++ b/t/t6046-merge-skip-unneeded-updates.sh
@@ -71,16 +71,16 @@ test_expect_success '1a-L: Modify(A)/Modify(B), change on B subset of A' '
 
 		git checkout A^0 &&
 
-		test-tool chmtime =31337 b &&
-		test-tool chmtime -v +0 b >expected-mtime &&
+		test-tool chmtime =-1 b &&
+		test-tool chmtime --get b >old-mtime &&
 
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "Skipped b" out &&
 		test_must_be_empty err &&
 
-		test-tool chmtime -v +0 b >actual-mtime &&
-		test_cmp expected-mtime actual-mtime &&
+		# Make sure b was NOT updated
+		test-tool chmtime --get b >new-mtime &&
+		test_cmp old-mtime new-mtime &&
 
 		git ls-files -s >index_files &&
 		test_line_count = 1 index_files &&
@@ -102,9 +102,14 @@ test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
 
 		git checkout B^0 &&
 
+		test-tool chmtime =-1 b &&
+		test-tool chmtime --get b >old-mtime &&
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
 
-		test_i18ngrep "Auto-merging b" out &&
+		# Make sure b WAS updated
+		test-tool chmtime --get b >new-mtime &&
+		test $(cat old-mtime) -lt $(cat new-mtime) &&
+
 		test_must_be_empty err &&
 
 		git ls-files -s >index_files &&
@@ -165,10 +170,10 @@ test_expect_success '2a-L: Modify/rename, merge into modify side' '
 
 		git checkout A^0 &&
 
+		test_path_is_missing c &&
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped c" out &&
-		test_must_be_empty err &&
+		test_path_is_file c &&
 
 		git ls-files -s >index_files &&
 		test_line_count = 1 index_files &&
@@ -193,9 +198,14 @@ test_expect_success '2a-R: Modify/rename, merge into rename side' '
 
 		git checkout B^0 &&
 
+		test-tool chmtime =-1 c &&
+		test-tool chmtime --get c >old-mtime &&
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped c" out &&
+		# Make sure c WAS updated
+		test-tool chmtime --get c >new-mtime &&
+		test $(cat old-mtime) -lt $(cat new-mtime) &&
+
 		test_must_be_empty err &&
 
 		git ls-files -s >index_files &&
@@ -256,16 +266,15 @@ test_expect_success '2b-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
 
 		git checkout A^0 &&
 
-		test-tool chmtime =31337 c &&
-		test-tool chmtime -v +0 c >expected-mtime &&
-
+		test-tool chmtime =-1 c &&
+		test-tool chmtime --get c >old-mtime &&
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "Skipped c" out &&
 		test_must_be_empty err &&
 
-		test-tool chmtime -v +0 c >actual-mtime &&
-		test_cmp expected-mtime actual-mtime &&
+		# Make sure c WAS updated
+		test-tool chmtime --get c >new-mtime &&
+		test_cmp old-mtime new-mtime &&
 
 		git ls-files -s >index_files &&
 		test_line_count = 1 index_files &&
@@ -290,9 +299,12 @@ test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
 
 		git checkout B^0 &&
 
+		test_path_is_missing c &&
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
 
-		test_i18ngrep "Auto-merging c" out &&
+		# Make sure c now present (and thus was updated)
+		test_path_is_file c &&
+
 		test_must_be_empty err &&
 
 		git ls-files -s >index_files &&
@@ -361,13 +373,18 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
 
 		git checkout A^0 &&
 
+		test-tool chmtime =-1 c &&
+		test-tool chmtime --get c >old-mtime &&
 		GIT_MERGE_VERBOSITY=3 &&
 		export GIT_MERGE_VERBOSITY &&
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
 		test_i18ngrep "CONFLICT (rename/add): Rename b->c" out &&
-		test_i18ngrep ! "Skipped c" out &&
-		test_must_be_empty err
+		test_must_be_empty err &&
+
+		# Make sure c WAS updated
+		test-tool chmtime --get c >new-mtime &&
+		test $(cat old-mtime) -lt $(cat new-mtime)
 
 		# FIXME: rename/add conflicts are horribly broken right now;
 		# when I get back to my patch series fixing it and
@@ -460,11 +477,13 @@ test_expect_success '3a-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
 		git checkout A^0 &&
 
+		test_path_is_missing bar/bq &&
 		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
 
+		test_path_is_file bar/bq &&
+
 		git ls-files -s >index_files &&
 		test_line_count = 2 index_files &&
 
@@ -488,11 +507,13 @@ test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
 		git checkout B^0 &&
 
+		test_path_is_missing bar/bq &&
 		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
 
+		test_path_is_file bar/bq &&
+
 		git ls-files -s >index_files &&
 		test_line_count = 2 index_files &&
 
@@ -552,11 +573,13 @@ test_expect_success '3b-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
 		git checkout A^0 &&
 
+		test_path_is_missing bar/bq &&
 		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
 
+		test_path_is_file bar/bq &&
+
 		git ls-files -s >index_files &&
 		test_line_count = 2 index_files &&
 
@@ -580,11 +603,13 @@ test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
 		git checkout B^0 &&
 
+		test_path_is_missing bar/bq &&
 		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
-		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
 
+		test_path_is_file bar/bq &&
+
 		git ls-files -s >index_files &&
 		test_line_count = 2 index_files &&
 
@@ -654,16 +679,16 @@ test_expect_failure '4a: Change on A, change on B subset of A, dirty mods presen
 		git checkout A^0 &&
 		echo "File rewritten" >b &&
 
-		test-tool chmtime =31337 b &&
-		test-tool chmtime -v +0 b >expected-mtime &&
+		test-tool chmtime =-1 b &&
+		test-tool chmtime --get b >old-mtime &&
 
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "Skipped b" out &&
 		test_must_be_empty err &&
 
-		test-tool chmtime -v +0 b >actual-mtime &&
-		test_cmp expected-mtime actual-mtime &&
+		# Make sure b was NOT updated
+		test-tool chmtime --get b >new-mtime &&
+		test_cmp old-mtime new-mtime &&
 
 		git ls-files -s >index_files &&
 		test_line_count = 1 index_files &&
@@ -722,16 +747,16 @@ test_expect_success '4b: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mo
 		git checkout A^0 &&
 		echo "File rewritten" >c &&
 
-		test-tool chmtime =31337 c &&
-		test-tool chmtime -v +0 c >expected-mtime &&
+		test-tool chmtime =-1 c &&
+		test-tool chmtime --get c >old-mtime &&
 
 		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "Skipped c" out &&
 		test_must_be_empty err &&
 
-		test-tool chmtime -v +0 c >actual-mtime &&
-		test_cmp expected-mtime actual-mtime &&
+		# Make sure c was NOT updated
+		test-tool chmtime --get c >new-mtime &&
+		test_cmp old-mtime new-mtime &&
 
 		git ls-files -s >index_files &&
 		test_line_count = 1 index_files &&
-- 
gitgitgadget
SZEDER Gábor· Mar 12, 2020, 13:20 UTC · re: Elijah Newren via GitGitGadget · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
Show 28 quoted lines
> From: Elijah Newren <newren@gmail.com>
> 
> In t6022, we were testing for file being overwritten (or not) based on
> an output message instead of checking for the file being overwritten.
> Since we can check for the file being overwritten via mtime updates,
> check that instead.
> 
> In t6046, we were largely checking for both the expected behavior and a
> proxy for it, which is unnecessary.  The calls to test-tool also were a
> bit cryptic.  Make them a little clearer.
> 
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
>  t/t6022-merge-rename.sh                | 15 ++++-
>  t/t6046-merge-skip-unneeded-updates.sh | 89 +++++++++++++++++---------
>  2 files changed, 70 insertions(+), 34 deletions(-)
> 
> diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> index 6f196aaf276..d97cf48495b 100755
> --- a/t/t6022-merge-rename.sh
> +++ b/t/t6022-merge-rename.sh
> @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
>  	rm -f A M N &&
>  	git reset --hard &&
>  	git checkout change+rename &&
> +
> +	test-tool chmtime =31337 B &&
> +	test-tool chmtime --get B >old-mtime &&
Nit: I think it's possible to change the mtime and print it in a
single invocation with:
  test-tool chmtime --get =1234 file
>  	GIT_MERGE_VERBOSITY=3 git merge change >out &&
Nit: The output of 'git merge' is still redirected to a file, but ...
> -	test_i18ngrep "^Skipped B" out &&
... the only command looking at the output is now removed.
Show 10 quoted lines
> +	test-tool chmtime --get B >new-mtime &&
> +	test_cmp old-mtime new-mtime &&
> +
>  	git reset --hard HEAD^ &&
>  	git checkout change &&
> +
> +	test-tool chmtime =-1 M &&
> +	test-tool chmtime --get M >old-mtime &&
>  	GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> -	test_i18ngrep ! "^Skipped B" out
Likewise.
> +	test-tool chmtime --get B >new-mtime &&
> +	test $(cat old-mtime) -lt $(cat new-mtime)
I saw this test fail today in one of my custom CI builds:
  +git checkout change
  Switched to branch 'change'
  +test-tool chmtime =-1 M
  +test-tool chmtime --get M
  +GIT_MERGE_VERBOSITY=3 git merge change+rename
  +test-tool chmtime --get B
  +cat old-mtime
  +cat new-mtime
  +test 1583967731 -lt 1583967731
  error: last command exited with $?=1
  not ok 12 - merge of identical changes in a renamed file

The contents of 'out', i.e. the output of the 'git merge' command before the failure is:

  Auto-merging B
  Merge made by the 'recursive' strategy.
   A => B | 0
   1 file changed, 0 insertions(+), 0 deletions(-)
   rename A => B (100%)

This is a rare failure, this is the first time I saw it, and to make things worse, this one time it happened on big-endian and with all the GIT_TEST_* knobs enabled.

  https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020

I've been running './t6022-merge-rename.sh --stress -r 1,12' both with and without all the GIT_TEST_* knobs for a few hundred repetitions, but couldn't trigger the failure yet...

I wonder whether comparing the mtimes with '-le' instead of '-lt' is acceptable in this test case, but don't have enough insight to form an opinion. Note that this patch added a few similar mtime comparisons to t6046 below, and they might be prone to the same issue as well.

Show 236 quoted lines
>  test_expect_success 'setup for rename + d/f conflicts' '
> diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh
> index b7e46698321..962030ecdb6 100755
> --- a/t/t6046-merge-skip-unneeded-updates.sh
> +++ b/t/t6046-merge-skip-unneeded-updates.sh
> @@ -71,16 +71,16 @@ test_expect_success '1a-L: Modify(A)/Modify(B), change on B subset of A' '
>  
>  		git checkout A^0 &&
>  
> -		test-tool chmtime =31337 b &&
> -		test-tool chmtime -v +0 b >expected-mtime &&
> +		test-tool chmtime =-1 b &&
> +		test-tool chmtime --get b >old-mtime &&
>  
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep "Skipped b" out &&
>  		test_must_be_empty err &&
>  
> -		test-tool chmtime -v +0 b >actual-mtime &&
> -		test_cmp expected-mtime actual-mtime &&
> +		# Make sure b was NOT updated
> +		test-tool chmtime --get b >new-mtime &&
> +		test_cmp old-mtime new-mtime &&
>  
>  		git ls-files -s >index_files &&
>  		test_line_count = 1 index_files &&
> @@ -102,9 +102,14 @@ test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
>  
>  		git checkout B^0 &&
>  
> +		test-tool chmtime =-1 b &&
> +		test-tool chmtime --get b >old-mtime &&
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
>  
> -		test_i18ngrep "Auto-merging b" out &&
> +		# Make sure b WAS updated
> +		test-tool chmtime --get b >new-mtime &&
> +		test $(cat old-mtime) -lt $(cat new-mtime) &&
> +
>  		test_must_be_empty err &&
>  
>  		git ls-files -s >index_files &&
> @@ -165,10 +170,10 @@ test_expect_success '2a-L: Modify/rename, merge into modify side' '
>  
>  		git checkout A^0 &&
>  
> +		test_path_is_missing c &&
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped c" out &&
> -		test_must_be_empty err &&
> +		test_path_is_file c &&
>  
>  		git ls-files -s >index_files &&
>  		test_line_count = 1 index_files &&
> @@ -193,9 +198,14 @@ test_expect_success '2a-R: Modify/rename, merge into rename side' '
>  
>  		git checkout B^0 &&
>  
> +		test-tool chmtime =-1 c &&
> +		test-tool chmtime --get c >old-mtime &&
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped c" out &&
> +		# Make sure c WAS updated
> +		test-tool chmtime --get c >new-mtime &&
> +		test $(cat old-mtime) -lt $(cat new-mtime) &&
> +
>  		test_must_be_empty err &&
>  
>  		git ls-files -s >index_files &&
> @@ -256,16 +266,15 @@ test_expect_success '2b-L: Rename+Mod(A)/Mod(B), B mods subset of A' '
>  
>  		git checkout A^0 &&
>  
> -		test-tool chmtime =31337 c &&
> -		test-tool chmtime -v +0 c >expected-mtime &&
> -
> +		test-tool chmtime =-1 c &&
> +		test-tool chmtime --get c >old-mtime &&
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep "Skipped c" out &&
>  		test_must_be_empty err &&
>  
> -		test-tool chmtime -v +0 c >actual-mtime &&
> -		test_cmp expected-mtime actual-mtime &&
> +		# Make sure c WAS updated
> +		test-tool chmtime --get c >new-mtime &&
> +		test_cmp old-mtime new-mtime &&
>  
>  		git ls-files -s >index_files &&
>  		test_line_count = 1 index_files &&
> @@ -290,9 +299,12 @@ test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
>  
>  		git checkout B^0 &&
>  
> +		test_path_is_missing c &&
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
>  
> -		test_i18ngrep "Auto-merging c" out &&
> +		# Make sure c now present (and thus was updated)
> +		test_path_is_file c &&
> +
>  		test_must_be_empty err &&
>  
>  		git ls-files -s >index_files &&
> @@ -361,13 +373,18 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
>  
>  		git checkout A^0 &&
>  
> +		test-tool chmtime =-1 c &&
> +		test-tool chmtime --get c >old-mtime &&
>  		GIT_MERGE_VERBOSITY=3 &&
>  		export GIT_MERGE_VERBOSITY &&
>  		test_must_fail git merge -s recursive B^0 >out 2>err &&
>  
>  		test_i18ngrep "CONFLICT (rename/add): Rename b->c" out &&
> -		test_i18ngrep ! "Skipped c" out &&
> -		test_must_be_empty err
> +		test_must_be_empty err &&
> +
> +		# Make sure c WAS updated
> +		test-tool chmtime --get c >new-mtime &&
> +		test $(cat old-mtime) -lt $(cat new-mtime)
>  
>  		# FIXME: rename/add conflicts are horribly broken right now;
>  		# when I get back to my patch series fixing it and
> @@ -460,11 +477,13 @@ test_expect_success '3a-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
>  
>  		git checkout A^0 &&
>  
> +		test_path_is_missing bar/bq &&
>  		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped bar/bq" out &&
>  		test_must_be_empty err &&
>  
> +		test_path_is_file bar/bq &&
> +
>  		git ls-files -s >index_files &&
>  		test_line_count = 2 index_files &&
>  
> @@ -488,11 +507,13 @@ test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
>  
>  		git checkout B^0 &&
>  
> +		test_path_is_missing bar/bq &&
>  		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped bar/bq" out &&
>  		test_must_be_empty err &&
>  
> +		test_path_is_file bar/bq &&
> +
>  		git ls-files -s >index_files &&
>  		test_line_count = 2 index_files &&
>  
> @@ -552,11 +573,13 @@ test_expect_success '3b-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
>  
>  		git checkout A^0 &&
>  
> +		test_path_is_missing bar/bq &&
>  		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped bar/bq" out &&
>  		test_must_be_empty err &&
>  
> +		test_path_is_file bar/bq &&
> +
>  		git ls-files -s >index_files &&
>  		test_line_count = 2 index_files &&
>  
> @@ -580,11 +603,13 @@ test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
>  
>  		git checkout B^0 &&
>  
> +		test_path_is_missing bar/bq &&
>  		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
>  
> -		test_i18ngrep ! "Skipped bar/bq" out &&
>  		test_must_be_empty err &&
>  
> +		test_path_is_file bar/bq &&
> +
>  		git ls-files -s >index_files &&
>  		test_line_count = 2 index_files &&
>  
> @@ -654,16 +679,16 @@ test_expect_failure '4a: Change on A, change on B subset of A, dirty mods presen
>  		git checkout A^0 &&
>  		echo "File rewritten" >b &&
>  
> -		test-tool chmtime =31337 b &&
> -		test-tool chmtime -v +0 b >expected-mtime &&
> +		test-tool chmtime =-1 b &&
> +		test-tool chmtime --get b >old-mtime &&
>  
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep "Skipped b" out &&
>  		test_must_be_empty err &&
>  
> -		test-tool chmtime -v +0 b >actual-mtime &&
> -		test_cmp expected-mtime actual-mtime &&
> +		# Make sure b was NOT updated
> +		test-tool chmtime --get b >new-mtime &&
> +		test_cmp old-mtime new-mtime &&
>  
>  		git ls-files -s >index_files &&
>  		test_line_count = 1 index_files &&
> @@ -722,16 +747,16 @@ test_expect_success '4b: Rename+Mod(A)/Mod(B), change on B subset of A, dirty mo
>  		git checkout A^0 &&
>  		echo "File rewritten" >c &&
>  
> -		test-tool chmtime =31337 c &&
> -		test-tool chmtime -v +0 c >expected-mtime &&
> +		test-tool chmtime =-1 c &&
> +		test-tool chmtime --get c >old-mtime &&
>  
>  		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
>  
> -		test_i18ngrep "Skipped c" out &&
>  		test_must_be_empty err &&
>  
> -		test-tool chmtime -v +0 c >actual-mtime &&
> -		test_cmp expected-mtime actual-mtime &&
> +		# Make sure c was NOT updated
> +		test-tool chmtime --get c >new-mtime &&
> +		test_cmp old-mtime new-mtime &&
>  
>  		git ls-files -s >index_files &&
>  		test_line_count = 1 index_files &&
> -- 
> gitgitgadget
> 
Elijah Newren· Mar 12, 2020, 16:48 UTC · re: SZEDER Gábor · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Mar 12, 2020 at 6:20 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
Show 35 quoted lines
>
> On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > From: Elijah Newren <newren@gmail.com>
> >
> > In t6022, we were testing for file being overwritten (or not) based on
> > an output message instead of checking for the file being overwritten.
> > Since we can check for the file being overwritten via mtime updates,
> > check that instead.
> >
> > In t6046, we were largely checking for both the expected behavior and a
> > proxy for it, which is unnecessary.  The calls to test-tool also were a
> > bit cryptic.  Make them a little clearer.
> >
> > Signed-off-by: Elijah Newren <newren@gmail.com>
> > ---
> >  t/t6022-merge-rename.sh                | 15 ++++-
> >  t/t6046-merge-skip-unneeded-updates.sh | 89 +++++++++++++++++---------
> >  2 files changed, 70 insertions(+), 34 deletions(-)
> >
> > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > index 6f196aaf276..d97cf48495b 100755
> > --- a/t/t6022-merge-rename.sh
> > +++ b/t/t6022-merge-rename.sh
> > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> >       rm -f A M N &&
> >       git reset --hard &&
> >       git checkout change+rename &&
> > +
> > +     test-tool chmtime =31337 B &&
> > +     test-tool chmtime --get B >old-mtime &&
>
> Nit: I think it's possible to change the mtime and print it in a
> single invocation with:
>
>   test-tool chmtime --get =1234 file
Oh, cool.
Show 7 quoted lines
> >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
>
> Nit: The output of 'git merge' is still redirected to a file, but ...
>
> > -     test_i18ngrep "^Skipped B" out &&
>
> ... the only command looking at the output is now removed.
Indeed.
Show 44 quoted lines
> > +     test-tool chmtime --get B >new-mtime &&
> > +     test_cmp old-mtime new-mtime &&
> > +
> >       git reset --hard HEAD^ &&
> >       git checkout change &&
> > +
> > +     test-tool chmtime =-1 M &&
> > +     test-tool chmtime --get M >old-mtime &&
> >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > -     test_i18ngrep ! "^Skipped B" out
>
> Likewise.
>
> > +     test-tool chmtime --get B >new-mtime &&
> > +     test $(cat old-mtime) -lt $(cat new-mtime)
>
> I saw this test fail today in one of my custom CI builds:
>
>   +git checkout change
>   Switched to branch 'change'
>   +test-tool chmtime =-1 M
>   +test-tool chmtime --get M
>   +GIT_MERGE_VERBOSITY=3 git merge change+rename
>   +test-tool chmtime --get B
>   +cat old-mtime
>   +cat new-mtime
>   +test 1583967731 -lt 1583967731
>   error: last command exited with $?=1
>   not ok 12 - merge of identical changes in a renamed file
>
> The contents of 'out', i.e. the output of the 'git merge' command
> before the failure is:
>
>   Auto-merging B
>   Merge made by the 'recursive' strategy.
>    A => B | 0
>    1 file changed, 0 insertions(+), 0 deletions(-)
>    rename A => B (100%)
>
> This is a rare failure, this is the first time I saw it, and to make
> things worse, this one time it happened on big-endian and with all the
> GIT_TEST_* knobs enabled.
>
>   https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020
This is very troubling.  The workflow is basically:
  - Manually set the mtime to a file to something old (I happened to
pick 1 second before now, but picking something from 1970 would have
been fine too).
  - Run a merge which is known to need to overwrite the file.  Your
output ("Auto-merging B") suggests that we should have been in such a
case.
  - Verify that the file was actually updated as expected.  Since the
OS is supposed to update the mtime when it writes the file, it should
have set it to something recent, i.e. something *different* than what
it had before.
So, now I'm left wondering how the mtime possibly could have been not
updated.  Maybe the file wasn't actually written?  (But if so, why
didn't other people see the failure?  Or your stress runs not see it?)
 Or maybe it was written but all file contents and metadata were
delayed in writing to disk such that a subsequent command still sees
the old file??  Or maybe it was written but the mtime update was
delayed and the test was able to check it in that intermediate
state???  Or perhaps the mtime check before the merge raced with the
setting of the mtime backwards and got the mtime before it was
rewound????

I don't have a plausible scenario under which any of these should be possible; I'm at a loss.

> I've been running './t6022-merge-rename.sh --stress -r 1,12' both with
> and without all the GIT_TEST_* knobs for a few hundred repetitions,
> but couldn't trigger the failure yet...
I'm not sure if that makes me feel better or worse.
> I wonder whether comparing the mtimes with '-le' instead of '-lt' is
> acceptable in this test case, but don't have enough insight to form an
> opinion.  Note that this patch added a few similar mtime comparisons
> to t6046 below, and they might be prone to the same issue as well.

-le would not be acceptable; if we were to do that we may as well throw away the test. The test exists to check that the file contents were actually written, which I was assuming could be verified by an mtime update of what otherwise used to be an "old" file.

Elijah Newren· Mar 12, 2020, 17:35 UTC · re: Elijah Newren · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Mar 12, 2020 at 9:48 AM Elijah Newren <newren@gmail.com> wrote:
Show 120 quoted lines
>
> On Thu, Mar 12, 2020 at 6:20 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
> >
> > On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > > From: Elijah Newren <newren@gmail.com>
> > >
> > > In t6022, we were testing for file being overwritten (or not) based on
> > > an output message instead of checking for the file being overwritten.
> > > Since we can check for the file being overwritten via mtime updates,
> > > check that instead.
> > >
> > > In t6046, we were largely checking for both the expected behavior and a
> > > proxy for it, which is unnecessary.  The calls to test-tool also were a
> > > bit cryptic.  Make them a little clearer.
> > >
> > > Signed-off-by: Elijah Newren <newren@gmail.com>
> > > ---
> > >  t/t6022-merge-rename.sh                | 15 ++++-
> > >  t/t6046-merge-skip-unneeded-updates.sh | 89 +++++++++++++++++---------
> > >  2 files changed, 70 insertions(+), 34 deletions(-)
> > >
> > > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > > index 6f196aaf276..d97cf48495b 100755
> > > --- a/t/t6022-merge-rename.sh
> > > +++ b/t/t6022-merge-rename.sh
> > > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> > >       rm -f A M N &&
> > >       git reset --hard &&
> > >       git checkout change+rename &&
> > > +
> > > +     test-tool chmtime =31337 B &&
> > > +     test-tool chmtime --get B >old-mtime &&
> >
> > Nit: I think it's possible to change the mtime and print it in a
> > single invocation with:
> >
> >   test-tool chmtime --get =1234 file
>
> Oh, cool.
>
> > >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
> >
> > Nit: The output of 'git merge' is still redirected to a file, but ...
> >
> > > -     test_i18ngrep "^Skipped B" out &&
> >
> > ... the only command looking at the output is now removed.
>
> Indeed.
>
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test_cmp old-mtime new-mtime &&
> > > +
> > >       git reset --hard HEAD^ &&
> > >       git checkout change &&
> > > +
> > > +     test-tool chmtime =-1 M &&
> > > +     test-tool chmtime --get M >old-mtime &&
> > >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > > -     test_i18ngrep ! "^Skipped B" out
> >
> > Likewise.
> >
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test $(cat old-mtime) -lt $(cat new-mtime)
> >
> > I saw this test fail today in one of my custom CI builds:
> >
> >   +git checkout change
> >   Switched to branch 'change'
> >   +test-tool chmtime =-1 M
> >   +test-tool chmtime --get M
> >   +GIT_MERGE_VERBOSITY=3 git merge change+rename
> >   +test-tool chmtime --get B
> >   +cat old-mtime
> >   +cat new-mtime
> >   +test 1583967731 -lt 1583967731
> >   error: last command exited with $?=1
> >   not ok 12 - merge of identical changes in a renamed file
> >
> > The contents of 'out', i.e. the output of the 'git merge' command
> > before the failure is:
> >
> >   Auto-merging B
> >   Merge made by the 'recursive' strategy.
> >    A => B | 0
> >    1 file changed, 0 insertions(+), 0 deletions(-)
> >    rename A => B (100%)
> >
> > This is a rare failure, this is the first time I saw it, and to make
> > things worse, this one time it happened on big-endian and with all the
> > GIT_TEST_* knobs enabled.
> >
> >   https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020
>
> This is very troubling.  The workflow is basically:
>   - Manually set the mtime to a file to something old (I happened to
> pick 1 second before now, but picking something from 1970 would have
> been fine too).
>   - Run a merge which is known to need to overwrite the file.  Your
> output ("Auto-merging B") suggests that we should have been in such a
> case.
>   - Verify that the file was actually updated as expected.  Since the
> OS is supposed to update the mtime when it writes the file, it should
> have set it to something recent, i.e. something *different* than what
> it had before.
>
> So, now I'm left wondering how the mtime possibly could have been not
> updated.  Maybe the file wasn't actually written?  (But if so, why
> didn't other people see the failure?  Or your stress runs not see it?)
>  Or maybe it was written but all file contents and metadata were
> delayed in writing to disk such that a subsequent command still sees
> the old file??  Or maybe it was written but the mtime update was
> delayed and the test was able to check it in that intermediate
> state???  Or perhaps the mtime check before the merge raced with the
> setting of the mtime backwards and got the mtime before it was
> rewound????
>
> I don't have a plausible scenario under which any of these should be
> possible; I'm at a loss.

Hmm. Maybe leap seconds, or clock updates via ntp at an unfortunate time? Perhaps just setting the "old" time to something more than one second in the past would avoid this?

Anyone have a clue if this is a useful guess or not?
SZEDER Gábor· Mar 12, 2020, 20:01 UTC · re: Elijah Newren · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Mar 12, 2020 at 10:35:52AM -0700, Elijah Newren wrote:
Show 124 quoted lines
> On Thu, Mar 12, 2020 at 9:48 AM Elijah Newren <newren@gmail.com> wrote:
> >
> > On Thu, Mar 12, 2020 at 6:20 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
> > >
> > > On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > > > From: Elijah Newren <newren@gmail.com>
> > > >
> > > > In t6022, we were testing for file being overwritten (or not) based on
> > > > an output message instead of checking for the file being overwritten.
> > > > Since we can check for the file being overwritten via mtime updates,
> > > > check that instead.
> > > >
> > > > In t6046, we were largely checking for both the expected behavior and a
> > > > proxy for it, which is unnecessary.  The calls to test-tool also were a
> > > > bit cryptic.  Make them a little clearer.
> > > >
> > > > Signed-off-by: Elijah Newren <newren@gmail.com>
> > > > ---
> > > >  t/t6022-merge-rename.sh                | 15 ++++-
> > > >  t/t6046-merge-skip-unneeded-updates.sh | 89 +++++++++++++++++---------
> > > >  2 files changed, 70 insertions(+), 34 deletions(-)
> > > >
> > > > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > > > index 6f196aaf276..d97cf48495b 100755
> > > > --- a/t/t6022-merge-rename.sh
> > > > +++ b/t/t6022-merge-rename.sh
> > > > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> > > >       rm -f A M N &&
> > > >       git reset --hard &&
> > > >       git checkout change+rename &&
> > > > +
> > > > +     test-tool chmtime =31337 B &&
> > > > +     test-tool chmtime --get B >old-mtime &&
> > >
> > > Nit: I think it's possible to change the mtime and print it in a
> > > single invocation with:
> > >
> > >   test-tool chmtime --get =1234 file
> >
> > Oh, cool.
> >
> > > >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
> > >
> > > Nit: The output of 'git merge' is still redirected to a file, but ...
> > >
> > > > -     test_i18ngrep "^Skipped B" out &&
> > >
> > > ... the only command looking at the output is now removed.
> >
> > Indeed.
> >
> > > > +     test-tool chmtime --get B >new-mtime &&
> > > > +     test_cmp old-mtime new-mtime &&
> > > > +
> > > >       git reset --hard HEAD^ &&
> > > >       git checkout change &&
> > > > +
> > > > +     test-tool chmtime =-1 M &&
> > > > +     test-tool chmtime --get M >old-mtime &&
> > > >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > > > -     test_i18ngrep ! "^Skipped B" out
> > >
> > > Likewise.
> > >
> > > > +     test-tool chmtime --get B >new-mtime &&
> > > > +     test $(cat old-mtime) -lt $(cat new-mtime)
> > >
> > > I saw this test fail today in one of my custom CI builds:
> > >
> > >   +git checkout change
> > >   Switched to branch 'change'
> > >   +test-tool chmtime =-1 M
> > >   +test-tool chmtime --get M
> > >   +GIT_MERGE_VERBOSITY=3 git merge change+rename
> > >   +test-tool chmtime --get B
> > >   +cat old-mtime
> > >   +cat new-mtime
> > >   +test 1583967731 -lt 1583967731
> > >   error: last command exited with $?=1
> > >   not ok 12 - merge of identical changes in a renamed file
> > >
> > > The contents of 'out', i.e. the output of the 'git merge' command
> > > before the failure is:
> > >
> > >   Auto-merging B
> > >   Merge made by the 'recursive' strategy.
> > >    A => B | 0
> > >    1 file changed, 0 insertions(+), 0 deletions(-)
> > >    rename A => B (100%)
> > >
> > > This is a rare failure, this is the first time I saw it, and to make
> > > things worse, this one time it happened on big-endian and with all the
> > > GIT_TEST_* knobs enabled.
> > >
> > >   https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020
> >
> > This is very troubling.  The workflow is basically:
> >   - Manually set the mtime to a file to something old (I happened to
> > pick 1 second before now, but picking something from 1970 would have
> > been fine too).
> >   - Run a merge which is known to need to overwrite the file.  Your
> > output ("Auto-merging B") suggests that we should have been in such a
> > case.
> >   - Verify that the file was actually updated as expected.  Since the
> > OS is supposed to update the mtime when it writes the file, it should
> > have set it to something recent, i.e. something *different* than what
> > it had before.
> >
> > So, now I'm left wondering how the mtime possibly could have been not
> > updated.  Maybe the file wasn't actually written?  (But if so, why
> > didn't other people see the failure?  Or your stress runs not see it?)
> >  Or maybe it was written but all file contents and metadata were
> > delayed in writing to disk such that a subsequent command still sees
> > the old file??  Or maybe it was written but the mtime update was
> > delayed and the test was able to check it in that intermediate
> > state???  Or perhaps the mtime check before the merge raced with the
> > setting of the mtime backwards and got the mtime before it was
> > rewound????
> >
> > I don't have a plausible scenario under which any of these should be
> > possible; I'm at a loss.
> 
> Hmm.  Maybe leap seconds, or clock updates via ntp at an unfortunate
> time?

I'm now fairly confident that 'git merge' is OK, and suspect that it's an issue with Travis CI's s390x environment (multi-architecture support is an alpha-stage feature).

I kicked off a build that only runs 't6022 --stress -r 1,12', and it failed with the same error on s390x fairly quickly, but haven't failed in any of our "standard" CI jobs until the jobs timeouted.

  https://travis-ci.org/github/szeder/git/jobs/661647673#L2084
Then I kicked off a build to run the test below with --stress:
  test_expect_success 'test' '
  	touch file &&
  	old=$(test-tool chmtime --get =-1 file) &&
  	touch file &&
  	new=$(test-tool chmtime --get file) &&
  	test $old -lt $new
  '
This failed fairly quickly with the same error on s390x as well.
  https://travis-ci.org/github/szeder/git/jobs/661654844#L1275

So I think 'git merge' should be okay, because the failure could be triggered without it as well.

> Perhaps just setting the "old" time to something more than one
> second in the past would avoid this?
> 
> Anyone have a clue if this is a useful guess or not?

I kicked off a build where I set an older timestamp ('=-10' instead of '=-1'), to see what happens, but the s390x job in that build just doesn't want to start... (alpha feature? :) Anyway, the job's output should eventually be available here, will try to check back tomorrow:

  https://travis-ci.org/github/szeder/git/jobs/661657781
SZEDER Gábor· Mar 13, 2020, 17:17 UTC · re: SZEDER Gábor · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Mar 12, 2020 at 09:01:57PM +0100, SZEDER Gábor wrote:
Show 61 quoted lines
> > > > I saw this test fail today in one of my custom CI builds:
> > > >
> > > >   +git checkout change
> > > >   Switched to branch 'change'
> > > >   +test-tool chmtime =-1 M
> > > >   +test-tool chmtime --get M
> > > >   +GIT_MERGE_VERBOSITY=3 git merge change+rename
> > > >   +test-tool chmtime --get B
> > > >   +cat old-mtime
> > > >   +cat new-mtime
> > > >   +test 1583967731 -lt 1583967731
> > > >   error: last command exited with $?=1
> > > >   not ok 12 - merge of identical changes in a renamed file
> > > >
> > > > The contents of 'out', i.e. the output of the 'git merge' command
> > > > before the failure is:
> > > >
> > > >   Auto-merging B
> > > >   Merge made by the 'recursive' strategy.
> > > >    A => B | 0
> > > >    1 file changed, 0 insertions(+), 0 deletions(-)
> > > >    rename A => B (100%)
> > > >
> > > > This is a rare failure, this is the first time I saw it, and to make
> > > > things worse, this one time it happened on big-endian and with all the
> > > > GIT_TEST_* knobs enabled.
> > > >
> > > >   https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020
> > >
> > > This is very troubling.  The workflow is basically:
> > >   - Manually set the mtime to a file to something old (I happened to
> > > pick 1 second before now, but picking something from 1970 would have
> > > been fine too).
> > >   - Run a merge which is known to need to overwrite the file.  Your
> > > output ("Auto-merging B") suggests that we should have been in such a
> > > case.
> > >   - Verify that the file was actually updated as expected.  Since the
> > > OS is supposed to update the mtime when it writes the file, it should
> > > have set it to something recent, i.e. something *different* than what
> > > it had before.
> > >
> > > So, now I'm left wondering how the mtime possibly could have been not
> > > updated.  Maybe the file wasn't actually written?  (But if so, why
> > > didn't other people see the failure?  Or your stress runs not see it?)
> > >  Or maybe it was written but all file contents and metadata were
> > > delayed in writing to disk such that a subsequent command still sees
> > > the old file??  Or maybe it was written but the mtime update was
> > > delayed and the test was able to check it in that intermediate
> > > state???  Or perhaps the mtime check before the merge raced with the
> > > setting of the mtime backwards and got the mtime before it was
> > > rewound????
> > >
> > > I don't have a plausible scenario under which any of these should be
> > > possible; I'm at a loss.
> > 
> > Hmm.  Maybe leap seconds, or clock updates via ntp at an unfortunate
> > time?
> 
> I'm now fairly confident that 'git merge' is OK, and suspect that it's
> an issue with Travis CI's s390x environment (multi-architecture
> support is an alpha-stage feature).

I could finally reproduce the issue on my own machine, so apparently it's not an issue in Travis CI's multi-arch environments.

Show 7 quoted lines
>   test_expect_success 'test' '
>   	touch file &&
>   	old=$(test-tool chmtime --get =-1 file) &&
>   	touch file &&
>   	new=$(test-tool chmtime --get file) &&
>   	test $old -lt $new
>   '

I wanted to report the issue to Travis CI, and in order to do so I turned the above test case into a script that doesn't at all depend on out test framework and 'test-tool' but uses only coreutils commands (with a whole lot of GNUisms...):

  cat >timestamp.sh <<-\EOF
  #!/bin/sh
  
  set -ex
  
  i=0
  while true
  do
  	printf "$i\r"
  	i=$((i + 1))
  
  	# set a file's mtime to one second ago
  	now=$(date "+%s.%N")
  	one_sec_ago=$(date -d "@$((${now%.*} - 1))" "+%Y%m%d%H%M%S")
  	touch -t ${one_sec_ago%??}.${one_sec_ago#????????????} file
  	# save its actual mtime
  	old=$(date -r file "+%s.%N")
  	# set its mtime to now
  	touch file
  	# the current the mtime should be different, but sometimes it isn't
  	new=$(date -r file "+%s.%N")
  	test "${old%.*}" != "${new%.*}"
  done
  EOF
  chmod u+x timestamp.sh
  ./timestamp.sh 2>out
  echo
  tail -n11 out

And this script usually fails after a few hundred iterations on my machine:

  308
  + date +%s.%N
  + now=1584118408.002458987
  + date -d @1584118407 +%Y%m%d%H%M%S
  + one_sec_ago=20200313175327
  + touch -t 202003131753.27 file
  + date -r file +%s.%N
  + old=1584118407.000000000
  + touch file
  + date -r file +%s.%N
  + new=1584118407.997464837
  + test 1584118407 != 1584118407

Note that the mtime update at the end of the iteration results in an mtime that is _before_ the current time at the beginning of the iteration. Well, I'm puzzled :)

> > Perhaps just setting the "old" time to something more than one
> > second in the past would avoid this?

Yes, setting the old timestamp 2 seconds in the past seems to be sufficient, at least I haven't seen it fail in a few 100k repetitions.

Elijah Newren· Mar 13, 2020, 17:45 UTC · re: SZEDER Gábor · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Fri, Mar 13, 2020 at 10:17 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
Show 128 quoted lines
>
> On Thu, Mar 12, 2020 at 09:01:57PM +0100, SZEDER Gábor wrote:
> > > > > I saw this test fail today in one of my custom CI builds:
> > > > >
> > > > >   +git checkout change
> > > > >   Switched to branch 'change'
> > > > >   +test-tool chmtime =-1 M
> > > > >   +test-tool chmtime --get M
> > > > >   +GIT_MERGE_VERBOSITY=3 git merge change+rename
> > > > >   +test-tool chmtime --get B
> > > > >   +cat old-mtime
> > > > >   +cat new-mtime
> > > > >   +test 1583967731 -lt 1583967731
> > > > >   error: last command exited with $?=1
> > > > >   not ok 12 - merge of identical changes in a renamed file
> > > > >
> > > > > The contents of 'out', i.e. the output of the 'git merge' command
> > > > > before the failure is:
> > > > >
> > > > >   Auto-merging B
> > > > >   Merge made by the 'recursive' strategy.
> > > > >    A => B | 0
> > > > >    1 file changed, 0 insertions(+), 0 deletions(-)
> > > > >    rename A => B (100%)
> > > > >
> > > > > This is a rare failure, this is the first time I saw it, and to make
> > > > > things worse, this one time it happened on big-endian and with all the
> > > > > GIT_TEST_* knobs enabled.
> > > > >
> > > > >   https://travis-ci.org/github/szeder/git-cooking-topics-for-travis-ci/jobs/661294571#L4020
> > > >
> > > > This is very troubling.  The workflow is basically:
> > > >   - Manually set the mtime to a file to something old (I happened to
> > > > pick 1 second before now, but picking something from 1970 would have
> > > > been fine too).
> > > >   - Run a merge which is known to need to overwrite the file.  Your
> > > > output ("Auto-merging B") suggests that we should have been in such a
> > > > case.
> > > >   - Verify that the file was actually updated as expected.  Since the
> > > > OS is supposed to update the mtime when it writes the file, it should
> > > > have set it to something recent, i.e. something *different* than what
> > > > it had before.
> > > >
> > > > So, now I'm left wondering how the mtime possibly could have been not
> > > > updated.  Maybe the file wasn't actually written?  (But if so, why
> > > > didn't other people see the failure?  Or your stress runs not see it?)
> > > >  Or maybe it was written but all file contents and metadata were
> > > > delayed in writing to disk such that a subsequent command still sees
> > > > the old file??  Or maybe it was written but the mtime update was
> > > > delayed and the test was able to check it in that intermediate
> > > > state???  Or perhaps the mtime check before the merge raced with the
> > > > setting of the mtime backwards and got the mtime before it was
> > > > rewound????
> > > >
> > > > I don't have a plausible scenario under which any of these should be
> > > > possible; I'm at a loss.
> > >
> > > Hmm.  Maybe leap seconds, or clock updates via ntp at an unfortunate
> > > time?
> >
> > I'm now fairly confident that 'git merge' is OK, and suspect that it's
> > an issue with Travis CI's s390x environment (multi-architecture
> > support is an alpha-stage feature).
>
> I could finally reproduce the issue on my own machine, so apparently
> it's not an issue in Travis CI's multi-arch environments.
>
> >   test_expect_success 'test' '
> >       touch file &&
> >       old=$(test-tool chmtime --get =-1 file) &&
> >       touch file &&
> >       new=$(test-tool chmtime --get file) &&
> >       test $old -lt $new
> >   '
>
> I wanted to report the issue to Travis CI, and in order to do so I
> turned the above test case into a script that doesn't at all depend on
> out test framework and 'test-tool' but uses only coreutils commands
> (with a whole lot of GNUisms...):
>
>   cat >timestamp.sh <<-\EOF
>   #!/bin/sh
>
>   set -ex
>
>   i=0
>   while true
>   do
>         printf "$i\r"
>         i=$((i + 1))
>
>         # set a file's mtime to one second ago
>         now=$(date "+%s.%N")
>         one_sec_ago=$(date -d "@$((${now%.*} - 1))" "+%Y%m%d%H%M%S")
>         touch -t ${one_sec_ago%??}.${one_sec_ago#????????????} file
>         # save its actual mtime
>         old=$(date -r file "+%s.%N")
>         # set its mtime to now
>         touch file
>         # the current the mtime should be different, but sometimes it isn't
>         new=$(date -r file "+%s.%N")
>         test "${old%.*}" != "${new%.*}"
>   done
>   EOF
>   chmod u+x timestamp.sh
>   ./timestamp.sh 2>out
>   echo
>   tail -n11 out
>
> And this script usually fails after a few hundred iterations on my
> machine:
>
>   308
>   + date +%s.%N
>   + now=1584118408.002458987
>   + date -d @1584118407 +%Y%m%d%H%M%S
>   + one_sec_ago=20200313175327
>   + touch -t 202003131753.27 file
>   + date -r file +%s.%N
>   + old=1584118407.000000000
>   + touch file
>   + date -r file +%s.%N
>   + new=1584118407.997464837
>   + test 1584118407 != 1584118407
>
> Note that the mtime update at the end of the iteration results in an
> mtime that is _before_ the current time at the beginning of the
> iteration.  Well, I'm puzzled :)
Interesting...

From https://stackoverflow.com/questions/14392975/timestamp-accuracy-on-ext4-sub-millsecond: ''' date will call the gettimeofday system call which will always return the most accurate time available based on the cached kernel time, adjusted by the CPU cycle time if available to give nanosecond resolution. The timestamps stored in the file system however, are only based on the cached kernel time. ie The time calculated at the last timer interrupt. '''

also, from https://apenwarr.ca/log/20181113 says:

''' Does mtime get set to >= the current time?

No, this depends on clock granularity. For example, gettimeofday() can return times in microseconds on my system, but ext4 rounds timestamps down to the previous ~10ms (but not exactly 10ms) increment, with the surprising result that a newly-created file is almost always created in the past:

  $ python -c "
  import os, time
  t0 = time.time()
  open('testfile', 'w').close()
  print os.stat('testfile').st_mtime - t0
  "
  -0.00234484672546
'''

So, perhaps instead of setting the file mtime relative to the current clock (and truncated to the nearest integer), we need to set the file mtime relative to its recorded mtime.

> Yes, setting the old timestamp 2 seconds in the past seems to be
> sufficient, at least I haven't seen it fail in a few 100k repetitions.

Or yeah, we could just use bigger numbers, but given that wall clock and file-system clock are apparently different, I like the idea of setting them relative to the mtimes that existed before. Looks like `test-tool chmtime` supports that too, so I'll cook up a patch.

SZEDER Gábor· Mar 13, 2020, 17:12 UTC · re: Elijah Newren via GitGitGadget · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
Show 11 quoted lines
> diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> index 6f196aaf276..d97cf48495b 100755
> --- a/t/t6022-merge-rename.sh
> +++ b/t/t6022-merge-rename.sh
> @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
>  	rm -f A M N &&
>  	git reset --hard &&
>  	git checkout change+rename &&
> +
> +	test-tool chmtime =31337 B &&
> +	test-tool chmtime --get B >old-mtime &&
Here you compare the mtime of 'B' before ...
Show 5 quoted lines
>  	GIT_MERGE_VERBOSITY=3 git merge change >out &&
> -	test_i18ngrep "^Skipped B" out &&
> +
> +	test-tool chmtime --get B >new-mtime &&
> +	test_cmp old-mtime new-mtime &&
... and after the merge.
Show 6 quoted lines
> +
>  	git reset --hard HEAD^ &&
>  	git checkout change &&
> +
> +	test-tool chmtime =-1 M &&
> +	test-tool chmtime --get M >old-mtime &&
Here, however, you compare the mtime of 'M' before the merge ...
Show 5 quoted lines
>  	GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> -	test_i18ngrep ! "^Skipped B" out
> +
> +	test-tool chmtime --get B >new-mtime &&
> +	test $(cat old-mtime) -lt $(cat new-mtime)

... with the mtime of 'B' after the merge, i.e. the mtimes of two different files.

Is that right?
Elijah Newren· Mar 13, 2020, 17:18 UTC · re: SZEDER Gábor · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Fri, Mar 13, 2020 at 10:12 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
Show 43 quoted lines
>
> On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > index 6f196aaf276..d97cf48495b 100755
> > --- a/t/t6022-merge-rename.sh
> > +++ b/t/t6022-merge-rename.sh
> > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> >       rm -f A M N &&
> >       git reset --hard &&
> >       git checkout change+rename &&
> > +
> > +     test-tool chmtime =31337 B &&
> > +     test-tool chmtime --get B >old-mtime &&
>
> Here you compare the mtime of 'B' before ...
>
> >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
> > -     test_i18ngrep "^Skipped B" out &&
> > +
> > +     test-tool chmtime --get B >new-mtime &&
> > +     test_cmp old-mtime new-mtime &&
>
> ... and after the merge.
>
> > +
> >       git reset --hard HEAD^ &&
> >       git checkout change &&
> > +
> > +     test-tool chmtime =-1 M &&
> > +     test-tool chmtime --get M >old-mtime &&
>
> Here, however, you compare the mtime of 'M' before the merge ...
>
> >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > -     test_i18ngrep ! "^Skipped B" out
> > +
> > +     test-tool chmtime --get B >new-mtime &&
> > +     test $(cat old-mtime) -lt $(cat new-mtime)
>
> ... with the mtime of 'B' after the merge, i.e. the mtimes of two
> different files.
>
> Is that right?

No, sounds like a copy-and-paste-and-forget-to-update bug. Thanks for spotting it. Since you found it, do you want to submit a patch with the fix?

SZEDER Gábor· Mar 13, 2020, 17:30 UTC · re: Elijah Newren · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Fri, Mar 13, 2020 at 10:18:29AM -0700, Elijah Newren wrote:
Show 48 quoted lines
> On Fri, Mar 13, 2020 at 10:12 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
> >
> > On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > > index 6f196aaf276..d97cf48495b 100755
> > > --- a/t/t6022-merge-rename.sh
> > > +++ b/t/t6022-merge-rename.sh
> > > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> > >       rm -f A M N &&
> > >       git reset --hard &&
> > >       git checkout change+rename &&
> > > +
> > > +     test-tool chmtime =31337 B &&
> > > +     test-tool chmtime --get B >old-mtime &&
> >
> > Here you compare the mtime of 'B' before ...
> >
> > >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
> > > -     test_i18ngrep "^Skipped B" out &&
> > > +
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test_cmp old-mtime new-mtime &&
> >
> > ... and after the merge.
> >
> > > +
> > >       git reset --hard HEAD^ &&
> > >       git checkout change &&
> > > +
> > > +     test-tool chmtime =-1 M &&
> > > +     test-tool chmtime --get M >old-mtime &&
> >
> > Here, however, you compare the mtime of 'M' before the merge ...
> >
> > >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > > -     test_i18ngrep ! "^Skipped B" out
> > > +
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test $(cat old-mtime) -lt $(cat new-mtime)
> >
> > ... with the mtime of 'B' after the merge, i.e. the mtimes of two
> > different files.
> >
> > Is that right?
> 
> No, sounds like a copy-and-paste-and-forget-to-update bug.  Thanks for
> spotting it.  Since you found it, do you want to submit a patch with
> the fix?

No :) There is no file 'B' in branch 'change', so I think the fix requires your expertise to make sure that the test checks what it was supposed to check.

Elijah Newren· Mar 13, 2020, 18:11 UTC · re: Elijah Newren · lore

Re: [PATCH 4/5] t6022, t6046: test expected behavior instead of testing a proxy for it

On Fri, Mar 13, 2020 at 10:18 AM Elijah Newren <newren@gmail.com> wrote:
Show 49 quoted lines
>
> On Fri, Mar 13, 2020 at 10:12 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
> >
> > On Thu, Feb 27, 2020 at 12:14:23AM +0000, Elijah Newren via GitGitGadget wrote:
> > > diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
> > > index 6f196aaf276..d97cf48495b 100755
> > > --- a/t/t6022-merge-rename.sh
> > > +++ b/t/t6022-merge-rename.sh
> > > @@ -242,12 +242,23 @@ test_expect_success 'merge of identical changes in a renamed file' '
> > >       rm -f A M N &&
> > >       git reset --hard &&
> > >       git checkout change+rename &&
> > > +
> > > +     test-tool chmtime =31337 B &&
> > > +     test-tool chmtime --get B >old-mtime &&
> >
> > Here you compare the mtime of 'B' before ...
> >
> > >       GIT_MERGE_VERBOSITY=3 git merge change >out &&
> > > -     test_i18ngrep "^Skipped B" out &&
> > > +
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test_cmp old-mtime new-mtime &&
> >
> > ... and after the merge.
> >
> > > +
> > >       git reset --hard HEAD^ &&
> > >       git checkout change &&
> > > +
> > > +     test-tool chmtime =-1 M &&
> > > +     test-tool chmtime --get M >old-mtime &&
> >
> > Here, however, you compare the mtime of 'M' before the merge ...
> >
> > >       GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
> > > -     test_i18ngrep ! "^Skipped B" out
> > > +
> > > +     test-tool chmtime --get B >new-mtime &&
> > > +     test $(cat old-mtime) -lt $(cat new-mtime)
> >
> > ... with the mtime of 'B' after the merge, i.e. the mtimes of two
> > different files.
> >
> > Is that right?
>
> No, sounds like a copy-and-paste-and-forget-to-update bug.  Thanks for
> spotting it.  Since you found it, do you want to submit a patch with
> the fix?

Actually, it wasn't wrong. Definitely needs some comments explaining the weird construct, though.

← back to recent threads