Reputation: 4344
Anytime I try to merge in SVN I get piles of tree conflicts. Well, in the case of this sample script just one, but still.
#!/bin/bash
svnadmin create repo
svn checkout file://`pwd`/repo wc
cd wc
mkdir trunk branches
svn add trunk branches
svn commit -m 'created trunk and branches'
echo red > trunk/colors
svn add trunk/colors
svn commit trunk -m 'created trunk/colors with red inside'
svn copy trunk branches/a
svn commit branches/a -m 'created branches/a'
echo green >> trunk/colors
svn commit trunk -m 'added green to trunk/colors'
echo blue >> branches/a/colors
svn commit branches/a -m 'added blue to branches/a/colors'
svn update
svn merge ^/trunk branches/a
My result is:
Checked out revision 0.
A trunk
A branches
Adding branches
Adding trunk
Committed revision 1.
A trunk/colors
Adding trunk/colors
Transmitting file data .
Committed revision 2.
A branches/a
Adding branches/a
Adding branches/a/colors
Committed revision 3.
Sending trunk/colors
Transmitting file data .
Committed revision 4.
Sending branches/a/colors
Transmitting file data .
Committed revision 5.
Updating '.':
At revision 5.
--- Merging r2 through r5 into 'branches/a':
C branches/a/colors
--- Recording mergeinfo for merge of r2 through r5 into 'branches/a':
U branches/a
Summary of conflicts:
Tree conflicts: 1
I know that SVN isn't known for being merge friendly, but I have to assume that in this case it is my fault somehow. Thanks for any pointers.
Upvotes: 2
Views: 3878
Reputation: 106
The problem you've encountered is not due to the 'local copy'[1] per se. The problem lies in the fact that, in revision 3, you copy a mixed-revision working copy (http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.basic.in-action.mixedrevs).
If we run your script right up until you copy 'trunk' to 'branches/a' we find that we have a mixed-revision working copy:
>svn st -v
0 0 ? .
1 1 pburba branches
1 1 pburba trunk
2 2 pburba trunk\colors
So when you copy 'trunk' to 'branches/a', you are actually copying trunk@1 and trunk/colors@2:
>svn copy trunk branches\a
A branches\a
>svn st -v
0 0 ? .
1 1 pburba branches
A + - 1 pburba branches\a
A + - 2 pburba branches\a\colors
1 1 pburba trunk
2 2 pburba trunk\colors
>svn ci -m "Copy mixed-rev trunk"
Adding branches\a
Adding branches\a\colors
Committed revision 3.
We can see this most clearly when looking at the verbose log for r3:
>svn log -v -r3
------------------------------------------------------------------------
r3 | pburba | 2013-03-11 15:31:23 -0400 (Mon, 11 Mar 2013) | 1 line
Changed paths:
A /branches/a (from /trunk:1)
A /branches/a/colors (from /trunk/colors:2)
Copy mixed-rev trunk
------------------------------------------------------------------------
Skipping ahead to the problematic merge, we have a "clean" working copy with no mixed-revisions and no local modifications. So far so good:
>svn st -v
5 5 pburba .
5 5 pburba branches
5 5 pburba branches\a
5 5 pburba branches\a\colors
5 4 pburba trunk
5 4 pburba trunk\colors
But if we use the svn mergeinfo command to preview what revisions will be merged we notice that revsion 2 is eligible:
>svn mergeinfo --show-revs eligible ^/trunk branches\a
r2
r4
But wait, revison 2 is the addition of 'colors',
>svn log -v -r2
------------------------------------------------------------------------
r2 | pburba | 2013-03-11 15:43:52 -0400 (Mon, 11 Mar 2013) | 1 line
Changed paths:
A /trunk/colors
created trunk/colors with red inside
------------------------------------------------------------------------
and we already copied that when we created the branch in revision 3! So why is Subversion trying to merge it again? The reason comes back to the mixed-revision WC-to-WC copy we made. The merge target 'branch/a' knows that it was copied from trunk@1, prior to the addition of 'trunk/colors'. So the merge thinks that revision 2 hasn't been been merged to branch/a and tries to merge this change, adding 'colors' into a 'a' where a file of the same name already exists, which results in a tree conflict:
>svn merge ^/trunk branches\a
--- Merging r2 through r5 into 'branches\a':
C branches\a\colors
--- Recording mergeinfo for merge of r2 through r5 into 'branches\a':
U branches\a
Summary of conflicts:
Tree conflicts: 1
>svn st
M branches\a
C branches\a\colors
> local file obstruction, incoming file add upon merge
Summary of conflicts:
Tree conflicts: 1
So we tricked Subversion with our mixed-revision WC-to-WC copy, which effectively brought revision 2 to the branch during the copy. So why can't Subversion detect this and skip revision 2? In this case we conceivably could do that, but what if revision 2 included other changes to 'trunk'? e.g.:
>svn log -v -r2
------------------------------------------------------------------------
r2 | pburba | 2013-03-11 15:43:52 -0400 (Mon, 11 Mar 2013) | 1 line
Changed paths:
A /trunk/colors
A /trunk/README
M /trunk
created trunk/colors with red inside, add a README file, and set the
svn:ignore property on trunk
------------------------------------------------------------------------
Subversion doesn't support merging part of a revision to a given path, it's either all-or-nothing.
~~~~~
So how to avoid this problem? As you've already discovered, doing a URL-to-URL copy solves it. Why? Because when the copy source is a URL, it is, by definition, at some uniform revision:
>svn log -v -r3
------------------------------------------------------------------------
r3 | pburba | 2013-03-11 16:02:59 -0400 (Mon, 11 Mar 2013) | 1 line
Changed paths:
A /branches/a (from /trunk:2)
Create branch 'a' from 'trunk' with a URL-to-URL copy.
------------------------------------------------------------------------
However you can also accomplish the same thing with a URL-to-WC copy:
svn commit trunk -m 'created trunk/colors with red inside'
-svn copy trunk branches/a
+svn copy ^/trunk branches/a
svn commit branches/a -m 'created branches/a'
Or simply by updating the WC prior to the WC-to-WC copy in your original script:
svn commit trunk -m 'created trunk/colors with red inside'
+svn update
svn copy trunk branches/a
svn commit branches/a -m 'created branches/a'
Your original solution, the URL-to-URL copy followed by an update, is the best option IMO. The update & WC-to-WC copy and the URL-to-WC copy both allow for the possibility that additional changes can be made to the copy before it is committed -- it's best that the revision in which a branch is created has no other changes besides the copy. That said, all of these option will work fine[2].
[1] In Subversion-speak we usually refer to this as a working-copy-to-working-copy copy, or WC-to-WC copy for short. Contrast this with URL-to-URL, URL-to-WC, or WC-to-URL copies. WC-to-URL copies are also vulnerable to the problem described above. See also 'svn copy --help'.
[2] Of course even using one of these three options still results in a text conflict, but that is expected since we made incompatible changes to the 'colors' file on both trunk and the branch after the branch was created.
>svn merge ^/trunk branches\a
--- Merging r3 through r5 into 'branches\a':
C branches\a\colors
--- Recording mergeinfo for merge of r3 through r5 into 'branches\a':
U branches\a
Summary of conflicts:
Text conflicts: 1
Conflict discovered in file 'branches\a\colors'.
Select: (p) postpone, (df) diff-full, (e) edit, (m) merge,
(mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p
>svn st
M branches\a
C branches\a\colors
? branches\a\colors.merge-left.r2
? branches\a\colors.merge-right.r5
? branches\a\colors.working
Summary of conflicts:
Text conflicts: 1
Upvotes: 9
Reputation: 401
The case you demonstrated should generate a normal conflict, it should not result in a tree conflict.
I suspect your use case is performing a subtree merge (notice the subtree paths in your svn merge command). All the mergeinfo stuff is only stored in the top level of your checkout
From 'svn help merge':
SOURCE specifies the branch from where the changes will be pulled, and
TARGET_WCPATH specifies a working copy of the target branch to which
the changes will be applied. Normally SOURCE and TARGET_WCPATH should
each correspond to the root of a branch. (If you want to merge only a
subtree, then the subtree path must be included in both SOURCE and
TARGET_WCPATH; this is discouraged, to avoid subtree mergeinfo.)
The following snippet shows the functionality I believe you are looking for while avoiding the subtree merge. It uses the 'svn switch' to avoid the subtree merge and multiple WCs
export SVN_REPO=~/svntest
cd $SVN_REPO
rm -rf $SVN_REPO/*
svnadmin create repo
svn mkdir file:///$SVN_REPO/repo/trunk -m "created trunk"
svn mkdir file:///$SVN_REPO/repo/branches -m "created branches"
#
svn checkout file:///$SVN_REPO/repo/trunk wc
cd wc
echo red > colors
svn add colors
svn commit . -m 'created trunk/colors with red inside'
#
svn cp file:///$SVN_REPO/repo/trunk file:///$SVN_REPO/repo/branches/a -m 'created branches/a'
#
echo green >> colors
svn commit . -m 'added green to trunk/colors'
#
svn switch file:///$SVN_REPO/repo/branches/a .
echo blue >> colors
svn commit -m 'added blue to branches/a/colors'
svn update
svn merge file:///$SVN_REPO/repo/trunk
Results:
Committed revision 1.
SVN_REPO/repo/branches -m "created branches"
Committed revision 2.
SVN_REPO/repo/trunk wc
Checked out revision 2.
cd wc
echo red > colors
svn add colors
A colors
svn commit . -m 'created trunk/colors with red inside'
Adding colors
Transmitting file data .
Committed revision 3.
SVN_REPO/repo/branches/a -m 'created branches/a'
Committed revision 4.
echo green >> colors
svn commit . -m 'added green to trunk/colors'
Sending colors
Transmitting file data .
Committed revision 5.
SVN_REPO/repo/branches/a .
U colors
Updated to revision 5.
echo blue >> colors
svn commit -m 'added blue to branches/a/colors'
Sending colors
Transmitting file data .
Committed revision 6.
svn update
Updating '.':
At revision 6.
Conflict discovered in '/home/jbellamy/svntest/wc/colors'.
Select: (p) postpone, (df) diff-full, (e) edit,
(mc) mine-conflict, (tc) theirs-conflict,
(s) show all options: p
--- Merging r4 through r6 into '.':
C colors
--- Recording mergeinfo for merge of r4 through r6 into '.':
U .
Summary of conflicts:
Text conflicts: 1
svn diff
Index: .
===================================================================
--- . (revision 6)
+++ . (working copy)
Property changes on: .
___________________________________________________________________
Added: svn:mergeinfo
Merged /trunk:r4-6
Index: colors
===================================================================
--- colors (revision 6)
+++ colors (working copy)
@@ -1,2 +1,6 @@
red
+<<<<<<< .working
blue
+=======
+green
+>>>>>>> .merge-right.r6
Upvotes: 0
Reputation: 4344
It appears that the issue lies in the use of a local svn copy
rather than remote.
-svn copy trunk branches/a
-svn commit branches/a -m 'created branches/a'
+svn copy ^/trunk ^/branches/a -m 'server side copy from trunk to branches/a'
+svn update
While the SVN book says that the local copy "technique isn't recommended", it does not list this as a reason. Rather it simply talks about cheap server side copies, disk usage, time, etc.
Upvotes: 1