Sophie

Sophie

distrib > Mandriva > 2008.1 > x86_64 > by-pkgid > 1a3ab26f23e0518b4054a503057127e3 > files > 46

subversion-doc-1.4.6-5mdv2008.1.x86_64.rpm

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Advanced Merging</title><link rel="stylesheet" href="styles.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Version Control with Subversion" /><link rel="up" href="svn.branchmerge.html" title="Chapter 4. Branching and Merging" /><link rel="prev" href="svn.branchmerge.basicmerging.html" title="Basic Merging" /><link rel="next" href="svn.branchmerge.switchwc.html" title="Traversing Branches" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Advanced Merging</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="svn.branchmerge.basicmerging.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Branching and Merging</th><td width="20%" align="right"> <a accesskey="n" href="svn.branchmerge.switchwc.html">Next</a></td></tr></table><hr /></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="svn.branchmerge.advanced"></a>Advanced Merging</h2></div></div></div><p>Here ends the automated magic.  Sooner or later, once you
      get the hang of branching and merging, you're going to have to
      ask Subversion to merge <span class="emphasis"><em>specific</em></span> changes
      from one place to another.  And in order to do this, you're
      going to have to start passing more complicated arguments
      to <span class="command"><strong>svn merge</strong></span>.  This next section describes
      the fully-expanded syntax of the merge command, and discusses a
      number of common scenarios that require it.</p><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.cherrypicking"></a>Cherrypicking</h3></div></div></div><p>Just as the term “<span class="quote">changeset</span>” is often used in
        version control systems, so is the term
        of <em class="firstterm">cherrypicking</em>.  This word refers to
        the act of choosing <span class="emphasis"><em>one</em></span> specific
        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
        typical merging scenarios, where the “<span class="quote">next</span>”
        contiguous range of revisions is duplicated
        automatically.</p><p>Why would people want to replicate just a single change?
        It comes up more often than you'd think.  For example, let's
        go back in time and imagine that you haven't yet merged your
        private feature-branch back to the trunk.  At the
        water-cooler, you get word that Sally made an interesting
        change to <code class="filename">integer.c</code> on the trunk.
        Looking over the history of commits to the trunk, you see that
        in revision 355 she fixed a critical bug that directly
        impacts the feature you're working on.  You might not be ready
        to merge all the trunk changes to your branch just yet, but
        you certainly need that particular bugfix in order to continue
        your work.</p><pre class="screen">
$ svn diff -c 355 http://svn.example.com/repos/calc/trunk

Index: integer.c
===================================================================
--- integer.c	(revision 354)
+++ integer.c	(revision 355)
@@ -147,7 +147,7 @@
     case 6:  sprintf(info-&gt;operating_system, "HPFS (OS/2 or NT)"); break;
     case 7:  sprintf(info-&gt;operating_system, "Macintosh"); break;
     case 8:  sprintf(info-&gt;operating_system, "Z-System"); break;
-    case 9:  sprintf(info-&gt;operating_system, "CP/MM");
+    case 9:  sprintf(info-&gt;operating_system, "CP/M"); break;
     case 10:  sprintf(info-&gt;operating_system, "TOPS-20"); break;
     case 11:  sprintf(info-&gt;operating_system, "NTFS (Windows NT)"); break;
     case 12:  sprintf(info-&gt;operating_system, "QDOS"); break;
</pre><p>Just as you used <span class="command"><strong>svn diff</strong></span> in the prior
        example to examine revision 355, you can pass the same option
        to <span class="command"><strong>svn merge</strong></span>:</p><pre class="screen">
$ svn merge -c 355 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c
</pre><p>You can now go through the usual testing procedures before
        committing this change to your branch.  After the commit,
        Subversion marks r355 as having been merged to the branch, so
        that future “<span class="quote">magic</span>” merges that synchronize your
        branch with the trunk know to skip over r355.  (Merging the
        same change to the same branch almost always results in a
        conflict!)</p><pre class="screen">
$ cd my-calc-branch

$ svn propget svn:mergeinfo .
/trunk:341-349,355

$ svn mergeinfo .
Path: .
  Source path: /trunk
    Merged ranges: r341:349,r355
    Eligible ranges: r350:354,r356:360

