[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