[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Subversion merge creates bogus tree conflicts

From: Julian Foad <julianfoad_at_btopenworld.com>
Date: Tue, 15 Jan 2013 13:38:49 +0000 (GMT)

Ben Reser wrote:

> On Mon, Jan 14, 2013 at 8:49 AM, David Moon <david.moon_at_versata.com> wrote:
>> Subversion creates an unnecessary tree conflict when two branches of a
>> directory are merged.  See the transcript below.  Since the contents of the
>> directory do not conflict between the two branches, it should just add both
>> subdirectories to the directory and not report any conflict.  In my opinion,
>> that should be the default merge behavior, but there isn't even any way
>> using svn resolve to tell it to do that!  The only thing svn resolve can do
>> is lose the incoming change.
>>
>> The unnecessary tree conflict does not always happen, based on I know not
>> what.  But the transcript below seems to be a reliable way to reproduce it.
>>
>> This transcript is boiled down to a simple test case.  In the real thing
>> there were too many of these tree conflicts to fix by hand, so fixing it by
>> hand is not a solution even if I could figure out how to fix it by hand.
>>
>> $REPOS is file:/// something.
>>
>>> svn --version
>> svn, version 1.7.8 (r1419691)
>>     compiled Dec 12 2012, 14:18:28
>>
>>> svn mkdir --parents $REPOS/test1/branches/B1/D1/D2/A -m xx
>>
>>> svn mkdir --parents $REPOS/test1/tags/T1/D1/D2/B -m xx
>>
>>> svn co $REPOS/test1/branches/B1/D1/D2/A
>>> echo test > A/test1.txt
>>> cd A; svn add test1.txt
>>> svn commit -m xx
>>> cd ..
>>
>>> svn co $REPOS/test1/tags/T1/D1/D2/B
>>> echo test > B/test2.txt
>>> cd B; svn add test2.txt
>>> svn commit -m xx
>>> cd ..
>>
>>> svn copy $REPOS/test1/tags/T1 $REPOS/test1/branches/B2 -m xx
>>
>>> svn list -R $REPOS/test1/branches/B1
>> D1/
>> D1/D2/
>> D1/D2/A/
>> D1/D2/A/test1.txt
>>> svn list -R $REPOS/test1/branches/B2
>> D1/
>> D1/D2/
>> D1/D2/B/
>> D1/D2/B/test2.txt
>>
>>> svn co $REPOS/test1/branches/B2
>>> cd B2
>>> svn merge $REPOS/test1/branches/B1
>> --- Merging r167502 through r167505 into '.':
>>     C D1/D2/A
>> --- Recording mergeinfo for merge of r167502 through r167505 into '.':
>>   U  .
>> Summary of conflicts:
>>   Tree conflicts: 1
>>> svn status
>>   M      .
>> !    C D1/D2/A
>>       >  local delete, incoming edit upon merge
>> Summary of conflicts:
>>   Tree conflicts: 1
>>> ls D1/D2
>> B/
>>
>> Note: ls D1/D2 should say A/ B/ and there should be no tree conflict.
>>
>> In the real thing, the svn status explanation of the tree conflict is
>> usually "local add, incoming add upon merge."  I can't explain why the
>> simple test case says local delete instead.  I don't know if this matters.
>>
>> I can't say that Subversion is not behaving according to the documentation,
>> because the documentation at http://svnbook.red-bean.com/en/1.7/index.html
>> is extremely vague about this aspect of Subversion.  But it's not behaving
>> in a useful way.
>>
>> I hope this bug report is useful to you.  I can't wait for a fix, so I am
>> changing to a different strategy that does not depend on Subversion merge.
>
> Looks to me like you're using the merge command wrong.
>
> 1) You're using the sync merge format of the 1.7.x merge command,
> however the two branches you're merging (B1 and B2) have no common
> ancestor.  Since B2 was copied from T1 which you independently
> created, there's really no way for the command to know what to do.
> The fact that it does anything can be considered a bug, I'll note that
> on trunk right now the example you give above fails with the following
> error:
> [[[
> svn: E205000: Try 'svn help merge' for more information
> svn: E205000: Source and target must be different but related branches
> svn: E205000: Source and target have no common ancestor:
> 'file:///Users/breser/moon/repo/test1/branches/B1_at_head' and
> '._at_unspecified'
> ]]]
>
> Based on the behavior it seems that in 1.7.x the command is somehow
> trying to do a cherry-pick.
>
> 2) If what you're really intending to do here is a cherry-pick merge
> of the changes that were made on B1 (adding A and test1.txt) then you
> want to use the cherry-pick form:
> svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]
>
> Note that you need either -c or -r to specify the range of revisions.
>
> You might be tempted to try (assuming that r1 is where A was added)
> svn merge -r0:HEAD $REPOS/test1/branches/B1
>
> However, you'll still get the conflict:
> [[[
> $ svn merge -r0:HEAD $REPOS/test1/branches/B1
> --- Merging r2 through r5 into '.':
>   C D1/D2/A
> --- Recording mergeinfo for merge of r2 through r5 into '.':
> U  .
> Summary of conflicts:
>   Tree conflicts: 1
> ]]]
>
> and a similar status:
> [[[
> $ svn status
> M      .
> !    C D1/D2/A
>       >  local delete, incoming edit upon merge
> Summary of conflicts:
>   Tree conflicts: 1
> ]]]
>
> If I modify your example by adding the following two commands onto the front:
> [[[
> svn mkdir --parents $REPOS/test1/branches/B1/D1/D2 -m xx
> svn mkdir --parents $REPOS/test1/tags/T1/D1/D2 -m xx
> ]]]
>
> Which shifts the creation of your shared paths from the differences
> onto separate versions (and as such A isn't created until r3) then I
> get the following a successful merge by doing:
> [[[
> $ svn merge -r2:HEAD $REPOS/test1/branches/B1
> --- Merging r3 through r7 into '.':
> A    D1/D2/A
> A    D1/D2/A/test1.txt
> --- Recording mergeinfo for merge of r3 through r7 into '.':
> U  .
>
> $ svn status
> M      .
> A  +    D1/D2/A
>
> ]]]
>
> So what's happening here is that Subversion is conflicting on trying
> to create D1 and D2 because the creation of these directories are in
> the revision you're trying to merge.
>
> You can work around this by doing:
> svn mkdir D1/D2/A
>
> and then doing the merge as follows:
> svn merge -r0:HEAD $REPOS/test1/branches/B1/D1/D2/A D1/D2/A
>
> This works because the creation of those directories is now filtered
> out as happening below the path you're merging.
>
> It's hard to say if there's a bug here or not based on what you've
> said so far.  Your example use case seems so far from any realistic
> scenario that it's hard to envision what you're actually doing here.
>
> The local delete on the status notification is a bug of some sort in
> 1.7 but it doesn't happen in trunk and so many things have changed
> between the two it's really hard to be sure why.
>
> I can understand the argument that maybe we should handle this
> directory creation conflict a little more gracefully, but it does seem
> to be a legitimate tree conflict.
>
> Hopefully someone else can chime in with a little more knowledge of
> this code than I have.

I think the main point is you need to understand that

  "svn merge" doesn't calculate the union of two sets of nodes, it combines
  two sets of *changes* that have been made since a common starting point.

You might want to read the section on merging in The Book <http://svnbook.red-bean.com/>.

The other point of note is that the difference between getting an "add versus add" tree conflict in your real case and a "delete versus add" conflict in your test case points to your test case not reflecting your real case -- those are two very different scenarios.

- Julian
Received on 2013-01-15 14:55:57 CET

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.