Contents
- 1 Three steps for initializing bi-directional merging
- 2 Step 1: Create Your Feature Branch
- 3 Step 2: Initialize Merge Tracking To The Branch
- 4 Step 3: Initialize Merge Tracking To The Trunk
- 5 Setting Up Your Environment
- 6 Keeping your feature branch up to date
- 7 Merging Changes from the trunk to your branch
- 8 Merging Changes from the feature branch back to the trunk
This tutorial discusses what feature branches are and how to implement and manage them using Subversion and svnmerge.py. If you’ve made it here, I’m going to assume that you are already interested in using feature branches, so I’ll skip the sales pitch for the most part.
Feature branches offer a very stable development model where the trunk is always buildable and stable. Features are developed on private branches, and only when they are complete and working are they merged to the trunk. This model allows developers to commit “early and often” without fear of breaking the trunk and stalling other developers. Also, changes can easily be reviewed if needed as check-ins are made on the feature branch.
Note that this tutorial assumes that you are using the standard Subversion “branches, tags, trunk” repository layout. If not, you’ll have to translate the paths to your repository layout, but the concepts illustrated here still hold true. All the examples provided use the subversion command line and svnmerge.py.
The layout of the repository that is used in this example looks like the following:
Three steps for initializing bi-directional merging
- Create your feature branch
- Initialize merge tracking to the branch
- Initialize merge tracking to the trunk
Step 1: Create Your Feature Branch
Creating your feature branch is just like creating any other branch in subversion, you use the subversion copy command. We will branch remotely, so for this operation, no working copy is needed. Here we are branching (copying) the trunk to our private branch.
C:\temp\test>svn copy -m "create my feature branch" https://svn.example.com/svn/test/project1/trunk/ https://svn.example.com/svn/test/project1/branches/private/user1/feature1/ Committed revision 9. C:\temp\test>
The subversion client requires a working copy for just about everything it does, so next we will create a directory to hold the trunk and check it out.
C:\temp\test> mkdir trunk C:\temp\test>cd trunk C:\temp\test>svn co https://svn.example.com/svn/test/project1/trunk . A file1.cpp Checked out revision 9. C:\temp\test\trunk>
We will also check-out our feature branch now as well.
C:\temp\test\trunk>cd .. C:\temp\test>mkdir feature1 C:\temp\test>cd feature1 C:\temp\test\feature1>svn co https://svn.example.com/svn/test/project1/branches/private/user1/feature1 . A trunk A trunk\file1.cpp Checked out revision 9. C:\temp\test\feature1>
In step 1 we have created the feature branch, created local directories to hold both the branches and checked both branches out into the newly created directories. No lets initialize merge tracking.
Step 2: Initialize Merge Tracking To The Branch
In this step we will initialize merge tracking from the trunk to the branch. This will allow the trunk to track changes in the branch. For this operation we need to be in the directory we checked-out the trunk to.
C:\temp\test\feature1>cd ..\trunk C:\temp\test\trunk>svnmerge.py init -f commit.txt https://svn.example.com/svn/test/project1/branches/private/user1/feature1 property 'svnmerge-integrated' set on '.' C:\temp\test\trunk>svn ci -F commit.txt Sending . Committed revision 10. C:\temp\test\trunk>rm commit.txt C:\temp\test\trunk>
The “-f” argument tells svnmerge.py to create a file using the specified name (commit.txt) containing an appropriate check-in comment, and the URL specifies the location of the branch to track (out feature branch in this case). At this point we have merge tracking initialized in one direction. The trunk is tracking changes on the branch. Next we need to initialize merge tracking in the other direction.
Step 3: Initialize Merge Tracking To The Trunk
In this step we will initialize merge tracking from the feature branch to the trunk. This will allow the feature branch to track changes that occur on the trunk and will complete the initialization of bi-directional merging. Initializing merge tracking in this direction is similar to previous step, except we don’t specify a URL for the branch. This causes svnmerge.py to search though the history looking for “copies” to determine where to start tracking changes.
C:\temp\test\trunk>cd ..\feature1 C:\temp\test\feature1>svnmerge.py init -f commit.txt property 'svnmerge-integrated' set on '.' C:\temp\test\feature1>svn ci -F commit.txt Sending . Committed revision 11. C:\temp\test\feature1>
Now we have merge tracking initialized in both directions and can begin merging changes with svnmerge.py.
Setting Up Your Environment
To make things easier, I have a couple of scripts that reduce the amount of typing that is needed when using svnmerge.py. When using svnmerge.py, the URL of the branch that you are merging from must be provided every time. The goal is to be able to type one simple command to merge. I start out by defining two environment variables containing the URLs to the trunk and the private branch.
Under Windows Vista, to add environment variables you can go the control panel and type “environ” in the search bar, then select “Edit environment variables for your account”. Then add the variables under the “users” section. Something like “MYTRUNK=http://…” and “MYBRANCH=http://…”. On Unix’s, you will need to edit your shell’s start up file (eg. .bash_profile for the bash shell) and export the new variables there.
Example lines to add to a .bash_profile:
MYTRUNK=https://svn.example.com/svn/test/project1/trunk/ MYBRANCH=https://svn.example.com/svn/test/project1/branches/private/user1/feature1/ export MYTRUNK MYBRANCH
Next create two batch files (shell scripts on Unix), mergefrombranch.bat and mergefromtrunk.bat.
Contents of mergefrombranch.bat
@svnmerge.py merge -b -S %MYBRANCH%
Contents of mergefromtrunk.bat
@svnmerge.py merge -b -S %MYTRUNK%
The “-b” flag tells svnmerge.py that this is a bidirectional merge so that it knows to filter out reflected revisions from the merge candidates. Without it, you will see reflected revisions (svnmerge’s own commits) and may have other problems as a result. The “-S” flag tells svnmerge.py what URL to use as the source of the merge.
Keeping your feature branch up to date
When using feature branches, all your changes happen on your own private branch. Others are making changes on their private branches and when those changes are ready, they merge them to the trunk. To minimize the amount of work required to merge, you might want to merge trunk changes onto your private branch periodically. I recommend once a week or more often, but you will have to determine what interval is best for your situation. This will help reduce the number of changes in any given merge to a manageable number, making the merge easier and less risky. The longer you wait, the bigger the delta between your branch and the trunk, which will result in more risk that something will conflict or otherwise go wrong.
Merging Changes from the trunk to your branch
This is something that you should do often and the mergefromtrunk.bat script makes it easy. It may be a good idea to see what changes are available in the trunk before you actually merge. To do this, use the following command from a DOS window (or the equivalent in a Unix shell):
Check for available revisions to merge
c:\temp\test\feature1>svnmerge.py avail -b -S %MYTRUNK% 11 c:\temp\test\feature1>
This shows that there is one revision available (revision 11). If there are more, they will all be listed with contiguous revisions being represented by ranges (1-9 11-15 etc). Next we’ll merge revision 11 into our feature branch using the mergefromtrunk.bat script.
Merge revision from the trunk to the feature branch
c:\temp\test\feature1>mergefromtrunk.bat property 'svnmerge-integrated' deleted from '.'. property 'svnmerge-blocked' deleted from '.'. U file1.cpp property 'svnmerge-integrated' set on '.' property 'svnmerge-blocked' deleted from '.'. C:\temp\test\feature1>
You can see that one file (file1.cpp) was updated and several properties which svnmerge.py uses to track revisions were updated. Check to make sure there are no conflicts listed for any files in the output. If there are, you will need to resolve those before proceeding. Next, compile the code if needed, make sure that unit tests pass (you do use unit tests … right?), whatever you do to verify that you have a working, healthy tree. Once that is done, it is time to check-in the merged changes. Note that svnmerge.py also created a file named “svnmerge-commit-message.txt” which contains the check-in comments for all the revisions being merged. This should be used as the check-in comment when we commit the merged changes.
Commit the merged changes to the feature branch
C:\temp\test\feature1>svn ci -F svnmerge-commit-message.txt Sending . Sending file1.cpp Transmitting file data . Committed revision 12. C:\temp\test\feature1>del svnmerge-commit-message.txt C:\temp\test\feature1>
In this example the contents of svnmerge-commit-message.txt look like this:
Merged revisions 8,10-11 via svnmerge from https://svn.example.com/svn/test/project1/trunk ........ r11 | jss | 2008-01-17 22:39:39 -0800 (Thu, 17 Jan 2008) | 1 line added a comment line ........
Merging Changes from the feature branch back to the trunk
You typically do this when you are ready to make your changes/features available to others. The same basic operations that we performed to merge in changes from the trunk are used here, but instead of using the mergefromtrunk.bat script, the mergefrombranch.bat script is used instead.
C:\temp\test\trunk>svnmerge.py avail -b -S %MYBRANCH% 13 C:\temp\test\trunk>mergefrombranch.bat property 'svnmerge-integrated' deleted from '.'. property 'svnmerge-blocked' deleted from '.'. U file1.cpp property 'svnmerge-integrated' set on '.' property 'svnmerge-blocked' deleted from '.'. C:\temp\test\trunk>svn ci -F svnmerge-commit-message.txt Sending . Sending file1.cpp Transmitting file data . Committed revision 14. C:\temp\test\trunk>del svnmerge-commit-message.txt C:\temp\test\trunk>
Now you have completed merges in both directions and should be prepared to happily use your new feature branch. Good luck!