[svnbook commit] r2918 - trunk/src/en/book
sussman
noreply at red-bean.com
Fri Dec 14 21:43:52 CST 2007
Author: sussman
Date: Fri Dec 14 21:43:52 2007
New Revision: 2918
Log:
* ch04-branching-and-merging.xml: finish up first draft of rewrite.
Modified:
trunk/src/en/book/ch04-branching-and-merging.xml
Modified: trunk/src/en/book/ch04-branching-and-merging.xml
==============================================================================
--- trunk/src/en/book/ch04-branching-and-merging.xml (original)
+++ trunk/src/en/book/ch04-branching-and-merging.xml Fri Dec 14 21:43:52 2007
@@ -630,7 +630,7 @@
this command records the changes that have been duplicated
to your branch, so that Subversion is aware of exactly which
changes exist in each location (see
- <xref linkend="svn.branchmerge.basicmerging.basicmergetracking.mergeinfo"/>.)
+ <xref linkend="svn.branchmerge.basicmerging.mergeinfo"/>.)
This is a critical feature that makes branch management
usable; without it, users would have to manually keep notes
on which sets of changes have or haven't been merged
@@ -731,15 +731,15 @@
Subversion knows how to pick up where it left off. In our
prior examples, you can see that first it merges the ranges
345:356 from trunk to branch; later on, it continues by
- merging the next contiguously available range, 357:380. When
- doing the final sync, it merges the range 381:385.</para>
+ merging the next contiguously available range, 356:380. When
+ doing the final sync, it merges the range 380:385.</para>
<para>When merging your branch back to the trunk, however, the
underlying mathematics is quite different. Your feature
branch is now a mish-mosh of both duplicated trunk changes and
private branch changes, so there's no simple contiguous range
of revisions to copy over. By specifying
- the <option>--integrate</option> option, you're asking
+ the <option>--reintegrate</option> option, you're asking
Subversion to carefully replicate <emphasis>only</emphasis>
those changes unique to your branch. (And in fact it does
this by comparing the latest trunk tree with the latest branch
@@ -770,89 +770,77 @@
</sect2>
<!-- =============================================================== -->
- <sect2 id="svn.branchemerge.basicmerging.basicmergetracking">
- <title>Basic Merge Tracking</title>
+ <sect2 id="svn.branchmerge.basicmerging.mergeinfo">
+ <title>Mergeinfo and Previews</title>
- <para>Of course, there's a lot more detail to the story of
- merging changes. Subversion does its best to note where
- changes have happened and where they've been duplicated, but
- it's far from perfect. In this section, we'll show you how
- some of the magic works.</para>
-
- <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
- <sect3 id="svn.branchmerge.basicmerging.basicmergetracking.mergeinfo">
- <title>Mergeinfo and Previews</title>
-
- <para>The basic mechanism Subversion uses to track
- changesets—that is, which changes have been merged to
- which branches—is by recording data in properties.
- Specifically, merge data is tracked in
- the <literal>svn:mergeinfo</literal> property attached to
- files and directories. (If you're not familiar with
- Subversion properties, now is the time to go skim over
- <xref linkend="svn.advanced.props"/>.)</para>
+ <para>The basic mechanism Subversion uses to track
+ changesets—that is, which changes have been merged to
+ which branches—is by recording data in properties.
+ Specifically, merge data is tracked in
+ the <literal>svn:mergeinfo</literal> property attached to
+ files and directories. (If you're not familiar with
+ Subversion properties, now is the time to go skim over
+ <xref linkend="svn.advanced.props"/>.)</para>
- <para>You can examine the property, just like any
- other:</para>
+ <para>You can examine the property, just like any
+ other:</para>
- <screen>
+ <screen>
$ cd my-calc-branch
$ svn propget svn:mergeinfo .
/trunk:341-390
</screen>
- <para>It is <emphasis>not</emphasis> recommended that you
- change the value of this property yourself, unless you
- really know what you're doing. (An example of this comes up
- in <xref linkend="svn.branchmerge.advanced.blockchanges"/>.)
- In general, this property is automatically maintained by
- Subversion whenever you run <command>svn merge</command>,
- and its value indicates which changes have already been
- replicated into a particular directory.</para>
-
- <para>As we saw earlier, there's also a
- subcommand <command>svn mergeinfo</command>, which can be
- helpful in seeing not only which changesets a directory has
- absorbed, but also which changesets it's still eligible to
- receive. This gives a sort of preview of the next set of
- changes that <command>svn merge</command> will replicate to
- your branch.</para>
+ <para>It is <emphasis>not</emphasis> recommended that you change
+ the value of this property yourself, unless you really know
+ what you're doing. (An example of this comes up in
+ <xref linkend="svn.branchmerge.advanced.blockchanges"/>.) In
+ general, this property is automatically maintained by
+ Subversion whenever you run <command>svn merge</command>, and
+ its value indicates which changes have already been replicated
+ into a particular directory.</para>
+
+ <para>As we saw earlier, there's also a subcommand <command>svn
+ mergeinfo</command>, which can be helpful in seeing not only
+ which changesets a directory has absorbed, but also which
+ changesets it's still eligible to receive. This gives a sort
+ of preview of the next set of changes that <command>svn
+ merge</command> will replicate to your branch.</para>
- <screen>
+ <screen>
$ svn mergeinfo .
Path: .
- Source path: /trunk
- Merged ranges: r341:390
- Eligible ranges: r391:395
-</screen>
+Source path: /trunk
+Merged ranges: r341:390
+Eligible ranges: r391:395
+</screen>
+
+ <para>The <command>svn mergeinfo</command> command, by default,
+ looks back at the history of the thing you're querying and
+ tries to make a sane guess about the <quote>source</quote> you
+ want to be copying changes from. In our prior example,
+ because we're querying our branch working copy, the command
+ assumes we're interested in receiving changes
+ from <filename>/trunk</filename> (since that's where our
+ branch was originally copied from.) However, if another
+ coworker had a different branch going on that we wanted to
+ merge changes from, we could have this command tell us about
+ eligible changesets from that source too:</para>
- <para>The <command>svn mergeinfo</command> command, by
- default, looks back at the history of the thing you're
- querying and tries to make a sane guess about
- the <quote>source</quote> you want to be copying changes
- from. In our prior example, because we're querying our
- branch working copy, the command assumes we're interested in
- receiving changes from <filename>/trunk</filename> (since
- that's where our branch was originally copied from.)
- However, if another coworker had a different branch going on
- that we wanted to merge changes from, we could have this
- command tell us about eligible changesets from that source
- too:</para>
-
- <screen>
+ <screen>
$ svn mergeinfo --from-source \
- http://svn.example.com/repos/calc/branches/other-branch .
+http://svn.example.com/repos/calc/branches/other-branch .
Path: .
- Source path: /branches/other-branch
- Merged ranges:
- Eligible ranges: r360:364
+Source path: /branches/other-branch
+Merged ranges:
+Eligible ranges: r360:364
</screen>
- <para>Another way to get a more precise preview of a merge
- operation is to use
- the <option>--dry-run</option> option.</para>
+ <para>Another way to get a more precise preview of a merge
+ operation is to use the <option>--dry-run</option>
+ option.</para>
- <screen>
+ <screen>
$ svn merge http://svn.example.com/repos/calc/trunk --dry-run
U integer.c
@@ -860,45 +848,42 @@
# nothing printed, working copy is still unchanged.
</screen>
- <para>The <option>--dry-run</option> option doesn't actually
- apply any local changes to the working copy. It only shows
- status codes that <emphasis>would</emphasis> be printed in a
- real merge. It's useful for getting a <quote>high
- level</quote> preview of the potential merge, for those
- times when running <command>svn diff</command> gives too
- much detail.</para>
-
- <para>Of course, the best way to preview a merge operation is
- to just do it! Remember, running <command>svn
- merge</command> isn't an inherently risky thing; if you
- don't like the results, simply <command>svn revert -R</command>
- the changes from your working copy and retry the command
- with different options. The merge isn't final until you
- actually <command>svn commit</command> the results.</para>
-
- <tip>
- <para>While it's perfectly fine to experiment with merges by
- running <command>svn merge</command> and <command>svn
- revert</command> over and over, you may run into some
- annoying (but easily bypassed) roadblocks. For example,
- if the merge operation adds a new file (i.e. schedules it
- for addition), then <command>svn revert</command> won't
- actually remove the file; it simply unschedules the
- addition. You're left with an unversioned file. If you
- then attempt to run the merge again, you may get conflicts
- due to the unversioned file <quote>being in the
- way</quote>. Solution? After performing a revert, be
- sure to clean up the working copy and remove unversioned
- files and directories. The output of <command>svn
- status</command> should be as clean (read: empty) as
- possible!</para>
- </tip>
+ <para>The <option>--dry-run</option> option doesn't actually
+ apply any local changes to the working copy. It only shows
+ status codes that <emphasis>would</emphasis> be printed in a
+ real merge. It's useful for getting a <quote>high
+ level</quote> preview of the potential merge, for those times
+ when running <command>svn diff</command> gives too much
+ detail.</para>
+
+ <para>Of course, the best way to preview a merge operation is to
+ just do it! Remember, running <command>svn merge</command>
+ isn't an inherently risky thing; if you don't like the
+ results, simply <command>svn revert -R</command> the changes
+ from your working copy and retry the command with different
+ options. The merge isn't final until you
+ actually <command>svn commit</command> the results.</para>
- </sect3>
+ <tip>
+ <para>While it's perfectly fine to experiment with merges by
+ running <command>svn merge</command> and <command>svn
+ revert</command> over and over, you may run into some
+ annoying (but easily bypassed) roadblocks. For example, if
+ the merge operation adds a new file (i.e. schedules it for
+ addition), then <command>svn revert</command> won't actually
+ remove the file; it simply unschedules the addition. You're
+ left with an unversioned file. If you then attempt to run
+ the merge again, you may get conflicts due to the
+ unversioned file <quote>being in the way</quote>. Solution?
+ After performing a revert, be sure to clean up the working
+ copy and remove unversioned files and directories. The
+ output of <command>svn status</command> should be as clean
+ (read: empty) as possible!</para>
+ </tip>
</sect2>
- </sect1>
+ </sect1>
<!-- ================================================================= -->
<!-- ================================================================= -->
@@ -924,7 +909,7 @@
version control systems, so is the term
of <firstterm>cherrypicking</firstterm>. This word refers to
the act of choosing <emphasis>one</emphasis> specific
-p changeset from a branch and replicating it to another.
+ changeset from a branch and replicating it to another.
Cherrypicking may also refer to the act of duplicating a
particular set of (not necessarily contiguous!) changesets
from one branch to another. This is in contrast to more
@@ -1375,7 +1360,19 @@
<para>Although our example shows us resurrecting a file, note
that these same techniques work just as well for resurrecting
- deleted directories.</para>
+ deleted directories. Also note that a resurrection doesn't
+ have to happen in your working copy—it can happen
+ entirely in the repository:</para>
+
+ <screen>
+$ svn copy http://svn.example.com/repos/calc/trunk/real.c@807 \
+ http://svn.example.com/repos/calc/trunk/
+Committed revision 1390.
+
+$ svn update
+A real.c
+Updated to revision 1390.
+</screen>
</sect2>
@@ -1763,7 +1760,7 @@
<screen>
$ cd calc/trunk
- $ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
+ $ svn merge --reintegrate http://svn.example.com/repos/calc/branches/my-calc-branch
D integer.c
A whole.c
</screen>
@@ -1790,223 +1787,35 @@
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<sect2 id="svn.branchmerge.advanced.pre1.5clients">
- <title>Blocking Pre-1.5 Clients</title>
-
- <para>###TODO: Why you might want to do this. (How did we
- decide to do this??)</para>
-
- </sect2>
-
- </sect1>
-
- <!-- ================================================================= -->
- <!-- ================================================================= -->
- <!-- ================================================================= -->
- <sect1 id="svn.branchmerge.commonpatterns">
- <title>Common Branching Patterns</title>
-
- <para>There are many different uses for branching and <command>svn
- merge</command>, and this section describes the most common ones
- you're likely to run into.</para>
-
- <para>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.</para>
-
- <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
- <sect2 id="svn.branchmerge.commonpatterns.release">
- <title>Release Branches</title>
-
- <para>Most software has a typical lifecycle: 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 bugfix without having to wait for a major new
- release.</para>
-
- <para>Here's where version control can help. The typical
- procedure looks like this:</para>
-
- <itemizedlist>
-
- <listitem>
- <para><emphasis>Developers commit all new work to the
- trunk.</emphasis>
-
- Day-to-day changes are committed to
- <filename>/trunk</filename>: new features, bugfixes, and
- so on.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>The trunk is copied to a
- <quote>release</quote> branch.</emphasis>
-
- When the team thinks the software is ready for release
- (say, a 1.0 release), then <filename>/trunk</filename>
- might be copied to
- <filename>/branches/1.0</filename>.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>Teams continue to work in parallel.</emphasis>
-
- One team begins rigorous testing of the release branch,
- while another team continues new work (say, for version
- 2.0) on <filename>/trunk</filename>. 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 <quote>frozen</quote> for
- final testing right before a release.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>The branch is tagged and released.</emphasis>
-
- When testing is complete,
- <filename>/branches/1.0</filename> is copied to
- <filename>/tags/1.0.0</filename> as a reference
- snapshot. The tag is packaged and released to
- customers.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>The branch is maintained over time.</emphasis>
-
- While work continues on <filename>/trunk</filename> for
- version 2.0, bugfixes continue to be ported from
- <filename>/trunk</filename> to
- <filename>/branches/1.0</filename>. When enough
- bugfixes have accumulated, management may decide to do a
- 1.0.1 release: <filename>/branches/1.0</filename> is
- copied to <filename>/tags/1.0.1</filename>, and the tag
- is packaged and released.</para>
- </listitem>
+ <title>Blocking Merge-Unaware Clients</title>
- </itemizedlist>
-
- <para>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 <quote>maintenance</quote> mode, and a number
- of tags representing final shipped versions.</para>
-
- </sect2>
-
- <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
- <sect2 id="svn.branchmerge.commonpatterns.feature">
- <title>Feature Branches</title>
-
- <para>A <firstterm>feature branch</firstterm> 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
- <filename>/trunk</filename>. It's a temporary branch
- created to work on a complex change without interfering with
- the stability of <filename>/trunk</filename>. Unlike
- release branches (which may need to be supported forever),
- feature branches are born, used for a while, merged back to
- the trunk, then ultimately deleted. They have a finite span
- of usefulness.</para>
-
- <para>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
- <filename>/trunk</filename> 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
- <emphasis>ever</emphasis> 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.</para>
-
- <para>Most projects take a middle-of-the-road approach. They
- commonly insist that <filename>/trunk</filename> compile and
- pass regression tests at all times. A feature branch is
- only required 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
- <filename>/trunk</filename> were never destabilized), would it
- be too large a change to review? If the answer to that
- question is <quote>yes</quote>, then 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.</para>
-
- <para>Finally, there's the issue of how to best keep a feature
- branch in <quote>sync</quote> 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.</para>
-
- <para>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. Take
- care when doing this; the merging needs to be hand-tracked to
- avoid the problem of repeated merges. You'll need to write
- careful log messages detailing exactly which revision ranges
- have been merged already. It may sound intimidating, but it's
- actually pretty easy to do.</para>
-
- <para>At some point, you'll be ready to merge the
- <quote>synchronized</quote> 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. So in this special case, you would merge
- by comparing the branch with the trunk:</para>
-
- <para>###TODO: SIMPLIFY THIS EXAMPLE TO SHOW MERGE-TRACKING</para>
-
- <screen>
- $ cd trunk-working-copy
-
- $ svn update
- At revision 1910.
-
- $ svn merge http://svn.example.com/repos/calc/trunk@1910 \
- http://svn.example.com/repos/calc/branches/mybranch@1910
- U real.c
- U integer.c
- A newdirectory
- A newdirectory/newfile
- …
- </screen>
-
- <para>By comparing the <literal>HEAD</literal> revision of the
- trunk with the <literal>HEAD</literal> revision of the
- branch, you're defining a delta that describes only the
- changes you made to the branch; both lines of development
- already have all of the trunk changes.</para>
-
- <para>Another way of thinking about this pattern is that your
- weekly sync of trunk to branch is analogous to running
- <command>svn update</command> in a working copy, while the
- final merge step is analogous to running <command>svn
- commit</command> from a working copy. After all, what else
- <emphasis>is</emphasis> a working copy but a very shallow
- private branch? It's a branch that's only capable of
- storing one change at a time.</para>
+ <para>If you've just upgraded your server to Subversion 1.5 or
+ later, then there's a significant risk that pre-1.5 Subversion
+ clients can mess up your merge-tracking. Why is this? When a
+ pre-1.5 Subversion client performs <command>svn
+ merge</command>, it doesn't modify the value of
+ the <literal>svn:mergeinfo</literal> property at all. So the
+ subsequent commit, despite being the result of a merge,
+ doesn't tell the repository about the duplicated
+ changes—that information is lost. Later on,
+ when <quote>merge-aware</quote> clients attempt automatic
+ merging, they're likely to run into all sorts of conflicts
+ resulting from repeated merges.</para>
+
+ <para>If you and your team are relying on the merge-tracking
+ features of Subversion, then you may want to configure your
+ repository to prevent older clients from committing changes.
+ The easy way to do this is by inspecting
+ the <quote>capabilities</quote> parameter in
+ the <literal>start-commit</literal> hook script. If the
+ client reports itself as having <literal>mergeinfo</literal>
+ capabilities, the hook script can allow the commit to start.
+ If the client doesn't report that capability, have the hook
+ deny the commit. We'll learn more about hook scripts in the
+ next chapter; see
+ <xref linkend="svn.reposadmin.create.hooks"/> and
+ <xref linkend="svn.ref.reposhooks.start-commit"/> for
+ details.</para>
</sect2>
@@ -2143,16 +1952,31 @@
<para>Because <command>svn switch</command> is essentially a
variant of <command>svn update</command>, it shares the same
behaviors; any local modifications in your working copy are
- preserved when new data arrives from the repository. This
- allows you to perform all sorts of clever tricks.</para>
+ preserved when new data arrives from the repository.</para>
- <para>For example, suppose you have a working copy of
- <filename>/calc/trunk</filename> and make a number of changes to
- it. Then you suddenly realize that you meant to make the
- changes to a branch instead. No problem! When you <command>svn
- switch</command> your working copy to the branch, the local
- changes will remain. You can then test and commit them to the
- branch.</para>
+
+ <tip>
+ <para>Have you ever found yourself making some complex edits
+ (in your <filename>/trunk</filename> working copy) and
+ suddenly realized, <quote>hey, these changes ought to be in
+ their own branch?</quote> A great technique to do this can
+ be summarized in two steps:</para>
+
+ <screen>
+$ svn copy http://svn.example.com/repos/calc/trunk \
+ http://svn.example.com/repos/calc/branches/newbranch
+Committed revision 353.
+
+$ svn switch http://svn.example.com/repos/calc/branches/newbranch
+At revision 353.
+</screen>
+
+ <para>The <command>svn switch</command> command, like
+ <command>svn update</command>, preserves your local edits.
+ At this point, your working copy is now a reflection of the
+ newly created branch, and your next <command>svn
+ commit</command> invocation will send your changes
+ there.</para> </tip>
</sect1>
@@ -2191,7 +2015,7 @@
http://svn.example.com/repos/calc/tags/release-1.0 \
-m "Tagging the 1.0 release of the 'calc' project."
-Committed revision 351.
+Committed revision 902.
</screen>
<para>This example assumes that a
@@ -2204,9 +2028,9 @@
copy. Of course you might want to be more precise about
exactly which revision you copy, in case somebody else may
have committed changes to the project when you weren't
- looking. So if you know that revision 350 of
+ looking. So if you know that revision 901 of
<filename>/calc/trunk</filename> is exactly the snapshot you
- want, you can specify it by passing <option>-r 350</option> to
+ want, you can specify it by passing <option>-r 901</option> to
the <command>svn copy</command> command.</para>
<para>But wait a moment: isn't this tag-creation procedure the
@@ -2233,7 +2057,7 @@
approach, however, isn't usually necessary. If a user
accidentally commits a change to a tag-directory, you can
simply undo the change as discussed in the previous section.
- This is version control, after all.</para>
+ This is version control, after all!</para>
</sect2>
@@ -2273,7 +2097,7 @@
$ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag
-Committed revision 352.
+Committed revision 940.
</screen>
<para>Now there is a new directory in the repository,
@@ -2301,29 +2125,6 @@
This makes it very difficult (later on) to identify a single
revision number as a branch point.</para>
- <tip>
- <para>Have you ever found yourself making some complex edits
- (in your <filename>/trunk</filename> working copy) and
- suddenly realized, <quote>hey, these changes ought to be in
- their own branch?</quote> A great technique to do this can
- be summarized in two steps:</para>
-
- <screen>
-$ svn copy http://svn.example.com/repos/calc/trunk \
- http://svn.example.com/repos/calc/branches/newbranch
-Committed revision 353.
-
-$ svn switch http://svn.example.com/repos/calc/branches/newbranch
-At revision 353.
-</screen>
-
- <para>The <command>svn switch</command> command, like
- <command>svn update</command>, preserves your local edits.
- At this point, your working copy is now a reflection of the
- newly created branch, and your next <command>svn
- commit</command> invocation will send your changes
- there.</para> </tip>
-
</sect2>
</sect1>
@@ -2431,12 +2232,12 @@
always bring it back. Resurrecting data is very easy in
Subversion. If there's a deleted directory (or file) that
you'd like to bring back into <literal>HEAD</literal>, simply
- use <command>svn copy -r</command> to copy it from the old
+ use <command>svn copy</command> to copy it from the old
revision:</para>
<screen>
-$ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch \
- http://svn.example.com/repos/calc/branches/my-calc-branch
+$ svn copy http://svn.example.com/repos/calc/branches/my-calc-branch@374 \
+ http://svn.example.com/repos/calc/branches/my-calc-branch
Committed revision 376.
</screen>
@@ -2472,12 +2273,216 @@
ports bug fixes over to the stable branch. Even after the
stable branch has shipped, you'll probably continue to
maintain the branch for a long time—that is, as long
- as you continue to support that release for customers.</para>
+ as you continue to support that release for customers. We'll
+ discuss this more in the next section.</para>
</sect2>
</sect1>
+ <!-- ================================================================= -->
+ <!-- ================================================================= -->
+ <!-- ================================================================= -->
+ <sect1 id="svn.branchmerge.commonpatterns">
+ <title>Common Branching Patterns</title>
+
+ <para>There are many different uses for branching and <command>svn
+ merge</command>, and this section describes the most common ones
+ you're likely to run into.</para>
+
+ <para>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.</para>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <sect2 id="svn.branchmerge.commonpatterns.release">
+ <title>Release Branches</title>
+
+ <para>Most software has a typical lifecycle: 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 bugfix without having to wait for a major new
+ release.</para>
+
+ <para>Here's where version control can help. The typical
+ procedure looks like this:</para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para><emphasis>Developers commit all new work to the
+ trunk.</emphasis>
+
+ Day-to-day changes are committed to
+ <filename>/trunk</filename>: new features, bugfixes, and
+ so on.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>The trunk is copied to a
+ <quote>release</quote> branch.</emphasis>
+
+ When the team thinks the software is ready for release
+ (say, a 1.0 release), then <filename>/trunk</filename>
+ might be copied to
+ <filename>/branches/1.0</filename>.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Teams continue to work in parallel.</emphasis>
+
+ One team begins rigorous testing of the release branch,
+ while another team continues new work (say, for version
+ 2.0) on <filename>/trunk</filename>. 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 <quote>frozen</quote> for
+ final testing right before a release.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>The branch is tagged and released.</emphasis>
+
+ When testing is complete,
+ <filename>/branches/1.0</filename> is copied to
+ <filename>/tags/1.0.0</filename> as a reference
+ snapshot. The tag is packaged and released to
+ customers.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>The branch is maintained over time.</emphasis>
+
+ While work continues on <filename>/trunk</filename> for
+ version 2.0, bugfixes continue to be ported from
+ <filename>/trunk</filename> to
+ <filename>/branches/1.0</filename>. When enough
+ bugfixes have accumulated, management may decide to do a
+ 1.0.1 release: <filename>/branches/1.0</filename> is
+ copied to <filename>/tags/1.0.1</filename>, and the tag
+ is packaged and released.</para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>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 <quote>maintenance</quote> mode, and a number
+ of tags representing final shipped versions.</para>
+
+ </sect2>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <sect2 id="svn.branchmerge.commonpatterns.feature">
+ <title>Feature Branches</title>
+
+ <para>A <firstterm>feature branch</firstterm> 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
+ <filename>/trunk</filename>. It's a temporary branch
+ created to work on a complex change without interfering with
+ the stability of <filename>/trunk</filename>. Unlike
+ release branches (which may need to be supported forever),
+ feature branches are born, used for a while, merged back to
+ the trunk, then ultimately deleted. They have a finite span
+ of usefulness.</para>
+
+ <para>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
+ <filename>/trunk</filename> 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
+ <emphasis>ever</emphasis> 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.</para>
+
+ <para>Most projects take a middle-of-the-road approach. They
+ commonly insist that <filename>/trunk</filename> compile and
+ pass regression tests at all times. A feature branch is
+ only required 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
+ <filename>/trunk</filename> were never destabilized), would it
+ be too large a change to review? If the answer to that
+ question is <quote>yes</quote>, then 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.</para>
+
+ <para>Finally, there's the issue of how to best keep a feature
+ branch in <quote>sync</quote> 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.</para>
+
+ <para>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. Take
+ care when doing this; the merging needs to be hand-tracked to
+ avoid the problem of repeated merges. You'll need to write
+ careful log messages detailing exactly which revision ranges
+ have been merged already. It may sound intimidating, but it's
+ actually pretty easy to do.</para>
+
+ <para>At some point, you'll be ready to merge the
+ <quote>synchronized</quote> 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. So in this special case, you would merge
+ back with the <option>--reintegrate</option> option:</para>
+
+ <screen>
+ $ cd trunk-working-copy
+
+ $ svn update
+ At revision 1910.
+
+ $ svn merge --reintegrate http://svn.example.com/repos/calc/branches/mybranch@1910
+ U real.c
+ U integer.c
+ A newdirectory
+ A newdirectory/newfile
+ …
+ </screen>
+
+ <para>Another way of thinking about this pattern is that your
+ weekly sync of trunk to branch is analogous to running
+ <command>svn update</command> in a working copy, while the
+ final merge step is analogous to running <command>svn
+ commit</command> from a working copy. After all, what else
+ <emphasis>is</emphasis> a working copy but a very shallow
+ private branch? It's a branch that's only capable of
+ storing one change at a time.</para>
+
+ </sect2>
+
+ </sect1>
<!-- ================================================================= -->
<!-- ================================================================= -->
@@ -2856,9 +2861,104 @@
repository.</para>
<para>Remember the Subversion mantra: branches and tags are cheap.
- So use them liberally! At the same time, don't forget to use
- good merging habits. Cheap copies are only useful when you're
- careful about tracking your merging actions.</para>
+ So use them liberally!</para>
+
+ <para>As a helpful reminder of all the operations we've discussed,
+ here's a handy reference table you can consult as you begin to
+ make use of branches.</para>
+
+ <table id="svn.branchemerge.summary.tbl-1">
+ <title>Branching and Merging Commands</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Action</entry>
+ <entry>Command</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Create a branch or tag</entry>
+ <entry>svn copy URL1 URL2</entry>
+ </row>
+
+ <row>
+ <entry>Switch a working copy to a branch or tag</entry>
+ <entry>svn switch URL</entry>
+ </row>
+
+ <row>
+ <entry>Synchronize a branch with trunk</entry>
+ <entry>svn merge trunkURL; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>See merge history or eligible changests</entry>
+ <entry>svn mergeinfo target [--from-source=URL]</entry>
+ </row>
+
+ <row>
+ <entry>Merge a branch back into trunk</entry>
+ <entry>svn merge --reintegrate branchURL; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>Merge one specific change</entry>
+ <entry>svn merge -c REV URL; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>Merge a range of changes</entry>
+ <entry>svn merge -r REV1:REV2 URL; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>Block a change from automatic merging</entry>
+ <entry>svn propset svn:mergeinfo newvalue; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>Preview a merge</entry>
+ <entry>svn merge URL --dry-run</entry>
+ </row>
+
+ <row>
+ <entry>Abandon merge results</entry>
+ <entry>svn revert -R .</entry>
+ </row>
+
+ <row>
+ <entry>Resurrect something from history</entry>
+ <entry>svn copy URL at REV local-path</entry>
+ </row>
+
+ <row>
+ <entry>Undo a committed change</entry>
+ <entry>svn merge -c -REV URL; svn commit</entry>
+ </row>
+
+ <row>
+ <entry>Examine merge-sensitive history</entry>
+ <entry>svn log -g; svn blame -g</entry>
+ </row>
+
+ <row>
+ <entry>Create a tag from a working copy</entry>
+ <entry>svn copy . tagURL</entry>
+ </row>
+
+ <row>
+ <entry>Rearrange a branch or tag</entry>
+ <entry>svn mv URL1 URL2</entry>
+ </row>
+
+ <row>
+ <entry>Remove a branch or tag</entry>
+ <entry>svn rm URL</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
</sect1>
More information about the svnbook-dev
mailing list