History for Development Branches and Merging Changes
changed:
-
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/.
* A more "CVS":https://www.cvshome.org/ oriented but not-so-in-depth discussion
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.