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


Version Control on Mac OS X, Part 2

by Kevin O'Malley
08/08/2003

The first article of this series introduced you to the basics of version control, discussed some examples of using it on projects, and outlined what version control systems are available under Mac OS X. In this article, you'll get a chance to apply this knowledge by using one of the most popular version control systems, Concurrent Versions System (CVS), on a sample project under Project Builder. Specifically, you will learn how to set up CVS for remote access, import a project into CVS, and use CVS commands from within Project Builder. Before continuing, make sure you have read the first article of this series and "In Sync with CVS," by James Duncan Davidson. In addition, make sure you install the Apple developer tools, freely available from Apple’s developer site.

The Apple developer tools come with an IDE called Project Builder, which includes a development environment to edit, build, debug, and run Mac OS X applications. Project Builder does not include the main development tools (compiler, linker, version control, etc.) as part of the program. Instead, it uses UNIX development tools such as gcc, g++, gdb, and CVS. In a sense, Project Builder is evolutionary; it continues the line of IDE-based development environments for the Macintosh, but breaks with tradition by using external UNIX-based tools for implementing its development tasks. This strikes a nice balance by providing a modern interface for application development while leveraging the strengths of the UNIX tool set.

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

Project Builder supports access to CVS through its GUI. As you learned in the first article, CVS is by no means the only version control system for Mac OS X. However, it is one of the most popular systems from the UNIX world to find its way to Mac OS X, and is supported by Project Builder. For these reasons, as well as its popularity in the open source community, it makes a good choice for version control under Mac OS X.

Setting Up CVS

There are two ways to set up CVS -- for local access and for remote access. Local access means that the CVS repository is located on your development machine. With remote access, the repository resides on a machine on the network and you develop on any machine you wish, connecting to the repository over the network.

Though both methods have their advantages and disadvantages, I prefer remote access. These days, networks are ubiquitous, so I can keep my repository in one place and access it from many locations. Another good reason for choosing remote access is for use on projects with a geographically distributed development team. For example, envision the following: you and some friends want to develop a new editor for Mac OS X. Each person lives in a different part of the country. A requirement of the project is that members need to access and update each other’s work at any time. Since CVS is designed to work over the network, one developer sets up a CVS repository on a single, networked machine. The other developers configure their environment to remotely access the repository over the network. Now all developers have access to CVS as though the repository were accessible within their file system.

The following steps show you how to set up CVS for remote access. To do this, you will need two machines connected to the network; one that holds the CVS repository and one for development. Let’s call the machine that holds the repository the "CVS remote host" and the development machine the "CVS client host."

The first step is to set up the CVS remote host. For this example, I will assume the host name of the CVS remote host is "cvshost.somedomain.edu," the username is "omalley," and the CVS repository is located in /Users/omalley/cvs-repository.

  1. If necessary, create an account for yourself and others that will access to the repository.
  2. Open System Preferences (located in /Applications), click on the Sharing icon, and click on the Remote Login checkbox. This runs the SSH daemon, enabling you to connect to the machine over SSH.
  3. Open the Terminal application (located in /Applications/Utilities) and create a directory called cvs-repository, which will hold the CVS repository for your projects. Place this directory on a disk partition that is large enough to handle the anticipated file storage requirements. Try to be overly conservative when estimating your disk requirement. For this example, place the repository in your home directory.
    % mkdir ~/cvs-repository
  4. Run the CVS initialization command to set up the repository. You only need to run this command once, before anyone uses the new repository.
    % cvs –d /Users/omalley/cvs-repository init
  5. Update the cvswrapper file, located in /Users/omalley/cvs-repository/CVSROOT, to properly handle binary files and bundles. To do this, see "In Sync with CVS" and Apple’s Project Builder documentation.

Now that the repository is ready, you can set up the CVS client host. To do this, perform the following steps on the CVS client host.

  1. Generate your SSH keypair files, using the same password as your user account on the CVS remote host.
    
    % ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/Users/omalley/.ssh/id_rsa): 
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /Users/omalley/.ssh/id_rsa.
    Your public key has been saved in /Users/omalley/.ssh/id_rsa.pub.
    The key fingerprint is:
    1a:61:41:ad:a2:f2:e3:4b:d9:bd:49:af:c5:5e:46:66 omalley@G4.local.
    
  2. Add your public key to the CVS remote host’s authorized_keys2 file. The following example assumes you do not have a ~/.ssh directory or an authorized_keys2 file on the CVS remote host.
    
    % scp ~/.ssh/id_rsa.pub omalley@cvshost.somedomain.edu:
    The authenticity of host cvshost.somedomain.edu (123.123.12.3)' 
    can't be established.
    RSA key fingerprint is 
    1a:61:41:ad:a2:f2:e3:4b:d9:bd:49:af:c5:5e:46:66.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added 'cvshost.somedomain.edu,123.123.12.3' 
    (RSA) to the list of known hosts.
    omalley@cvshost.somedomain.edu’s password: 
    id_rsa.pub           100% 
    |*******************************************|   227       00:00    
    % ssh cvshost.somedomain.edu
    omalley@ cvshost.somedomain.edu’s password: 
    Last login: Mon Jul 28 20:40:10 2003 from bgp786711bgs.na
    Welcome to Darwin!
    % mkdir .ssh
    % cat id_rsa.pub > ~/.ssh/authorized_keys2 
    % rm id_rsa.pub
    % exit
    
  3. Add the following lines to your ~/.cshrc file on the client host:
    
    setenv CVS_RSH ssh
    setenv CVSEDITOR emacs
    setenv CVSROOT :ext:omalley@ 
    cvshost.somedomain.edu:/Users/omalley/cvs-repository
    eval `ssh-agent`
    ssh-add
    
  4. Add the following lines to your ~/.logout file on the client host. This prevents multiple ssh-agents from building up on your system by killing the process when you exit the shell.
    
    eval `ssh-agent -k`
    sleep 1
    
  5. Create a directory called ~/MacOSX on the client host, create a file called ~/MacOSX/environment.plist, and add the following lines to the file:
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist SYSTEM
    "file://localhost/System/Library/DTDs/PropertyList.dtd">
    <plist version="0.9">
    <dict>
            <key>CVS_RSH</key>
            <string>/usr/bin/ssh</string>
    </dict>
    </plist>
    

Log out of and back into the active account for the settings to take effect.

To Test your Configuration

  1. Open a new shell in the Terminal and when prompted, enter the same passphrase (password) as you did when creating your SSH keypair files.
  2. Check out CVSROOT into a temporary directory:
    
    % cd ~/tmp
    % cvs co CVSROOT .
    cvs server: Updating CVSROOT
    U CVSROOT/checkoutlist
    U CVSROOT/commitinfo
    U CVSROOT/config
    U CVSROOT/cvswrappers
    U CVSROOT/editinfo
    U CVSROOT/loginfo
    U CVSROOT/modules
    U CVSROOT/notify
    U CVSROOT/rcsinfo
    U CVSROOT/taginfo
    U CVSROOT/verifymsg
    cvs server: Updating .
    cvs server: Updating CVSROOT
    

Sample Program: MyPing

Now that CVS is set up, we can begin looking at CVS and Project Builder. The example program you will use in this article is MyPing -- a Cocoa front end for the UNIX ping tool. You use ping to test if a host is available on the network and measure how long packets take to reach it. For example, imagine you are trying to access a web site, but for some reason, it is taking a long time to reach the site. Using ping, you can verify that the site is alive (online) and measure the delay. (To determine the origin of the delay, use traceroute.)


# Determine if a host is alive.
% ping www.gnu.org
PING www.gnu.org (199.232.41.10): 56 data bytes
64 bytes from 199.232.41.10: icmp_seq=0 ttl=47 time=89.463 ms
64 bytes from 199.232.41.10: icmp_seq=1 ttl=47 time=45.884 ms


# Determine if host is alive, waiting 2 seconds between pings, 
# sending 4 pings.
% ping -i2 -c4 www.gnu.org
PING www.gnu.org (199.232.41.10): 56 data bytes
64 bytes from 199.232.41.10: icmp_seq=0 ttl=47 time=67.659 ms
64 bytes from 199.232.41.10: icmp_seq=1 ttl=47 time=45.732 ms
64 bytes from 199.232.41.10: icmp_seq=2 ttl=47 time=45.299 ms
64 bytes from 199.232.41.10: icmp_seq=3 ttl=47 time=47.636 ms

