Checking it out: CVS!

Making the Concurrent Version System (CVS) work with you can sometimes be a pain in the #include<rear_end.h>. Fortunately, Vincent Danen has overcome these little hassles and is here to help you out.

In a previous Daily Drill Down, “Source Code Management: Installing CVS,” I introduced you to the Concurrent Version System (CVS). I covered the many benefits of CVS for anyone working on a text-based project, whether it is source code, a Web site, a manual, or any other kind of project using text files as “source” that can take advantage of CVS' tracking and revision system. In the second Daily Drill Down of this series on source code management, we will begin to use CVS and take a look at some practical examples of how to make the most of this tool.

Checking it in
Previously, we examined how to initialize the CVS repository, also known as the CVS root directory. To recap, this is accomplished using the following command:
cvs init

cvs init -d /usr/local/cvsroot

This command will initialize the directory pointed to by the CVSROOT environment variable or specified on the command line with the -d option. In our previous example, CVSROOT was set to /usr/local/cvsroot, so let's continue with that example and assume that we set up our /etc/profile file to export the CVSROOT variable to that directory. Now we have a directory called CVSROOT in our /usr/local/cvsroot directory, which holds all of our administrative information for the CVS repository.

Let’s set up our first project. For this example, let's assume that we have a Web site that we want to have CVS manage. Our Web site is located on the same machine in the /var/www/html directory, and we want to include the contents of this directory and its subdirectories in CVS because they contain our entire Web site.

In order to set up our project, we need to import it into CVS. We can accomplish this by using the following commands:
cd /var/www/html
cvs import -m “My Website” website joe base

This tells CVS to import the contents of the current directory, including subdirectories, into the module named “website.” It also tells CVS to give the imported files a description with the -m parameter, which in this case is simply “My Website.” We also give it a vendor tag of “joe.” Let's assume that your username is joe, and this is Joe's Web site, so we set the vendor tag to his user name. Finally, we give the release tag information, which, in this case, we call “base,” meaning it's our base Web site.

Once this command is completed, take a look in the /usr/local/cvsroot directory. You will see a new directory called website/. In this directory are the contents of your /var/www/html directory. Your Web site is now imported into CVS! At this point, you may want to make a backup of your /var/www/html directory and store it someplace safe. Now that it is in CVS, you don't want to be working on a non-CVS version of the Web site by accident. Personally, I like to use a tar archive with bzip2 compression, which you can also do using these commands:
cd /var/www
tar cvyf html.tar.bz2 html/

This will create an archive called html.tar.bz2, which contains your entire Web site. Store this file somewhere safe. If you need to unarchive it, you can do so using
tar xvyf html.tar.bz2

Now that you have backed up and removed the old Web site code, you are ready to check out your CVS code. This is called creating a sandbox, and the name is appropriate. When you are working on CVS code, you are really playing in a “sandbox” of sorts, and now it's time to get your hands dirty. When you first import code into CVS, every file is assigned a revision number of 1.1 and is incremented by .1 for each new commit. Right now, each of your files has a revision number of 1.1, or the first pristine version.

The other things you may want to pay attention to are permissions on the module directory and the files it contains. If user joe is the only person to make changes to the Web site, the initial ownership of user joe and group joe is probably enough. However, if you need or want others to work on the code as well, they will also need read and write access to the repository. The safest way to do this, in Joe's case, would be to make the files owned by the user joe and the group webdev (or something similar). All users who will be making changes to the repository should then belong to the group webdev, and the users and group should have read/write permission to all files in the repository. You can change the permissions on the files in the repository by using these commands:
cd /usr/local/cvsroot/website
chown -R joe.webdev *
chmod 664 *.php

Be careful how you use chmod. You don't want to remove the execute bit from the subdirectories, which would prevent anyone from entering those directories. It may be safer to use chmod g+rw to add the rw permissions to the group instead of using the absolute bits. Make sure you do this to all files in the repository, in both top-level and subdirectories, so that each file in the repository is read and write by both the user and the group. The chown command will recursively change the ownership of all files and directories to the user joe and the group webdev.

Checking it out
At this point, you need to keep one small thing in mind. All developers on a given project should have their own sandbox. Typically, most developers will have a local copy of the repository on their own computer, so this isn't really an issue. If you’re going to be doing your development on the same machine where the repository resides, you’ll need to create a new sandbox for yourself. Typically, this should be in your home directory. Let's go further with Joe as he creates a sandbox for himself.

Change to your home directory and run the checkout command using
cd ~
cvs checkout website

Here, we tell CVS that we want to check out the module named “website.” You will now have a subdirectory in your home directory called website/. If you go into the website directory, you will see that it contains a copy of everything that was in the repository.