$ svn merge http://svn.example.com/repos/calc/trunk
--- Merging r350 through r354 into '.':
U  integer.c
U  Makefile
--- Merging r356 through r360 into '.':
U  integer.c
U  button.c
</pre><p>This use-case of replicating
        (or <em class="firstterm">backporting</em>) bugfixes from one
        branch to another is perhaps the most popular reason for
        cherrypicking changes; it comes up all the time, for example,
        when a team is maintaining a “<span class="quote">release branch</span>” of
        software.  (We discuss this pattern in
        <a class="xref" href="svn.branchmerge.commonpatterns.html#svn.branchmerge.commonpatterns.release" title="Release Branches">the section called “Release Branches”</a>.)</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Warning</h3><p>Did you notice how, in the last example, the merge
        invocation caused two distinct ranges of merges to be applied?
        The <span class="command"><strong>svn merge</strong></span> command applied two
        independent patches to your working copy in order to skip over
        changeset 355, which your branch already contained.  There's
        nothing inherently wrong with this, except that it has the
        potential to make conflict-resolution more tricky.  If the
        first range of changes creates conflicts,
        you <span class="emphasis"><em>must</em></span> resolve them interactively in
        order for the merge process to continue and apply the second
        range of changes.  If you postpone a conflict from the first
        wave of changes, the whole merge command will bail out with an
        error message.<sup>[<a id="id372144" href="#ftn.id372144" class="footnote">23</a>]</sup></p></div><p>A word of warning: while <span class="command"><strong>svn diff</strong></span> and
        <span class="command"><strong>svn merge</strong></span> are very similar in concept, they
        do have different syntax in many cases.  Be sure to read about
        them in <a class="xref" href="svn.ref.html" title="Chapter 9. Subversion Complete Reference">Chapter 9, <i>Subversion Complete Reference</i></a> for details, or ask
        <span class="command"><strong>svn help</strong></span>.  For example, <span class="command"><strong>svn
        merge</strong></span> requires a working-copy path as a target, i.e.
        a place where it should apply the generated patch.  If the
        target isn't specified, it assumes you are trying to perform
        one of the following common operations:</p><div class="orderedlist"><ol type="1"><li><p>You want to merge directory changes into your current
            working directory.</p></li><li><p>You want to merge the changes in a specific file into
            a file by the same name which exists in your current working
            directory.</p></li></ol></div><p>If you are merging a directory and haven't specified a
        target path, <span class="command"><strong>svn merge</strong></span> assumes the first case
        above and tries to apply the changes into your current
        directory.  If you are merging a file, and that file (or a file
        by the same name) exists in your current working directory,
        <span class="command"><strong>svn merge</strong></span> assumes the second case and tries
        to apply the changes to a local file with the same name.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.advancedsyntax"></a>Merge Syntax:  Full Disclosure</h3></div></div></div><p>You've now seen some examples of the <span class="command"><strong>svn
        merge</strong></span> command, and you're about to see several more.
        If you're feeling confused about exactly how merging works,
        you're not alone.  Many users (especially those new to version
        control) are initially perplexed about the proper syntax of
        the command, and about how and when the feature should be
        used.  But fear not, this command is actually much simpler
        than you think!  There's a very easy technique for
        understanding exactly how <span class="command"><strong>svn merge</strong></span>
        behaves.</p><p>The main source of confusion is the
        <span class="emphasis"><em>name</em></span> of the command.  The term
        “<span class="quote">merge</span>” somehow denotes that branches are
        combined together, or that there's some sort of mysterious
        blending of data going on.  That's not the case.  A better
        name for the command might have been <span class="command"><strong>svn
        diff-and-apply</strong></span>, because that's all that happens:
        two repository trees are compared, and the differences are
        applied to a working copy.</p><p>If you're using <span class="command"><strong>svn merge</strong></span> to do basic
        copying of changes between branches, it will generally do the
        right thing automatically.  For example, a command like</p><pre class="screen">
$ svn merge http://svn.example.com/repos/calc/some-branch
</pre><p>... will attempt to duplicate any changes made
        on <code class="filename">some-branch</code> into your current working
        directory, which is presumably a working copy that shares some
        historical connection to the branch.  The command is smart
        enough to only duplicate changes that your working copy
        doesn't yet have.  If you repeat this command once a week, it
        will only duplicate the “<span class="quote">newest</span>” branch changes
        that happened since you last merged.</p><p>If you choose to use the <span class="command"><strong>svn merge</strong></span>
        command in all its full glory by giving it specific revision
        ranges to duplicate, then the command takes three main
        arguments:</p><div class="orderedlist"><ol type="1"><li><p>An initial repository tree (often called the
        <em class="firstterm">left side</em> of the
        comparison),</p></li><li><p>A final repository tree (often called the
        <em class="firstterm">right side</em> of the
        comparison),</p></li><li><p>A working copy to accept the differences as
        local changes (often called the <em class="firstterm">target</em>
        of the merge).</p></li></ol></div><p>Once these three arguments are specified, the two trees
        are compared, and the resulting differences are applied to the
        target working copy as local modifications.  When the command
        is done, the results are no different than if you had
        hand-edited the files, or run various <span class="command"><strong>svn
        add</strong></span> or <span class="command"><strong>svn delete</strong></span> commands
        yourself.  If you like the results, you can commit them.  If
        you don't like the results, you can simply <span class="command"><strong>svn
        revert</strong></span> all of the changes.</p><p>The syntax of <span class="command"><strong>svn merge</strong></span> allows you to
        specify the three necessary arguments rather flexibly.  Here
        are some examples:</p><pre class="screen">
