Products Docs & Support Community

Branches in CVS

CVS allows you to isolate changes onto a separate line of development, known as a branch. When you change files on a branch, those changes do not appear on the main trunk or other branches. Later you can move changes from one branch to main trunk (or from main trunk to the branch) by merging. Merging involves first running cvs update -j, to merge the changes into the working directory. You can then commit that revision, and thus effectively copy the changes onto another branch.

When to create a new branch

For NetBeans/Forte I see two possible reasons for creating a new branch:
  1. Official branch for a new release. Such a branch will be created by an experienced person who knows what he is doing and therefore it might be expected that there won't be any problem.
  2. Private branch(es) created by a developer. Such branches are typically used for unstable code that will be merged into the main trunk as soon as it will became stable. This document presents some recommendations that should be followed when someone will create a private branch.

Recommendations for working with branches

  1. Read carefully CVS documentation. If you have any problem, don't hesitate to ask someone who can help you.
  2. Do not use any CVS expandable keywords ($Id$, $Log$, etc.), as these interact poorly with merges. For example, suppose that you have used $Revision$ in your code. And that you have two versions of the file with revisions 1.2, Then you might get the following results from a merge:
    <<<<<<< file1
    key $Revision: 1.2 $
    key $Revision: $
    If you *really* need to use CVS keywords, please, consult CVS documentation or (better) ask someone who knows how to do it.

  3. Make a tag (cvs tag) whenever you do anything at all interesting (esp. before and after any merge); it never hurts and it may save you later. Remember that tags are global, so choose them descriptively so they will not conflict. For example unstable_rmi_merged_on_Sep_6 can be used.

    It may be useful for example if you perform more sequential merges. Suppose you have an branch R1fix and you have completed the first step of your unstable development. You want to merge it into the main trunk and then continue development in unstable branch. After some time you will repeat merging, etc.

    +-----+    +-----+    +-----+    +-----+    +-----+
    ! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !   <- The main trunk
    +-----+    +-----+    +-----+    +-----+    +-----+
                    !                           *
                    !                          * (merge)
                    !                         *
                    !   +---------+    +---------+    +---------+
    Branch R1fix -> +---! !----! !----! !
                        +---------+    +---------+    +---------+
    If you just use the cvs update -j R1fix m.c command again, CVS will attempt to merge again the changes which you have already merged, which can have undesirable side effects.

    If you tag R1fix branch after every merge into the trunk, it is possible to use the new tag in merge command and merge only what was changed since the last merge.

  4. When you make modifications and check them in, change as few lines as possible (e.g. do not reindent the whole file just for fun), and don't mix unrelated changes in a single commit; these will make merging go more smoothly and reduce the chance of error.

  5. Use branches if you need to do some unstable development - it is good for you because you can store all your versions and CVS server is probably backed up, it is good for other people because your code doesn't break builds. But do not use a lot of local unstable branches, because they may be hard to keep track of. If you need to do some unstable development for a while and need a branch, then use one.

