Personal tools
You are here: Home / cm / Wiki / Development Branches and Merging Changes
Navigation
Log in


Forgot your password?
 

Development Branches and Merging Changes

Think carefully before developing on a branch as it may make significantly more work for you.

Better Times to Use a Branch

Sometimes it may be a good idea to develop on a branch from the main

development trunk. Two cases where this seems like a good idea are:

1 A release branch is made at a (hopefully stable) point on the trunk. This allows bug fixes to be done on the release branch, and isolates the release branch from changes on the trunk that may introduce new bugs.

In this case the number of changes on the branch is probably small,

consisting only of small bug fixes. It is easier to manage the changes if they are first made to the branch then merged to the trunk. However often a bug fix may have already been applied to the trunk and will then be copied to the branch. Changes are usually small and therefore fairly easy to manage.

2 A development branch is made off the trunk so that a developer can record their (unstable) development without affecting the stability of the trunk.

In this case there are significantly more changes and there is therefore

more work involved in merging changes from branch to trunk or vice versa.

It will be easier for the developer to merge the branch into the trunk

later if changes on the trunk are regularly merged into the branch so that developments are done on the latest version of the code. However, as the branch becomes more and more different from the trunk merging the trunk changes can become more difficult.

Once the development is stable it can be merged into the trunk. After this

there is no need to continue development on the branch. Development can proceed on the trunk or a new branch can be created.

When Branches Do Not Work Well

The more different a branch becomes from the trunk the more work is involved

in merging changes, so keep use of branches to a minimum.

The merging processes are much simpler if during all merges all changes from

the branch or trunk are applied to the other. If there is not an intention that branch and trunk become the same and so some differences are not copied during a merge then things are going to be much more difficult.

If there are multiple people working on branch then the merging process can

become more difficult. The person merging changes may not be the person who first made the change and will have less understanding of what is involved in making the code consistent or resolving conflicts.

Watch out for changes made by others while merging.

Links

  • A discussion of some of the issues involved in "merging changes when using

development branches":http://www.gnu.org/software/gnu-arch/tutorial/development-branches.html is provided by "GNU arch":http://www.gnu.org/software/gnu-arch/.

is in Chapter 4 of the 3rd Edition of "Open Source Development with CVS":http://cvsbook.red-bean.com/

Instructions for Working with Branches.

Merging changes made from branch to trunk or vice versa can be a time

consuming and error prone process, but errors and time involved can be reduced significantly by automating as much as possible. This requires maintaining a record of the common ancestor.

Setting Up a Branch

To ensure that the ancestor is really at the start of the branch, it is

best to create an ancestor tag first and then start the branch on the ancestor tag. e.g.:

cvs rtag BRANCH_NAME_ancestor MODULES/FOR/BRANCH

cvs rtag -r BRANCH_NAME_ancestor -b BRANCH_NAME MODULES/FOR/BRANCH

If the ancestor is not set up when the branch is created, then there are some cases where it is not possible to determine the ancestor from the branch point. e.g. If a file is added on the branch after being added on the trunk after the creation of the branch, then the branch point in CVS will appear to be at the point where the file was added on the branch.

Creating a ancestor at the beginning also makes merging commands easier

(more consistent at least).

Checking Out and Updating a working directory

A CVS working directory can be set up with the command:

cvs co -r BRANCH_NAME MODULES

Usually a simple 'cvs up' will bring in any changes made to the branch. Beware though that 'cvs up -d' may well checkout new directories on the trunk instead of the branch, so use '-r BRANCH_NAME' when using the '-d' option.

Merging Some Changes

The command 'cvs up -j FROM_REVISION -j TO_REVISION FILE' can be useful

for transferring small changes between a branch and trunk.

This is most useful when used with a release branch. Bug fixes made on the trunk or release branch will likely also be applicable to the other.

However, for larger development lines, the transfer of small changes between development lines can interfere with the automation of the merging process. In order for a common ancestor to be maintained all unmerged changes from one development line need be merged to the other at once (as described below).

Beware that with 'cvs up' commands involving the '-j' options, CVS will exit success even if it cannot perform to merge. If the file has been modified on one development line and removed on the other or has been added on both lines, then it can't perform the merge. It will issue a message but if you miss this then there is no record that the merge failed and you risk losing the changes that you were trying to merge. Check the messages carefully.

As with normal updates:

  • CVS will exit success even if there are conflicts, but for simple conflicts markers are at least placed in the file that prevent a commit.
  • Binary files are handled badly. CVS normally just picks one version to use, moves the other out of the way, and exit success. CVS is not appropriate for handling binary files, but, if you do use them, check the messages carefully

