In this series, we’ve covered a lot of ground. We’ve looked at the basics of Subversion and talked about why you might want to use it for a writing project. We examined some of the advanced features, and how to dive into the history of your work. Then, we detailed how Subversion can be used for collaboration: the way locks help writers to own their ideas, how the log facilitates communication, and the way in which branches help to prepare drafts for review.
There is really only one thing left to talk about: conflicts, errors, and their resolution.
Conflicts: The Headaches of Version Control
Conflicts are incompatible changes made to the same file. They happen when someone modified one of the files you are working on and committed changes, or, when a file was moved, renamed, or deleted with changes still pending. They typically come up at the most inopportune of times, such as when you’re getting ready to head home for the day but want to commit the changes to your draft.
Here’s what happens: you’ve been working all day, maybe you’ve finished an important section needed by one of your co-authors. You’re excited to see your spouse and children, maybe you’ve got evening plans. But, when you try and commit your changes, you discover that you aren’t able to. Subversion tells you that other commits have been made and you need to update your files before you commit your changes. You run “SVN update,” which is successful … mostly. As you scan the log of the update, you see an error telling you that one of the files has a conflict, and you won’t be able to submit your revisions until the conflict has been resolved.
A “conflict” happens when incompatible changes are made to the same file. They have to be “resolved” by merging your changes with the most version recent of the file on the server.
When they happen, conflicts seem big and scary. They demand that you deal with them (since it’s impossible to submit additional commits until you have). But even so, they aren’t anything to be afraid of. Resolving most conflicts involves nothing more complicated than merging two files (the “trunk” version from from the repository and your local copy).
Indeed, you might even think of conflicts as a way of protecting your work. They prevent Subversion from doing mindless things. As an example, consider the screenshot below.
The file was moved to a new location in the repository, as part of a restructuring of the book it belonged to. However, this happened before I was able to commit changes to it. On the machine where I had made the changes, Subversion moved all of the files but kept my local copy of this one, protecting the work I’ve done. In this sense, I was very happy to have the conflict.
With that said, it is better to prevent conflicts through the use of locks and good communication. Event though they’re meant to prevent problems, they’re something of a pain (and they always come up at inopportune times). But when they do come up, knowing how to resolve them without losing your work is a very important skill.
Types of Conflicts
In general, there are two types of conflicts:
- File conflicts, which happen when incompatible changes were made to the same file.
- Tree conflicts, which happen when one person has made changes and another moves it before those are committed.
The conflict from above is a tree conflict.
The path that you take to resolving the conflict depends on what the conflict is and what you would like to do about it. For file conflicts, it’s usually pretty simple:
- compare your copy of the file to the version from repository
- find the differences between the two using a diff tool or another program
- save a new copy of the file with both sets of changes in-tact
For tree conflicts, it’s similar, but with one additional step:
- figure out what caused the conflict (whether the file was moved, deleted, or renamed)
- determine what files need to be compared
- make the comparisons and merge the differences
- save a new copy of the file with the changes in-tact
In both cases, the key is to compare your copy of the file to the version from the repository. From there, you know what has changed and can generate a new file with the changes from both.
To learn more about the conflict and begin to resolve it, you can click on the “Edit conflicts” link from the TortoiseSVN context menu.
If you are working with text files, this is usually pretty easy. You can right click on the file with the conflict (which will have a warning icon, as in the screenshot above), and then select “Edit conflicts” from the menu. TortoiseSVN will launch a merge tool, such as TortoiseMerge, which allows you to see what has changed and what has not.
Nearly all Subversion clients come with a merge tool, such as TortoiseMerge, which allows you to compare text files and see what has changed. These tools are helpful for resolving conflicts as they will allow you to incorporate changes from the repository file and from your local draft.
Mac OS X also includes a tool to merge differences, called DiffMerge. It works in much the same way as TortoiseMerge. It loads two text files side by side and you then choose the components that you wish to keep from both.
Using the merge tool, you then select which revisions you want to keep and those that you would like to discard. You can even go in and edit the conflict between the two files by hand. When you are finished, you can save the new file and mark it as “resolved.” You can then go about your business and commit your changes to the repository.
For programmers, this method of resolving conflicts works wonderfully, as they primarily work with text files. It’s less magical for those whose repositories are filled with Microsoft Word documents, images, SVG drawings, and other types of files.
Other (Binary) Types of Files
For other types of files, comparing the two documents and generating a list of changes is a little bit more involved than just using the built-in merge tool. It’s still nothing to be afraid of, though.
For every conflict, Subversion places three files in your directory. They are:
- filename.ext.mine: this is the file as it existed in your working copy before you made the update (and caused the conflict). It has your latest changes in it, all intact.
- filename.ext.rNEWREV: this is the file that was downloaded from the server during the update. It corresponds to the HEAD revision of the repository.
- filename.ext.rOLDREV: this is the file as it existed before you made your changes or it was updated in the repository.
filename.ext is the name of the file with the conflict.
These three files are used to merge the different versions and generate a new file without problems. Here’s how you do it:
- Rename the files. Instead of filename.ext.mine, the file extensions should come after the revision information: filename.mine.ext,
- Open two of the three files in a program that can compare them and make any required changes
- Save the new file with the changes over the copy with the conflict
Again, the most important step is number two, where you compare the two files and make any required changes. What makes this step different for binary files, though, is that you won’t always use the same program. You need to use one that is capable of comparing the file type.
If you are working with Word documents, then you will use Word to compare the two files and make the changes. If the files are Photoshop objects, you’ll use a program like ComparePSD. If you write with LyX, you’ll use LyX. Because the file formats are more complicated than simple text, you have to use a program that can read and parse them correctly. If not, you’ll make the conflict worse.
To get the changes you want, you also have to compare the right two versions of the file. To see how the latest version from the repository differs from the original, you compare filename.rOLDREV.ext and filename.rNEWREV.ext (remember that you’ve changed the names so that the program will read them correctly). To see how your version and the repository differ, you compare filename.mine.ext and filename.rNEWREV.ext.
In most cases, you will want to compare filename.mine.ext and filename.rNEWREV.ext.
When there is a conflict in a file that isn’t text based, you need to use a tool that is capable of making comparisons. That means, if you write with LyX, that you use LyX; Microsoft Word documents should be loaded in Word; Photoshop documents in ComparePSD; and so forth.
Using a separate program to directly compare the different versions of the conflict will create a new file with the differences you are interested in.
If you have a tree conflict with a binary file, the process is similar to resolving a file conflict. Except, you won’t have access to filename.ext.mine, filename.ext.rNEWVERSION, and filename.ext.rOLDVERSION. Instead, you will compare the version marked with the error and the version downloaded from repository.
If the file was moved, or renamed, Subversion can help you to find where it got moved to. If using TortoiseSVN, you can access this information by clicking on the “Check for Modifications” dialog. If the file was deleted in the repository, you will need to resolve the problem using the repository browser. In this case, it’s a good idea to move your (locally modified) version to another folder for safe keeping. After you restore the version in the repository (using the repo-browser), you can copy it back.
Marking a Conflict as Resolved
Once you’ve merged the new and old versions of the file, you’re ready to submit your commit to the repository and get back to your life … almost. There is just one last step, you have to mark the conflict as resolved. This is done by clicking on the (now merged) file, and selecting “Resolved…” from the context menu.
The last step required to resolve the conflict, commit your work, and return to life is to mark the commit as “resolved.”
Before marking a conflict as resolved, though, make sure that all of the problems have been fixed. When you click on “Resolved…”, it removes all of the other versions of the files (.rOLDVERSION, r.NEWVERSION, and .mine) and prepares it to be committed. If there is a conflict you haven’t mediated, then you might be in danger of losing your work.
Though they may look scary, conflicts are actually a good thing. They let you know when there is a problem with your project, provide a way to protect your work, and provide you with a way to merge your changes into those of others. The secret to successfully handling conflicts is not to be afraid of them. If a conflict happens after you update from the repository, or merge from a branch, just deal with it:
- figure out what caused the conflict
- compare the different files
- generate a new, merged copy
- mark it as resolved
- commit the changes
Then, you can get back to your life.