Merging using SVK
There are two common use cases that correspond to two types of branches (discussed in the "Branching" article).
Short-lived branches (
These tend to be local-only branches created for the purposes of working on a specific bug or feature without disrupting the trunk (or other important branch from which they were originally created). These are generally created like this:
# make the local-only branch using "cp"; check out a working copy svk cp //mirrors/project/trunk //local/project/bug-351 svk co //local/project/bug-351 src cd src
To merge work being down on the trunk:
To merge back to the trunk once the work is finished:
svk push --verbatim
If renames are involved, we need to use
svk smerge and pass the
--track-rename switch (
svk push is equivalent to
svk smerge -If . but
svk push doesn’t support all of the options that
svk smerge does). In my opinion
--track-rename should be on by default, but it is somewhat slower than a normal
svk push so that is probably why it has to be turned on explicitly:
# if many changes are involved, it may be wise to do a dry-run first svk smerge -If . --verbatim --track-rename --check-only svk smerge -If . --verbatim --track-rename
Once all the work on the branch is done:
svk co --detach cd .. rm -r src
So the basic characteristics here are:
- At the end we want there to be total synchronization between the branch and the trunk.
- That is, the development lines are not intended to diverge in the long term.
- Any divergence is intended to be temporary only.
- The purpose of the short-lived branch is to shield the trunk from disturbance and temporary breakage during this period of minimal divergence.
- Merges are wholesale; that is, when pushing and pulling we want all changes to be merged.
- Once the work on the branch is completed its usefulness has effectively come to an end.
- With such a short lifetime there is probably no reason for the branch to exist in the remote repository; a local branch is good enough.
- The local branch provides a degree of backup, redundancy and progress tracking that would not be possible without it (the developer would have to "hold back" their changes and only commit them at the very end).
- SVK makes pushing and pulling very easy, so this kind of short-lived branch is very feasible.
Long-lived branches (cherry-picking)
As a one-man shop I don’t have the time or the resources to actively work on multiple long-lived branches. This does not mean, however, that long-lived branches don’t have their usefulness; rather, it means that the branches tend to fall into two categories:
- Active branches: typically there is only one active branch, the trunk.
- Maintenance branches: long-lived but not very active branches. These correspond to stable points in the development which may need to receive minimal updates periodically.
These branches are typically created as in the following example. This is a project,
project_x which has just released version 2.8. Development work is to continue on the trunk (for version 3.0) and a maintenance branch will be created for the 2.8 series:
svk cp //mirrors/project_x/trunk //mirrors/project_x/branches/2.8 svk cp //mirrors/project_x/trunk //mirrors/project_x/tags/2.8-release
Note that the created branch is not a remote, non-local branch. We also create a tag for the release. The tag and the branch are very similar; the tag is basically like a read-only branch.
The kind of merging that typically gets done between the active branch and the maintenance branch is called "Cherry picking". Rather than merging all of the work that’s being done on the trunk into the maintenance branch we are typically interested in an isolated bugfix or minor feature addition. This kind of merge would usually be done as follows; in this example we merge only the change corresponding to revision 2200 (strictly speaking the difference between 2199 and 2200):
# from inside the 2.8-branch working copy svk merge -c 2200 //mirrors/project_x/trunk
You can even back-out a change by prepending the change number with a minus sign:
svk merge -c -2200 //mirrors/project_x/trunk
Here is a similar example that merges a range of 5 different changes:
svk merge -r 2130:2135 //mirrors/project_x/trunk
---------------------------------------------------------------------- r2340 (orig r330): wincent | 2007-03-22 19:07:11 +0100 Add symbolic link to WOCommon (non-nested checkout)
I am not sure, but I believe that SVK is smart enough to not re-apply the same merge twice even if you ask it to. I’ve sought clarification in this mailing list thread but haven’t yet received a reply.
Merging using Subversion
The following example is based on the procedure described in the Subversion book. The objective is to merge all the changes that have been made on the
panther branch into the
Assume we have a working copy of the
panther branch at
project-name/panther/ and a copy of the
project-name/trunk/. Both copies must be up to date.
# either checkout the trunk svn co svn+ssh://svn.example.com/project-name/trunk trunk # or update an existing copy already on the disk svn up trunk # make sure the branch from which you wish to merge is up to date and committed svn up panther svn status panther svn commit -m "Commit that bug fix before the merge!" # optionally, view the differences between the two branches opendiff panther trunk # identify the revision number when the branch was first done svn log --stop-on-copy panther
In this case we see the following, indicating that the current HEAD is revision 293:
r293 | wincent | 2006-09-21 21:20:31 +0200 (Thu, 21 Sep 2006) | 1 line Synergy Advance now lives in a repository of its own
And the branch was created at revision 274:
r274 | wincent | 2006-09-19 15:17:24 +0200 (Tue, 19 Sep 2006) | 1 line Creating Panther branch
So to perform the merge:
# do the merge svn merge -r 274:293 panther trunk # view what actually happened svn diff trunk | less # if all went well, commit and include the revision numbers in the commit message svn commit -m "Merge r274:293 from panther branch to trunk" trunk
It is critical that you include the revision numbers because you’ll need them later in future merges to keep track of what has already been merged (you don’t want to merge the same changes twice).
# looking for previous merge svn up trunk # show current revision svn info trunk # look for merge details svn log trunk
This is what you’re looking for:
r294 | wincent | 2006-10-26 13:50:05 +0200 (Thu, 26 Oct 2006) | 1 line Merge r274:293 from panther branch to trunk
This is why we used
svn merge -r 274:293 instead of
svn merge -r 274:HEAD; it is important to keep track of which changes have already been merged. A subsequent merge would start at revision 294.
See "Synergy branch notes" for information about the changes that have been merged.
# check out up-to-date working copies svn co svn+ssh://.../trunk trunk svn co svn+ssh://.../branches/panther panther # visually inspect differences between the branches opendiff panther trunk # determine the current revision number of the branch (330) svn log panther|less # determine where the branch started (274) svn log panther --stop-on-copy|less # determine where the last merge was performed (294) svn log trunk|less # merge unmerged changes back onto trunk (no conflicts) svn merge -r 294:330 panther trunk # visually inspect the result opendiff panther trunk svn diff trunk|less # commit the changes including the merged range svn commit -m "Merge r294:330 from panther branch to trunk" trunk
One last merge
svn co svn+ssh://svn.wincent.com/Synergy/trunk trunk svn co svn+ssh://svn.wincent.com/Synergy/branches/panther panther opendiff panther trunk svn log panther --limit 10 svn log trunk --limit 1 svn merge -r 331:332 panther trunk svn diff trunk|less svn commit -m "Merge r331:332 from panther branch to trunk" trunk rm -rf trunk panther
I then disposed of my old SVK working copy (of the
panther branch) and checked out a new copy of the trunk:
svk co --detach src rm -r src svk co //mirrors/Synergy/trunk src # after setting final version number svk ci -m "Set version numbers for 3.1.1 release"
After building the final release:
# tag cd src svk cp //mirrors/Synergy/trunk //mirrors/Synergy/tags/3.1.1 -m "Tag 3.1.1 release" # after bumping version number (to 3.1.1+) svk ci -m "Bump version number post-release" # branch svk cp //mirrors/Synergy/trunk //mirrors/Synergy/branches/jaguar -m "Create Jaguar branch from trunk" # switch svk switch //mirrors/Synergy/branches/jaguar svk info # elsewhere, continue Leopard development cd ../path_to_leopard_dev_area svk co //mirrors/Synergy/trunk
From this point on I’ll be using SVK to manage these branches as described elsewhere (expect mostly cherry picking). Leopard development will occur on the trunk (version 3.5) and maintenance releases (3.1 series, possibly 3.2, 3.3 and 3.4 as well) will continue on the Jaguar branch. The ill-fated Panther branch will be allowed to die off.