There are many different uses for branching and svn merge, and this section describes the most common.
Version control is most often used for software development, so here's a quick peek at two of the most common branching/merging patterns used by teams of programmers. If you're not using Subversion for software development, feel free to skip this section. If you're a software developer using version control for the first time, pay close attention, as these patterns are often considered best practices by experienced folk. These processes aren't specific to Subversion; they're applicable to any version control system. Still, it may help to see them described in Subversion terms.
Most software has a typical life cycle: code, test, release, repeat. There are two problems with this process. First, developers need to keep writing new features while quality assurance teams take time to test supposedly stable versions of the software. New work cannot halt while the software is tested. Second, the team almost always needs to support older, released versions of software; if a bug is discovered in the latest code, it most likely exists in released versions as well, and customers will want to get that bug fix without having to wait for a major new release.
Here's where version control can help. The typical procedure looks like this:
Developers commit all new work to the
trunk.
Day-to-day changes are committed to
/trunk
: new features, bug fixes, and
so on.
The trunk is copied to a
“release” branch.
When the team thinks the software is ready for release
(say, a 1.0 release), /trunk
might be copied to
/branches/1.0
.
Teams continue to work in parallel.
One team begins rigorous testing of the release branch,
while another team continues new work (say, for version
2.0) on /trunk
. If bugs are
discovered in either location, fixes are ported back and
forth as necessary. At some point, however, even that
process stops. The branch is “frozen” for
final testing right before a release.
The branch is tagged and released.
When testing is complete,
/branches/1.0
is copied to
/tags/1.0.0
as a reference
snapshot. The tag is packaged and released to
customers.
The branch is maintained over time.
While work continues on /trunk
for
version 2.0, bug fixes continue to be ported from
/trunk
to
/branches/1.0
. When enough
bug fixes have accumulated, management may decide to do a
1.0.1 release: /branches/1.0
is
copied to /tags/1.0.1
, and the tag
is packaged and released.
This entire process repeats as the software matures: when the 2.0 work is complete, a new 2.0 release branch is created, tested, tagged, and eventually released. After some years, the repository ends up with a number of release branches in “maintenance” mode, and a number of tags representing final shipped versions.
A feature branch is the sort of
branch that's been the dominant example in this chapter (the
one you've been working on while Sally continues to work on
/trunk
). It's a temporary branch created
to work on a complex change without interfering with the
stability of /trunk
. Unlike release
branches (which may need to be supported forever), feature
branches are born, used for a while, merged back to the trunk,
and then ultimately deleted. They have a finite span of
usefulness.
Again, project policies vary widely concerning exactly
when it's appropriate to create a feature branch. Some
projects never use feature branches at all: commits to
/trunk
are a free-for-all. The
advantage to this system is that it's simple—nobody
needs to learn about branching or merging. The disadvantage
is that the trunk code is often unstable or unusable. Other
projects use branches to an extreme: no change is
ever committed to the trunk directly.
Even the most trivial changes are created on a short-lived
branch, carefully reviewed, and merged to the trunk. Then
the branch is deleted. This system guarantees an
exceptionally stable and usable trunk at all times, but at
the cost of tremendous process overhead.
Most projects take a middle-of-the-road approach. They
commonly insist that /trunk
compile and
pass regression tests at all times. A feature branch is
required only when a change requires a large number of
destabilizing commits. A good rule of thumb is to ask this
question: if the developer worked for days in isolation and
then committed the large change all at once (so that
/trunk
were never destabilized), would it
be too large a change to review? If the answer to that
question is “yes,” the change should be
developed on a feature branch. As the developer commits
incremental changes to the branch, they can be easily reviewed
by peers.
Finally, there's the issue of how to best keep a feature branch in “sync” with the trunk as work progresses. As we mentioned earlier, there's a great risk to working on a branch for weeks or months; trunk changes may continue to pour in, to the point where the two lines of development differ so greatly that it may become a nightmare trying to merge the branch back to the trunk.
This situation is best avoided by regularly merging trunk changes to the branch. Make up a policy: once a week, merge the last week's worth of trunk changes to the branch.
At some point, you'll be ready to merge the
“synchronized” feature branch back to the trunk.
To do this, begin by doing a final merge of the latest trunk
changes to the branch. When that's done, the latest versions
of branch and trunk will be absolutely identical except for
your branch changes. You would then merge back with
the --reintegrate
option:
$ cd trunk-working-copy $ svn update At revision 1910. $ svn merge --reintegrate ^/branches/mybranch --- Merging differences between repository URLs into '.': U real.c U integer.c A newdirectory A newdirectory/newfile U . …
Another way of thinking about this pattern is that your weekly sync of trunk to branch is analogous to running svn update in a working copy, while the final merge step is analogous to running svn commit from a working copy. After all, what else is a working copy but a very shallow private branch? It's a branch that's capable of storing only one change at a time.