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:
- 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).
- You merge the source branch into that working copy.
- 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
trunkinto the feature branch (e.g. intomy_branch). Periodically merging the changes that occurred on thetrunkallows the feature branch to catch up, so that this feature branch does not lag too far behind the current state of thetrunk. 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 thetrunk. 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 thetrunk, 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).