Changes to your sandbox files are not synchronized with the repository until you run the cvs commit command. This command is best run from the root directory of your sandbox, and it must be run from within the sandbox.

Commit to the repository frequently. Rules of thumb for when to commit include “every time the code compiles cleanly” and “every day before lunch and before you leave.” Speak to your project manager about the preferred working style, or read Chapter 7 for ideas.

In programming projects with several developers, try to avoid committing code that doesn’t compile, unless your project manager tells you that your project is using a different working style.

When you commit, CVS examines each directory and subdirectory under the current working directory. It searches for files that it is tracking that have changed, and commits all changes to the repository. See Example 2-12 for an example of committing files. Remember that the repository path is stored in the sandbox, so you don’t need to specify the path explicitly in your cvs commit command.

Example 2-12. Committing files

$ cd ~/cvs/example$ cvs commitcvs commit: Examining .

If your repository is not on the local machine and your repository server doesn’t have your SSH public key, CVS asks for a password for the remote machine. If the server has the public key, your SSH client can use the private key to authenticate you. It’s much easier to use CVS if the server has the public key.

Keeping a public key on the server is a security decision: it’s much more convenient to use CVS if you don’t have to keep typing in your password, but it also means that if an intruder has access to your shell, he can gain access to the data stored in the repository. I keep my desktop’s public key on the server, but not my laptop’s. It’s always possible that I’ll lose my laptop, so I don’t want it to have password-free access into our network. The desktop computer is a little bit harder to lose.

SSH is a system of authentication and encryption that relies on a public key and a private key. Only you (or your sandbox computer) should ever know your private key. Anyone (or any computer) can be told your public key, which they use to encrypt messages specifically for you. Your SSH documentation should include instructions for making and storing your private and public keys.

If any files have been changed, CVS opens an editor to allow you to record a change message. By default, the editor is vi, just as when importing a project. Chapter 3 gives instructions on changing your editor.

I strongly recommend meaningful change notes. If you’re trying to do a rollback and all you have are messages that say “fixed a few bugs,” you won’t know which revision to roll back to. See Example 2-13 for an example of a good change note.

If a revision in the repository is more recent than the revision the sandbox was based on, cvs commit fails. Use the cvs update command to merge the changed files; resolve the conflict as shown in the next section, “Updating Sandboxes,” then run cvs commit again. Example 2-15 shows the response to a failed commit.

The cvs update command checks your sandbox against the repository and downloads any changed files to the sandbox. It complements the cvs commit command, which uploads changes from the sandbox to the repository. Use the -d command option to download new directories as well. Example 2-16 shows the use of cvs update.

As with committing, you should not have to specify the repository; it should be stored in the special CVS subdirectory in the sandbox. You must run cvs update from within the sandbox, and it is best to run it from the root directory of the sandbox to ensure that it checks all the subdirectories.

Note that -d means two different things, depending on where it is in the command. Recall that CVS commands take the following form:

cvs [cvs-options] command [command-options]

As a CVS option, -d defines the repository path. As a command option to the update command, -d downloads directories that were not previously in the sandbox. This is explained in more detail in Chapter 3.

As the update command runs, it generates a list of files that are modified. To the immediate left of each filename is a single uppercase letter. Those letters report the status of each file listed, and they have the following meanings:

A filename

Marked for addition but not yet added to the repository (need to run a cvs commit).

C filename

There was a conflict between the repository copy and your copy. The conflict requires human intervention.

M filename

Modified in your working directory. The file in the sandbox is more recent than the repository version, or the sandbox and the repository both had changes that the system could safely merge into your sandbox copy (need to run a cvs commit).

P filename

Patched in the sandbox; similar to U (updated successfully), but in this case CVS sent only the changes, not the entire file.

R filename

Marked for removal but not yet removed from the repository (need to run a cvs commit).

U filename

Updated successfully. A newer version in the repository has replaced your sandbox version.

? filename

The file is in your working directory but not in the repository. CVS doesn’t know what to do with it. It’s common to see this marker next to temporary or intermediate files that you haven’t cleaned up.

The A, R, and M codes mean that your sandbox contains changes that are not in the repository and it would be a good idea to run a cvs commit.

Figure 2-8 shows the update dialog from gCVS. In the background, at the bottom right, you can see the dialog from a successful update, showing that file1 is modified in the sandbox.

Figure 2-8. File update with gCVS

If CVS can’t merge a modified file successfully with the copy in the repository, it announces the conflict in the output of cvs update, as shown in Example 2-17. Figure 2-9 shows a conflict in gCVS.

