Subversion 1.6.0 and Tree Conflicts

Most Subversion users are familiar with text conflicts. The classic case: You have a locally edited file in your working copy, an svn update brings a change to the same file from the repository, that incoming change cannot be merged cleanly into your local change, and the result is a text conflict. Subversion 1.6.0 expands this concept to cover conflicts at the directory level, e.g. you locally delete a file then an update tries to bring a text change down on that file. These new types of conflicts are called tree conflicts.

Let’s look at a simple example of what you can expect with tree conflicts, but first we’ll look at the “old” behavior under Subversion 1.5.6.

Like text conflicts, tree conflicts can occur during updates, switches, or merges (and technically you can get them on checkouts too). In this example we will look at a merge from our ‘trunk’ into our current working directory which is a working copy for a branch of trunk.


In our working copy we have a file with some text edits:

1.5.6> svn stM notesobliterateobliterate-functional-spec.txt

Checking what is available to be merged from trunk we find r36680:

1.5.6> svn mergeinfo --show-revs eligible %URL%/trunk .r36680

Consulting the log for r36680 we see that Barry renamed the very file that we have local edits on:

1.5.6> svn log -v -r36680 %URL%------------------------------------------------------------------------r36680 | Barry | 2009-03-19 11:21:25 -0400 (Thu, 19 Mar 2009) | 1 lineChanged paths:A /trunk/notes/obliterate/obliterate-func-spec.txt (from/trunk/notes/obliterate/obliterate-functional-spec.txt:36679)D /trunk/notes/obliterate/obliterate-functional-spec.txt
Just making a file name a bit shorter------------------------------------------------------------------------

Recall that Subversion performs renames as a combination of a copy and a delete. When we merge r36680 the addition half of the rename occurs, but the deletion half does not because the file to be deleted has local modifications and Subversion generally endeavors to not remove unversioned modifications:

1.5.6> svn merge %URL%/trunk . -c36680--- Merging r36680 into '.':A notesobliterateobliterate-func-spec.txtSkipped 'notesobliterateobliterate-functional-spec.txt'

After the merge we are left with two versioned copies of our spec file, the one added by Barry as part of the rename and our original local edit. We could commit this as-is, but it is quite unlikely this is what we really want. Getting the correct result in this case, namely our text edits in the renamed file, requires jumping through a few hoops due to Subversion’s method of treating renames as separate copy and delete actions:

First we need to copy our modified file onto its new location with an OS copy:

1.5.6> copy notesobliterateobliterate-functional-spec.txt notesobliterateobliterate-func-spec.txtOverwrite notesobliterateobliterate-func-spec.txt? (Yes/No/All): y 1 file(s) copied.

Then use Subversion to revert our original locally modified file:

1.5.6> svn revert notesobliterateobliterate-functional-spec.txtReverted 'notesobliterateobliterate-functional-spec.txt'

Which now allows us to delete it:

Alternatively you could simply use the –force option with delete and combine the first two steps. IMHO that is a bad habit though, as it is a good way to wipe out all your local changes when you don’t mean to. Recall how I said Subversion endeavors preserve unversioned local modifications? svn delete –force is one of the cases outside of the revert subcommand that does just that, so use with caution.

1.5.6> svn del notesobliterateobliterate-functional-spec.txtD notesobliterateobliterate-functional-spec.txt

Leaving us with what we likely want, ‘notesobliterateobliterate-func-spec.txt’ added but with our original local changes.

1.5.6> svn st M .D notesobliterateobliterate-functional-spec.txtA + notesobliterateobliterate-func-spec.txt

Now in the above example it is fairly obvious what happened. But what if we were merging in hundreds of revisions and changing hundreds of paths? It would be quite easy to miss those ‘Skipped’ messages and commit the merge. Our mistake might not be caught until much later.


Now let’s look at what happens with tree conflict handling in 1.6.0. Given the same starting branch working copy:

1.6.0> svn stM notesobliterateobliterate-functional-spec.txt

We perform the same merge. Notice the first significant change wrought by tree conflicts, in the notifications we see a tree conflict reported rather than a skip:

1.6.0> svn merge %URL%/trunk . -c36680--- Merging r36680 into '.':A notesobliterateobliterate-func-spec.txtC notesobliterateobliterate-functional-spec.txtSummary of conflicts: Tree conflicts: 1

Checking the status of our working copy we see a second difference compared with 1.5.6. The addition half of the rename occurs as before, but now we see a tree conflict reported on ‘notesobliterateobliterate-functional-spec.txt’ (the ‘C’ in the 7th column). Also, there is the additional information about the nature of the tree conflict, specifically that we have a’local edit’ with an ‘incoming delete’ when we performed a ‘merge’.

1.6.0> svn st M .M C notesobliterateobliterate-functional-spec.txtA + notesobliterateobliterate-func-spec.txt

Let’s assume for a moment we don’t care about the tree conflict and want to commit anyway. Here is a third significant change, Subversion will not allow you to commit a working copy with unresolved tree conflicts:

1.6.0> svn ci -m "I don't care what happened! Commit away" .svn: Commit failed (details follow):svn: Aborting commit: 'C:SVN1.6.0.WCmy_branch_WCnotesobliterateobliterate-functional-spec.txt' remains in conflict

To commit this merge we need to decide what we want. If for some reason we wanted both files to remain we could simply resolve and commit the change. Chances are we want to apply our changes to the new file. In that case we would resolve the conflict:

