MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 http://www.macdevcenter.com/pub/a/mac/2003/08/29/version_control_two.html
 See this if you're having trouble printing code examples


Version Control on Mac OS X, Part 3

by Kevin O'Malley
08/29/2003

Introduction

Welcome back. In the first article of this series, you were introduced to version control and learned some of its concepts. The second article gave you a chance to apply this knowledge by using CVS from the command line, as well as under Project Builder, on a simple Cocoa program -- MyPing. In this final article of this series, we will look at creating software releases using the CVS tag and branch commands, as well as some Mac OS X GUIs for interacting with a CVS repository.

Adding New Features to MyPing

The first version of MyPing contained minimal functionality. It enabled you to ping a host, control the number of pings, the delay between pings, and view ping's output. These features are useful, but we can do better.

Let's add two new features that enable you to log the output of ping to a file and clear the output of the text view area between pings. I've added these features to MyPing, which you can download and integrate into your latest version. The new additions are notated in the code with comments. You will also need to update your Nib file to get the interface changes. Once integrated, make sure you commit your changes to CVS.

Release Basics

Before using the CVS commands to create a new release, let's look briefly at the basics of release management. Against this backdrop, you will have a better sense for the software release process and get a feel for how it works. If you already understand release management, feel free to skip this section.

Related Reading

Essential CVS

Essential CVS
By Jennifer Vesperman

Table of Contents
Index
Sample Chapter

Read Online--Safari Search this book on Safari:
 

Code Fragments only

As you recall from the first article, version control is a process, supported by software, that helps you manage aspects of the software development process. These include recording changes to your project's source files, controlling access to shared files, and managing releases. Even though the software release process varies from project to project and industry to industry, we can abstract some general properties that enable us to understand the process better.

There are typically three players in the release process: developers, maintainers, and release managers (though more can exist, such as testers). A developer is responsible for writing code, fixing bugs, and adding features to components of the software system. Maintainers are accountable for a particular part (or parts) of the software package. They take contributions from developers, verify additions, and integrate them into the module's code base. Additionally, they can also develop code for a module. The release manager performs the necessary steps to put together and release the software package. This typically involves tasks such as testing, communicating with maintainers, enforcing a code freeze, reviewing code/feature, etc. The release manager can be one person, or a group.

A code freeze is a policy, usually enforced by the release manager, that stops some, or all, types of development on the software system. Freezing can include feature freeze, where no new functionality is added to the software (though small additions like minor bug fixes can be applied) and hard freeze, where nothing new is added to the system.

On many projects, the release policy functions as follows. Developers submit code (bug fixes, patches, features, etc.) to the maintainer of a module. The maintainer accepts or rejects submissions and integrates accepted code into the working code base for the module. Once the principal project members decide to release a new version (the policy varies from project to project), the release manager enforces a code freeze, gets updates from module maintainers, accepts/rejects submissions, performs or coordinates testing, reviews code, and makes sure that the new release is stable. Once the release is ready to go, the release manager announces the new release and makes it available to the public.

Remember, this is the general case; each project can, and often will, have a different release model (for example, the Linux kernel or the Apache server).

See "Release Management Within Open Source Projects" by Justin R. Erenkrantz for more information on release management on open source projects.

For the MyPing project, there is a single developer, you, who will take on all of the roles. Nonetheless, it is useful to understand the usual model so you can see the different roles of the release cycle, even though you are performing all tasks.

Creating a Release of MyPing

CVS contains two commands that help you create software releases: tag and branch. A tag is a label you give to a set of revisions, or files, enabling you to snapshot a fixed point in the project. As you continue editing tagged files, and committing them to CVS, the tag you created remains fixed with the state of the files when they were tagged. At any point, you can return to the past simply by checking out the files by their tag name. We will use the tag command to create a release of MyPing.

The branch command helps you create and manage different versions of your project -- such as release versions, debugged versions, and optimized versions -- without disturbing the main trunk. The trunk is the current state of development; a branch is a specialized version, or diversion. Each branch has a root (the initial state of the branch) and a tip (the current state of the branch). If you create a branch, modify files, and commit the changes, the root and the tip will contain different versions of your files. Further, all commits to a branch do not affect the trunk, only the branch. We will use the branch command to create a specialized version, in our case a bug fix version, of MyPing.

The following diagram shows an example of three branches rooted on the trunk, as well as branches rooted on branches.

Performing a Release with CVS tag

The following steps outline how to create a new release of MyPing.

Step 1: Institute a Code Freeze

Institute a code freeze for the MyPing project, where nothing new is added to the system. At this point, you should commit all code to CVS.

Step 2: Test the Release

Once the code freeze is in place, you can begin creating a new release. First, make sure everything works correctly by performing your predefined tests. For many projects, this means running a script that executes regression testing to verify that the release is stable. If any code is changed, make sure you commit your changes to CVS.

Note: This version of MyPing contains an intentional bug. I did this to demonstrate how to use the CVS branch command to perform bug fixes in a past release. I discuss this technique in the section "Updating a Released Version with CVS Branch."

Step 3: Tag the Release

The next step is to record the new release. To do this, you use the CVS tag command to associate a label with all files that make up the release.

% cd MyPing
% cvs tag release_1_0-public-release

Since the tag command does not support adding comments (like import and commit), it's a good idea to include as much information as possible in the tag name.

Step 4: Announce the Release

Finally, notify users that the new release is ready and tell them where they can download the software.

Updating a Released Version with CVS branch

A common maintenance task is fixing bugs in released versions. If a bug in an earlier release is discovered, you are faced with a problem. How do you fix the bug when new code has been added to the trunk? One solution is to check out the tagged release and fix the bug, but how do you record the changes with CVS? Another option is to fix the bug in your current code base, but here you run the risk of releasing a new version that is unstable. To solve this problem, you use the CVS branch command.

As you saw earlier, the branch command enables you to create and manage different versions of your project. Basically, a branch is a specialized version of your program, rooted on a tagged trunk or possibly another branch. The following steps show how to fix the bug in the released version of MyPing and record the changes with CVS, using the branch command.

Step 1: Check out the Version to Update

This first step is to check out the release you will fix. In this case, the version we tagged as tag release_1_0-public-release.

% cvs co -d MyPing_release_1_0-public-release -r release_1_0-public-
release MyPing

Step 2: Create a New Branch

To create a branch, follow these steps.

  1. Create a new branch. Conceptually, think of a branch as rooted to the trunk, starting with a release tag and labeled using a branch tag.

    % cvs tag -b [name-of-branch-tag]} 

    For our project, do the following:

    % cd MyPing_release_1_0-public-release
    % cvs tag -b release_1_0-public-release-branch-bug-fixes
  2. Check out the newly tagged branch. This is the version you wish to update with your changes, which forms the basis for the new updated release.

    Note: there are different ways to do this, but this is the simplest and safest.

    % cvs co -d [dir-to-place-version] -r [release-tag] [location-of-dir-
    in-cvs]
    
    % cvs co -d release_1_0-public-release-bug-fixes -r
    release_1_0-public-release-branch-bug-fixes MyPing
    
  3. Update/modify branched code. Make changes in the branched version and commit changes. It's important to remember that committed changes go to the branch, not the trunk.

    The bug in MyPing occurs in the appendOutput method in PingController.m.

    // ---------------- Begin New Code for Article 3 ------------------
      // Log to the file, only if the file point is valid.
      if (fp != NULL) {
        fprintf(fp, "%s", [outStr cString]);
    
        [AppSupport setStatusMsg:output theMsg:outStr];
        [self performSelector:@selector(scrollToVisible:) withObject:nil 
    afterDelay:0.0];
      }
      // ---------------- End New Code for Article 3 ------------------

    As you can see, the bug is caused by placing the output statements within the braces. To fix the problem, simply remove the braces.

    // ---------------- Begin New Code for Article 3 ------------------
      // Log to the file, only if the file point is valid.
      if (fp != NULL)
        fprintf(fp, "%s", [outStr cString]);
    
      [AppSupport setStatusMsg:output theMsg:outStr];
      [self performSelector:@selector(scrollToVisible:) withObject:nil 
    afterDelay:0.0];
      // ---------------- End New Code for Article 3 ------------------

    Make sure you perform all tests and verify your changes, making sure the release is stable before continuing.

  4. Tag the branch. Once the changes are complete, tag the branch, incrementing by 1 after each fix.
    % cvs tag [tag-name]
    % cvs tag release_1_0-public-release-bug-fixes-fix-number-1

Here is the reason for re-tagging the branch: it's possible that the changes you make to a branched version should be integrated into the trunk. For example, imagine fixing a bug in the branched version of a core library. Since the code on a branch is independent of the trunk, this does not fix the bug in the trunk, just the bug in the branch. Given that the bug still exists on the trunk, you need to fix this by updating the trunk's code with the fixed version.

Related Reading

CVS Pocket Reference

CVS Pocket Reference
By Gregor N. Purdy

You re-tag the branch to address this situation. Imagine, after a few weeks, you need to fix a bug on the branch. If the branch is not tagged, the branch contains (at its root) the initial version before any changes, the first modified released version, and the bug fixes you just made to the previously released version. If you merge your changes back to the trunk, you get conflicts. This is because the merge command merges all changes from the branch's root to the tip, which contains your new bug fixes. The conflicts occur because you already merged some of these changes after the first release.