CVS automatically merges files when the changes are on different lines. If a line in the repository copy is different from the corresponding line in the sandbox copy, CVS reports a conflict and creates a file with the two revisions of the line surrounded by special marks, as shown in Example 2-18.

Example 2-18. Conflict marks

<<<<<<<file 2 This line came from the sandbox. = = = = = = = This line came from the repository. >>>>>>> 1.4

The contents of the original file are stored in .#file.revision in the file’s working directory, and the results of the merge are stored as the original filename.

To resolve the conflict, search the file with the original filename for the pattern of repeated greater-than or less-than symbols. Study the two options for each set of changes, and discuss the changes with the person who committed the previous ver sion of the file. (Use cvs log filename to find out who committed the previous version; see Chapter 5 for information on cvs log.)

Once you and the previous author have agreed on how the file needs to be changed, edit the file accordingly and remove the conflict markers. Then commit the changed file.

How I Lost Penny’s Changes

The first time I used CVS, I had no idea what I was doing. My coworker sent me to man cvs, which is just a summary of the commands, with no tutorial information. Penny, who I was working with, had never used it either.

We struggled through, until we reached our first conflict. We couldn’t understand why Penny couldn’t commit, and after some time, decided to remove Penny’s existing file and update from the repository. I’m sure we thought CVS must have saved it somehow.

If only we’d updated with Penny’s file still in her sandbox. But we didn’t, and the work she had done was lost.

Penny, if you ever read this, I’m sorry. And now I know what I should have done.

{mospagebreak title=Adding Files}

To add a file to a project in the repository, first create the file in your sandbox. Be sure to consider your project’s structure and place the file in the correct directory. Then, issue the following command from the sandbox directory containing the file:

cvs add filename

This command marks the new file for inclusion in the repository. Directories are added with the same command. Files within a directory can’t be added until the directory itself is added. A file is only marked for addition when you run cvs add; it is actually added to the repository when the next cvs commit is run. A directory is added to the repository immediately. Example 2-19 shows a file being created and added to the repository. Remember that the file is not actually stored in the repository until the cvs commit command is run. Figure 2-10 shows the same file addition in gCVS.

If you have binary files or other files that are not plain text, please see the section “Binary Files and Wrappers” in Chapter 3 before adding them to the repository. These files should be added with the -kb command option.

As with committing for other reasons, an editor window will open asking you to enter a log message describing the files to be added.

{mospagebreak title=Removing Files}

To remove a file from the repository, first remove the file from the sandbox directory, then run the following command from the sandbox directory that contained the file:

cvs remove filename

The deletion does not take effect until the next cvs commit command is run; the file remains in the repository until then.

Example 2-20 shows a deletion, and Figure 2-11 shows the same deletion in gCVS. After the cvs commit is run, CVS doesn’t remove the file entirely; it puts it in a special subdirectory in the repository called Attic. This step saves the file history and enables the file to be returned to the repository later.

CVS opens an editor so you can record the reason for the file deletion, as it does when you commit changes.

CVS does not remove directories from the repository, because doing so would break the change tracking. Use the -P flag to cvs checkout and cvs update to avoid empty directories in your sandbox.

{mospagebreak title=Exporting and Building Projects}

When you’re ready to compile and test a program, to send a book to an editor, or to post a web site to the server, you will want a copy of the project that doesn’t have the CVS administrative files. You could use cvs checkout and remove the CVS subdirectories, but that’s tedious and unnecessary. Use cvs export instead. It works exactly the same as checkout, but doesn’t create the administrative files necessary for a sandbox.

If you need to compile a program, distribute the project’s files, or otherwise modify the results of the export, I recommend using a build tool such as make on the exported project. Unfortunately, the use of make is beyond the scope of this book, but

Figure 2-11. Removing a file with gCVS

I recommend the article “Introduction to Make” at http://www.linuxdevcenter.com/ pub/a/linux/2002/01/31/make_intro.html.

Quick Tips for Success

CVS is a tool for improving project development and system maintenance. Like all tools, there are ways to use it most efficiently:

Synchronize the clocks of computers sharing a repository to the same universal time. CVS relies on file timestamps to determine which files have changed. NTP (Network Time Protocol) is a very useful tool for time synchronization.

Give each developer his own sandbox, and communicate all file changes through CVS. This method maintains change tracking and prevents developers from irretrievably overwriting each other’s work.

Update frequently, at least before starting work every day, to keep your sandbox current.

Commit frequently to keep your repository current. Programmers typically commit every time their code compiles cleanly; other people may commit after completing each block of work.

Programming teams: use build-management tools and ensure that all files in the build are committed to the repository. Ensure that builds for testing or release come from the repository rather than a sandbox, but allow sandbox builds for programmers to do prealpha tests.