1.6.0> svn resolve --accept working -R .Resolved conflicted state of 'notesobliterateobliterate-functional-spec.txt'
1.6.0> svn st M .M notesobliterateobliterate-functional-spec.txtA + notesobliterateobliterate-func-spec.txt

Then follow the same steps we did in 1.5.6.

Note: In 1.6.0 the various –accept options to the svn resolve subcommand have no differing effects on tree conflicts. As far as tree conflicts go, the result is the same as if you used the deprecated svn resolved command. Only with traditional text conflicts does the –accept option matter.

It is worth remembering that the concept of tree conflicts is not new. You could have a tree conflict in any version of Subversion prior to 1.6. The 1.5.6 example above is a “tree conflict”, it simply isn’t flagged as such nor handled in a very useful way. 1.6.0’s tree conflict features are primarily about identifying tree conflicts and preventing them from getting into the repository without first being resolved.

As with any new feature, more remains to be done in future releases. In this case making command line resolution of tree conflicts easier is a high priority. If you are a user of the Eclipse based Collabnet Desktop however, then you are in luck. The 1.8 release of the Desktop, scheduled for March of 2009, includes a host of tree conflict resolution features. These features do much of the “heavy lifting” of tree conflict resolution for you. In our previous example you could easily merge your local changes from ‘obliterate-functional-spec.txt’ to ‘obliterate-func-spec.txt’ and remove the former from version control and from disk, all via one dialog. So if you are interested in 1.6 for its tree conflict features, but are not using the Collabnet Desktop, it might be a good time to check it out.

Paul Burba

Paul is a committer on the Apache Software Foundation's Subversion project and has worked on Subversion for the past nine years. He works as a software engineer for Collabnet from his home in New Hampshire and when not coding he can usually be found skiing with his nephews, mountain biking with friends, or traveling with his wife. Some time in the distant past Paul graduated from the University of New Hampshire with a degree in Business. Somewhat more recently he obtained a masters in computer science from Boston University.

Posted in Subversion
7 comments on “Subversion 1.6.0 and Tree Conflicts
  1. Matt Doar says:

    This is good, clear explanation of current and future behavior, thank you.
    It’s also a good explanation of why you always want to do merges into local copies with no modifications. My working practice is to run “svn diff” before any merge to avoid these problems. Of course, there are other kinds of tree conflicts that I hope svn 1.6 handles more cleanly. Any chance of a follow-on article about those?
    >Recall that Subversion performs renames as a combination of a copy and a delete.
    This might be clearer if written as “Recall that Subversion performs renames as a combination of an addition and a delete.” since the next sentence talks about the “addition half”

  2. Paul T Burba says:

    > Of course, there are other kinds of tree conflicts that I
    > hope svn 1.6 handles more cleanly. Any chance of a follow-on
    > article about those?
    Can’t commit to that immediately, but I don’t see why not, particularly if there is interest. In the meantime be sure to check out the Subversion Book,, if you haven’t already. There is a section in the nightly build for 1.6 on tree conflicts.
    >> Recall that Subversion performs renames as a combination of a copy and a delete.
    > This might be clearer if written as “Recall that Subversion performs
    > renames as a combination of an addition and a delete.” since the next
    > sentence talks about the “addition half”
    Actually svn move/rename really is equivalent to svn copy SRC DST followed by svn delete SRC. You are correct in a sense about the addition, since any copy done within the working copy implies a path or paths scheduled for addition; in this case DST would be scheduled for addition with history.
    Possibly the statement “the addition half of the rename occurs” is the problem here. It is equally accurate in this example to say “the addition-with-history *component* of the rename occurs”…if that makes it a bit clearer.

  3. Robz says:

    What happens when a file is deleted in a branch and also deleted on the trunk? Then merge up to the branch and I have an error resolving a conflict on a file that doesn’t exist.
    “Error: Aborting commit: ‘C:SVNsource-branchsrcitem.vssscc’ remains in conflict ”
    Call me an idiot, but I can’t resolve a conflict on a non-existent file. TortoiseSVN and command line won’t let me do anything with the tree conflict either, it doesn’t think there is anything to resolve. It just won’t let me commit.

  4. pburba says:

    Hi Rob,
    I just wrote a long response, but then something occurred to me: What version of TortoiseSVN are you using? I haven’t downgraded to check exactly what happens, but if you are not using TortoiseSVN 1.6+ then you will definitely have problems with tree conflicts if you are using a 1.6+ command line client.
    Of course if you *are* using TortoiseSVN 1.6+ then let me know and we can go from there.

  5. Robz says:

    I am using TortoiseSVN 1.6.0 build 15855
    The SVN server is not 1.6 yet. Would that cause any issues?

  6. Robz says:

    Oh wait. Rereading… I misread b/c I use TortoiseSVN only as a client tool. We use VirtualSVN Server in the back end and it is not upgraded.
    The link you pointed me to mentioned I would have some issues.
    This is most likely the culprit.
    Thanks man! Much appreciated.

  7. Nathan says:

    This is a very good article about SVN tree conflicts. Very clear and well explained.

3 Pings/Trackbacks for "Subversion 1.6.0 and Tree Conflicts"
  1. […] CollabNet’s Subversion Blog has a great article on Tree Conflicts. […]

  2. […] CollabNet’s Subversion Blog has a great article on Tree Conflicts. […]

Leave a Reply

Your email address will not be published. Required fields are marked *