git/t/t6406-merge-attr.sh

#!/bin/sh
#
# Copyright (c) 2007 Junio C Hamano
#

test_description='per path merge controlled by merge attribute'

GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh

test_expect_success setup '

	for f in text binary union
	do
		echo Initial >$f && git add $f || return 1
	done &&
	test_tick &&
	git commit -m Initial &&

	git branch side &&
	for f in text binary union
	do
		echo Main >>$f && git add $f || return 1
	done &&
	test_tick &&
	git commit -m Main &&

	git checkout side &&
	for f in text binary union
	do
		echo Side >>$f && git add $f || return 1
	done &&
	test_tick &&
	git commit -m Side &&

	git tag anchor &&

	cat >./custom-merge <<-\EOF &&
	#!/bin/sh

	orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
	orig_name="$6" our_name="$7" their_name="$8"
	(
		echo "orig is $orig"
		echo "ours is $ours"
		echo "theirs is $theirs"
		echo "path is $path"
		echo "orig_name is $orig_name"
		echo "our_name is $our_name"
		echo "their_name is $their_name"
		echo "=== orig ==="
		cat "$orig"
		echo "=== ours ==="
		cat "$ours"
		echo "=== theirs ==="
		cat "$theirs"
	) >"$ours+"
	cat "$ours+" >"$ours"
	rm -f "$ours+"

	if test -f ./please-abort
	then
		echo >>./please-abort killing myself
		kill -9 $$
	fi
	exit "$exit"
	EOF
	chmod +x ./custom-merge
'

test_expect_success merge '

	cat >.gitattributes <<-\EOF &&
	binary -merge
	union merge=union
	EOF

	if git merge main
	then
		echo Gaah, should have conflicted
		false
	else
		echo Ok, conflicted.
	fi
'

test_expect_success 'check merge result in index' '

	git ls-files -u | grep binary &&
	git ls-files -u | grep text &&
	! (git ls-files -u | grep union)

'

test_expect_success 'check merge result in working tree' '

	git cat-file -p HEAD:binary >binary-orig &&
	grep "<<<<<<<" text &&
	cmp binary-orig binary &&
	! grep "<<<<<<<" union &&
	grep Main union &&
	grep Side union

'

test_expect_success 'retry the merge with longer context' '
	echo text conflict-marker-size=32 >>.gitattributes &&
	git checkout -m text &&
	sed -ne "/^\([<=>]\)\1\1\1*/{
		s/ .*$//
		p
	}" >actual text &&
	grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual &&
	grep "================================" actual &&
	grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual
'

test_expect_success 'custom merge backend' '

	echo "* merge=union" >.gitattributes &&
	echo "text merge=custom" >>.gitattributes &&

	git reset --hard anchor &&
	git config --replace-all \
	merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
	git config --replace-all \
	merge.custom.name "custom merge driver for testing" &&

	git merge main &&

	cmp binary union &&
	sed -e 1,3d text >check-1 &&
	o=$(git unpack-file main^:text) &&
	a=$(git unpack-file side^:text) &&
	b=$(git unpack-file main:text) &&
	base_revid=$(git rev-parse --short main^) &&
	sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
	sed -e 1,3d $a >check-2 &&
	cmp check-1 check-2 &&
	rm -f $o $a $b
'

test_expect_success 'custom merge backend' '

	git reset --hard anchor &&
	git config --replace-all \
	merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" &&
	git config --replace-all \
	merge.custom.name "custom merge driver for testing" &&

	if git merge main
	then
		echo "Eh? should have conflicted"
		false
	else
		echo "Ok, conflicted"
	fi &&

	cmp binary union &&
	sed -e 1,3d text >check-1 &&
	o=$(git unpack-file main^:text) &&
	a=$(git unpack-file anchor:text) &&
	b=$(git unpack-file main:text) &&
	base_revid=$(git rev-parse --short main^) &&
	sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
	sed -e 1,3d $a >check-2 &&
	cmp check-1 check-2 &&
	sed -e 1,3d -e 4q $a >check-3 &&
	echo "path is text" >expect &&
	cmp expect check-3 &&
	rm -f $o $a $b
'

test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' '
	test_when_finished "rm -f output please-abort" &&

	git reset --hard anchor &&
	git config --replace-all \
	merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
	git config --replace-all \
	merge.custom.name "custom merge driver for testing" &&

	>./please-abort &&
	echo "* merge=custom" >.gitattributes &&
	test_expect_code 2 git merge main 2>err &&
	grep "^error: failed to execute internal merge" err &&
	git ls-files -u >output &&
	git diff --name-only HEAD >>output &&
	test_must_be_empty output
'

test_expect_success 'up-to-date merge without common ancestor' '
	git init repo1 &&
	git init repo2 &&
	test_tick &&
	(
		cd repo1 &&
		>a &&
		git add a &&
		git commit -m initial
	) &&
	test_tick &&
	(
		cd repo2 &&
		git commit --allow-empty -m initial
	) &&
	test_tick &&
	(
		cd repo1 &&
		git fetch ../repo2 main &&
		git merge --allow-unrelated-histories FETCH_HEAD
	)
'

test_expect_success 'custom merge does not lock index' '
	git reset --hard anchor &&
	write_script sleep-an-hour.sh <<-\EOF &&
		sleep 3600 &
		echo $! >sleep.pid
	EOF

	test_write_lines >.gitattributes \
		"* merge=ours" "text merge=sleep-an-hour" &&
	test_config merge.ours.driver true &&
	test_config merge.sleep-an-hour.driver ./sleep-an-hour.sh &&

	# We are testing that the custom merge driver does not block
	# index.lock on Windows due to an inherited file handle.
	# To ensure that the backgrounded process ran sufficiently
	# long (and has been started in the first place), we do not
	# ignore the result of the kill command.
	# By packaging the command in test_when_finished, we get both
	# the correctness check and the clean-up.
	test_when_finished "kill \$(cat sleep.pid)" &&
	git merge main
'

test_expect_success 'binary files with union attribute' '
	git checkout -b bin-main &&
	printf "base\0" >bin.txt &&
	echo "bin.txt merge=union" >.gitattributes &&
	git add bin.txt .gitattributes &&
	git commit -m base &&

	printf "one\0" >bin.txt &&
	git commit -am one &&

	git checkout -b bin-side HEAD^ &&
	printf "two\0" >bin.txt &&
	git commit -am two &&

	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
	then
		test_must_fail git merge bin-main >output
	else
		test_must_fail git merge bin-main 2>output
	fi &&
	grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
'

test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' '
	test_when_finished "rm -f output please-abort" &&
	test_when_finished "git checkout side" &&

	git reset --hard anchor &&

	git checkout -b base-a main^ &&
	echo base-a >text &&
	git commit -m base-a text &&

	git checkout -b base-b main^ &&
	echo base-b >text &&
	git commit -m base-b text &&

	git checkout -b recursive-a base-a &&
	test_must_fail git merge base-b &&
	echo recursive-a >text &&
	git add text &&
	git commit -m recursive-a &&

	git checkout -b recursive-b base-b &&
	test_must_fail git merge base-a &&
	echo recursive-b >text &&
	git add text &&
	git commit -m recursive-b &&

	git config --replace-all \
	merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
	git config --replace-all \
	merge.custom.name "custom merge driver for testing" &&

	>./please-abort &&
	echo "* merge=custom" >.gitattributes &&
	test_expect_code 2 git merge recursive-a 2>err &&
	grep "error: failed to execute internal merge" err &&
	git ls-files -u >output &&
	git diff --name-only HEAD >>output &&
	test_must_be_empty output
'

test_done