$ svn merge http://svn.example.com/repos/branch1@150 \
            http://svn.example.com/repos/branch2@212 \
            my-working-copy

$ svn merge -r 100:200 http://svn.example.com/repos/trunk my-working-copy

$ svn merge -r 100:200 http://svn.example.com/repos/trunk
</pre><p>The first syntax lays out all three arguments explicitly,
        naming each tree in the form <span class="emphasis"><em>URL@REV</em></span> and
        naming the working copy target.  The second syntax can be used
        as a shorthand for situations when you're comparing two
        different revisions of the same URL.  The last syntax shows
        how the working-copy argument is optional; if omitted, it
        defaults to the current directory.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.undo"></a>Undoing Changes</h3></div></div></div><p>Another common use for <span class="command"><strong>svn merge</strong></span> is to
        roll back a change that has already been committed.  Suppose
        you're working away happily on a working copy of
        <code class="filename">/calc/trunk</code>, and you discover that the
        change made way back in revision 303, which changed
        <code class="filename">integer.c</code>, is completely wrong.  It never
        should have been committed.  You can use <span class="command"><strong>svn
        merge</strong></span> to “<span class="quote">undo</span>” the change in your
        working copy, and then commit the local modification to the
        repository.  All you need to do is to specify a
        <span class="emphasis"><em>reverse</em></span> difference.  (You can do this by
        specifying <code class="option">--revision 303:302</code>, or by an
        equivalent <code class="option">--change -303</code>.)</p><pre class="screen">
$ svn merge -c -303 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff
…
# verify that the change is removed
…

$ svn commit -m "Undoing change committed in r303."
Sending        integer.c
Transmitting file data .
Committed revision 350.
</pre><p>As we mentioned earlier, one way to think about a
        repository revision is as a specific changeset.  By using the
        <code class="option">-r</code> option, you can ask <span class="command"><strong>svn
        merge</strong></span> to apply a changeset, or whole range of
        changesets, to your working copy.  In our case of undoing a
        change, we're asking <span class="command"><strong>svn merge</strong></span> to apply
        changeset #303 to our working copy
        <span class="emphasis"><em>backwards</em></span>.</p><p>Keep in mind that rolling back a change like this is just
        like any other <span class="command"><strong>svn merge</strong></span> operation, so you
        should use <span class="command"><strong>svn status</strong></span> and <span class="command"><strong>svn
        diff</strong></span> to confirm that your work is in the state you
        want it to be in, and then use <span class="command"><strong>svn commit</strong></span>
        to send the final version to the repository.  After
        committing, this particular changeset is no longer reflected
        in the <code class="literal">HEAD</code> revision.</p><p>Again, you may be thinking: well, that really didn't undo
        the commit, did it?  The change still exists in revision 303.
        If somebody checks out a version of the
        <code class="filename">calc</code> project between revisions 303 and
        349, they'll still see the bad change, right?</p><p>Yes, that's true.  When we talk about
        “<span class="quote">removing</span>” a change, we're really talking about
        removing it from the <code class="literal">HEAD</code> revision.  The
        original change still exists in the repository's history.  For
        most situations, this is good enough.  Most people are only
        interested in tracking the <code class="literal">HEAD</code> of a
        project anyway.  There are special cases, however, where you
        really might want to destroy all evidence of the commit.
        (Perhaps somebody accidentally committed a confidential
        document.)  This isn't so easy, it turns out, because
        Subversion was deliberately designed to never lose
        information.  Revisions are immutable trees which build upon
        one another.  Removing a revision from history would cause a
        domino effect, creating chaos in all subsequent revisions and
        possibly invalidating all working copies.
        <sup>[<a id="id372581" href="#ftn.id372581" class="footnote">24</a>]</sup>
      </p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.resurrect"></a>Resurrecting Deleted Items</h3></div></div></div><p>The great thing about version control systems is that
        information is never lost.  Even when you delete a file or
        directory, it may be gone from the <code class="literal">HEAD</code>
        revision, but the object still exists in earlier revisions.
        One of the most common questions new users ask is, “<span class="quote">How
        do I get my old file or directory back?</span>”.</p><p>The first step is to define
        exactly <span class="bold"><strong>which</strong></span> item you're
        trying to resurrect.  Here's a useful metaphor: you can think
        of every object in the repository as existing in a sort of
        two-dimensional coordinate system.  The first coordinate is a
        particular revision tree, and the second coordinate is a path
        within that tree.  So every version of your file or directory
        can be defined by a specific coordinate pair.  (Remember the
        “<span class="quote">peg revision</span>” syntax—foo.c@224
        —mentioned back in
        <a class="xref" href="svn.advanced.pegrevs.html" title="Peg and Operative Revisions">the section called “Peg and Operative Revisions”</a>.) </p><p>First, you might need to use <span class="command"><strong>svn log</strong></span> to
        discover the exact coordinate pair you wish to resurrect.  A
        good strategy is to run <span class="command"><strong>svn log
        --verbose</strong></span> in a directory which used to contain your
        deleted item.  The <code class="option">--verbose (-v)</code> option shows a
        list of all changed items in each revision; all you need to do
        is find the revision in which you deleted the file or
        directory.  You can do this visually, or by using another tool
        to examine the log output (via <span class="command"><strong>grep</strong></span>, or
        perhaps via an incremental search in an editor).</p><pre class="screen">