--- www.gnu.org ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 45.299/51.581/67.659 ms

MyPing adds a Cocoa interface to ping, making the program accessible to users who are not comfortable with the UNIX command line. In addition, it is a nice example of how you can leverage the power of command-line tools to create new programs -- something UNIX people do all the time using scripting languages like Perl and Python.


This version of MyPing implements the basics. In the next article, we will add some new features, including logging ping output to a file. Before continuing, download MyPing.

Importing MyPing into CVS

Now that your environment is set up and you have downloaded the MyPing project, it is time to get started using CVS under Project Builder. Unfortunately, Project Builder only supports a subset of CVS functionally from its interface. To access all CVS commands, you can use the command line or the GUI-based Concurrent Versions Librarian (CVL) (more on this in the final article). For this article, we will use the command line.

One CVS function not accessible from Project Builder is the import command. As you know, before CVS can track changes to your project, you need to import the project into CVS, which places it under version control. To accomplish this, you use the CVS import command. The import command takes the files in the current working directory (and its subdirectories) and adds them to the CVS repository.


cvs import [-options] repository vendortag releasetag 

The options argument specifies options applied by the import command. The repository argument indicates the directory where CVS will store the project within the repository. For example, if you specify MyPing as the repository name, the MyPing directory is stored at ${CVSROOT}/MyPing, or /Users/omalley/cvs-repository/MyPing, on the CVS remote host. See the CVS man page or online manual for more information on the import command.

To import MyPing, change directory to the location of the downloaded MyPing directory and type the following command (the example assumes MyPing was downloaded into your projects directory):


% cd ~/projects/MyPing
% cvs import –m "Import of MyPing." MyPing example-
program-myping myping_0

Finally, remove your local MyPing directory and check out the imported version:


% cd ~/projects
% rm –rf MyPing
% cvs co MyPing

Running Project Builder

One of the difficulties in getting Project Builder to work with a remote CVS repository is getting Project Builder to resolve your password. To accomplish this, you need to launch Project Builder from the right environment. There are two techniques for achieving this: running Project Builder from a shell with the right environment variables set, or using the SSHPassKey program. We will use the open method, but if you prefer, you can use the SSHPassKey program.

To get Project Builder to resolve your password:

  1. Make sure you set your environment variables correctly, as described in the Setting up CVS section.
  2. Open a shell in the Terminal, enter your passphrase, and enter the following commands:
    
    % cd ~/projects/MyPing
    % open /Developer/Applications/Project\ Builder.app/ 
    MyPing.pbproj
    

The open command launches Project Builder as if you had double-clicked the file's icon, but within the right environment. Now, Project Builder can connect to the repository and resolve your password for accessing files. Note the Project Builder status bar, which now indicates its connection to the CVS repository.

Project Builder’s CVS Integration

One of the first differences you will notice when Project Builder is connected to a CVS repository is that the Groups & Files list contains a new column, with a CVS icon. This indicates that Project Builder recognizes the project is under version control.


Project Builder uses this column to display the status of your files. The following table shows the meaning of the status information character.

Symbol Description
A File added
C File merged, changes
E File exported
F File released
G File merge successful
M File modified
N New file added
O File checked out
R File removed
T Tag
U File exists in repository, new revision created
W File removed from entries file
? File not in repository
- File, or folder, is not in repository

As you perform CVS operations such as committing and adding files, this column reflects the file status. For example, if you modify a file in your working copy, Project Builder displays an M next to the file, indicating that you've modified the local file and that it is different from the head version in CVS.

Take a quick look at Project Builder’s CVS preferences by choosing Preferences from the Project Builder menu and clicking on the CVS icon. Most of the options are straightforward, but an interesting one is the Tool for Comparisons pop-up menu. This menu enables you to choose the program that Project Builder uses to display the differences between local files and files in the repository. Currently, you can select Apple’s FileMerge program, BBEdit’s file differences feature, or a different diff tool. FileMerge, a GUI-based file comparison and merging program located in the /Developer/Applications folder, is implemented as a GUI application that uses the UNIX diff command, outputting its result to an ed script.


