Version Control with CVS on Mac OS X
Ryan writes "Apple recently published an article on using CVS with Mac OS X. 'This article covers some of the most commonly used features of CVS, with emphasis on using CVS with static and interpreted web files (HTML, PHP, Perl, etc).'" It's a decent article, a good primer on how to get started, and it's nice that Mac OS X comes with a CVS server. Personally, I really dig maccvs for my CVS client, and there are plenty of other clients for Mac OS out there too.
Why don't they use BitKeeper instead? :)
My other account has a 3-digit UID.
Reminds me of an Philip Greenspun article on the exact same topic. I think Linux Journal ran some ideas on that as well. Not exactly original.
-- Ken Kinder ken@_nospam_kenkinder.com http://kenkinder.com/
If you could run cvs over the web in conjunction with apache and webdav (since they're both supposedly included with OS X)
By default, cvs version 1.10 is installed which caused me some problems when connecting from a remote client to a repository on OSX.
It was easy to build the latest version but I hope Apple updates the default soon.
Chang
Apparently, there is some CVS integration in project builder as well, but this article doesn't really talk about it based on the quick glance I gave.
I haven't tried to use the CVS integration, but thought it was interesting.
"He who would learn astronomy, and other recondite arts, let him go elsewhere. " -- John Calvin, commenting on Genesis 1
the article makes several references to using BASH and/or SH, yet, by default OS X doesn't include BASH (i had to compile and install it myself, and then figure out netinfo to get to be my default shell).
I use Perforce rather than CVS. It is too bad that Perforce doesn't integrate with project builder they way it does with Metroworks CW. I hope that changes soon.
The Concurrent Versions System (CVS) is a powerful open-source tool for source code maintenance. It is provided on the the Developer Tools CD that accompanies Mac OS X or as a part of Mac OS X Developer Tools online. This article covers some of the most commonly used features of CVS, with emphasis on using CVS with static and interpreted web files (HTML, PHP, Perl, etc).
/usr/local/cvsrep
/usr/local/cvsrep /usr/local/cvsrep
/usr/local/cvsrep
/usr/local/cvsrep switch to your CVS commands. The easiest option in the long run, however, is to insert the export or setenv command above into your shell startup file. See the manpage for your default shell with, for example, man sh or man tcsh if you don't know how to set up a startup file.
/home/username/Sites/. Assuming you have Apache running on your Mac OS X machine, files placed in this directory can be viewed in a web browser via http://localhost/~username.
v cvs add another.html
. /usr/local/cvsrep/myproj/another.html,ve cking in another.html;t m l,v rm another.html
.t m l,v
/usr/local/cvsrep commit
/Library/WebServer/Documentse nts> cvs export -D "now" myproj
/Applications/Utilities, and follow these steps:
/usr/local/cvsrep
:pserver:anonymous@
:pserver:anonymous@remote_server:
/path/to/re pository checkout module_name
/Users/liz/.ssh/identity. /Users/liz/.ssh/identity.pub.
l host:Sites> export CVS_RSH="ssh"
CVS Overview and Terminology
Before you get started, take a moment to review a few common CVS terms. A repository is a place where CVS keeps master copies of all the files it knows about, along with information about the histories of those files. A project or module is a collection of files that belong together, such as all the files that make up a particular web site. To checkout a project is to make a local copy of all the related files so you can make and test changes on your own without affecting what others see. To commit is to save your changes back to the repository, where they are available to others (or to yourself, in case you ever want to roll back to a previous version).
Getting Started
CVS comes with Mac OS X; so you don't need to install anything new if you've installed the programs on the Developer Tools CD or downloaded the developer tools from http://developer.apple.com/tools/.
To get started, create a new directory for your CVS repository as the root user:
liz@localhost:~> sudo sh
Password:
root@localhost:~> mkdir
Now look at the default owner and group for the directory you just created:
root@localhost:~> ls -ld
drwxr-xr-x 3 root wheel 58 Dec 9 21:30
As you see above, the new directory is owned by the root user and belongs to the wheel group. You want to make sure you can write to the directory even when you aren't working as the root user. You probably want to make your CVS directory available to your system's primary user. In my Mac OS X installion, liz was the first account created, and by default Mac OS X made liz a member of wheel and other administrative groups:
root@localhost:~> groups liz
staff wheel admin
The instructions that follow work on most single-user Mac OS X installations. You can also read more about multi-user CVS below.
First you need to change the permissions to the newly created directory so that the system's defualt user can read and write to the directory.
root@localhost:~> chmod g+w
root@localhost:~> exit
liz@localhost:~>
Now create the CVSROOT shell environment variable. This variable tells CVS where its repository lives. (Note: if you don't know what shell you're using, it's probably tcsh.)
In bash or sh, the command is:
liz@localhost:~> export CVSROOT=/usr/local/cvsrep
Or, in tcsh:
liz@localhost:~> setenv CVSROOT "/usr/local/cvsrep"
I also like to change the CVSEDITOR environment variable to be my favorite text editor, emacs. If you're happy with vi (the default), you can ignore this variable. If you're new to the Unix world, you might want to go with the simple editing program, pico. Later in this article you'll see how to use CVS with BBedit.
liz@localhost:~> export CVSEDITOR=emacs
Or, in tcsh:
liz@localhost:~> setenv CVSEDITOR "emacs"
Now you're ready to set up your CVS repository with the cvs init command:
liz@localhost:~> cvs init
liz@localhost:~>
Once you log out of the current terminal window, you'll lose the CVSROOT environment variable. That means the next time you try to use CVS, you'll see an error message much like this:
cvs import: No CVSROOT specified! Please use the `-d' option
cvs [import aborted]: or set the CVSROOT environment variable.
There are three ways to work around this error. You can manually set the CVSROOT (and the optional CVSEDITOR) variables when you want to use CVS. Or you can add the -d
Creating Your First Project
Once you have initialized CVS, you can create a new project. First, create a directory called "myproj" under your home directory:
liz@localhost:wherever> cd ~
liz@localhost:~> mkdir myproj
liz@localhost:~> cd myproj
Now you can create a new file as part of your project. For test purposes, create an HTML file called ~/myproj/index.html with some basic text:
First
First File in First CVS Project
Now you can add this new project to your CVS repository using the import command:
liz@localhost:myproj>cvs import -m "My First Project"
myproj vendor-tag start
N myproj/index.html
No conflicts created by this import
liz@localhost:myproj>
You can read more about the optional vendor-tag here.
Creating Working Copies of a Project
Now that you have created a new CVS project containing a single file, you (and others) can use the checkout command to make working copies of the project. You can make changes to this project and test them. Then, once you are satisfied, commit the changes to the main repository.
A sensible place to check out copies of web projects is into your individual web directory. By default, this directory is
This is how I check out a copy of a new project to my ~/Sites directory:
liz@localhost:~> cd ~/Sites
liz@localhost:Sites> cvs checkout myproj
cvs checkout: Updating myproj
U myproj/index.html
liz@localhost:Sites> cd myproj
You can now view this page from your browser:
At this point, you can delete the original project file from your home directory (~/myproj/index.html)if you like. The file is now in the CVS repository, and you can check it out whenever you want.
Making Changes
Now you can make some changes to your local copy of myproj/index.html, the one in the ~/Sites directory. For example, you might add a line of text:
First
First File in First CVS Project
I have added some text to the file.
Once you've made the changes, commit the updated file to the repository. CVS expects you to supply a comment with your commit that summarized the work you've done. If you do not supply a comment using the -m flag, CVS will dump you into a text editor (see CVSEDITOR above) and expect you to type and save a comment there before proceeding.
liz@localhost:myproj> cvs commit -m "updated index.html" index.html
Checking in index.html;
/usr/local/cvsrep/myproj/index.html,
cvs add: scheduling file `another.html' for addition
cvs add: use 'cvs commit' to add this file permanently
liz@localhost:myproj>
liz@localhost:myproj> cvs commit -m "added another.html"
cvs commit: Examining
RCS file:
done
Ch
/usr/local/cvsrep/myproj/another.h
liz@localhost:myproj> cvs remove another.html
cvs remove: scheduling `another.html' for removal
cvs remove: use 'cvs commit' to remove this file permanently
liz@localhost:myproj> cvs commit -m "removed another.html"
cvs commit: Examining
Removing another.html;
/usr/local/cvsrep/myproj/another.h
Restoring a Previous Version
If you'd like to check out a project as it existed some time in the past, you can use the -D flag. For example, to get the project as it existed at a certain date and time, create a fresh directory (or move or delete your existing project files), and run cvs checkout -D date project. For example:
liz@localhost:Sites> cvs checkout -D "2001-11-29 18:00" myproj
cvs checkout: Updating myproj
U myproj/index.html
Working with Multiple Projects
Your CVS repository can contain as many projects as your hard disk's capacity will allow. Just create new projects with the cvs import command. When you check out a version of a project, cd into your local project directory and run your CVS commit, add, remove, etc. commands from there. Actually, there's no reason you can't have more than one CVS repository as well. You can specify different repositories using the -d flag or by changing the CVSROOT environment variable. All the examples in this article, however, assume a single local repository.
Using CVS with BBEdit 6.5
Traditionally, Mac internet developers have used BBEdit to develop files locally, then FTP-ed them to a remote server. But BBEdit 6.5 runs natively on Mac OS X and it supports integrated Unix scripting. This means you can write simple shell scripts to access CVS without leaving BBEdit.
To run a Unix shell script from within BBEdit 6.5 or greater, you will use the menu option labeled #! (often pronounced "shebang"). First, create a directory to hold your scripts.
liz@localhost:~> mkdir scripts
Now you can create a file in ~/scripts called, for example, "commit". If you already know you'll be working with multiple projects, you might want to give the file a more specific name, like "myproj_commit". The file would have the following contents:
#!/bin/sh
cd ~/Sites/myproj
cvs -d
To run your new commit script, select Run File from the shebang menu in BBEdit. The first time you run it, you'll have to tell BBEdit where the script is. In the future, BBEdit will remember recently executed scripts and display them for you.
Using CVS Branches
Branches can be useful for even the simplest web applications. For example, you may want to release one version of a site while you're working on the next version. With CVS branches, you cannot only keep track of multiple releases, you can easily reference your various versions by name, making bug fixes and code maintenance much simpler. You can create branches by using the -b option to the cvs tag command.
liz@localhost:myproj> cvs tag -b phase_one
cvs tag: Tagging myproj
T myproj/index.html
Now that you've tagged the project for the first time, you need to take one extra step: release your current working directory and checkout a new copy of the project, specifying the new branch name (the -d flag deletes the files currrently in use):
liz@localhost:Sites> cvs release -d myproj
You have [0] altered files in this repository.
Are you sure you want to release (and delete) directory `myproj': y
liz@localhost:Sites> cvs checkout -r phase_one myproj
cvs checkout: Updating myproj
U myproj/index.html
Publishing Code with CVS
If you look at your project directory under ~/Sites/myproj, you'll see that your source files aren't the only things there. There are also CVS administrative files and directories:
liz@localhost:myproj>ls -l
total 8
drwxr-xr-x 5 liz staff 264 Dec 11 23:23 CVS
-rw-r--r-- 1 liz staff 125 Dec 12 17:19 index.html
These are necessary while you're working on a project, but you don't really want them to be part of your published website. To obtain a copy of a project without any CVS extras, you can use the CVS export command. For example, here's how I would export my web project to my Apache server's main document root. Note: I'm specifying the date of now because export expects either a branch tag or a date.
liz@localhost:~> cd
liz@localhost:Docum
cvs export: Updating myproj
U myproj/index.html
liz@localhost:Documents> cd myproj
liz@localhost:myproj>ls -l
total 8
-rw-r--r-- 1 liz staff 186 Dec 12 20:29 index.html
Using CVS with Multiple Developers
CVS will let more than one person work on the same file at the same time. In fact, if two people make changes to the same file and commit them, CVS will try to accommodate both changes. If CVS cannot accomodate your changes (for example, two people have both changed the same area of the same file), it will let you know that you need to hand-edit the file -- and probably communicate with the other developer -- in order to resolve the conflict.
If many people access the same CVS repository on the same machine, it helps to create a new CVS login group so that all developers can have write access to the repository.
To create a new group under Mac OS X, launch the NetInfo Manager application from
Click the lock icon and enter your password in order to make changes.
Click the word "groups" in order to display groups.
You are going to make a duplicate copy of an existing group and re-name it. Start by clicking on "daemon".
Click the "duplicate" icon (the image of two folders).
Click on the name of the newly created duplicate group and change it to "cvs-user".
Change the group id (gid) to some number that hasn't been used yet, like 501.
Highlight the "users" field in the lower pane of the window.
For each user you would like to associate with this group, select "insert value" from the directory menu. Double-click each newly inserted value and change the name to the short name of your desired user.
When you're done, you should see a window something like this ("liz" and "jay" are the two users I have associated with the "cvs-user" group):
Once you're done, you can go back to the shell to confirm that the users "liz" and "jay" are members of the cvs-user group:
liz@localhost:~groups liz
staff wheel admin cvs-user
liz@localhost:~>groups jay
staff cvs-user
Finally, change the group ownership of your CVS repository so that the members of your new cvs-user group can access it:
liz@localhost:~>sudo chgrp cvs-user
Password:
CVS has several useful commands for multi-developer collaboration. These include cvs status, which shows information about files you've changed that have also been changed by others; cvs release, which tells CVS that you're done working on a project; and cvs update, which updates your working copy of a project to reflect changes made by others. For more information on these and other commands see the suggestions for further reading.
Using CVS Across a Network
So far, you've learned how to deal with repositories and projects stored on your local Mac OS X machine. CVS will also let you to get repositories residing on other machines on the Internet, providing they're running one of a few services.
CVS pservers
You can checkout files from a remote repository by accessing a public CVS pserver (password server). Many pservers, especially those for open-source projects, allow read-only access under the username "anonymous" with the password "anonymous". You can access a remote server (specified with the -d flag) by first using the login command, followed by checkout.
liz@localhost:Sites> cvs -d
remote_server:/path/to/repository login
CVS password: anonymous
liz@localhost:Sites> cvs -d
There is a significant disadvantage to using this method for anything but anonymous access, however. CVS pservers transmit the login name and password unencrypted. The module files are transmitted in the clear as well. This means that any unfriendly folks who may be listening in on your network traffic have full access to not only your login information, but your data.
There are ways to set up cvs-only usernames and passwords (see suggestions for further reading for directions), but it can be much nicer, in my view, to use CVS entirely through an encrypted connection. For this, you can use ssh (secure shell).
Secure CVS via ssh
It's simple to use CVS entirely over ssh. If you set the environment variable CVS_RSH (CVS Remote Shell) to "ssh", you can run CVS commands securely across the Internet, just as though you were running them locally. Mac OS X 10.1 users have ssh and scp (secure copy) available by default. If you're using an earlier version of Mac OS X, you can visit http://www.openssh.com/ to obtain ssh.
If you'd like to use ssh without having to type your password every time (useful if you are accessing CVS via a shell script in BBEdit, for example), you'll want to create a public/private key pair with the ssh-keygen command. The advantage of doing this is that your scripts will be able to run without human intervention. The disadvantage is that anyone who can access your account on your local Mac OS X box will also be able to access those remote servers which have stored your public key. (Note: If you hit return when you are asked for a passphrase you will end up with an empty passphrase. )
liz@localhost:~> cd ~/.ssh
liz@localhost:.ssh> ssh-keygen
Generating public/private rsa1 key pair.
Enter file in which to save the key (/Users/liz/.ssh/identity):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in
Your public key has been saved in
The key fingerprint is:
[your key fingerprint, followed by your email address]
liz@localhost:.ssh>ls
identity identity.pub known_hosts
Once you have created your private and public keys, you need to place your public key on the remote host in a place where ssh and scp can recognize it. Use ssh to connect to the remote host(s) on which you want to publish your projects. Then add the contents of your local ~/.ssh/identity.pub file to a file in your remote ~/.ssh directory called "authorized_keys" (if you don't have one already, you can create it).
Whether or not you've placed your public key on the remote server, you can run CVS securely through ssh:
liz@localhost:Sites> export CVSROOT=":ext:your_login@
your_remote_server:/path/to/repository"
liz@loca
liz@localhost:Sites> cvs checkout remote_module_name
Conclusion and Suggestions for Further Reading
In this article you've seen how to create CVS repositories and projects, add and remove files, make changes and commit them, and access CVS on servers other than your own. There are many more features in CVS for you to explore, and I encourage you to follow the links below to learn more.
For more information about CVS, visit http://www.cvshome.org/. Of particular interest is the Official CVS Manual. On your local OS X system, you'll also find the standard CVS documentation, when you type "man cvs". Finally, there's also the handy paperback CVS Pocket Reference, written by Gregor N. Purdy, and published by O'Reilly.
Hey Pudge, how'd you get maccvs to work? I tried using it with a fink install of python, but it didn't seem to like that and said it preferred MacPython. Then I tried getting that to work with no luck as well (I didn't exactly try too hard since the idea of keeping two python installs on one machine didn't appeal to me). Any ideas? I don't really feel like messing with a GUI for CVS unless I know it'll work...
Umm... shouldn't that be "version-controlling-y-goodness?"
(Sorry! Couldn't resist!)