$ cd parent-dir
$ svn log -v
…
------------------------------------------------------------------------
r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.
…
</pre><p>In the example, we're assuming that you're looking for a
        deleted file <code class="filename">real.c</code>.  By looking through
        the logs of a parent directory, you've spotted that this file
        was deleted in revision 808.  Therefore, the last version of
        the file to exist was in the revision right before that.
        Conclusion: you want to resurrect the path
        <code class="filename">/calc/trunk/real.c</code> from revision
        807.</p><p>That was the hard part—the research.  Now that you
        know what you want to restore, you have two different
        choices.</p><p>One option is to use <span class="command"><strong>svn merge</strong></span> to apply
        revision 808 “<span class="quote">in reverse</span>”.  (We've already
        discussed how to undo changes, see
        <a class="xref" href="svn.branchmerge.advanced.html#svn.branchmerge.advanced.undo" title="Undoing Changes">the section called “Undoing Changes”</a>.)  This
        would have the effect of re-adding <code class="filename">real.c</code>
        as a local modification.  The file would be scheduled for
        addition, and after a commit, the file would again exist
        in <code class="literal">HEAD</code>.</p><p>In this particular example, however, this is probably not
        the best strategy.  Reverse-applying revision 808 would not
        only schedule <code class="filename">real.c</code> for addition, but
        the log message indicates that it would also undo certain
        changes to <code class="filename">integer.c</code>, which you don't
        want.  Certainly, you could reverse-merge revision 808 and
        then <span class="command"><strong>svn revert</strong></span> the local modifications to
        <code class="filename">integer.c</code>, but this technique doesn't
        scale well.  What if there were 90 files changed in revision
        808?</p><p>A second, more targeted strategy is not to use
        <span class="command"><strong>svn merge</strong></span> at all, but rather the
        <span class="command"><strong>svn copy</strong></span> command.  Simply copy the exact
        revision and path “<span class="quote">coordinate pair</span>” from the
        repository to your working copy:</p><pre class="screen">
$ svn copy http://svn.example.com/repos/calc/trunk/real.c@807 ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.
</pre><p>The plus sign in the status output indicates that the item
        isn't merely scheduled for addition, but scheduled for
        addition “<span class="quote">with history</span>”.  Subversion remembers
        where it was copied from.  In the future, running <span class="command"><strong>svn
        log</strong></span> on this file will traverse back through the
        file's resurrection and through all the history it had prior
        to revision 807.  In other words, this new
        <code class="filename">real.c</code> isn't really new; it's a direct
        descendant of the original, deleted file.  This is usually
        considered a good and useful thing.  If, however, you wanted
        to resurrect the file <span class="emphasis"><em>without</em></span>
        maintaining a historical link to the old file, this technique
        works just as well:</p><pre class="screen">
$ svn cat http://svn.example.com/repos/calc/trunk/real.c@807 &gt; ./real.c

$ svn add real.c
A  real.c

$ svn commit -m "Recreated real.c from revision 807."
Adding         real.c
Transmitting file data .
Committed revision 1390.
</pre><p>Although our example shows us resurrecting a file, note
        that these same techniques work just as well for resurrecting
        deleted directories.  Also note that a resurrection doesn't
        have to happen in your working copy—it can happen
        entirely in the repository:</p><pre class="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.
