Most of you probably already know how to do branches with svn and subversion, but I haven’t been able to get off my butt to make it second nature until now.
I found myself needing to make a rather large set of changes (adding a python scripting daemon, 8+ weeks of work) to my working copy while still fixing bugs in the trunk. These changes were very experimental, and since we are nearing a release I didn’t want to clutter the trunk with it. Plus, a co-worker could save weeks of critical work on the release if he could start using our scripting engine for batch processing. batch processing requires C++ that has nothing to do with the release, and nothing to do with my daemon work.
So I find myself essentially supporting three features. The release code in the trunk is frozen, but I will want to commit my batch processing fixes as soon as the release is over. Unfortunately the release has dragged on for a couple of months and the trunk is still frozen. My daemon changes are finally stable and I’d like to start using that branch for script development to test it a bit before I merge the changes into the trunk. To make matters worse, I now need the changes from the batch processing in my daemon branch. I hope you follow me.
It looks like I need a branch of a branch:
trunk -> batch branch -> daemon branch
The daemon branch is experimental and so highly private, but includes all of the changes from the batch branch. This way, I can commit the changes from the batch branch first without cluttering up the commit with the enormous changes from the daemon branch. My co-workers test the code a little bit, I fix some bugs, then I go all in with the daemon code.
Whew, that’s a complicated situation!
The biggest problem was that I really sucked at merging and branching even with ONE branch, so I decided to finally dive in with both feet and learn the svn syntax. Turns out it’s actually quite easy.
If you want to get the changes from one revision to another, regardless of branch or trunk or whatever, you just do this:
$ svn diff -rLOWER:HIGHER
The key is that you can do this anywhere at it will produce a patch file. You don’t have to think about branching or merging at all to understand this.
But how do you merge? I was always afraid of thumping my working copy with a merge because I didn’t know if it would commit things, or what other persistent effects it would have. “svn merge” doesn’t do anything to your working copy, it only updates the source just like ‘patch’, except it makes it easy to resolve conflicts using a text editor. If you want to get new changes from your trunk into your branch, just make sure all changes in your branch are committed, and do this from the branch dir:
$ svn merge -r
If you want to see what changes it will apply to your branch, just switch ‘merge’ with ‘diff’. There are two keys here: 1) keep track of the revision that you last updated your branch with. The syntax ‘svn merge -rA:B ../source’ means I want all of the changes from rev A to rev B in the source dir. svn doesn’t even care if you already did this or not, it will try to apply them. Then try the code out, and if it works, commit the changes with a good message for next time that records the revision you merged from, like “scripting branch: merged changes from trunk, rev B”.
Since the changes don’t commit anything, just try it out and revert the changes.
But then I need to update my daemon branch (a branch of a branch) with those changes as well. I’ll do this from my daemon branch dir:
$ svn merge -rB:B+1 ../scripting
Since all of the trunk changes from rev A to rev B are now included in the scripting branch, I just need to grab all those changes from that latest revision after I committed them with my useful message. That’s rev B to rev B+1.
Once you do this once or twice it’s actually pretty easy. I hope this helps.
I hear git is much better and much faster at this than svn since it does all the work on your own machine and doesn’t require the server. It sounds like git is best suited for situations where you want to be able to do lots of little private commits for your own work between real commits to the team’s repos.