Re: needs merge

From: Junio C Hamano <junkio@cox.net>
Date: 2006-01-08 08:06:49
"Brown, Len" <len.brown@intel.com> writes:

> yes, it did the right thing to the source.
>
> as a user, "cute" isn't the first word that comes to mind:-)

Fair enough.  I agree that it is rather unnerving from the
user's point of view, especially because the "recursive merging
of the ancestors" part is not trivially obvious why it works.

The issue it tries to address (and the other merge strategy,
"resolve", addresses a bit differently) is to detect when two
sides of criss-cross merged branches resolved the same conflicts
differently in the past.  When merging E and F in this ancestry
graph:

             A----C---E----?
            / \  /        /
           /   \/        /
          /    /\       /
         /    /  \     /
        O----B----D---F

suppose that A and B made conflicting modifications on path P, C
and D resolved the conflicts by taking its own side, and E and F
further made independent changes to the path.  

When we try to merge E and F, if we happen to pick B as the
merge base, normal three-way merge between E and F using B as
base would say that E has change between B and C while F's
version stayed the same in the area that change touches, and you
would end up with merge result that is diff(D,F) on top of E.
If we used A, then the result would be diff(C,E) on top of F.
Doing either of these silently is not desirable [*1*].

When we use a tree that is an automerge (with conflict markers)
between A and B as the merge base for E and F, what happens is
that the three-way merge notices the part with conflict markers
as a whole is rewritten to one version in E and to another in F,
removes the original (which is the conflicting merge between A
and B) and replaces that with conflicts between E and F with its
own conflict markers, so decision to resolve the conflicting
merge between A and B which C and D resolved differently is
given to the user who tries to merge E and F.

If C and D resolved the conflicting merge the same way, the
"phantom conflict" you observed still happens while merging A
and B to come up with the merge base tree, but the part will be
rewritten the same way in both E and F, so the result will merge
cleanly.

[Footnote]

*1* While I was trying out the above scenario, I noticed that
resolve strategy does not notice this and ended up picking one
ancestor at random.  I think this is because case #16 covers
only the case where the path P is not changed between C and E
and D and F, and case #11 must pick an ancestor and picks one at
random.  Daniel, any thoughts on this?

-- >8 --
#!/bin/sh

unset GIT_DIR GIT_OBJECT_DIRECTORY
test -d .git || {
	echo Run this in a newly created empty directory please.
	exit 1
}

: <<\EOF

	Criss Cross Merge

             A----C---E----?
            / \  /        /
           /   \/        /
          /    /\       /
         /    /  \     /
        O----B----D---F

EOF

git init-db

for l in a b c d e f g h i j k l m n o p q r s t u v w x y z
do
	echo $l
done >testfile
git add testfile
git commit -m 'Initial'
git tag O
git branch bottom
git checkout -b top
cp testfile testfile.orig
tr 'm-p' 'M-P' <testfile.orig >testfile
git update-index testfile
git commit -m A
git tag A
git checkout bottom
tr 'm-p' 'A-D' <testfile.orig >testfile
git update-index testfile
git commit -m B
git tag B
git merge -s ours 'D=A+B=B' HEAD A
git tag D
git checkout top
git merge -s ours 'C=A+B=A' HEAD B
git tag C
mv testfile j; tr 'a' 'A' <j >testfile; rm -f j
git update-index testfile
git commit -m E
git tag E
git checkout bottom
mv testfile j; tr 'z' 'Z' <j >testfile; rm -f j
git update-index testfile
git commit -m F
git tag F

rm -f testfile.orig

git checkout master
git reset --hard A
git merge -s resolve V HEAD B
git update-index testfile
git commit -m 'V'
git tag V

git show-branch

git checkout top
echo "** RECURSIVE **"
git merge -s recursive recursive HEAD F
echo theirs
git diff --theirs
echo ours
git diff --ours

git reset --hard
echo "** RESOLVE **"
git merge -s resolve resolve HEAD F

echo theirs
git diff HEAD^2
echo ours
git diff HEAD^

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Sun Jan 08 08:08:39 2006

This archive was generated by hypermail 2.1.8 : 2006-01-08 08:08:46 EST