If the merge transferred all the changes since the common ancestor from one development line to the other (i.e. FROM_REVISION is BRANCH_NAME_ancestor), then move BRANCH_NAME_ancestor to the revision from which all changes have been merged.

'cvs tag' only affects files that are checked out but this does not include files that have been removed, so 'cvs rtag' should be used to make tags:

cvs rtag -r TO_REVISION -F BRANCH_NAME_ancestor FILE

If FROM_REVISION was not BRANCH_NAME_ancestor then do not move the common ancestor. The TO_REVISION will not be a common ancestor if all changes made on its development line are not yet merged into the other development line. Using it as an ancestor for a subsequent merge from the other development line would undo the unmerged changes.

Merging All Changes From Branch or Trunk

The process of merging changes from branch or trunk to the other involves

applying all unmerged changes from the source (branch or trunk) to the destination and then moving the ancestor tag to indicate the new common ancestor, which is the revision from which the latest changes were merged.

Care needs to be taken that the ancestor tag is placed exactly at the revision on the branch or trunk from which all changes have been merged. If the tag is applied to the head of the trunk or branch after transferring changes it could be on a revision containing newly committed but unmerged changes. Moving the ancestor tag can be done safely by tagging the revision from which changes will be merged before the merge is done. (If only one person is working on a branch and changes are merged from the branch to trunk, not from the trunk, then this tagging can be skipped if the person is not going to make any changes to the branch during the merge and move ancestor process.)

1 Tag the revision from which changes are to be merged.

If merging from the branch:

cvs rtag -r BRANCH_NAME BRANCH_NAME_to_merge MODULES/TO/MERGE

or if merging from the trunk:

cvs rtag -r HEAD BRANCH_NAME_to_merge MODULES/TO/MERGE

Some people suggest giving the tag BRANCH_NAME_to_merge a unique name, and not removing it when done so that it can be used in the commit message. The files will soon have many tags if this is done.

2 Make a working copy based on the common ancestor and apply the changes to

merge.

In a fresh directory:

cvs co -r BRANCH_NAME_ancestor -j BRANCH_NAME_ancestor -j BRANCH_NAME_to_merge MODULES/TO/MERGE

The CVS manual would have you believe that you can use the '-j' options of 'cvs up' to apply changes directly to the destination. While this is suitable for small changes, as discussed above it is not good at recording conflicts involving added or removed files. It is better to use the '-j' options in a manner that ensures the changes will be applied successfully and then use an update command without '-j' options to do the merging.

3 Update the working copy to the head of the development line to which you

are applying the changes.

If merging into the trunk (from the branch):

cvs up -A

or if merging into the branch (from the trunk):

cvs up -r BRANCH_NAME

If these update commands are issued from within pcl-cvs in emacs, then it records the ancestor revision and the 'cvs-mode-image' command (key-sequence 'd E') can be very useful to interactively resolve conflicts by redoing the three-way diff.

If the file has been modified on one development line and removed on the other or has been added on both lines, then CVS can't perform the merge. But, with this use of 'cvs update', it does exit with failure status (non-zero), the changes are still present in the working directory, and an accidental commit is prevented because CVS has recorded that the working copy is based on the BRANCH_NAME_ancestor revision and will not let you commit to a revision tag.

4 Resolve all conflicts.

5 Commit the changes.

The best log messages would repeat the reason for the changes. i.e. copy the messages from the log of the source development line. If there are two many changes to copy them all, then a message describing where the changes came from (BRANCH_NAME or trunk) is necessary for others to find the original log messages.

6 Move the common ancestor tag:

cvs rtag -r BRANCH_NAME_to_merge -F BRANCH_NAME_ancestor MODULES/MERGED

7 Remove the BRANCH_NAME_to_merge tag as you have finished with it:

cvs rtag -d BRANCH_NAME_to_merge MODULES/MERGED

If the whole branch has been merged to the trunk you can now make a new branch off the trunk and work on that.

Scripts

There are some old scripts in 'esu1:/usr/people/tomlinso/scripts/cvs' that

automate creating a branch (removing any old branch of the same name) and merging changes on a branch to the trunk, while maintaining an ancestor tag. They do not handle merging trunk changes to the branch (and updating the ancestor).

Unfortunately, the scripts deal directly with the CVS repository rather than

going through CVS. This means that they may need changing if the CVS configuration changes and won't work through a CVS server.