subversion-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Kevin Cathcart <>
Subject Bugs? with "automatic" merge and sparse working copies
Date Thu, 15 May 2014 20:25:29 GMT
I've found what really looks to be a buggy edge case with
the automatic merge feature of 1.8.X which can occur if:
* a partial (reintegrate-like) merge into a sparse working 
  copy is committed
* and then immediately followed by merging the same branch again
 into a full working copy.

By a partial merge I specifically mean that the sparse working
copy was missing a target of the merge.

Here is a diagram of the branching in question:

        3-X4------6-+---+   ^/branch/some_feature
       /         /   &   \
  1-2-+------Y5-+-----7---8 ^/trunk

(Obviously the ampersand indicates the partial merge due
to sparse WC. Also two commits are showing a name along
with a revision number).

With that particular pattern I see two anomalies
that do not occur if the order of commits X and Y are

        3----X5---6-+---+   ^/branch/some_feature
       /         /   &   \
  1-2-+---Y4----+-----7---8 ^/trunk

As for what I see, if the commented-out line in the script below
is left commented-out, then I get what appears to be a bogus tree
conflict during the merge to create r8.

If you uncomment the line, the merge occurs perfectly with no
conflicts, except that something weird happens with the mergeinfo.
I'll describe that in more detail below the script.

Since commits 4 and 5 are in parallel branches, the order of them
really should not make a difference, which is why I'm pretty
sure this is a bug.


svnadmin create repo
svn mkdir $url/B1 ^/trunk ^/branch -m "create structure" 

svn co $url/trunk wc
cd wc

mkdir A 
#echo 1 >B.txt
echo 1 >C.txt
echo 1 > A/D.txt
svn add *
svn ci -m "Simulate some development" 

svn cp ^/trunk ^/branch/some_feature -m "Create feature branch"

svn sw ^/branch/some_feature
echo 2 >> A/D.txt
echo 2 >> B.txt
sed -i '1i 0' C.txt #prepend a 0 line
svn add --force B.txt
svn ci -m "Simulate some development on the branch".

svn sw ^/trunk
echo 2 >> C.txt
sed -i '1i 0' C.txt #prepend a 0 line
svn ci -m "Simulate concurrent trunk changes".

svn sw ^/branch/some_feature
svn merge ^/trunk
svn ci -m "Sync Trunk to branch"

#set up a sparse trunk checkout
svn sw ^/trunk
svn up --set-depth exclude A
#merge into the sparse trunk checkout
svn merge ^/branch/some_feature
svn ci -m "Partial merge due to sparse checkout" #r7

#create clean full checkout
rm -r *
svn revert -R .
svn up --set-depth infinity
svn merge ^/branch/some_feature
svn ci -m "Re-Merge"


Now with the line B uncommented everything works
as expected, without any attempt to double merge 
the changes from either the trunk or branch, which
further supports my contention that the tree conflict
is bogus.

But for the mergeinfo issue I mentioned, look closely at
the output from the last merge:

$ svn merge ^/branch/some_feature
--- Merging differences between repository URLs into '.':
U    A/C.txt
--- Recording mergeinfo for merge between repository URLs into '.':
--- Eliding mergeinfo from 'A':
 U   A
--- Eliding mergeinfo from 'A':
 U   A
 U   .
 U   A

Notice that the properties of the directory named A got changed
3 different times. 

Lets look at the mergeinfo for revision 7:

$ svn pg svn:mergeinfo ^/trunk@7
$ svn pg svn:mergeinfo ^/trunk/A@7
$ svn pg svn:mergeinfo ^/trunk/A/D.txt@7
$ svn pg svn:mergeinfo ^/trunk/B.txt@7
$ svn pg svn:mergeinfo ^/trunk/C.txt@7

That is obviously correct. The mergeinfo must be non-inheriting
because A was not merged, and it must be duplicated onto B.txt
and C.txt because they were merged.

Now lets look at what happened with revision 8:

$ svn pg svn:mergeinfo ^/trunk@8
$ svn pg svn:mergeinfo ^/trunk/A@8
$ svn pg svn:mergeinfo ^/trunk/A/D.txt@8
$ svn pg svn:mergeinfo ^/trunk/B.txt@8
$ svn pg svn:mergeinfo ^/trunk/C.txt@8

Ok, so it did the right thing with the trunk, but why on
earth did it add explicit mergeinfo to ^/trunk/A? According
to the output I quoted earlier in this message, it tried
to elide that mergeinfo twice. 

It gets worse. The only reason that things work if you reverse
the order of commit X and Y is due to what looks like another bug.
SVN gets confused as to the direction of the last full merge
which was in fact from trunk to branch. This causes it to run
a sync-like merge on what happens to be precisely the correct
commits to avoid double merging changes that originated on the

To support that assertion: Try swapping the order of the X and
Y chunks in the script, and commenting out the last merge
and commit. Now in the working copy:

$ svn mergeinfo ^/branch/some_feature
    youngest common ancestor
    |         last full merge
    |         |        tip of branch
    |         |        |         repository path

    2         4        7
    |         |        |
       --| |------------         branch/some_feature
      /        \
     /          \
  -------| |------------         trunk

Hmm.. If X and Y are reversed then Y is commit
number 4. Commit Y is a commit on the trunk,
that was merged into the branch. That diagram
has the merge backwards!

Let me paste the diagram for commit Y before X
again for reference:

        3----X5---6-+---+   ^/branch/some_feature
       /         /   &   \
  1-2-+---Y4----+-----7---8 ^/trunk

Ok. Try running the merge anyway:

$ svn merge ^/branch/some_feature
--- Merging r5 through r6 into 'A':
U    A/D.txt
--- Recording mergeinfo for merge of r3 through r7 into '.':
 U   .
--- Recording mergeinfo for merge of r3 through r7 into 'A':
 G   A
--- Eliding mergeinfo from 'A':
 U   A

Notice how it references specific revision numbers?
That only happens in a sync-like merge.

What s going on here? Am I fundamentally misunderstanding 
something or are these really bugs? 

Note: The above was tested with 1.8.8, 1.8.9 (prior to announcement),
and build of trunk from May 13 2014.

View raw message