Subversion Branch & Merge Checklist

In Subversion, branches and tags are nothing more than copies of a revision. The fact that branches are located in the branches directory and tags in the tags directory is purely convention.

In the examples below, my-repo is the hostname of the Subversion server, project-x is the project that we wish to branch, and my-branch is the name of the branch that we want to create.

Branching

Here's how to create a branch called my-branch from the trunk of a project called project-x using either the svn command or the Subclipse user interface.

  • Using command line:
    Branching with svn

    svn copy -r HEAD -m "Created branch my-branch." svn://my-repo/project-x/trunk svn://my-repo/project-x/branches/my-branch

  • Using Subclipse:
  1. In the Navigator view, right-click on project-x.
  2. Click Team then Branch/Tag...
    Branch menu

  3. In From, enter the path to the trunk for project-x. In this case, svn://my-repo/project-x/trunk.
  4. In To, enter the path to the new branch for project-x. In this case, svn://my-repo/project-x/branches/my-branch. (This path should not exist.)
  5. In the revision, choose HEAD.
  6. Enter a comment using the convention Created branch my-branch.
  7. Click OK.
    Branch dialog

  8. Go to the SVN Console and record the revision created by the branch (i.e. copy) operation. This will be used during a merge.
    SVN Console

Merging

There are a few Subversion fundamentals that should be presented first. In Subversion, a revision is a snapshot of the entire repository. So saying "revision 18 of HelloWorld.java" is incorrect. Instead, you should say "HelloWorld.java in revision 18 of the repository."

Can you just ask a tool to compare two revisions? Asking a tool to compare revision 18 and revision 32 is asking the tool to compare the entire repository at versions 18 and 32. This is almost certainly not what you want. Instead, you must supply paths along with revisions!

A path and revision combination can be written as sourceURL@Z where sourceURL is the full path (including hostname) and Z is the revision. The @-symbol is the separator. Now it makes sense to compare svn://repo/project-x/trunk/HelloWorld.java@18 and svn://repo/project-x/trunk/HelloWorld.java@32.

Merging (in the context of revision control) is usually a 3-way merge. A 3-way merge is when two descendant files are merged with a common ancestor as the base. The algorithm goes like this: Start with the base, take per-line changes from left and right as long as they do not conflict. If they conflict, let the developer resolve the line(s) manually. So given two sourceUrl@Z values, and the fact that merges involve three files, what's the third? It's the common ancestor of the two sourceURL@Z values!

Useful Information

How do I know what the common ancestor of two files is? In general, it's the last revision when the two files were synchronized. In practice, it's usually the revision when the copy was made--in other words, when the branch was created.

Merge Example: Merge Branch Changes Into Trunk

Assume that you want to merge svn://repo/project-x/branches/my-branch@HEAD and svn://repo/project-x/trunk@HEAD and put the result into the trunk. And assume that the common ancestor of both is svn://repo/project-x/trunk@19015. Finally assume that your working copy at /home/jsmith/project-x points to svn://repo/project-x/trunk@HEAD.

What does this look like conceptually?

Conceptual Diagram of 3-Way Merge

What does this look like on the command line and in Subclipse?

  • Using command line (/home/jsmith/project-x is the working copy):
    Merging with svn

    svn merge svn://repo/project-x/trunk@19015 svn://repo/project-x/branches/my-branch@HEAD /home/jsmith/project-x

  • Using Subclipse:
  1. In the Navigator view, right-click on project-x.
  2. Click Team then Merge...
    Merge menu

  3. In From, enter the value svn://repo/project-x/trunk.
  4. In the revision, enter 19015.
  5. In To, enter the value svn://repo/project-x/branches/my-branch.
  6. In the revision, choose HEAD.
  7. Enter the comment Created branch my-branch.
  8. Click Merge. (Click Dry Run to show the changes that will be made.)
    Merge dialog

How does the conceptual diagram of a 3-way merge compare to this merge dialog?

Mapping from conceptual diagram to dialog fields

Useful Information

"I see a From and a To; where's the third path in the 3-way merge?!" The third path is your working copy, which we stated is pointing to the HEAD revision of the trunk path.

Merge Example: Merge Trunk Changes Into Branch

If you would like to get the changes that have occurred in the trunk while you've been working in your branch, complete a merge that looks like the following:

Merge dialog

Now here's how the conceptual diagram relates to the merge dialog.

Mapping from conceptual diagram to dialog fields

Useful Information

After a merge from the trunk into your branch, you should record the end revision of the trunk (i.e. the HEAD revision at the time of the merge). Let's call this revision r1. r1 becomes the ancestor in future merges!

Checklist

Now that we have the fundamentals down, let's cover the actual checklist.

  1. Tag destination branch(es). Use the naming convention before-merge-source-branch.
  2. Lock (aka freeze) source branch(es) indefinitely. (Subversion has no high-level freeze operation but the administrator can mark the branch(es) as read-only.)
  3. Checkout destination branch(es) into working copy (or update and make sure there are no modifications in the working copy).
  4. Merge source branch(es) to destination branch(es). (Use the example above as a guide.)
  5. Resolve conflicts. (Subversion will automatically merge files and the files that it cannot automatically merge will be marked with a Conflict.) Here's a quote from the Subversion manual:

    Another small difference between svn update and svn merge are the names of the full-text files created when a conflict happens. In the section called "Resolve Conflicts (Merging Others' Changes)", we saw that an update produces files named filename.mine, filename.rOLDREV, and filename.rNEWREV. When svn merge produces a conflict, though, it creates three files named filename.working, filename.left, and filename.right. In this case, the terms "left" and "right" are describing which side of the double-tree comparison the file came from. In any case, these differing names will help you distinguish between conflicts that happened as a result of an update versus ones that happened as a result of a merge.


    To help you understand how the .left, .right, and .working files that are generated during a conflict relate to the conceptual 3-way merge diagram, the diagram below shows those three files in the same setup as the 3-way diagram from earlier.
    How the files generated during a conflict relate to 3-way conceptual diagram

    1. Find the conflicts. (The svn console will have notified you of conflicts (denoted with a 'C')).
    2. Right-click the file marked as Conflict and click Edit Conflict.
    3. Manually resolve lines.
    4. Click Mark Resolved.
  6. Test merged code locally by:
    1. Running any applicable unit tests.
    2. Running any development or production build scripts.
    3. Running the code.
  7. Commit merged code.
  8. Tag destination branch(es). Use the naming convention after-merge-source-branch.

References