Creating and using CVS Branches

The following is how to setup and maintain a CVS branch of a particular BFD project. All details of branch management are not attempted here, just enough to enable you to create a personal branch for yourself, commit to that branch without effecting the mainline of that projects development, then merge your changes from the branch back into the trunk.

Note that this is a very CVS-centric document though the intent is for this branching to done in conjunction with using BFD. When BFD's support of branching is more mature many (if not all) of the CVS commands here will be replaced with BFD commands.

Table of Contents References
  1. Background
  2. Creating a branch
  3. Working on a branch
  4. Merging a branch
  5. Summary

  1. Background
  2. Once an understanding of a few terms and concepts are in place, along with a way to visualize these things, CVS branching and merging becomes much easier to understand. Here goes...
    The Trunk is the mainline of CVS work. It can be though of as the branch with no name.
    A branch is an alternate place in CVS for commits to go other than the Trunk. Branches are generally created for one of two reasons: for side/temporary project development, or a place to patch existing releases after new features (for a future release) have been added into the Trunk. Here we are using the branch for the former reason.
    Working dir
    A working directory is the tree of files and directories that is created when you do a checkout. It is the place where you do your work: editing, updates, commits, etc. A working directory can be checked out from either the Trunk or a branch. It is important to know from where a working dir was checked out when doing commits as that is where the commit will go (back to the Trunk or that branch). The 'cvs stat [file]' command will show you this information about what branch the files in a working directory are on.
    A tag identifies a set of revisions of files in the repository. There are two kinds of tags: static and branch.

    A static tag is a 'snapshot in time' identifying a specific set of files each at a specific revision. Static tags are used to identify a release or some other milestone in development.

    A branch tag identifies a specific branch rather than a set of files each at some revision. This will become clearer below..

    The term 'sticky' in CVS has two somewhat different meanings depending on the context. If you have checked out from a static tag, then those files in your working dir will be 'stuck' to that tag - and you won't be able to commit changes to them until you 'unstick' them. This is because there may not be a place for that change to go. For example the tag may identify revision 1.5 of a file which already has a revision 1.6. Since there already is a revision 1.6, you can't make a new revision 1.6 (unless of course you make a branch...)

    Branches on the other hand, are implemented in CVS by being 'sticky' to a branch tag where commits are allowed and go into that branch rather than the Trunk. In this sense a branch tag is also known as a 'sticky tag'. So if you have a branch based at the above file's 1.5 revision you can now make a commit to that branch. The new file will have revision number similar to 1.5.1 meaning it is the first revision based on revision 1.5. This is a completely different revision of the file from 1.6 as it is on the '1.5 branch' of that file.

  3. Creating a branch
  4. Creating a branch is done with two commands: 1) Tag the base point of the branch and 2) Create the branch tag. These are both done with the 'cvs tag' command and I suggest using the following convention for naming tags: USERNAME_PROJECT-NAME_[BASE|BRANCH]_YYYYMMDD. It is kind of long but it clearly identifies what the tag is for. There are a few things to consider before creating the branch so here it is spelled out:

    1. Decide on the project to branch and update it to the latest revisions
    2. Start in your BFD workspace in the project directory you want to branch and bring it up-to-date with the head of the Trunk.

      Let's use as an example the daq-common project.

      $ cd ~/BFD_workspace/daq-common

      This next command will update all the files from the current directory on down, to be the most recent revisions committed to the Trunk. This is most likely where you want to start your branch.

      $ cvs up -A

      This can be confirmed with the 'cvs stat' command on a sample file

        $ cvs stat project.xml
        File: project.xml       Status: Up-to-date
           Working revision:    1.8
           Repository revision: 1.8     /home/icecube/cvsroot/daq-common/project.xml,v
           Sticky Tag:          (none)
           Sticky Date:         (none)
           Sticky Options:      (none)

      Note that both the working revision (yours) and repository revision (the latest) are the same (1.8) and not sticky to anything.

    3. Tag the base point of the branch.
    4. Now create the base tag, identifying the 'branch point' of your branch:

      Following the USERNAME_PROJECT-NAME_[BASE|BRANCH]_YYYYMMDD naming convention for creating tags:

      $ cvs tag KSB_DAQ-COMMON_BASE_20041025

      This will print out information about all the files it is tagging.

    5. Create the branch.
    6. Again following the tag naming convention:

      $ cvs tag -r KSB_DAQ-COMMON_BASE_20041025 -b KSB_DAQ-COMMON_BRANCH_20041025

      This creates the BRANCH tag based at the BASE tag created above.

    7. Switch your working directory to be on the branch.
    8. This can be done to any working directory of the project if you want to work on the branch in that working directory.

      Use the 'cvs update' command to move your working directory onto the branch:

      $ cvs up -r KSB_DAQ-COMMON_BRANCH_20041025

      This makes all the files in your working directory 'sticky' (in the second sense) to the branch you just created. It doesn't change the files, just where commits to them will go. To confirm this with particular file, use the 'cvs stat' command again:

        $ cvs stat project.xml
        File: project.xml       Status: Up-to-date
           Working revision:    1.8
           Repository revision: 1.8     /home/icecube/cvsroot/daq-common/project.xml,v
           Sticky Tag:          KSB_DAQ-COMMON_BRANCH_20041025 (branch: 1.8.4)
           Sticky Date:         (none)
           Sticky Options:      (none)

      Notice that the revision is the same, but now there is a sticky tag and it is the branch we just created.

  5. Working on a branch
  6. Working on the branch, once your working directory is sticky to that branch, is done exactly the same as if working on the Trunk. The difference is that commits go onto the branch rather than on the trunk. This can be confirmed by using the 'cvs stat' command:

    After making a changes to a file, the global '-n' flag used with the 'cvs update' command is useful to quickly see the state of a working directory:

      $ cvs -nq up
      M project.xml

    So the file 'project.xml' has modifications. Committing those changes with the 'bfd archive' command:

      $ bfd ar -m "Minor changes to branch demo" project.xml
      Checking in project.xml;
      /home/icecube/cvsroot/daq-common/project.xml,v  <--  project.xml
      new revision:; previous revision: 1.8

    The new revision number '' rather than '1.9' means that this commit is on the branch (based at revision 1.8) rather than going onto the Trunk, which would have been revisions '1.9'. Here is what 'cvs stat project.xml' has to say:

      $ cvs stat project.xml
      File: project.xml       Status: Up-to-date
         Working revision:
         Repository revision: /home/icecube/cvsroot/daq-common/project.xml,v
         Sticky Tag:          KSB_DAQ-COMMON_BRANCH_20041025 (branch: 1.8.4)
         Sticky Date:         (none)
         Sticky Options:      (none)

    So we are up-to-date and on the branch.

    There is one important point to make when working on a branch - do not do a 'bfd deliver' when working on a branch. This is because a deliver creates a new baseline and those should only be on the Trunk and not on a branch.

  7. Merging a branch
  8. Once you are finished with the branch and made all your commits to it, you merge all the changes made on that branch into the Trunk. This is, in effect, simply replaying all the modifications you made on the branch, into a Trunk working directly, then committing those changes to the Trunk. Breaking this down into 3 steps:

    1. Switch your working directory to be on the Trunk.
    2. To merge into the Trunk you need to work in a Trunk working directory. Switching back to a Trunk working directory uses the same command as used in step 2a above.

      Use the 'cvs up -A' command to move your working directory onto the branch:

      $ cvs up -A

      This removes all 'stickyness' of your working directory in effect making your working directory based on the Trunk. If you have any uncommitted changes in your branch working directory this command will keep those files there. I'm assuming here that you don't have any such files. If you do, then you are probably better off also using the '-C' flag of update or simply using a newly checked out working directory for this merge.

      A 'cvs stat' now shows we are on the Trunk (no stickyness):

        $ cvs stat project.xml
        File: project.xml       Status: Up-to-date
           Working revision:    1.8     Mon Oct 25 23:52:33 2004
           Repository revision: 1.8     /home/icecube/cvsroot/daq-common/project.xml,v
           Sticky Tag:          (none)
           Sticky Date:         (none)
           Sticky Options:      (none)

      You now have the old (Trunk) revision of the project.xml in your working dir but that's about to change.

    3. Do the merge
    4. Doing the merge itself uses the (surprise) 'update' command. This is done by identifying the branch you which to merge into the current working directory.

      The '-j' (for join, I think) flag is used for this:

        $ cvs up -j KSB_DAQ-COMMON_BRANCH_20041025
        cvs update: Updating .
        RCS file: /home/icecube/cvsroot/daq-common/project.xml,v
        retrieving revision 1.8
        retrieving revision
        Merging differences between 1.8 and into project.xml

      Examining this output is important as there is a chance that there are conflicts as a result of the merge. This could only happen if someone made changes to the same files, in the same places, on the Trunk, as you did on the branch. If that is the case then you will see messages about these conflicts and you will need to resolve those conflicts before proceeding.

    5. Complete the merge
    6. Completing the merge is done by doing a commit into the Trunk of the changes that came from the branch. As you should always be concerned with, when doing a commit, make sure that the changes you are making are indeed correct by examining the code, running unit tests, etc.

      Again the global '-n' flag used with the 'cvs update' command is useful to quickly see the state of a working directory:

        $ cvs -nq up
        M project.xml

      Here we see that only the project.xml file is modified, and there are no conflicts (which are marked with a 'C' rather than an 'M'). Doing the commit:

        $ cvs ci -m "Results of merge from KSB_DAQ-COMMON_BRANCH_20041025 branch."
        cvs commit: Examining .
        Checking in project.xml;
        /tmp/cvsroot/daq-common/project.xml,v  <--  project.xml
        new revision: 1.9; previous revision: 1.8

      Making a note in the commit log that it is the result of a merge and the branch merged from can be very helpful if a particular change needs to be tracked to it's original commit. I've also used the 'cvs ci' command here rather than the 'bfd archive' command, this is because bfd does not yet have full support for branching and merging - we are stepping a bit behind the 'bfd curtain' here by doing this...

  9. Summary
  10. Although there are several things that can complicate branching and merging, when creating a personal branch where only one person is working on the branch and no one is working on the Trunk at the same time, things are quite simple. Here is a review of the procedure, with brief explanations of each command:

      $ cd ~/BFD_workspace/daq-common                                               # Beginning in the project you wish to branch
      $ cvs up -A                                                                   # Bring to head of the Trunk
      $ cvs tag KSB_DAQ-COMMON_BASE_20041025                                        # Tag the base point of the new branch
      $ cvs tag -r KSB_DAQ-COMMON_BASE_20041025 -b KSB_DAQ-COMMON_BRANCH_20041025   # Create branch based on base point.
      $ cvs up -r KSB_DAQ-COMMON_BRANCH_20041025     # Move your working directory onto the branch
      $ vi                                  # Edit in branch working directory
      $ bfd ar                              # Commit changes onto branch with 'bfd archive', not 'bfd deliver'
      $ cvs up -A                                                                 # Get a Trunk working dir, to do the merge into
      $ cvs up -j KSB_DAQ-COMMON_BRANCH_20041025                                  # Merge in changes from branch
      $ cvs -nq up                                                                # Look for conflicts
      $ ant test                                                                  # Run unit tests
      $ cvs ci -m "Results of merge from KSB_DAQ-COMMON_BRANCH_20041025 branch."  # Commit changes into Trunk with meaningfull log message.

This page maintained by Keith Beattie
Page last modified: