Professional Subversion Hosting
Free SVN Hosting, SSL, ACLs, Petabyte Storage | |||||||||||||||||||||||||||||||||||||||||||||
|
There are many different uses for branching and svn merge, and this section describes the most common ones you're likely to run into. 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 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
TipA nice way of finding the revision in which a branch was
created (the вbaseв of the branch) is to use the
So in our continuing example,
$ svn log -v --stop-on-copy \
http://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 Here's the final merging procedure, then: $ cd calc/trunk $ svn update At revision 405. $ svn merge -r 341:405 http://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
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
$ 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 http://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. Another common use for svn merge is to
roll back a change that has already been committed. Suppose
you're working away happily on a working copy of
$ 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. One way to think about a repository revision is as a
specific group of changes (some version control systems call
these changesets). By using the
Keep in mind that rolling back a change like this is just
like any other svn merge operation, so you
should use svn status and svn
diff to confirm that your work is in the state you
want it to be in, and then use svn commit
to send the final version to the repository. After
committing, this particular changeset is no longer reflected
in the 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
Yes, that's true. When we talk about
вremovingв a change, we're really talking about
removing it from 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 The first step is to define exactly which 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 вpeg revisionв syntaxвfoo.c@224 вmentioned back in the section called вPeg and Operative Revisionsв.) First, you might need to use svn log to
discover the exact coordinate pair you wish to resurrect. A
good strategy is to run svn log
--verbose in a directory which used to contain your
deleted item. The $ 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. в¦ In the example, we're assuming that you're looking for a
deleted file That was the hard partвthe research. Now that you know what you want to restore, you have two different choices. One option is to use svn merge to apply
revision 808 вin reverseв. (We've already
discussed how to undo changes, see
the section called вUndoing Changesв.) This
would have the effect of re-adding In this particular example, however, this is probably not
the best strategy. Reverse-applying revision 808 would not
only schedule A second, more targeted strategy is not to use svn merge at all, but rather the svn copy command. Simply copy the exact revision and path вcoordinate pairв from the repository to your working copy:
$ svn copy -r 807 \
http://svn.example.com/repos/calc/trunk/real.c ./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.
The plus sign in the status output indicates that the item
isn't merely scheduled for addition, but scheduled for
addition вwith historyв. Subversion remembers
where it was copied from. In the future, running svn
log 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
Although our example shows us resurrecting a file, note that these same techniques work just as well for resurrecting deleted directories. Version control is most often used for software development, so here's a quick peek at two of the most common branching/merging patterns used by teams of programmers. If you're not using Subversion for software development, feel free to skip this section. If you're a software developer using version control for the first time, pay close attention, as these patterns are often considered best practices by experienced folk. These processes aren't specific to Subversion; they're applicable to any version control system. Still, it may help to see them described in Subversion terms. Most software has a typical 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. Here's where version control can help. The typical procedure looks like this:
This entire process repeats as the software matures: when the 2.0 work is complete, a new 2.0 release branch is created, tested, tagged, and eventually released. After some years, the repository ends up with a number of release branches in вmaintenanceв mode, and a number of tags representing final shipped versions. A feature branch is the sort of
branch that's been the dominant example in this chapter, the
one you've been working on while Sally continues to work on
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
Most projects take a middle-of-the-road approach. They
commonly insist that Finally, there's the issue of how to best keep a feature branch in вsyncв with the trunk as work progresses. As we mentioned earlier, there's a great risk to working on a branch for weeks or months; trunk changes may continue to pour in, to the point where the two lines of development differ so greatly that it may become a nightmare trying to merge the branch back to the trunk. This situation is best avoided by regularly merging trunk changes to the branch. Make up a policy: once a week, merge the last week's worth of trunk changes to the branch. Take care when doing this; the merging needs to be hand-tracked to avoid the problem of repeated merges (as described in the section called вTracking Merges Manuallyв). You'll need to write careful log messages detailing exactly which revision ranges have been merged already (as demonstrated in the section called вMerging a Whole Branch to Anotherв). It may sound intimidating, but it's actually pretty easy to do. At some point, you'll be ready to merge the вsynchronizedв feature branch back to the trunk. To do this, begin by doing a final merge of the latest trunk changes to the branch. When that's done, the latest versions of branch and trunk will be absolutely identical except for your branch changes. So in this special case, you would merge by comparing the branch with the trunk:
$ 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
в¦
By comparing the Another way of thinking about this pattern is that your weekly sync of trunk to branch is analogous to running svn update in a working copy, while the final merge step is analogous to running svn commit from a working copy. After all, what else is a working copy but a very shallow private branchЁ It's a branch that's only capable of storing one change at a time. [23] The Subversion project has plans, however, to someday implement a command that would accomplish the task of permanently deleting information. In the meantime, see the section called вsvndumpfilterв for a possible workaround. | All Plans Include
| ||||||||||||||||||||||||||||||||||||||||||||