Merging a Whole Branch to Another
To complete our running example, we'll move forward in
time. Suppose several days have passed, and many changes have
happened on both the trunk and your private branch. Suppose
that you've finished working on your private branch; the
feature or bug fix is finally complete, and now you want to
merge all of your branch changes back into the trunk for
others to enjoy.
So how do we use
svn merge
in this
scenario? Remember that this command compares two trees, and
applies the differences to a working copy. So to receive the
changes, you need to have a working copy of the trunk. We'll
assume that either you still have your original one lying
around (fully updated), or that you recently checked out a
fresh working copy of /calc/trunk
.
But which two trees should be compared? At first glance,
the answer may seem obvious: just compare the latest trunk
tree with your latest branch tree. But beware—this
assumption is
wrong
, and has burned many
a new user! Since
svn merge
operates like
svn diff
, comparing the latest trunk and
branch trees will
not
merely describe
the set of changes you made to your branch. Such a comparison
shows too many changes: it would not only show the addition of
your branch changes, but also the
removal
of trunk changes that never happened on your branch.
To express only the changes that happened on your branch,
you need to compare the initial state of your branch to its
final state. Using
svn log
on your branch,
you can see that your branch was created in revision 341. And
the final state of your branch is simply a matter of using the
HEAD
revision. That means you want to
compare revisions 341 and HEAD
of your
branch directory, and apply those differences to a working
copy of the trunk.
Tip
A nice way of finding the revision in which a branch was
created (the “base” of the branch) is to use the
--stop-on-copy
option to
svn
log
. The log subcommand will normally show every
change ever made to the branch, including tracing back
through the copy which created the branch. So normally,
you'll see history from the trunk as well. The
--stop-on-copy
will halt log output as soon
as
svn log
detects that its target was
copied or renamed.
So in our continuing example,
$ svn log --verbose --stop-on-copy \
https://svn.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
A /calc/branches/my-calc-branch (from /calc/trunk:340)
$
As expected, the final revision printed by this command
is the revision in which my-calc-branch
was created by copying.
Here's the final merging procedure, then:
$ cd calc/trunk
$ svn update
At revision 405.
$ svn merge -r 341:405 https://svn.example.com/repos/calc/branches/my-calc-branch
U integer.c
U button.c
U Makefile
$ svn status
M integer.c
M button.c
M Makefile
# ...examine the diffs, compile, test, etc...
$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending integer.c
Sending button.c
Sending Makefile
Transmitting file data ...
Committed revision 406.
Again, notice that the commit log message very
specifically mentions the range of changes that was merged
into the trunk. Always remember to do this, because it's
critical information you'll need later on.
For example, suppose you decide to keep working on your
branch for another week, in order to complete an enhancement
to your original feature or bug fix. The repository's
HEAD
revision is now 480, and you're ready
to do another merge from your private branch to the trunk.
But as discussed in
the section called “Best Practices for Merging”, you
don't want to merge the changes you've already merged before;
you only want to merge everything “new” on your
branch since the last time you merged. The trick is to figure
out what's new.
The first step is to run
svn log
on the
trunk, and look for a log message about the last time you
merged from the branch:
$ cd calc/trunk
$ svn log
…
------------------------------------------------------------------------
r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line
Merged my-calc-branch changes r341:405 into the trunk.
------------------------------------------------------------------------
…
Aha! Since all branch-changes that happened between
revisions 341 and 405 were previously merged to the trunk as
revision 406, you now know that you want to merge only the
branch changes after that—by comparing revisions 406 and
HEAD
.
$ cd calc/trunk
$ svn update
At revision 480.
# We notice that HEAD is currently 480, so we use it to do the merge:
$ svn merge -r 406:480 https://svn.example.com/repos/calc/branches/my-calc-branch
U integer.c
U button.c
U Makefile
$ svn commit -m "Merged my-calc-branch changes r406:480 into the trunk."
Sending integer.c
Sending button.c
Sending Makefile
Transmitting file data ...
Committed revision 481.
Now the trunk contains the complete second wave of changes
made to the branch. At this point, you can either delete your
branch (we'll discuss this later on), or continue working on
your branch and repeat this procedure for subsequent
merges.