# [PATCH 5/5] t3030: merge-recursive backend test.

2 messages from 2007-04-07 to 2007-04-08. Participants: Junio C Hamano.
Thread: https://gitlist.dev/t/7562

## Junio C Hamano, 2007-04-07 14:43

Subject: [PATCH 5/5] t3030: merge-recursive backend test.
Message-ID: <7v4pnsckju.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7v4pnsckju.fsf%40assigned-by-dhcp.cox.net

```
We have fairly extensive coverage of read-tree 3-way machinery,
and many Porcelain-ish tests use git-merge front-end tests, but
we did not have good basic test for merge-recursive, which made
it very hard to hack on it.

I used this during the recent work to teach D/F conflicts to
merge-recursive.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t3030-merge-recursive.sh |  528 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 528 insertions(+), 0 deletions(-)
 create mode 100755 t/t3030-merge-recursive.sh

diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
new file mode 100755
index 0000000..aef92b9
--- /dev/null
+++ b/t/t3030-merge-recursive.sh
@@ -0,0 +1,528 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup 1' '
+
+	echo hello >a &&
+	o0=$(git hash-object a) &&
+	cp a b &&
+	cp a A &&
+	mkdir d &&
+	cp a d/e &&
+
+	test_tick &&
+	git add a b A d/e &&
+	git commit -m initial &&
+	c0=$(git rev-parse --verify HEAD) &&
+	git branch side &&
+	git branch df-1 &&
+	git branch df-2 &&
+	git branch df-3 &&
+	git branch remove &&
+
+	echo hello >>a &&
+	cp a d/e &&
+	o1=$(git hash-object a) &&
+
+	git add a d/e &&
+
+	test_tick &&
+	git commit -m "master modifies a and d/e" &&
+	c1=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o1	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o1	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'setup 2' '
+
+	rm -rf [Aabd] &&
+	git checkout side &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	echo goodbye >>a &&
+	o2=$(git hash-object a) &&
+
+	git add a &&
+
+	test_tick &&
+	git commit -m "side modifies a" &&
+	c2=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o2	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o2 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'setup 3' '
+
+	rm -rf [Aabd] &&
+	git checkout df-1 &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	rm -f b && mkdir b && echo df-1 >b/c && git add b/c &&
+	o3=$(git hash-object b/c) &&
+
+	test_tick &&
+	git commit -m "df-1 makes b/c" &&
+	c3=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o3	b/c"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o3 0	b/c"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'setup 4' '
+
+	rm -rf [Aabd] &&
+	git checkout df-2 &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	rm -f a && mkdir a && echo df-2 >a/c && git add a/c &&
+	o4=$(git hash-object a/c) &&
+
+	test_tick &&
+	git commit -m "df-2 makes a/c" &&
+	c4=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o4	a/c"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o4 0	a/c"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'setup 5' '
+
+	rm -rf [Aabd] &&
+	git checkout remove &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	rm -f b &&
+	echo remove-conflict >a &&
+
+	git add a &&
+	git rm b &&
+	o5=$(git hash-object a) &&
+
+	test_tick &&
+	git commit -m "remove removes b and modifies a" &&
+	c5=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o5	a"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o5 0	a"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'setup 6' '
+
+	rm -rf [Aabd] &&
+	git checkout df-3 &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	d/e"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	rm -fr d && echo df-3 >d && git add d &&
+	o6=$(git hash-object d) &&
+
+	test_tick &&
+	git commit -m "df-3 makes d" &&
+	c6=$(git rev-parse --verify HEAD) &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "100644 blob $o0	A"
+		echo "100644 blob $o0	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o6	d"
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o6 0	d"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'merge-recursive simple' '
+
+	rm -fr [Aabd] &&
+	git checkout -f "$c2" &&
+
+	git-merge-recursive "$c0" -- "$c2" "$c1"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 1	a"
+		echo "100644 $o2 2	a"
+		echo "100644 $o1 3	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+	rm -fr [Aabd] &&
+	git checkout -f "$c1" &&
+
+	git-merge-recursive "$c0" -- "$c1" "$c5"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 1	a"
+		echo "100644 $o1 2	a"
+		echo "100644 $o5 3	a"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f simple' '
+	rm -fr [Aabd] &&
+	git reset --hard &&
+	git checkout -f "$c1" &&
+
+	git-merge-recursive "$c0" -- "$c1" "$c3"
+'
+
+test_expect_success 'merge-recursive result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o1 0	a"
+		echo "100644 $o3 0	b/c"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+	rm -fr [Aabd] &&
+	git reset --hard &&
+	git checkout -f "$c1" &&
+
+	git-merge-recursive "$c0" -- "$c1" "$c4"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 1	a"
+		echo "100644 $o1 2	a"
+		echo "100644 $o4 0	a/c"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict the other way' '
+
+	rm -fr [Aabd] &&
+	git reset --hard &&
+	git checkout -f "$c4" &&
+
+	git-merge-recursive "$c0" -- "$c4" "$c1"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result the other way' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 1	a"
+		echo "100644 $o1 3	a"
+		echo "100644 $o4 0	a/c"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+	rm -fr [Aabd] &&
+	git reset --hard &&
+	git checkout -f "$c1" &&
+
+	git-merge-recursive "$c0" -- "$c1" "$c6"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o6 3	d"
+		echo "100644 $o0 1	d/e"
+		echo "100644 $o1 2	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+	rm -fr [Aabd] &&
+	git reset --hard &&
+	git checkout -f "$c6" &&
+
+	git-merge-recursive "$c0" -- "$c6" "$c1"
+	status=$?
+	case "$status" in
+	1)
+		: happy
+		;;
+	*)
+		echo >&2 "why status $status!!!"
+		false
+		;;
+	esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o6 2	d"
+		echo "100644 $o0 1	d/e"
+		echo "100644 $o1 3	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_expect_success 'reset and 3-way merge' '
+
+	git reset --hard "$c2" &&
+	git read-tree -m "$c0" "$c2" "$c1"
+
+'
+
+test_expect_success 'reset and bind merge' '
+
+	git reset --hard master &&
+	git read-tree --prefix=M/ master &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	M/A"
+		echo "100644 $o1 0	M/a"
+		echo "100644 $o0 0	M/b"
+		echo "100644 $o1 0	M/d/e"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual &&
+
+	git read-tree --prefix=a1/ master &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	M/A"
+		echo "100644 $o1 0	M/a"
+		echo "100644 $o0 0	M/b"
+		echo "100644 $o1 0	M/d/e"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	a1/A"
+		echo "100644 $o1 0	a1/a"
+		echo "100644 $o0 0	a1/b"
+		echo "100644 $o1 0	a1/d/e"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+	) >expected &&
+	git diff -u expected actual
+
+	git read-tree --prefix=z/ master &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o0 0	A"
+		echo "100644 $o0 0	M/A"
+		echo "100644 $o1 0	M/a"
+		echo "100644 $o0 0	M/b"
+		echo "100644 $o1 0	M/d/e"
+		echo "100644 $o1 0	a"
+		echo "100644 $o0 0	a1/A"
+		echo "100644 $o1 0	a1/a"
+		echo "100644 $o0 0	a1/b"
+		echo "100644 $o1 0	a1/d/e"
+		echo "100644 $o0 0	b"
+		echo "100644 $o1 0	d/e"
+		echo "100644 $o0 0	z/A"
+		echo "100644 $o1 0	z/a"
+		echo "100644 $o0 0	z/b"
+		echo "100644 $o1 0	z/d/e"
+	) >expected &&
+	git diff -u expected actual
+
+'
+
+test_done
+
-- 
1.5.1.81.gee969

```

