Tuesday, October 7, 2008

How to branch and merge with Subversion

These are my rules, your mileage may vary.

In the svn world, branches and tags are essentially the same thing. They are copies of code. By convention, trunk is for work for the next release, branches are for subsequent releases, and a tag is a snapshot of trunk or a branch.

Branching rules:
1. Branch late. Merging is hard, the longer you can put off making a branch, the easier it will be to merge it later on.
2. Branch only when necessary. It is necessary when the check in policy on a code line changes, for example, trunk is locked down as a release is pending and you have code for the next release.
3. Branch remotely. Create branches directly in the repository, not from your local working copies. The check in comment for a branch should state why the branch was created.
4. Branch from trunk. Avoid branching from a branch. Sometimes it is necessary, but avoid if at all possible. It just makes merging harder.
5. Keep a branch and merge map. Use a drawing or notes to keep track, check this document into subversion in the base of your project and keep it up to date.

Tagging rules:
1. Tag often. The check in comment for a tag should state why the tag was created.
2. Tag remotely. Create tags directly in the repository, not from your local working copies.
3. Tag releases. Make sure you can tell release tags from other tags.
4. Mark release tags on your branch and merge map.
5. Tags should be treated as immutable. Don't check in changes in a tag, make a new tag instead.

Merging rules:
1. Merge often. It is easier to do a number of small merges, like on a weekly basis, rather than a single large merge after months of changes.
2. Merge locally. Do clean check outs of the code to be merged, do the merge, build and run unit tests until you are convinced the merge is correct. Commit only when satisfied.
3. Mark merges on your branch and merge map.
4. Don't overlap merges. If you already merged from revisions 100 through 120, the next merge should not be 100 through 140, it should be 121 through 140. (Update: SVN 1.5 now handles this situation automatically, but you need both a 1.5 client and server.)

The Switching rule:
The svn "switch" command makes it easy to change your working copy from code in one branch to code in another. The rule for switching is that there must not be local modifications that have not yet been committed. Check status on your local working copy before switching to a new branch. Commit or revert any changes before switching, otherwise, locally modified files will not be part of the switch and you may inadvertently check in code on the wrong branch.

Branch command:

svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Creating a private branch of /calc/trunk."

Subversion will automatically commit with this command.

Tag command:

svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/tags/release-1.0 \
-m "Tagging the 1.0 release of the 'calc' project."

Notice this is identical to the branch command. Again, Subversion will automatically commit with this command. By convention, tags should be treated as immutable. This is not enforced by Subversion.

Switch command:
Switching is a two step process:

1. Check status of your local working copies:

svn status

Ensure there are no locally modified files. If there are, either commit or revert your changes before switching.

2. Do the switch:

svn switch http://svn.example.com/repos/calc/branches/newbranch

Merge command:

Merging is a several step process. In this example, the merge will be from trunk to a branch.
1. Create a new, empty directory in which to do the merge:

mkdir merge
cd merge

2. In this directory, check out both the source and destination branches. This gives you two directories, "branch" and "trunk":

svn co http://svn.example.com/repos/calc/trunk ./trunk
svn co http://svn.example.com/repos/calc/branches/my-calc-branch ./branch

3. cd into the top of the destination directory:

cd branch

4. From your branch and merge map, determine what revisions to merge. For example, your branch was created at revision 100 and you want to merge the latest code from trunk to your branch, so your revisions are 100:HEAD. If in doubt, do a log on the branch:

svn log --recursive -v .

The last line of the log output will tell you what revision the branch was created from. Read the log entries for notes about previous merges.

5. Do a dry run:

svn merge --dry-run -r 100:HEAD ../trunk .

This means merge the code in ../trunk into the current directory (which is 'branch') and put the results in the current directory. Start the merge at revision 100 and merge all changes through HEAD. --dry-run means don't really do the merge, just show the output of what would happen.

If you see a lot of "Skipping" messages, it means you are probably merging backwards. Order is important in the merge command, this command:

svn merge --dry-run -r 100:HEAD . ../trunk

does not do the same as the command above. The merge command could be read like this:

svn merge from_this_code to_this_code

The results will be put in the current directory.

6. Do the merge:

svn merge -r 100:HEAD ../trunk .

7, Resolve conflicts. This is usually the hard part, and may require assistance from the authors of the conflicting code.

8. Build and run unit tests.

9. Commit. Make a note of the revision number on your branch and merge map. Next week when you want to merge from trunk again, use this revision number as your starting revision.

No comments: