Subject: [RFC PATCH 4/9] Add testcases for improved file collision conflict handling

From: Elijah Newren <[email-masked]>
Date: Fri, 10 Nov 2017 14:21:51 -0800

Adds testcases dealing with file collisions for the following types of
conflicts:
  * add/add
  * rename/add
  * rename/rename(2to1)
These tests include expectations for new, smarter behavior provided by
handle_file_collision().  Since that function is not in use yet, the
tests are currently expected to fail.

Signed-off-by: Elijah Newren <[email-masked]>
---
 t/t6036-recursive-corner-cases.sh    |   8 +-
 t/t6042-merge-rename-corner-cases.sh | 210 +++++++++++++++++++++++++++++++++++
 2 files changed, 214 insertions(+), 4 deletions(-)

diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index 18aa88b5c0..8485e04b9b 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -208,11 +208,11 @@ test_expect_success 'git detects differently handled merges conflict' '
 	git cat-file -p C:new_a >>merge-me &&
 	>empty &&
 	test_must_fail git merge-file \
-		-L "Temporary merge branch 2" \
-		-L "" \
 		-L "Temporary merge branch 1" \
-		merged empty merge-me &&
-	sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
+		-L "" \
+		-L "Temporary merge branch 2" \
+		merge-me empty merged &&
+	sed -e "s/^\([<=>]\)/\1\1\1/" merge-me >merged-internal &&
 	test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
 '
 
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
index 411550d2b6..d8fe797f0d 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -575,4 +575,214 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
 	test ! -f c
 '
 
