Sincecd1d61c44f(make union merge an xdl merge favor, 2010-03-01), we pass NULL to ll_xdl_merge() for the "name" labels of the ancestor, ours and theirs buffers. We usually use these for annotating conflict markers left in a file. For a union merge, these shouldn't matter; the point of it is that we'd never leave conflict markers in the first place. But there is one code path where we may dereference them: if the file contents appear to be binary, ll_binary_merge() will give up and pass them to warning() to generate a message for the user (that was true even whencd1d61c44fwas written, though the warning was in ll_xdl_merge() back then). That can result in a segfault, though on many systems (including glibc), the printf routines will helpfully just say "(null)" instead. We can extend our binary-union test in t6406 to check stderr, which catches the problem on all systems. This also fixes a warning from "gcc -O3". Unlike lower optimization levels, it inlines enough to see that the NULL can make it to warning() and complains: In function ‘ll_binary_merge’, inlined from ‘ll_xdl_merge’ at ll-merge.c:115:10, inlined from ‘ll_union_merge’ at ll-merge.c:151:9: ll-merge.c:74:4: warning: ‘%s’ directive argument is null [-Wformat-overflow=] 74 | warning("Cannot merge binary files: %s (%s vs. %s)", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 | path, name1, name2); | ~~~~~~~~~~~~~~~~~~~ Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			229 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/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-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
 | 
						|
	(
 | 
						|
		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 '
 | 
						|
 | 
						|
	{
 | 
						|
		echo "binary -merge"
 | 
						|
		echo "union merge=union"
 | 
						|
	} >.gitattributes &&
 | 
						|
 | 
						|
	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" &&
 | 
						|
	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) &&
 | 
						|
	sh -c "./custom-merge $o $a $b 0 text" &&
 | 
						|
	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" &&
 | 
						|
	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) &&
 | 
						|
	sh -c "./custom-merge $o $a $b 0 text" &&
 | 
						|
	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 'up-to-date merge without common ancestor' '
 | 
						|
	test_create_repo repo1 &&
 | 
						|
	test_create_repo 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 &&
 | 
						|
 | 
						|
	test_must_fail git merge bin-main 2>stderr &&
 | 
						|
	grep -i "warning.*cannot merge.*HEAD vs. bin-main" stderr
 | 
						|
'
 | 
						|
 | 
						|
test_done
 |