Resolving Tree Conflicts using Subclipse
One of the major new features in Subversion 1.6 was the introduction of tree conflicts. Subversion already had the concept of text conflicts for the contents of files, such as when you update and Subversion cannot automatically merge the changes from the repository with the local edits you made. It also had the concept of property conflicts, which are basically the same as text conflicts except for Subversion’s versioned properties. One of the major advancements in Subversion 1.0 was that it versioned entire trees, and not just files as most version control tools did to that point. So it was only natural that Subversion would add the ability to report on conflicts in the structure of your application — or tree conflicts.
An example of a tree conflict would be when you delete a file that someone else has made edits to. You are changing the structure of the “tree” by removing the file, but since someone else has edited the file, Subversion wants to alert you to this so that you can factor in those changes into your decision to delete the file. Tree conflicts, like all conflicts, are an alert mechanism so that the tool can tell you to take a closer look at something before you do it.
As part of the work of adding tree conflicts to Subversion in 1.6, CollabNet developed a GUI to manage and assist with the process of resolving tree conflicts. We built this feature into Subclipse which is part of our CollabNet Desktop – Eclipse Edition. The rest of this blog post will give a couple of example tree conflict scenarios and how to resolve those from Eclipse. This post will likely be helpful even if you use the command line or another GUI tool as the underlying concepts are all from Subversion itself.
Case 1: Simple Delete
The first example will be a simple delete of a folder containing several files. Imagine I have deleted a folder, which in this case was a Java package from the Eclipse Java perspective. I then choose to commit those changes to my repository as you can see in the following screenshot:
However, in this example, I made sure that my working copy was not fully up to date with the repository. When I commit, I get an error from Subversion letting me know that the folder I am committing is out of date with the repository. This essentially tells me that something beneath this folder has been committed since the version I have in my working copy. In my case, it was one of the files in the package. The commit will fail with the following dialog showing me the Out of Date error.
So the natural next step is to update my working copy to HEAD. Note, that since I am deleting a folder and we now know it is out of date, it is an absolute certainty that the update is going to create a tree conflict. If the folder was not out of date, the commit would have just succeeded without producing any conflicts, but since it is out of date that means that something has changed in a child of this folder and that is the definition of a tree conflict.
Sure enough, when I update you can see that a tree conflict is created.
Subclipse provides a UI to work with all of the tree conflicts in my project. I just right click on the project and choose Team > Show Tree Conflicts and that opens the SVN Tree Conflicts view with the information on all of the tree conflicts in my working copy. You can see that it is telling me that I deleted a folder locally and when I updated there was an incoming edit that it could not apply.
If I right-click on the item I can take the Resolve option to launch a dialog that will help me resolve the tree conflict. This will also show me more information about the conflict.
In this case, I know that I just want to delete the folder. I do not care that there were files modified by other users, but I could have taken options to look at the history of changes to this folder to make certain. In the Resolve dialog, I just check the option to Mark the conflict resolve. This option is just me telling Subversion that I have looked at the tree conflict and done whatever I needed to do to resolve it. In this case, there is nothing to do because I am just deleting the folder.
After I click Finish, Subversion removes the tree conflict in the working copy. I can take the commit option again and since the folder is now up to date in my working copy the commit will succeed.
Case 2: Rename (complex delete)
I am not going to repeat all of the dialogs in the process because they are basically the same ones I showed in the previous example. The commit failed because my working copy was out of date. I take the option to Update to HEAD and a tree conflict is produced. I take the option to Show Tree conflicts and I see the exact same UI as the previous example. A rename in Subversion is implemented as copy + delete. So the delete part of the rename is where you will receive a tree conflict. Conceptually, however, the way I want to resolve this tree conflict is different than the last example. I did not just delete the folder, I renamed it. So the edits that Subversion wanted to make to my working copy when I took the update option need to still happen. However, they need to happen to the files in the renamed folder. Unfortunately, this is not something Subversion can do for you automatically yet. The tree conflict at least lets you know there is something for you to do so that you do not lose those changes that you did not have in your working copy.
Fortunately, the GUI we have developed for Subclipse does help you in this situation. Look what happens when I take the Resolve option from the tree conflicts view this time:
The dialog looks very similar to the previous example, except Subclipse figured out that I renamed the folder and it is proposing that I merge the changes into that folder. This has the effect of automating the update of those files with the changes I did not get when I ran update. When I click Finish, this is what I can see in the SVN Console:
Subclipse automatically merged the incoming changes from the repository and it marked the tree conflict resolved. Note, in this case, the merge was able to merge the changes without creating any text conflicts in the working copy. If you are doing complex refactoring and you are pulling in a lot of changes, then it is likely you will get some text conflicts. You resolve those conflicts the same way you always do when update or merge produce conflicts. The important thing is that the missing changes have now been applied to your working copy.
Once I am happy with the results, I can just commit again and since my working copy is now up to date and I do not have any more unresolved tree conflicts the commit will succeed.
Hopefully this post sheds some light on the Subversion Tree Conflicts feature as well as point you in the right direction in terms of resolving those conflicts. Something to keep in mind is that when possible the best strategy is to avoid the tree conflicts altogether. This will not always be possible, especially when they are produced by merge. When tree conflicts are produced by Update, they often could have been avoided by Updating BEFORE you make the changes to the tree structure. In both of these examples, if I had simply updated my Eclipse project to HEAD revision before I deleted or renamed the folder then I could have avoided the conflict entirely. Now in a very active project this is not always going to be possible, but you are certainly giving yourself the best chance to avoid the conflicts if you update, make your changes, and commit. The further away from HEAD you allow your working copy to get the more likely it is that you will be out of date when you commit, and this is when you will get a tree conflict.