</pre></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.mergeconflicts"></a>More on Merge Conflicts</h3></div></div></div><p>Just like the <span class="command"><strong>svn update</strong></span> command,
        <span class="command"><strong>svn merge</strong></span> applies changes to your working
        copy.  And therefore it's also capable of creating
        conflicts.  The conflicts produced by <span class="command"><strong>svn
          merge</strong></span>, however, are sometimes different, and this
        section explains those differences.</p><p>To begin with, assume that your working copy has no
        local edits.  When you <span class="command"><strong>svn update</strong></span> to a
        particular revision, the changes sent by the server will
        always apply “<span class="quote">cleanly</span>” to your working copy.
        The server produces the delta by comparing two trees: a
        virtual snapshot of your working copy, and the revision tree
        you're interested in.  Because the left-hand side of the
        comparison is exactly equal to what you already have, the
        delta is guaranteed to correctly convert your working copy
        into the right-hand tree.</p><p>But <span class="command"><strong>svn merge</strong></span> has no such guarantees
        and can be much more chaotic: the advanced user can ask the
        server to compare <span class="emphasis"><em>any</em></span> two trees at all,
        even ones that are unrelated to the working copy!  This means
        there's large potential for human error.  Users will sometimes
        compare the wrong two trees, creating a delta that doesn't
        apply cleanly.  <span class="command"><strong>svn merge</strong></span> will do its best
        to apply as much of the delta as possible, but some parts may
        be impossible.  Just as the Unix
        <span class="command"><strong>patch</strong></span> command sometimes complains about
        “<span class="quote">failed hunks</span>”, <span class="command"><strong>svn merge</strong></span>
        will complain about “<span class="quote">skipped targets</span>”:</p><pre class="screen">
