Skip to main content

Merging

Merging branches

The primary use of the merge command is to replicate changes that occurred on one branch into another branch. After the moment when two branches (typically, the trunk and some feature branch) split, each branch can receive commits that affect only that particular branch. So, the contents in these two branches start to diverge with each commit.

r0----r1----r2---------r5----r7---- trunk
\
r3----r4----r6------- my_branch

Merging allows you to combine the changes that occurred on the two branches, by bringing changes from one branch (the source branch to take the changes from) into another branch (the target branch to receive the changes).

For instance, in the example above, you may want to bring the changes that occurred on the trunk (the source) in revisions r5 and r7 into the branch my_branch (the target). Later, when your work on my_branch is complete, you may eventually want to bring the changes made on my_branch (the source), including changes made in revisions r4 and r6, into the trunk (the target).

Merge as a diff-and-apply

In Subversion, a merge is essentially an ancestry-aware diff-and-apply operation. A merge calculates the branch-specific changes that occurred on the source branch that you are merging. And it applies these same changes to the working copy of the target branch.

The merge procedure

A merge is always done into a working copy that reflects the target branch. The merge operation itself modifies only this working copy. The repository does not know anything about the merge until you commit the resulting merged changes from your working copy to the target branch in the repository.

In general, merging a source branch into a target branch works as follows:

  1. You get a working copy of the target branch (by checking out a new working copy of that target branch or by switching an existing working copy to reflect that target branch).
  2. You merge the source branch into that working copy.
  3. You commit the merged changes. Since your working copy reflects the target branch, the merged changes get committed to the target branch.

Merge direction: sync and reintegrate merges

Based on the direction of the merge, a merge can be considered a sync merge or a reintegrate merge. This depends on which of the two branches was historically created from which, and on the direction in which the changes are brought over (i.e. which of the two branches you use as the source and the target respectively). For example, a new feature branch (e.g. my_branch) starts as a copy of the trunk. If we use this example, then:

  • A sync merge is when you merge changes from the trunk into the feature branch (e.g. into my_branch). Periodically merging the changes that occurred on the trunk allows the feature branch to catch up, so that this feature branch does not lag too far behind the current state of the trunk. This is typically done while your work on the feature branch is still ongoing, and the feature branch is not yet ready for a reintegrate merge.

  • A reintegrate merge is when you merge changes from the feature branch (e.g. from my_branch) into the trunk. Typically, this is done when your work on the feature branch is finished, so that the work that has been done in isolation on the feature branch gets integrated into the trunk, the project’s main line of history.

The command to merge a branch

For the most typical automatic merge, the syntax of the merge command is as follows:

svn merge SOURCE_BRANCH_URL TARGET_WCPATH

This variant of the command is known as an automatic merge, because the command does not explicitly specify the revisions that you want to merge from the source branch.

