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

Re: question relating SVN file/directory merges and tree conflicts

From: Stefan Sperling <stsp_at_elego.de>
Date: Tue, 21 Jul 2009 17:11:09 +0100

On Tue, Jul 21, 2009 at 08:17:59AM -0500, Rastetter, Martin wrote:
> Hi Subversion Adepts,
>
> In some situations we're at a loss to figure out ourselves how to perform merges between the trunk (or codelines) to branches.
> Even after reading following sources thoroughly :-)
> http://svnbook.red-bean.com/nightly/en/svn.branchmerge.summary.html
> and your explanations about merging structural changes http://www.nabble.com/resolving-tree-conflicts-td24459864.html
>
> Consider a tiny trunk like this: (svn version 1.6.3)
>
> svnadmin create /tmp/repo
> svn co file:///tmp/repo repo
> cd repo; svn mkdir branches tags trunk/foo --parents
> cd trunk/foo; echo "main source file" > hallo.c ; echo "main config file" > config ; echo "main doc file" > readme ; svn add *
> cd ../.. ; svn ci -m "mainline + basic structure"
> cd ..
>
> Then a feature branch of it:
>
> svn copy file:///tmp/repo/trunk file:///tmp/repo/branches/b1m -m "first branch from mainline"
> svn co file:///tmp/repo/branches/b1m feat ; cd feat
>
> Now do some reorganisation, refactoring, implementations or modifications of such a kind:
>
> svn mkdir src doc conf
> svn mv foo/hallo.c src ; echo "new features added" >> src/hallo.c
> svn mv foo/config conf/feature.conf ; echo "feature config replaces main config" > conf/feature.conf
> svn mv foo/readme doc/readme.txt ; echo "additional options" >> doc/readme.txt
> svn rm foo
> svn ci -m "features now developed in branch b1m"
>
> Q1 : despite the files below foo have been marked deleted, they'd been again marked as deleted by "svn rm foo" command

Apparently Subversion does not check whether an item has already been
deleted when recursively deleting a directory which contains the item.
It schedules the item already scheduled for deletion for deletion again,
overwriting working copy meta data with the same value that was already
there in the process, and then notifies you about the redundant deletion
it just did. An interesting buglet, but that's totally harmless.

> Meanwhile trunk development proceeded:
>
> cd .. ; svn co file:///tmp/repo/trunk main ; cd main
> echo "more main config" >> foo/config ; echo "more main code" >> foo/hallo.c ; echo "more main doc" >> foo/readme
> svn mv foo/readme .
> svn ci -m "mainline changed"
>
> This should now be merged into the branch based on this mainline - but that's quite a bit painful :-)

Yeah the branches have already diverged in many ways.

You have textual edits in all files on both sides.
You have moved all files on the feature branch.
You have moved one file on trunk.

If you have read the thread you were linking to all the way,
you will have noted that I said in that thread:
"Don't do tree reorgs on isolated branches."

Seriously. Subversion is NOT ClearCase.
You can't just move things around everywhere and expect
them to fall back into place without major effort on your part.
Change your way of working now before it is too late.
If you need to refactor, the whole team needs to know about it.
Ideally, you'd merge all branches into trunk first, then refactor,
then branch again. But of course that's not always possible.

Right now, Subversion has a limited amount of tree conflict *detection*.
Detection just means detection. There is no magic in place that
will resolve things for you, none whatsoever. And the detection even
raises false positives.

Eventually, Subversion will get better at this. The detection is
just the first step towards a better solution. But right now,
Subversion forces you to work within certain restrictions if you
want to keep your sanity. In this way it does not differ much
from CVS, expect that CVS enforces even more restrictions.

I am assuming that you are doing the following merge from
trunk to the feature branch b1m:

> svn merge ^/trunk --depth infinity --dry-run
> --- Merging r2 through r4 into '.':
> C foo
> A readme
> Summary of conflicts:
> Tree conflicts: 1
>
> Q2: why readme appears here ? - it should be related to doc/readme.txt instead - at least the log reveals their relationship

You've added /trunk/readme in r4 by running

  svn mv foo/readme .

before committing.

