Personal tools
You are here: Home cm Wiki Development Branches and Merging Changes
Views

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.