“Just Do Git” reads the shirt I received from Atlassian.
Git is great; it is the new hotness in the version control universe.
There really is no number two product, or at least that’s what you’d be lead to believe if you listen to its advocates. But if you work for a company or organization that’s been around for more than a decade, there’s a good chance that you have your code stored in Subversion (SVN) – which was the new hotness before Git.
The creator of Git, Linus Torvalds, has said that one of the reasons he created Git was because branching and merging in SVN is not good and can be downright painful. For a long time I agreed with him, but as of late I’ve come to discover that branching and merging in SVN can be quite easy and fairly painless if you play by the SVN rules. If you don’t play by SVN’s rules, then it becomes the nightmare Linus claimed it to be.
Branch, Trunk, Tag, What?
SVN repositories have a tree style organization. The root of your repo is the container that holds all the different versions of your code. Inside of the root is the trunk, which is the the container for the base version of your codebase. Also in the root are branches and tags, which holds copies/versions of the trunk. There are functional differences between branches and tags, which is outside of this post’s scope, but for now just know that they are copies of the trunk.
Creating a Branch
When you start developing a new feature for your codebase, it’s best to create a branch. This allows you to have a trunk that is still available to do bug fixes on without having to comment out all your feature development before you put them live.
You create a branch like this:
$ svn cp /path/to/repo/trunk
At its very core creating a branch is making a copy of the trunk and putting that code in the branches directory. SVN will store some metadata within the branch, for our purposes, the most important is the revision of the trunk the branch is based upon.
Now you can check out the branch to a local copy and start working:
svn co /path/to/repo/branches/new_branch_name/
Merging: Trunk to Branch
While you develop within your pristine new branch, theoretically there will be other things happening to the trunk of your project. You’ll want to keep your branch up to date with those changes. Failing to do this is the basis for the previously mentioned nightmare.
The way you keep your branch up to date is:
branch$ svn merge path/to/repo/trunk
It is necessary that you do this within the base of your local working copy of the branch. This merge will bring the changes from the trunk into your local working copy of the branch, but not on the SVN server. At this point you should check to make sure nothing was broken in the process. If you do this regularly enough, then there’s a very good chance things will be fine because the changes are only put into your local copy; you will need to do a commit from the branch to actually save them into the branch on SVN.
A single command will get that done for you:
branch$ svn commit -m ‘post merge commit’
This both saves the changes and updates the revision number of the trunk that the branch is based upon.
Merging: Branch to Trunk:
You’re done working on your new feature and now it’s time to bring that feature into the trunk of the project. The process of bringing a branch back into the trunk has multiple steps, each of which is important if you are nightmare averse.
First make sure you have an up to date working copy of the trunk in your workspace. If you have uncommitted changes to the trunk, do that now. If you don’t have a copy then check it out now.
Now go to your local copy of the branch and update it from the trunk again:
branch$ svn merge path/to/repo/trunk
Check that everything is still good and commit:
branch$ svn commit -m ‘final post merge commit’
Next go into your local working copy of the trunk and update it. Yes this is redundant, but for your own sanity just do it:
cd /path/to/working/copy/of/trunk && svn up
Now your branch is based upon the current revision of the trunk and your working copy of the trunk is the current revision of the trunk. All that SVN needs to do at this point is look at what’s different between the branch and the trunk and bring in those changes. The code to bring the branch in is:
trunk$ svn merge –reintegrate
Similar to when we merged from the trunk to the branch the changes are brought into your local working copy of the trunk not the actual trunk. Check to make sure everything works as expected and if it does then commit this change.
trunk$ svn commit -m ‘reintegration merge complete’
Your branch is now part of the trunk. At this point the branch has become useless. SVN essentially marks it as complete and you can’t merge to or from it anymore.
Not keeping the branch up to date with the trunk and/or not keeping your local copy of the trunk up to date with the trunk is what causes much of the pain merging in SVN. It becomes much more difficult for SVN to tell what’s new and what’s not when the branch and trunk are not on equal footing. This will end up in conflicts, lots of conflicts and a serious headache.
With a little forethought and a few preparatory steps, you can branch and merge from SVN without nightmares.
A more thorough explanation of merging in SVN can be found in the SVN Book