To solve this problem, you add a branch tag after updating a branch. This step enables you to direct CVS to merge only the new changes back to the trunk. You can also accomplish this without adding a new tag and merging by date, but this is error-prone.

In addition, you may have noticed that we added the string -fix-number-k (where k is incremented after each fix) to your tag name. This makes it very clear what is going on in the branch.

Step 4: Announce the Release

Notify users that the new release is ready.

There you have it. These descriptions of common release scenarios are by no means inclusive, nor do they show all of the things that come up in release management. Nevertheless, they do convey the main points of performing a software release. The main thing that tags and branches give you is a method of returning to (and revising) the past, and recording any changes with CVS. Using branches can be confusing at first, but hang in there and get comfortable with the concepts and commands. Once you do, you will use them all the time for your release-management tasks.

One last thing: many of the ideas presented here are based on the book Open Source Development with CVS, by Karl Fogel and Moshe Bar. I highly recommend you get a copy of this book, and best of all, it's freely available from cvsbook.red-bean.com.

Other Interfaces to CVS

To conclude this article, let's take a brief look at four Mac OS X GUIs for CVS. The goal here is to give you the flavor of the tools and let you know that GUIs for CVS exist. I encourage you to try each program on your own and see if they fit into your development process and help you be more productive. You never know when you will find something that increases your productivity and fun factor.

BBEdit

Since the early days of the Macintosh, I have always been a big fan of BBEdit. If you have never used BBEdit, stop now, go to their web site, and download the demo. After using it a few times, I think you will be hooked.

BBEdit has always been a rock-solid editor with great features for software development and text-editing tasks. Some of the most useful stuff is its developer support, including syntax coloring, integration with the command line, internal support for Perl, Python, and shell scripting, integration with Project Builder, and most importantly for this article, integration with CVS.

To use BBEdit with your CVS repository, you can do the same trick you did with Project Builder, where you launched to from a shell with the open command. For example, change directory (cd) to the MyPing directory and type the following command (make sure BBEdit's command-line tools are installed):

% bbedit PingController.m

BBEdit's CVS menu items permit you to access its CVS commands. As you can see below, BBEdit supports many of the CVS commands you already know.

You can even configure BBEdit and Project Builder to work together. Once configured, when you open a source file in Project Builder, the file is loaded into BBEdit. When you save the file, BBEdit notifies Project Builder to update its state.

MacCVSClientX

MacCVSClientX is a free multithreaded CVS client for Mac OS X, as well as Mac OS (Classic). Unlike Project Builder and BBEdit, MacCVSClientX is not a development or edit environment. It enables you to see file differences, conflicts, administer adds and imports, and manage your project files' revision history.


Click for larger view


Concurrent Versions Librarian

Concurrent Versions Librarian (CVL) is a GUI for CVS, supporting many CVS commands, including commit, update, tag, checkout, import, status, log, and differences and tag inspectors. It uses Apple's FileMerge to resolve merge conflicts and permits you to interact with multiple repositories. CVL also supports integration with ProjectBuilder. If you double-click on a file in the Work Area, CVL will open the file in Project Builder.

You can perform many CVS commands by selecting a file and either right-clicking over the file or selecting the command from the CVL menus.

In addition, you can get information on a file through Tools->Inspector, including a file's status, log information, tags, and differences. For example, to get log information for a file, select the file from the work area and choose Tools->Inspector->Logs.

LinCVS

The last program we will look at is LinCVS. LinCVS is a freely available cross-platform GUI for CVS that runs under Mac OS X, UNIX, and Windows. The program uses the Qt GUI and application tools kit, providing single-source portability across platforms. As with the other programs we've discussed, LinCVS supports most of the CVS commands you need. For Mac OS X users, LinCVS comes as an application you can run from a shell or by double-clicking its icon. To access the remote CVS repository, I used the same trick as we did with Project Builder, where I launched to from a shell with the open command. Next, I created a new profile, entered my CVS information, and checked out the MyPing project.

If you develop cross-platform applications, and you are looking for a good GUI for CVS, LinCVS is definitely worth checking out.


Click for larger view

Conclusion

This concludes our look at version control under Mac OS X using CVS and Project Builder. I hope that this series of articles has convinced you that version control is an important part of any software development project and adding it to your development process is a relatively painless process.

Kevin O'Malley is a long time Macintosh and UNIX developer. His articles have appeared in Dr. Dobb's Journal, IEEE Internet Computing, and The Perl Journal, and he is the author of Programming Mac OS X: A Guide for UNIX Developers.


Return to the Mac DevCenter

Copyright © 2007 O'Reilly Media, Inc.