The SOURCE_BRANCH_URL is the URL of the source branch from which you are taking the changes to merge into the working copy. You can specify it as a relative URL, relative to the repository’s root. For example, a relative SOURCE_BRANCH_URL during a reintegrate merge may be "^/branches/my_branch". Or you can specify it as an absolute URL (e.g. https://svn.example.com/MyRepo/branches/my_branch).

TARGET_WCPATH is the local working copy path that reflects the target branch and is going to receive the incoming changes. An explicit TARGET_WCPATH is optional and might look like C:\Users\Alex\my_work_dir. When TARGET_WCPATH is not specified explicitly (as in the first two examples below), it means that the implied TARGET_WCPATH is the current directory in which you run the merge command. This means that when using the implied TARGET_WCPATH (as in the first two examples below), you should run the merge command from the root directory of your working copy, which should reflect the root of the target branch.

For example, the command for a sync merge, which brings changes from the trunk into a feature branch, may look as follows:

C:\Users\Alex\my_work_dir> svn merge "^/trunk"

This assumes that my_work_dir is currently switched to reflect the target feature branch (e.g. my_branch).

The command for a reintegrate merge, which brings changes from a feature branch (e.g. from my_branch) into the trunk, may look as follows:

C:\Users\Alex\my_work_dir> svn merge "^/branches/my_branch"

This assumes that my_work_dir is currently switched to reflect the trunk.

For an example of using sync and reintegrate merges in a workflow, see Step 4: Keep your feature branch in sync with trunk and Step 5: Merge (reintegrate) your feature branch into trunk in the feature branch workflow.

Alternatively, you can explicitly specify the TARGET_WCPATH (absolute or relative). In this case, you can run the merge command from a different directory. For instance, the preceding example can be also rewritten as:

C:\Users\Alex> svn merge "^/branches/my_branch" C:\Users\Alex\my_work_dir

This assumes that the my_work_dir directory located at the specified path is currently switched to reflect the trunk.

Automatic merge

Although you can explicitly specify which commits (revisions) you want to merge from the source branch, most often you don’t specify the revision numbers explicitly in the merge command. Such a merge where you don’t explicitly specify revisions to merge is known as an automatic merge. In most cases, an automatic merge is the preferable way to merge branches.

Such an automatic merge means that Subversion should merge all the changes from the source branch that have occurred since the moment when the source and the target branch split (and that have not yet been merged to the target branch).

In the example below, Subversion would know that the last common ancestor of the trunk and the branch named experiment_by_john is revision r44, after which they split:

r42----r43----r44-----------r47-----r49-----r50----- trunk
\
r45-----r46-----r48----------------- experiment_by_john

An automatic sync merge of changes from the trunk (the source) into the branch named experiment_by_john (the target), where you don’t specify revision numbers to merge, would look as follows:

C:\Users\John\my_work_dir> svn merge "^/trunk"

This assumes that my_work_dir is currently switched to reflect the branch experiment_by_john.

At this stage in history, this automatic sync merge would merge all the branch-specific changes that occurred on the trunk (i.e. changes from revisions r47, r49 and r50) into the working copy of experiment_by_john.

In modern Subversion, an automatic merge can automatically determine whether a merge is a sync merge or a reintegrate merge, and adjust how it calculates the source branch’s changes based on that.

An automatic merge is possible only for branches that are ancestrally related to each other. To ensure that, the branches must be created properly, see The command to create a new branch.

Merge tracking (mergeinfo)

Quite often you may want to repeatedly merge the same source branch into the same target branch. Typically, this occurs when you want to periodically sync changes from the trunk into your feature branch.

To prevent the next merge from merging revisions that have already been merged from that source to that target branch, Subversion records the so-called merge tracking information (mergeinfo), to keep track of which revisions it has already merged into the current branch (and from which source branch).

For example, suppose that at the following stage you want to sync changes from the trunk into the feature branch called my_branch:

r0----r1----r2---------r5----r7---- trunk
\
r3----r4----r6------- my_branch

To perform that sync merge, you perform an automatic merge of the trunk into your working copy that reflects my_branch:

C:\Users\Alex\my_work_dir> svn merge "^/trunk"

During this merge, Subversion records that all the changes that have occurred on the trunk since the splitting off of my_branch have now been merged into the working copy. Subversion records this by automatically creating or modifying a versioned property called svn:mergeinfo, which it attaches to the target path of the merge operation. Normally, the target path of the merge is the root directory of your working copy.

This information in the svn:mergeinfo property is recorded in a human-readable form. If you wish, you can display the full value of the svn:mergeinfo property attached to a working copy path of interest. To display the value of svn:mergeinfo attached to the root directory of your working copy, run the following command from the root of your working copy:

C:\Users\Alex\my_work_dir> svn propget svn:mergeinfo .

The dot . at the end means you want to get the value of the svn:mergeinfo property attached to the current directory in which you run the command (i.e. my_work_dir). In this example, the value of svn:mergeinfo would be /trunk:3-7, which means that any changes that occurred on the trunk in revisions from 3 through 7 have been merged into this working copy of my_branch.

Importantly, the svn:mergeinfo property is maintained and used automatically by Subversion, and you do not need to (and in fact must not) do anything yourself with this versioned property. You just need to be aware of it, so that you do not prevent these local automatic mergeinfo changes from being committed when committing the merge.

Suppose that you now commit all the changes that the working copy received via this merge, they get committed to my_branch as revision r8. This commit also contains the modified svn:mergeinfo property, which was a property change on working copy’s root directory and is committed as a property change on the corresponding directory in the repository (i.e. on the my_branch directory).

If later you decide to sync merge changes from the trunk into the branch my_branch again, Subversion will look at the svn:mergeinfo property at the root of your working copy (which would reflect the root of my_branch). And based on the value of this property it will know that it should sync only the eligible revisions that have not yet been synced to my_branch before. In the following example, these would be revisions r9 and r11 from the trunk:

r0----r1----r2---------r5----r7----r9----r11---- trunk
\ \
r3----r4----r6----r8----r10------- my_branch

Avoiding subtree mergeinfo

When you always merge the whole source branch into the root of a target branch, every branch will have all its mergeinfo recorded at the root of the branch, which is a good practice. You should avoid merging just a part of a source branch into a target branch, which is known as a subtree merge.

For example, imagine your trunk contains several subdirectories, such as build, docs and tests. If you merge only a specific subdirectory (for example, "^/trunk/tests") from the trunk (the source) into the corresponding subdirectory on your feature branch (the target), the mergeinfo will be recorded specifically on that subdirectory (in this example, on the tests subdirectory inside the target feature branch). Such mergeinfo that is attached to a subtree (a subdirectory or a file) inside the target branch is known as subtree mergeinfo. The subtree merges like the following should be avoided:

C:\Users\Alex\my_work_dir\tests> svn merge "^/trunk/tests"

Despite the fact that Subversion supports subtree merges and can handle the subtree mergeinfo that they create, avoiding subtree merges is strongly recommended, because they introduce unnecessary complexity. If you use subtree merges, over time your branches would gradually end up in a very complex state with mergeinfo on many subtree paths. This would slow down and potentially complicate future merges.

Non-automatic merges

Non-automatic variants of the merge command fall beyond the current scope of this guide, but they deserve a mention. For the non-automatic merges, Subversion supports extended syntax that gives you more granular control over the merge, as compared to the automatic merge.

Cherrypick merge

A “cherrypick” merge is only slightly more advanced than an automatic merge. The only difference is that it lets you manually specify the revisions (commits) that you want to merge from the source branch.

For example, the following command will merge only the changes made in revision r5 from the trunk:

C:\Users\Alex\my_work_dir> svn merge "^/trunk" -c 5

This assumes that my_work_dir is currently switched to reflect a desired target branch (e.g. my_branch).

Whereas the following command would merge the changes made in the revision range from r4 (non-inclusive) through r7 (inclusive) from the trunk:

C:\Users\Alex\my_work_dir> svn merge "^/trunk" -r 4:7

This assumes that my_work_dir is currently switched to reflect a desired target branch (e.g. my_branch).

For more information, see the section titled Cherrypicking in the book Version Control with Subversion.

Fully manual 2-URL merge

The 2-URL merge gives you the fullest control over defining the incoming merged changes. A 2-URL merge calculates the incoming changes as the difference between two directories at particular revisions, as specified by the two URLs in the command. These changes are then applied to your working copy.

As mentioned in the Best practices chapter, this fully manual variant should generally be avoided unless you are an expert at Subversion. This is because it allows you to compare any two URLs, even those that are not ancestrally related, and requires that you manually specify the left and right side of the comparison that calculates the incoming changes.

For example, the following command will merge the changes calculated as the difference between revision r4 and revision r7 of the trunk:

C:\Users\Alex\my_work_dir> svn merge "^/trunk@4" "^/trunk@7"

This assumes that my_work_dir is currently switched to reflect a desired target branch (e.g. my_branch).