> svn log -v | grep readme ; echo "-----" ; svn log ^/trunk -v | grep readme
> A /branches/b1m/doc/readme.txt (from /branches/b1m/foo/readme:2)
> A /trunk/foo/readme
> -----
> D /trunk/foo/readme
> A /trunk/readme (from /trunk/foo/readme:3)
Here it shows in the log ^^^^

So this should not surprise you. I guess you have already lost track
of the changes you have made in your own example :)
Tree changes are complex stuff.

> A /trunk/foo/readme
>
> Looking at this, a merge at this level might not lead to the result the developer is aiming for.

You have made the tree structure incompatible.
Subversion's merge algorithm assumes that they are equal.

> Thus stepping into the directories below ....

Don't. You can't avoid the tree conflict by merging into subdirectories.
Since you've already moved things around, and you've already been told
that the merge will cause a tree conflict, fix the tree conflict problem
now by making the directory structures match up on both sides of the merge.

>
> cd src
> svn merge ^/trunk/foo --depth files --dry-run
> --- Merging r2 through r4 into 'hallo.c':
> C hallo.c
> --- Merging r2 through r4 into '.':
> C config
> C readme
> Summary of conflicts:
> Text conflicts: 1
> Tree conflicts: 2
>
> That looks feasible, thus going forward whith the merge ...

No, stop.... you haven't solved the tree conflict yet.
Fix it first!

> svn merge ^/trunk/foo --depth files (perform the file merge on hallo.c eg. via edit & resolve)
> svn resolve --accept mine-full --depth files .

It does not help to resolve text conflicts right now.
Tree conflicts are more important.
 
> Next directory ....
>
> cd ../conf
> svn merge ^/trunk/foo --depth files --dry-run
> --- Merging r2 through r4 into '.':
> C hallo.c
> C config
> C readme
> Summary of conflicts:
> Tree conflicts: 3
>
> Q3: why feature.conf isn't going to be merged with its ancestor config, but encountering a tree conflict instead

Because the tree structures on the branches don't match.

> Q4: how a new developer becomes informed that conf/feature.conf in this branch originally stems from foo/config from the trunk without reading the complete log

You have to look at the log.
That's the only way to get this information if you are not
aware of refactoring other people are doing.

$ svn log -v --stop-on-copy file:///tmp/repo/branches/b1m/conf/feature.conf
------------------------------------------------------------------------
r3 | stsp | 2009-07-21 16:42:34 +0100 (Tue, 21 Jul 2009) | 1 line
Changed paths:
   A /branches/b1m/conf
   A /branches/b1m/conf/feature.conf (from /branches/b1m/foo/config:2)
                                  Here it tells you ^^^^^^^^^^^^^

   A /branches/b1m/doc
   A /branches/b1m/doc/readme.txt (from /branches/b1m/foo/readme:2)
   D /branches/b1m/foo
   A /branches/b1m/src
   A /branches/b1m/src/hallo.c (from /branches/b1m/foo/hallo.c:2)

features now developed in branch b1m
------------------------------------------------------------------------

> svn log -r4 -v ^/trunk/foo
> r4 | mrastett | 2009-07-21 09:49:10 +0200 (Tue, 21 Jul 2009) | 1 line
> Changed paths:
> M /trunk/foo/config
> M /trunk/foo/hallo.c
> D /trunk/foo/readme
> A /trunk/readme (from /trunk/foo/readme:3)
>
> svn merge -c4 ^/trunk/foo --depth files --dry-run
> --- Merging r4 into '.':
> C hallo.c
> C config
> C readme
> Summary of conflicts:
> Tree conflicts: 3
>
> Q5: why running into the same merge results/problems, if focussing only on the changes made in the files in commit 4

Because those files don't exist on the branch anymore.
You have moved them around. Subversion just realises that something
must be wrong, but it is not smart enough to know what is wrong.
It just tells you "Tree conflicts: 3" which means "Wait... do you really
know what you are doing in this merge? Cause I can't figure it out, and
it looks like you've moved something around so that I can't merge anymore..."