The first thing you should do now, since you removed the old copy of the Web site, is to copy it again to your /var/www/html directory. You may need or want to do this as root, but simply issue the following command:
cp -av ~joe/website/* /var/www/html

This will copy everything in the website subdirectory to /var/www/html. At this point, your Web site should be in the exact same shape as it was prior to checking it into CVS, except that it is now being managed by CVS. You should be able to look at your site the same way you did before checking it into CVS.

Now that your site is back up and running the same as before and you have a sandbox in which to play, you can begin making use of CVS' tracking capabilities. Go ahead and make some changes to any of the files in your sandbox. Test the new pages, edit them, and do whatever you need to do to them. Once you’ve finished, you'll be ready to make CVS aware of the changes. CVS doesn't know that you've changed anything until you specifically tell it that you have. To do this, you need to tell CVS to commit your changes to the repository. This process is the same for any developer working on a particular module. Change to the sandbox you’ve been playing in and execute the following commands. For example, let's assume that you've modified index.php and you want to commit the changes:
cd ~/website
cvs commit index.php

This will commit the new index.php to the source tree. If you’ve changed more than one file, you can use the following commands to commit each changed page to the repository:
cd ~/website
cvs commit

When you update source files in the repository, you’ll be asked to fill out a log entry, detailing the changes you've made. For instance, if you changed the pixel width on a few tables, updated some PHP code, and changed the layout a bit, you might want to type the following into the editor that pops up on your screen:
Changed pixel width from 750 to 730. Changed abstract database layer
 so we can use PostgreSQL and MySQL together easily.

This is, of course, an example of how it can be done. You can be as terse or verbose as you need or want to be. It's a good idea to put a note into the log so that other developers, or you yourself in time, know what happened between particular revisions.

In the future, you can view the changelog for any given file by using the log command with CVS. To see the log entry you just committed, type the following:
cvs log index.php

This will display the revision information, the log entry, the date of the new revision, who added it, and so on. It will allow you to keep complete track of what happens between revisions, when they are made, and by whom. This also illustrates why it is important to be a little informative in your log messages when you commit new versions of files to the source tree.

Other useful CVS commands
Let's take a look at some more useful commands that CVS provides. One very handy command is the diff command. This command compares, by default, the file specified in the current sandbox against the same file in the repository and outputs the differences. For anyone familiar with applying patches, the output is the same as that used by Larry Wall's patch command. By using redirection, you can create a diff file that others can apply to a release source copy. This may be useful if you want to share your changes with others without committing them to CVS. Use the following command
cvs diff index.php

if you want to generate a difference output for index.php. Alternatively, you can use the -u command switch to generate a diff file using the unified output format, which is more commonly used for patch files:
cvs diff -u index.php

If you need to update your sandbox to the latest revisions in the repository for the module you’re working on, you can do so with the update command. You can use it either in batch mode or for a single file. Make sure you are in your sandbox and execute
cvs update

to update every file in the sandbox. To update a single file, use
cvs update index.php

to update index.php alone. When you update files, CVS will give you an idea of the status of each file in the sandbox as it compares to the repository. CVS will use the following letters to notify you:
  • A: File has been added to your sandbox.
  • C: Conflict detected between the file in the repository and that in your sandbox.
  • M: File has been modified.
  • R: File has been removed from your sandbox.
  • U: File has been updated.

Of all the status letters CVS can give you, C is the one that may be of most concern. Typically, you will see this in multiuser projects where two people are working on the same source file. One developer commits the file to the repository before the other, and when the second attempts to do the same, CVS tells the second developer that his file is incompatible with what is currently in the repository. While CVS may be smart, it's not smart enough to be able to merge two incompatible files. For instance, if both developers work on the same function and both rewrite it but in two entirely different ways, CVS does not know how to resolve the differences between the two revisions. Because of this, you will need to update your sandbox and apply your changes to the new source file before committing it.

You can also add and remove files from the repository. If you add a new page to your Web site, you will want to add it to the repository so it can be under CVS' revisioning system, and if you remove a page from your site, you may no longer need or want it in the repository. Both commands are very simple to use. If you wanted to add a new file called info.php to the repository, you would create the file in your sandbox and then execute the following command:
cvs add info.php

Likewise, if you wanted to remove a file from the repository, let's say company.php, you would remove it from your sandbox first, and then use the following command:
cvs remove company.php

When you add and remove pages in the repository, you will always need to run the commit command afterwards in order to make the changes permanent. If you add a page and then do not commit it, the addition will not be saved to the repository. In the same fashion, if you remove a page from the repository but do not commit that change, it will not be deleted. Simply run
cvs commit

after any add or remove commands to make the changes permanent.

Another useful command is the status command. This command will print a brief summary report on the given file, providing you information such as the last modified date, the current revision, and so forth. To obtain the status of index.php, use the following command from within your sandbox:
cvs status index.php

If you want some really detailed information on a particular file, use the annotate command. This command gets very interesting on files with many changes and revisions because it prints out the contents of the file, line by line, and prefixing each line is the revision number in which the line was introduced. It will also show the date that line in the file was committed and the author who committed it. With this information, you can see which users modified which lines last and when, and in what revision the line was last changed. To obtain the annotate information on index.php, use
cvs annotate index.php

In this Daily Drill Down, we took a look at some of the everyday (and some of the not-so-everyday) commands that you can use with CVS to help you get the most out of the program for managing your source files. We also learned how to set up the initial repository and how to check out the source files.

In the next Daily Drill Down of this series, we’ll take a look at some of the macros you can embed in your source files to easily identify revision information in the files themselves. We will also examine accessing CVS repositories remotely and learn how to manipulate revision numbers so that they suit you, as opposed to following a set revision pattern that CVS uses.
The authors and editors have taken care in preparation of the content contained herein but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.


Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.

Editor's Picks