Project Builder enables access to CVS commands through its CVS menu. Let’s look at the commands Project Builder supports and their meaning.

A common operation, not listed in the CVS menu, is removing files from the repository. To remove a file, select the file you wish to remove, press the Delete key, and follow the on-screen instructions. Another useful action is to get status information for a file. To accomplish this, choose a file from the file list, select Project > Show Info, and select the CVS tab.

Using CVS within Project Builder

Let’s look at how to use CVS and Project Builder with the sample program. To start, build MyPing (Build > Build) and run it a few times. Try pinging some sites to get a feel for how the program works.

The first change we will make is to display a default host name in the Host text field. First, click on, and open, the Classes disclosure triangle (under Files & Groups), select PingController.m, and locate the init method. This method uses three setValue messages to set the initial host name and the displayed menu items. As you can see, these messages currently set the host name to an empty string and the menu items to position 2 and 1 –- using magic numbers.


[param setValue:@"-c":@"2"];
[param setValue:@"-i":@"1"];
[param setValue:@"-host":@""];

Let’s modify this by adding three constants and setting the values to these constant values. Update the code at the beginning of PingController.m to the following code snippet (the added code is marked in bold).


#import "PingController.h"
#import "AppSupport.h"

NSString *PING_CMD = @"/sbin/ping";
NSString *PING_BUTTON_IDLE = @"Ping";
NSString *PING_BUTTON_RUNNING = @"Stop";

NSString *DEFAULT_NUM_PINGS_POS = @"2";
NSString *DEFAULT_SECS_BETWEEN_PINGS = @"1";
NSString *DEFAULT_HOST = @"localhost";

@implementation PingController

- (id) init {
  self = [super init];
  param = [[PingParameters alloc] init];
  directory = NSHomeDirectory();
  pingInProgress = NO;

  [param setValue:@"-c":DEFAULT_NUM_PINGS_POS];
  [param setValue:@"-i":DEFAULT_SECS_BETWEEN_PINGS];
  [param setValue:@"-host":DEFAULT_HOST];
  
  return self;
}

Note that after saving the file, Project Builder displays an M in the CVS column. This indicates that you've modified the file. Rebuild the program and run. Now the new host name, "localhost," appears in the Host text field.

Since you added a new feature to the program, you should to record, or commit, your change into the CVS repository. To do this, select the modified file, PingController.m, from the file list, choose CVS > Commit Changes, enter a description of the change, and click the Commit button. Project Builder adds your changes to the CVS repository. Note that project Builder has removed the M from the CVS column and updated the ID tag at the top of the file.

Another common operation is to compare two versions of a file to see what has changed between revisions. For example, to compare what has changed between the two versions of PingController.m, select PingController.m from the file list and select CVS > Compare With > Previous. Project Builder uses the tool specified in its CVS preferences to compare the files, in this case FileMerge.


click for larger view

Another common action is to view status information on a file. Once again, select PingController.m from the file list, choose Project > Show Info, and select the CVS tab.


At this point, take some time to explore Project Builder’s other CVS commands. For example, try reverting to the original version of the PingController.m file by selecting it from the file list, choosing CVS > Update To > Revision, selecting version 1.1, and clicking on Update button. Remember, your changes are not lost; they are still in CVS as the head version of the file. This command just modifies your working copy.

Final Thoughts

This concludes our brief look at using CVS under Project Builder. As you have seen, Project Builder uses CVS as its default version control system, which means if you develop under Project Builder, you owe it to yourself to learn CVS. In spite of the fact that Project Builder only supports a subset of CVS commands, it does include enough functionally to make it very useful.

In the next article, we will add a few features to MyPing and create a release using the CVS tag and branch commands. In addition, we will look at other Mac OS X GUI-based interfaces to CVS, including BBEdit and Concurrent Versions Librarian.

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 MacDevCenter.com

Copyright © 2007 O'Reilly Media, Inc.