> Possible consequence could be that a developer who knows about the details about the mainline will remember the rename and perform a file merge between feature.conf and config. Whereas a developer who only want to integrate new data from the trunk has to ask around for advice or receives unwanted files.

The developer has to stop doing any merges in face of a tree
conflict. I'm repeating myself, but the best thing to do is
to forget about merging the branches right now, and figuring
out why their tree structures don't match up, making them match
up, and then trying to merge again.
You may still get tree conflicts flagged even after making the
tree structures match up, but they will likely be false positives,
unless you made a mistake matching up the trees.

> svn merge -c4 ^/trunk/foo --depth files --accept theirs-conflict .
> --- Merging r4 into '.':
> C hallo.c
> C config
> C readme
> Summary of conflicts:
> Tree conflicts: 3
>
> svn status -v
> M 4 3 mrastett .
> ! C readme
> > local delete, incoming delete upon merge
> ! C hallo.c
> > local missing, incoming edit upon merge
> ! C config
> > local missing, incoming edit upon merge
> M 4 3 mrastett feature.conf
>
> But no changes yet. I'd reckoned that I received some files from the trunk automatically, because I asked to merge on conflict with the trunk files.
> Q6: why not?

You are not getting any changes since the files are already marked
as tree conflict victims, and Subversion will skip all such items
during the merge.
  
> svn resolve --accept theirs-full readme
> Resolved conflicted state of 'readme'
>
> svn resolve --accept theirs-full hallo.c
> Resolved conflicted state of 'hallo.c'
>
> svn resolve --accept theirs-full config
> Resolved conflicted state of 'config'
>
> Although the file feature.conf had been tagged as modified, an I committed it back ...
>
> svn log -v ^/branches
> ------------------------------------------------------------------------
> r5 | mrastett | 2009-07-21 13:04:03 +0200 (Tue, 21 Jul 2009) | 1 line
> Changed paths:
> M /branches/b1m/conf
> M /branches/b1m/conf/feature.conf
>
> .... it still hasn't been updated, modified or replaced by the trunk data.
>
> svn diff ^/trunk/foo/config_at_4 ^/branches/b1m/conf/feature.conf
> Index: config
> ===================================================================
> --- config (.../trunk/foo/config) (revision 4)
> +++ config (.../branches/b1m/conf/feature.conf) (revision 5)
> @@ -1,2 +1 @@
> main config file
> -more main config
>
> Property changes on: config
> ___________________________________________________________________
> Added: svn:mergeinfo
> Merged /trunk/foo/feature.conf:r2-4
>
> Q7: why these differences remain?

That's interesting, but I can't explain it off the top of my head.
It probably should have been merged.

Then again, you were doing quite insane merges there, and the
directory you were merging into contained tree conflict victims,
which should not prevent a non-victim from receiving changes,
but maybe there's a bug which causes this.

> Looking at the mergeinfo property leads to more questions ....
>
> cd ..
> svn pg svn:mergeinfo . -R
> conf/feature.conf - /trunk/foo/feature.conf:2-5
> conf - /trunk/foo:2-5*
> src - /trunk/foo:2-4*
> src/hallo.c - /trunk/foo/hallo.c:2-4
>
> Q8: there is no /trunk/foo/feature.conf which could have been merged, it ought to be foo/config instead - where does this come from?

That certainly looks like a bug on the surface :)
I have asked Paul to look take a look at this.

> My conjecture is that I make something basically wrong with directory merging. As long as the directory structure and filenames aren't touched, subversion works satisfactory and predictable. But in all other cases it causes quite a lot of efforts to track down which changes in which file belong to another one.
  
Yes. And the answer right now is still: Don't expect Subversion to
be smart enough to do such merges. We have added warning signals
(tree conflict detection) to let you know when there *might* be a problem
with mismatching tree structures (which is the case in your example),
so that you can fix that up and then try the merge again.
There has been no change in the way Subversion's merge algorithm works!

> Could you please help me or direct to a resource which describes best practices handling structural changes and merging in subversion.

I hope I have given you enough answers in this email.
Feel free to ask more questions if something is still unclear.

Stefan
Received on 2009-07-21 18:12:32 CEST

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