Most of the ideas here were taken from Pragmatic Version Control Using CVS, by David Thomas and Andrew Hunt. The book covers branching in chapter 7. It's a good book. You should buy it.
Most CVS projects live on the mainline, an unbroken string of versions of the files in the project. A branch is a diversion from the mainline, sort of an 'alternate universe', where files are allowed to be different than the mainline. This can be useful in many situations. The classic reason for branching occurs when you create a release. The release may have bugs. You want to be able to create a future release with the bugs fixed, but no new features. But you want to continue working on the codebase as well. So you create a release branch, a sort of CVS deadend that only contains that release and any bugfix updates to it. Meanwhile, you continue developing on the mainline.
CVS allows you to create branches arbitrarily, including on other branches, which can create incredibly convoluted 'trees' of code. To avoid the confusion of arbitrary branching, we will never branch off branches, only off the mainline. And we will only create branches in two cases:
With that in mind, here are branching scenarios and how they should be handled.
This is what you do before you release, to prepare for a release without having to worry about other people checking in code. Presumably I'm the only one who does this.
I recently created a branch for release 2.2. I did this by getting
an up-to-date version of the mainline code
(cvs update; cvs commit
), then
running:
cvs rtag -b RB_2_2 calendar2
The 2_2
part changes depending on the release number.
To work on this branch, instead of the mainline, you need to check out
the code. By default, checking out will put your code in a directory
named calendar2
. To avoid confusion, it is better to
leave that
directory for mainline work. Instead, check out into a directory whose
name makes it clear that you're working on a branch.
cvs co -r RB_2_2 -d rb2.2 calendar2
This will put a copy of release branch 2.2's code in a directory called
rb2.2
. Again, obviously, the name will change depending on
the release number.
Let's say before you release 2.2 you want to fix bug #1234 in Bugzilla. Presumably you also want to fix this bug in the mainline, but first you do your work on the branch. The key is you want to tag your work both before and after you fix the bug, so that the changes can be easily made to the mainline.
First, check out a copy of release branch 2.2, as in Scenario #2. Then:
cd ~/rb2.2
cvs tag PRE_1234
// fix bug
cvs commit
cvs tag POST_1234
Note that these are 'tags', not 'rtags'.
If you want to make a fix that doesn't have an attached Bugzilla bug number,
you should make up a similar tag that is hopefully unique, involving
your institution or initials and the date. For example,
PRE_gsb20050222
, PRE_uw20051225
.
Or you could piggyback a fix onto a small Bugzilla bug fix, and tag it all
with the Bugzilla bug number.
If a bug has to be fixed twice, you should add the date to the end of the tags.
To fix the corresponding bug in the mainline, let's presume your
copy of the mainline's code is in ~/calendar2
.
cd ~/calendar2
cvs update // make sure you have the current version
cvs update -j PRE_1234 -j POST_1234
// test, fix conflicts, etc.
cvs commit
Note that you must commit after you do the update -j
--- update does not change the repository. Note also that
update -j
doesn't magically do everything for you. If someone's
moved around the files affected by your bugfix, you might have to fix things
by hand. And there could be conflicts. Best to do some testing. Testing
would of course be easiest if you had written unit or other tests back when
you made the fix in the release branch. [hint, hint]
Also note that you can fix bugs on the mainline and move the changes to a release branch in a similar manner, as long as you tag before and after the bug fix.
Again, presumably I'm the only one doing this for now.
cd ~/rb2.2
cvs update
// test
cvs tag REL_2_2
Unfortunately, it is rarely the case that a release works when tagged,
even after testing.
So you can think of the above tag as a candidate release tag.
After we've confirmed that everything works, and release notes have
been updated in the README
files, I tag again as, for example,
REL_2_2_RELEASED
.
You can checkout this tag to get the code as released.
I presume after the release we might still use the branch in case someone finds a bug, or we want to make other similar easy changes. Subsequent releases would be called 2.2.1, 2.2.2, etc. But once we move to release 2.3, we would create a new branch and the 2.2 branch would pretty much be dead.
If you're about to start on an involved change, it would be wise to create a branch and work on the branch. This prevents other people from messing your work up, and vice versa. Basically, this is the same as scenarios 1 and 2, except with a different branch name:
cvs rtag -b try_gb_20050222 calendar2
Use your initials instead of gb
. Instead of your initials,
you could also
use an abbreviation for the feature itself, like
try_newdb20050222
.
To put this branch in a directory called try_gb
:
cvs co -r try_gb_20050222 -d try_gb calendar2
When you are done, if everything works well, you can merge all the changes in a branch back into the mainline.
cd ~/try_gb
cvs commit
cd ~/calendar2
cvs update
cvs update -j try_gb_20050222
// test, etc.
cvs commit
A merge like this should only be done once. After the merge, the branch should no longer be used and you should work on the mainline or create a new branch.
|