## Junio C Hamano, 2007-04-08 05:17

Subject: Re: [PATCH 5/5] t3030: merge-recursive backend test.
Message-ID: <7vmz1ja1i2.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vmz1ja1i2.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <7v4pnsckju.fsf@assigned-by-dhcp.cox.net>

```
Junio C Hamano <junkio@cox.net> writes:

> +test_expect_success 'merge-recursive d/f conflict result' '
> +
> +	git ls-files -s >actual &&
> +	(
> +		echo "100644 $o0 0	A"
> +		echo "100644 $o0 1	a"
> +		echo "100644 $o1 2	a"
> +		echo "100644 $o4 0	a/c"
> +		echo "100644 $o0 0	b"
> +		echo "100644 $o1 0	d/e"
> +	) >expected &&
> +	git diff -u expected actual
> +
> +'

The expected result of this test is what I find very iffy.  The
input trees are:

 * Ancestor has a file "a"
 * Our tree has a file "a", which was modified from the
   ancestor's
 * The other tree has a file "a/c"

merge-recursive.c::git_merge_trees() calls unpack_trees() using
the threeway_merge function and what it gets back are stage 1
and 2 entries for "a", and stage 3 entry for "a/c" (you can see
it by running "git-read-tree -m $c0 $c1 $c4" instead).  But the
output from merge-recursive has "a/c" resolved down to stage 0.

The problem is that process_entry() finds that there is only
stage 3 entry for that path and decides to resolve it.  I think
it should do the D/F check and leave "a/c" in stage 3.

Looking at the D/F conflict detection code in merge-recursive.c
makes my head hurt, and I do not quite follow the logic there,
but it seems to be doing the following to me.

 * current_directory_set is initialized to be union of all
   directories that appear in our tree and the other tree (I am
   not sure if that set is updated and if so how);

 * when a path is about to be resolved to a blob, this set is
   consulted, and if there is a directory there, declare D/F
   conflict and blob is added under a name that does not
   conflict with the directory;

 * otherwise the path is resolved down to stage 0.

However, in this case, there is no such directory "a/c" but the
code forgets that adding a blob at "a/c" requires that there is
no file "a" at all.  I am not sure if this is deliberate or just
a simple oversight.

Also I suspect this can be done more easily with just checking
the index.  We know blob "a" is in the index, unresolved, when
we try to resolve path "a/c".
 

```