$ svn merge -r 1288:1351 http://svn.example.com/repos/branch
U  foo.c
U  bar.c
Skipped missing target: 'baz.c'
U  glub.c
U  sputter.h
Conflict discovered in 'glorb.h'.
Select: (p)ostpone, (d)iff, (e)dit, (h)elp for more options : 
</pre><p>In the previous example it might be the case that
        <code class="filename">baz.c</code> exists in both snapshots of the
        branch being compared, and the resulting delta wants to
        change the file's contents, but the file doesn't exist in
        the working copy.  Whatever the case, the
        “<span class="quote">skipped</span>” message means that the user is most
        likely comparing the wrong two trees; they're the classic
        sign of user error.  When this happens, it's easy to
        recursively revert all the changes created by the merge
        (<span class="command"><strong>svn revert --recursive</strong></span>), delete any
        unversioned files or directories left behind after the
        revert, and re-run <span class="command"><strong>svn merge</strong></span> with
        different arguments.</p><p>Also notice that the previous example shows a conflict
        happening on <code class="filename">glorb.h</code>.  We already
        stated that the working copy has no local edits: how can a
        conflict possibly happen?  Again, because the user can use
        <span class="command"><strong>svn merge</strong></span> to define and apply any old
        delta to the working copy, that delta may contain textual
        changes that don't cleanly apply to a working file, even if
        the file has no local modifications.</p><p>Another small difference between <span class="command"><strong>svn
          update</strong></span> and <span class="command"><strong>svn merge</strong></span> are the
        names of the full-text files created when a conflict
        happens.  In <a class="xref" href="svn.tour.cycle.html#svn.tour.cycle.resolve" title="Resolve Conflicts (Merging Others' Changes)">the section called “Resolve Conflicts (Merging Others' Changes)”</a>, we saw
        that an update produces files named
        <code class="filename">filename.mine</code>,
        <code class="filename">filename.rOLDREV</code>, and
        <code class="filename">filename.rNEWREV</code>.  When <span class="command"><strong>svn
          merge</strong></span> produces a conflict, though, it creates
        three files named <code class="filename">filename.working</code>,
        <code class="filename">filename.left</code>, and
        <code class="filename">filename.right</code>.  In this case, the
        terms “<span class="quote">left</span>” and “<span class="quote">right</span>” are
        describing which side of the double-tree comparison the file
        came from.  In any case, these differing names will help you
        distinguish between conflicts that happened as a result of an
        update versus ones that happened as a result of a
        merge.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.blockchanges"></a>Blocking Changes</h3></div></div></div><p>Sometimes there's a particular changeset which you don't
        want to be automatically merged.  For example, perhaps your
        team's policy is to do new development work on
        <code class="filename">/trunk</code>, but to be more conservative about
        backporting changes to a “<span class="quote">stable</span>” branch you use
        for releasing to the public.  On one extreme, you can manually
        cherrypick single changesets from trunk to the
        branch—only the changes that are stable enough to pass
        muster.  Maybe things aren't quite that strict, though;
        perhaps most of the time you'd like to just let <span class="command"><strong>svn
        merge</strong></span> automatically merge most changes from trunk to
        branch.  In this case, you'd like a way to mask a few specific
        changes out, i.e. prevent them from ever being automatically
        merged.</p><p>In Subversion 1.5, the only way to block a changeset is to
        make the system believe that the change has
        <span class="emphasis"><em>already</em></span> been merged.  You'll need to
        manually edit the <code class="literal">svn:mergeinfo</code> property on
        the branch, and add the blocked revision(s) to the
        list:</p><pre class="screen">
$ cd my-calc-branch

$ svn propget svn:mergeinfo .
/trunk:1680-3305

$ svn propset svn:mergeinfo "/trunk:1680-3305,3328" .
property 'svn:mergeinfo' set on '.'
</pre><p>This technique works, but it's also a little bit
        dangerous.  The main problem is that we're not differentiating
        between the ideas of “<span class="quote">I don't want this change</span>”
        and “<span class="quote">I don't have this change</span>”.  We're
        effectively lying to the system, making it think that the
        change was previously merged.  This puts the responsibility on
        you—the user—to remember that the change wasn't
        actually merged, it just wasn't wanted.  There's no way to ask
        Subversion for a list of “<span class="quote">blocked changelists</span>”.
        If you want to track them (so that you can unblock them
        someday) you'll need to record it in a text file somewhere, or
        perhaps in an invented property.  In Subversion 1.5,
        unfortunately, this is the only way to manage blocked
        revisions; the plans are to make a better interface for this
        in future versions.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.logblame"></a>Merge-Sensitive Logs and Annotations</h3></div></div></div><p>One of the main features of any version control system is
        to keep track of who changed what, and when they did it.
        The <span class="command"><strong>svn log</strong></span> and <span class="command"><strong>svn
        blame</strong></span> commands are just the tools for this: when
        invoked on individual files, they show not only the history of
        changesets that affected the file, but exactly which user
        wrote which line of code, and when they did it.</p><p>When changes start getting replicated between branches,
        however, things start to get complicated.  For example, if you
        were to ask <span class="command"><strong>svn log</strong></span> about the history of
        your feature branch, it shows exactly every revision that ever
        affected the branch:</p><pre class="screen">
$ cd my-calc-branch
$ svn log -q
------------------------------------------------------------------------
r390 | user | 2002-11-22 11:01:57 -0600 (Fri, 22 Nov 2002) | 1 line
------------------------------------------------------------------------
r388 | user | 2002-11-21 05:20:00 -0600 (Thu, 21 Nov 2002) | 2 lines
------------------------------------------------------------------------
r381 | user | 2002-11-20 15:07:06 -0600 (Wed, 20 Nov 2002) | 2 lines
------------------------------------------------------------------------
r359 | user | 2002-11-19 19:19:20 -0600 (Tue, 19 Nov 2002) | 2 lines
------------------------------------------------------------------------
r357 | user | 2002-11-15 14:29:52 -0600 (Fri, 15 Nov 2002) | 2 lines
------------------------------------------------------------------------
r343 | user | 2002-11-07 13:50:10 -0600 (Thu, 07 Nov 2002) | 2 lines
------------------------------------------------------------------------
r341 | user | 2002-11-03 07:17:16 -0600 (Sun, 03 Nov 2002) | 2 lines
------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
------------------------------------------------------------------------
</pre><p>But is this really an accurate picture of all the changes
        that happened on the branch?  What's being left out here is
        the fact that revisions 390, 381, and 357 were actually the
        results of merging changes from trunk.  If you look at a one
        of these logs in detail, the multiple trunk changesets that
        comprised the branch change are nowhere to be seen.</p><pre class="screen">
$ svn log -v -r 390
------------------------------------------------------------------------
r390 | user | 2002-11-22 11:01:57 -0600 (Fri, 22 Nov 2002) | 1 line
Changed paths:
   M /branches/my-calc-branch/button.c
   M /branches/my-calc-branch/README

Final merge of trunk changes to my-calc-branch.
</pre><p>We happen to know that this merge to the branch was
        nothing but a merge of trunk changes.  How can we see those
        trunk changes as well?  The answer is to use
        the <code class="option">--use-merge-history (-g)</code> option.  This
        option expands those “<span class="quote">child</span>” changes that were
        part of the merge.</p><pre class="screen">
$ svn log -v -r 390 -g
------------------------------------------------------------------------
r390 | user | 2002-11-22 11:01:57 -0600 (Fri, 22 Nov 2002) | 1 line
Changed paths:
   M /branches/my-calc-branch/button.c
   M /branches/my-calc-branch/README

Final merge of trunk changes to my-calc-branch.
------------------------------------------------------------------------
r383 | sally | 2002-11-21 03:19:00 -0600 (Thu, 21 Nov 2002) | 2 lines
Changed paths:
   M /branches/my-calc-branch/button.c
Result of a merge from: r390

Fix inverse graphic error on button.
------------------------------------------------------------------------
r382 | sally | 2002-11-20 16:57:06 -0600 (Wed, 20 Nov 2002) | 2 lines
Changed paths:
   M /branches/my-calc-branch/README
Result of a merge from: r390

Document my last fix in README.
</pre><p>By making the log operation use merge history, we see not
        just the revision we queried (r390), but the two revisions
        that came along on the ride with it—a couple of changes
        made by Sally to the trunk.  This is a much more complete
        picture of history!</p><p>The <span class="command"><strong>svn blame</strong></span> command also takes
        the <code class="option">--use-merge-history (-g)</code> option.  If this
        option is neglected, then somebody looking at a line-by-line
        annotation of <code class="filename">button.c</code> may get the
        mistaken impression that you were responsible for the lines
        that fixed a certain error:</p><pre class="screen">
$ svn blame button.c
...
   390    user    retval = inverse_func(button, path);
   390    user    return retval;
   390    user    }
