"Stephen J. Turnbull" <turnbull(a)sk.tsukuba.ac.jp> wrote:
On Ben's site, he recommends 3 concurrent branches. I
(provisionally)
agree with his theory, but one of the *BSD (probably FreeBSD) web
sites mentions that they had a lot of trouble managing 2 branches +
the trunk simultaneously. They didn't go into detail, just said that
a subsidiary branch that lasted more than 2-3 months was hard to
manage.
We're using CVS to manage a large internal project, and branches
are a %^&*#@?! pain in the b**t. If you don't use branches, or if you
only use vendor branches, CVS is great, but it's pretty useless for
serious branch work.
The basic problem is that CVS doesn't keep track of where/when you
merge. Let's say that you use the main trunk as the "main/current"
development branch, and that you have a branch for developing a future
version (in the case of XEmacs, this could be for developing an elisp
replacement). Now, the problem is that you want to keep the trunk and
branch in sync, at least as far as bugfixes are concerned.
Let's say that you make a change to the trunk, and now you want to
merge the change into the branch. CVS doesn't have an easy way to do
this, because there's no good way to refer to the main trunk, among
other things (last I checked, the special name "HEAD" referred to the
head of the current branch/trunk, and not the main trunk). If all your
sources have the same major revision number, I suppose you could use
that (e.g., "-j1"), but it's kludgy. The ugly way uses:
1. Check out branch sources. Sorry, you can't do this from the main
trunk.
2. For each file, determine the RCS revision numbers (e.g., 1.23) for
the bugfixes that you just checked into the trunk.
3. For each file/revision number, merge in trunk changes using (for
example):
cvs update -j1.22 -j1.23 file.c
^
|
+--- Bah! Previous revision to 1.23.
4. Commit the sucker.
(I believe CVS diehards will say that this is the wrong paradigm, that
all fixes, modifications, and changes should be made to branches, and
that the main trunk should only be used for release code. Well, that's
fine -- read on.) Another way uses tags, as mentioned below.
OK, let's say that you make all changes to the branches, and that
the main trunk is for release code only (i.e., changes are only made to
branches, and nothing is ever checked into the trunk -- only merges from
branches to the trunk are ever done). Again, the problem is that CVS
does not have good branch merging capabilities. Branch merging is a bit
easier when you merge from a branch to another branch or to the main
trunk (the previous example merged *from* the *main trunk*, and not from
a branch); however, the command that CVS provides, merges the ENTIRE
BRANCH to the destination (branch or trunk). Phooey. If you want to do
a partial branch merge, which is the usual case when dealing with
bugfixes, etc., you've got two choices (well, three -- see the bottom of
this message):
1. Whenever you merge, tag the source revisions that you merged. Then,
the next time you merge, you can use the tag to tell CVS to merge
only those changes from the tag to the latest version on the branch.
The problem with this is that you have to delete and move the tag
each time you merge, or you have to create a uniquely-named tag for
each merge.
Another possibility is, whenever you fix bugs, tag the sources that
are being fixed (before you fix them). Fix the sources and check
them in (commit them). Then, you can use the tag to merge into other
branches or the main trunk. Afterwards, you'll probably want to
delete the tag.
People may find this acceptable in a single-user or small group
environments, but it's impossible in a large, multi-user environment
where many people are making changes. In the large, multi-user case,
you'd need at least one tag for each person/branch (because each
person would need his own tag, for merging in his changes, and
possibly one for each branch). Heaven help you if deal with lots of
branches.
2. Use raw RCS revision numbers (e.g., "1.21.42.39"). Ackptuii. 'Nuff
said.
All this is even documented (somewhat) in the CVS manual. See the
section titled, "Merging from a branch several times". CVS seriously
needs a way of keeping of brange merge metadata around.
[ Hmmm, I suppose you could get this if, anytime *anything* is checked
in, a unique tag is automatically attached to all changes; you could
then use this tag to merge stuff. However, this would seriously bloat
the RCS files, unless changes are made to CVS. ]
However, things aren't quite as bleak as I make them. Someone
wrote a perl script called "cvslines" to help manage branches. See:
http://www.netapp.com/technology/freeware/cvslines/index.html
Basically, cvslines works at commit time; when you commit, it helps you
commit the changes into several branches. It's quite nice (it
supposedly even handles new file additions into branches), but it does
have downsides:
* cvslines needs direct access to the RCS files managed by CVS.
cvslines will *NOT* work via :pserver:.
* cvslines is unix-centric.
* cvslines works at commit time. This means that you must decide, at
commit time, what changes are to be merged into the branches. It
won't help you merge after the changes have been commit'ed.
P.S. -- I've also made some major changes to Frederic Lepied's cvs.el,
if anyone's interested (someday, I'll send these changes back to
him ;-().
* Listing the status of files in a directory and all
subdirectories is now more user-friendly. Also, unlike
PCL-CVS, Frederic's package does not require you to update
your sources to get their status.
* You can commit files as a group, instead of doing so
individually. This ability existed previously, but it's
easier now; you can use the status display to determine files
to commit.
* You can do simple merging from any branch to your current
sources, one file at a time. However, this uses raw RCS
revision numbers.
--
Darryl Okahata
darrylo(a)sr.hp.com
DISCLAIMER: this message is the author's personal opinion and does not
constitute the support, opinion, or policy of Hewlett-Packard, or of the
little green men that have been following him all day.