分支和svn merge有很多不同的用法,这个小节描述了最常见的用法。
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.
这是版本控制可以帮助你的。典型的过程如下:
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 ^/calc/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.