...
</pre><p>And while it's true that you did actually commit those
        three lines in revision 390, two of them were actually writen
        by Sally back in revision 383:</p><pre class="screen">
$ svn blame button.c -g
...
G    383    sally   retval = inverse_func(button, path);
G    383    sally   return retval;
     390    user    }
...
</pre><p>Now we know who to <span class="emphasis"><em>really</em></span> blame for
        those two lines of code!</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.ancestry"></a>Noticing or Ignoring Ancestry</h3></div></div></div><p>When conversing with a Subversion developer, you might
        very likely hear reference to the term
        <em class="firstterm">ancestry</em>.  This word is used to
        describe the relationship between two objects in a
        repository: if they're related to each other, then one
        object is said to be an ancestor of the other.</p><p>For example, suppose you commit revision 100, which
        includes a change to a file <code class="filename">foo.c</code>.
        Then <code class="filename">foo.c@99</code> is an
        “<span class="quote">ancestor</span>” of <code class="filename">foo.c@100</code>.
        On the other hand, suppose you commit the deletion of
        <code class="filename">foo.c</code> in revision 101, and then add a
        new file by the same name in revision 102.  In this case,
        <code class="filename">foo.c@99</code> and
        <code class="filename">foo.c@102</code> may appear to be related
        (they have the same path), but in fact are completely
        different objects in the repository.  They share no history
        or “<span class="quote">ancestry</span>”.</p><p>The reason for bringing this up is to point out an
        important difference between <span class="command"><strong>svn diff</strong></span> and
        <span class="command"><strong>svn merge</strong></span>.  The former command ignores
        ancestry, while the latter command is quite sensitive to it.
        For example, if you asked <span class="command"><strong>svn diff</strong></span> to
        compare revisions 99 and 102 of <code class="filename">foo.c</code>,
        you would see line-based diffs; the <code class="literal">diff</code>
        command is blindly comparing two paths.  But if you asked
        <span class="command"><strong>svn merge</strong></span> to compare the same two objects,
        it would notice that they're unrelated and first attempt to
        delete the old file, then add the new file;  the output would
        indicate a deletion followed by an add:</p><pre class="screen">
        D  foo.c
        A  foo.c
      </pre><p>Most merges involve comparing trees that are ancestrally
        related to one another, and therefore <span class="command"><strong>svn
          merge</strong></span> defaults to this behavior.  Occasionally,
        however, you may want the <code class="literal">merge</code> command
        to compare two unrelated trees.  For example, you may have
        imported two source-code trees representing different vendor
        releases of a software project (see <a class="xref" href="svn.advanced.vendorbr.html" title="Vendor branches">the section called “Vendor branches”</a>).  If you asked
        <span class="command"><strong>svn merge</strong></span> to compare the two trees, you'd
        see the entire first tree being deleted, followed by an add
        of the entire second tree!  In these situations, you'll want
        <span class="command"><strong>svn merge</strong></span> to do a path-based comparison
        only, ignoring any relations between files and directories.
        Add the <code class="option">--ignore-ancestry</code> option to your
        merge command, and it will behave just like <span class="command"><strong>svn
          diff</strong></span>.  (And conversely, the
        <code class="option">--notice-ancestry</code> option will cause
        <span class="command"><strong>svn diff</strong></span> to behave like the
        <code class="literal">merge</code> command.)</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.moves"></a>Merges and Moves</h3></div></div></div><p>A common desire is to refactor source code, especially
        in Java-based software projects.  Files and directories are
        shuffled around and renamed, often causing great disruption
        to everyone working on the project.  Sounds like a perfect
        case to use a branch, doesn't it?  Just create a branch,
        shuffle things around, then merge the branch back to the
        trunk, right?</p><p>Alas, this scenario doesn't work so well right now, and
        is considered one of Subversion's current weak spots.  The
        problem is that Subversion's <span class="command"><strong>update</strong></span>
        command isn't as robust as it should be, particularly when
        dealing with copy and move operations.</p><p>When you use <span class="command"><strong>svn copy</strong></span> to duplicate a
        file, the repository remembers where the new file came from,
        but it fails to transmit that information to the client
        which is running <span class="command"><strong>svn update</strong></span>
        or <span class="command"><strong>svn merge</strong></span>.  Instead of telling the
        client, “<span class="quote">Copy that file you already have to this new
          location</span>”, it instead sends down an entirely new
        file.  This can lead to problems, especially because the
        same thing happens with renamed files.  A lesser-known fact
        about Subversion is that it lacks “<span class="quote">true
          renames</span>”—the <span class="command"><strong>svn move</strong></span>
        command is nothing more than an aggregation of <span class="command"><strong>svn
          copy</strong></span> and <span class="command"><strong>svn delete</strong></span>.</p><p>For example, suppose that while working on your private
        branch, you rename <code class="filename">integer.c</code>
        to <code class="filename">whole.c</code>.  Effectively you've created
        a new file in your branch that is a copy of the original
        file, and deleted the original file.  Meanwhile, back
        on <code class="filename">trunk</code>, Sally has committed some
        improvements to <code class="filename">integer.c</code>.  Now you
        decide to merge your branch to the trunk:</p><pre class="screen">
        $ cd calc/trunk

        $ svn merge --reintegrate http://svn.example.com/repos/calc/branches/my-calc-branch
        D   integer.c
        A   whole.c
      </pre><p>This doesn't look so bad at first glance, but it's also
        probably not what you or Sally expected.  The merge
        operation has deleted the latest version
        of <code class="filename">integer.c</code> file (the one containing
        Sally's latest changes), and blindly added your
        new <code class="filename">whole.c</code> file—which is a
        duplicate of the <span class="emphasis"><em>older</em></span> version
        of <code class="filename">integer.c</code>.  The net effect is that
        merging your “<span class="quote">rename</span>” to the branch has removed
        Sally's recent changes from the latest revision!</p><p>This isn't true data-loss; Sally's changes are still in
        the repository's history, but it may not be immediately
        obvious that this has happened.  The moral of this story is
        that until Subversion improves, be very careful about
        merging copies and renames from one branch to
        another.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="svn.branchmerge.advanced.pre1.5clients"></a>Blocking Merge-Unaware Clients</h3></div></div></div><p>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 <span class="command"><strong>svn
        merge</strong></span>, it doesn't modify the value of
        the <code class="literal">svn:mergeinfo</code> 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 “<span class="quote">merge-aware</span>” clients attempt automatic
        merging, they're likely to run into all sorts of conflicts
        resulting from repeated merges.</p><p>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 “<span class="quote">capabilities</span>” parameter in
        the <code class="literal">start-commit</code> hook script.  If the
        client reports itself as having <code class="literal">mergeinfo</code>
        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
        <a class="xref" href="svn.reposadmin.create.html#svn.reposadmin.create.hooks" title="Implementing Repository Hooks">the section called “Implementing Repository Hooks”</a> and
        <a class="xref" href="svn.ref.reposhooks.start-commit.html" title="start-commit">start-commit</a> for
        details.</p></div><div class="footnotes"><br /><hr width="100" align="left" /><div class="footnote"><p><sup>[<a id="ftn.id372144" href="#id372144" class="para">23</a>] </sup>At least, this is true in
        Subversion 1.5 at the time of writing.  This behavior may
        improve in future versions of
        Subversion.</p></div><div class="footnote"><p><sup>[<a id="ftn.id372581" href="#id372581" class="para">24</a>] </sup>The Subversion project has plans, however, to someday
            implement a command that would accomplish the task of
            permanently deleting information.  In the meantime, see
            <a class="xref" href="svn.reposadmin.maint.html#svn.reposadmin.maint.tk.svndumpfilter" title="svndumpfilter">the section called “svndumpfilter”</a>
            for a possible workaround.</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="svn.branchmerge.basicmerging.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="svn.branchmerge.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="svn.branchmerge.switchwc.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Basic Merging </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Traversing Branches</td></tr></table></div></body></html>