How to work with branches

  1. create a new branch
    • cvs tag new_branch_name_root
      cvs tag -b -r new_branch_name_root new_branch_name

      it creates a tag for main trunk (before the merge) ant then it creates a branch based on the current revisions in the working copy, assigning that branch the name `new_branch_name'.

      cvs tag rmi_root
      cvs tag -b -r rmi_root unstable_rmi

      creates a new unstable branch for rmi module to main trunk

    • cvs tag -r some_existing_branch new_branch_root
      cvs tag -b -r new_branch_root new_branch_name

      -r new_branch_root says that this branch should be rooted at the revision that corresponds to the tag new_branch_root. It need not be the most recent revision -- it's often useful to split a branch off an old revision (for example, when fixing a bug in a past release otherwise known to be stable).

      cvs tag -r boston boston_rmi
      cvs tag -b -r boston_rmi unstable_rmi_boston

      creates a new unstable branch for rmi based on current boston branch

      Note: difference between tag and rtag is that tag command adds a tag to checked out version of files whereas rtag uses the most recent versions in the repository. These can have different effects, e.g. if someone checks in new stuff while you are working.

  2. access to an existing branch
    • cvs checkout -r branch_name

      cvs checkout -r boston

    • cvs update [-r branch_name]

      the above command switches to the specified branch name - it merges any changes.

      Note: do not use update -r. It is potentially very confusing (although it does do exactly what it says, usually you don't really want to do that). Always check out branches into separate, clearly labeled directories. For example, ~/src/boston for boston branch, ~/src/jaga for main trunk, etc. Make a fresh checkout of the whole repository, with most stuff on the trunk and just your unstable code on the branch.

      cd unstable_rmi
      cvs checkout nbsrc
      cvs checkout -r unstable_rmi rmi
      cvs update

  3. merging
    • cvs tag before_merge cvs update -j branch_name cvs rtag -r branch_name branch_name_merged_on_date cvs commit -m proper message" cvs tag after_merge

      merges all changes on branch to the main trunk, tags it and commit to CVS. It is also possible to used revision numbers instead of tags but tags makes work with merging more smooth.

    • cvs tag before_merge_on_date
      cvs update -j branch_name_merged_on_date -j branch_name
      cvs rtag -r branch_name branch_name_merged_on_date
      cvs commit -m proper message" cvs tag after_merge_on_date

      merges all changes since specified merge to the main trunk, tags it and commit to CVS.

      cd jaga/rmi
      cvs tag before_merge_Sep_10
      cvs update -j unstable_rmi_merged_on_Sep_6 -j unstable_rmi
      cvs rtag -r unstable_rmi unstable_rmi_merged_on_Sep_10
      cvs commit -m "merged a new feature" cvs tag after_merge_Sep_10

      It is also possible to merge main trunk into a branch. This is conceptually probably harder, but follows the same principles. Suppose an example that you are working on unstable branch and someone else has been checked some fixes into the main trunk. You want to integrate these fixes into your code too but not to affect the main trunk with your unstable code and later to merge your unstable code to the main trunk.

      ## create an unstable branch
      cvs rtag unstable_root my_module
      cvs rtag -b -r unstable_root unstable my_module
      mkdir unstable
      cd unstable
      cvs checkout -r unstable my_module
      ## some work in the branch whereas someone else has fixed trunk
      ## merge the trunk to the branch
      cvs rtag main_trunk_fixed my_module
      cvs tag unstable_before_merging_main_trunk_fixed
      cvs update -j unstable_root -j main_trunk_fixed
      [resolve conflicts and commit]
      cvs tag unstable_after_merging_main_trunk_fixed
      ## some work in both main trunk and the branch
      ## finally, merge the branch to the trunk
      cd stable
      cvs checkout my_module
      cvs tag before_merge_unstable
      cvs update -j main_trunk_fixed -j unstable
      [resolve conflicts and commit]
      cvs tag after_merge_unstable

      Suppose the following example:

          /- 1 | 2 | M1 | 5              (branch)
         \- 3 | 4 | 6 | 7 | M2               (trunk)
      Numbers are changes (revisions) made by user. Suppose that every revision is stored as a diff to previous revision.
      M1 is a result of merging the trunk (rev. 4) to the branch.
      M2 is a result of merging the branch (rev. 5)to the trunk.

      So current status at M1 is root+1+2+3+4, status at 5 is root+1+2+3+4+5 and status at 7 is root+3+4+6+7.

      Finally M2 is created (cvs update -j 4 -j branch) as <status at 7> + (<status at 5> - <status at 4>) = root+3+4+6+7 + (root+1+2+3+4+5 -(root+3+4)) = root+3+4+6+7 + (root+1+2+3+4+5 - root-3-4)) = root+3+4+6+7 + (+1+2+5) = root+1+2+3+4+5+6+7

      In practice, hovewer, update -j -j literally takes the two revisions you asked for, and more or less runs diff3 on them and your working file. It does not know or care what changes you made where--that is your job. cvs update always runs a single merge, it does not do anything in steps. The example above show how one can check whether result of merging would be what he wants.