+test_conflicts_with_adds_and_renames() {
+	test $1 != 0 && side1=rename || side1=add
+	test $2 != 0 && side2=rename || side2=add
+
+	# Setup:
+	#          L
+	#         / \
+	#   master   ?
+	#         \ /
+	#          R
+	#
+	# Where:
+	#   Both L and R have files named 'three-unrelated' and
+	#   'three-related' which collide.  Each of the colliding files
+	#   could have been involved in a rename, in which case there was a
+	#   file named 'one-[un]related' or 'two-[un]related' that was
+	#   modified on the opposite side of history and renamed into the
+	#   collision on this side of history.
+	#
+	# Questions for both three-unrelated and three-related:
+	#   1) The index should contain both a stage 2 and stage 3 entry
+	#      for the colliding file.  Does it?
+	#   2) When renames are involved, the content merges are clean, so
+	#      the index should reflect the content merges, not merely the
+	#      version of the colliding file from the prior commit.  Does
+	#      it?
+	#
+	# Questions for three-unrelated:
+	#   3) There should be files in the worktree named
+	#      'three-unrelated~HEAD' and 'three-unrelated~R^0' with the
+	#      (content-merged) version of 'three-unrelated' from the
+	#      appropriate side of the merge.  Are they present?
+	#   4) There should be no file named 'three-unrelated' in the
+	#      working tree.  That'd make it too likely that users would
+	#      use it instead of carefully looking at both
+	#      three-unrelated~HEAD and three-unrelated~R^0.  Is it
+	#      correctly missing?
+	#
+	# Questions for three-related:
+	#   3) There should be a file in the worktree named three-related
+	#      containing the two-way merged contents of the content-merged
+	#      versions of three-related from each of the two colliding
+	#      files.  Is it present?
+	#   4) There should not be any three-related~* files in the working
+	#      tree.
+	test_expect_success "setup simple $side1/$side2 conflict" '
+		git rm -rf . &&
+		git clean -fdqx &&
+		rm -rf .git &&
+		git init &&
+
+		# Create a simple file with 10 lines
+		ten="0 1 2 3 4 5 6 7 8 9" &&
+		for i in $ten
+		do
+			echo line $i in a sample file
+		done >unrelated1_v1 &&
+		# Create a second version of same file with one more line
+		cat unrelated1_v1 >unrelated1_v2 &&
+		echo another line >>unrelated1_v2 &&
+
+		# Create an unrelated simple file with 10 lines
+		for i in $ten
+		do
+			echo line $i in another sample file
+		done >unrelated2_v1 &&
+		# Create a second version of same file with one more line
+		cat unrelated2_v1 >unrelated2_v2 &&
+		echo another line >>unrelated2_v2 &&
+
+		# Create some related files now
+		for i in $ten
+		do
+			echo Random base content line $i
+		done >related1_v1 &&
+		cp -a related1_v1 related1_v2 &&
+		echo modification >>related1_v2 &&
+
+		cp -a related1_v1 related2_v1 &&
+		echo more stuff >>related2_v1 &&
+		cp -a related2_v1 related2_v2 &&
+		echo yet more stuff >>related2_v2 &&
+
+		# Use a tag to record both these files for simple access,
+		# and clean out these untracked files
+		git tag unrelated1_v1 `git hash-object -w unrelated1_v1` &&
+		git tag unrelated1_v2 `git hash-object -w unrelated1_v2` &&
+		git tag unrelated2_v1 `git hash-object -w unrelated2_v1` &&
+		git tag unrelated2_v2 `git hash-object -w unrelated2_v2` &&
+		git tag related1_v1 `git hash-object -w related1_v1` &&
+		git tag related1_v2 `git hash-object -w related1_v2` &&
+		git tag related2_v1 `git hash-object -w related2_v1` &&
+		git tag related2_v2 `git hash-object -w related2_v2` &&
+		git clean -f &&
+
+		# Setup merge-base, consisting of files named "one-*"
+		# and "two-*" if renames were involved.
+		touch irrelevant_file &&
+		git add irrelevant_file &&
+		if [ $side1 == "rename" ]; then
+			git show unrelated1_v1 >one-unrelated &&
+			git add one-unrelated
+			git show related1_v1 >one-related &&
+			git add one-related
+		fi &&
+		if [ $side2 == "rename" ]; then
+			git show unrelated2_v1 >two-unrelated &&
+			git add two-unrelated
+			git show related2_v1 >two-related &&
+			git add two-related
+		fi &&
+		test_tick && git commit -m initial &&
+
+		git branch L &&
+		git branch R &&
+
+		# Handle the left side
+		git checkout L &&
+		if [ $side1 == "rename" ]; then
+			git mv one-unrelated three-unrelated
+			git mv one-related   three-related
+		else
+			git show unrelated1_v2 >three-unrelated &&
+			git add three-unrelated
+			git show related1_v2 >three-related &&
+			git add three-related
+		fi &&
+		if [ $side2 == "rename" ]; then
+			git show unrelated2_v2 >two-unrelated &&
+			git add two-unrelated
+			git show related2_v2 >two-related &&
+			git add two-related
+		fi &&
+		test_tick && git commit -m L &&
+
+		# Handle the right side
+		git checkout R &&
+		if [ $side1 == "rename" ]; then
+			git show unrelated1_v2 >one-unrelated &&
+			git add one-unrelated
+			git show related1_v2 >one-related &&
+			git add one-related
+		fi &&
+		if [ $side2 == "rename" ]; then
+			git mv two-unrelated three-unrelated
+			git mv two-related three-related
+		else
+			git show unrelated2_v2 >three-unrelated &&
+			git add three-unrelated
+			git show related2_v2 >three-related &&
+			git add three-related
+		fi &&
+		test_tick && git commit -m R
+	'
+
+	test_expect_failure "check simple $side1/$side2 conflict" '
+		git reset --hard &&
+		git checkout L^0 &&
+
+		# Merge must fail; there is a conflict
+		test_must_fail git merge -s recursive R^0 &&
+
+		# Make sure the index has the right number of entries
+		test 5 = $(git ls-files -s | wc -l) &&
+		test 4 = $(git ls-files -u | wc -l) &&
+
+		# Nothing should have touched irrelevant_file
+		test $(git rev-parse :0:irrelevant_file) = $(git rev-parse master:irrelevant_file) &&
+
+		# Even for renames, make sure the index contains the MERGED
+		# version of the file, not the version of the file that existed
+		# on the given side.
+		test $(git rev-parse :2:three-unrelated) = $(git rev-parse unrelated1_v2) &&
+		test $(git rev-parse :3:three-unrelated) = $(git rev-parse unrelated2_v2) &&
+		test $(git rev-parse :2:three-related)   = $(git rev-parse related1_v2) &&
+		test $(git rev-parse :3:three-related)   = $(git rev-parse related2_v2) &&
+
+		# Make sure we have the correct number of untracked files
+		test 2 = $(git ls-files -o | wc -l) &&
+
+		# Make sure each file (with merging if rename involved) is
+		# present in the working tree for the user to work with.
+		test $(git hash-object three-unrelated~HEAD) = $(git rev-parse unrelated1_v2) &&
+		test $(git hash-object three-unrelated~R^0)  = $(git rev-parse unrelated2_v2) &&
+
+		# "three-unrelated" should not exist because there is no
+		# reason to give preference to either three-unrelated~HEAD
+		# or three-unrelated~R^0
+		test ! -f three-unrelated &&
+
+		# Make sure we have the correct merged contents for three-related
+		git show related1_v1 >expected &&
+		cat <<EOF >>expected &&
+<<<<<<< HEAD
+modification
+=======
+more stuff
+yet more stuff
+>>>>>>> R^0
+EOF
+
+		test_cmp expected three-related
+	'
+}
+
+test_conflicts_with_adds_and_renames 1 1
+test_conflicts_with_adds_and_renames 1 0
+test_conflicts_with_adds_and_renames 0 1
+test_conflicts_with_adds_and_renames 0 0
+
 test_done
-- 
2.15.0.46.g41dca04efb