Contents:

Introduction

CVS (Concurrent Versions System) provides the following functionality:

CVS works as follows. There is a "repository" containing the master version of the files, including a history of all previous versions. Each user "checks out" a working copy of the files.

Each user can edit his or her copy of the files arbitrarily, without affecting other users or the master version.

CVS is insurance against computer crashes

Over many years of teaching 6.170, we have observed that on average 2 students' computers will either die or be stolen during the semester. Therefore, you should either work on Athena, or commit your work to the repository often. Committing often

Setup: Checking out 6.170 psets module from CVS

You always edit your own personal copy of files that are under CVS control. Before you can make such edit, you must "check out" your own copies of the repository's master files. You only need to do this step once at the beginning of the term and once when you begin your final project. If you are tempted to use this command at other times, you most likely want CVS's "update" command. In the case that you plan to work at both Athena and home, you will need to do these setup steps for both your Athena account and your home computer.

A CVS repository groups its master files into "modules." When checking out files, you need to specify which module's files you wish to check out.

The following instructions assume you wish to check out a module named psets from a repository that is located at /mit/$USER/6.170/cvsroot such that your local copies of the files appear in the directory ~/6.170. (This is the configuration that is expected for Problem Set 0).

Command Line

Execute the following commands on the Athena prompt:

cd ~/6.170
cvs -d /mit/$USER/6.170/cvsroot checkout psets

After running the commands above, logout of athena then log back in.

Emacs

Use the command line.

Eclipse

The following example will show you how to use Eclipse to check out CVS modules. Before doing so, please check to make sure that you have properly set up the Eclipse environment for 6.170. We will assume that you will check out a CVS module called psets. When you check out the psets module, Eclipse automatically sets up an Eclipse project named "psets" for you. These instructions happen to complete the task assigned to you in ps0.

Using CVS in Emacs

When using Emacs, the majority of CVS commands ("add", "commit", and "remove") are accessed through Emacs's CVS mode. (The notable exception is "checkout".)

Switch Emacs into CVS mode by running Alt-x cvs-examine or Alt-x cvs-update. When prompted for CVS Examine (directory), enter the directory in which the files you wish to manipulate are located. This is called your "working directory." For example, ~/6.170/psets/ps0/. This step will bring up a new buffer that lists all the new and modified files in your working directory. Note that unmodified, non-new files are not shown.

In this CVS buffer, you can move the keyboard cursor over a directory or file and issue one of the following commands:

Note that issuing a command to a directory will apply that command, recursively, to all files and sub-directories within that directory.

If you wish to perform the same command on several files or directories, you can "mark" each file and directory. To mark a file or directory, move the keyboard cursor over the file or directory and press the 'm' key. Doing so will place a '*' next to the file or directory to show that it has been marked. Marking a directory, will recursively mark all of its files and sub-directories. You may unmark a file by pressing the 'u' key. Once you have marked all the files and directories you wish to operate on, you can issue a command, to be applied to all marked files, by pressing 'a', 'r', or 'c'.

Alt-x cvs-update is the same as Alt-x cvs-examine except that it updates all the files in the directory you are examining before displaying the directory's files.

Adding Files to CVS Control

If you create a new file in a directory which is under CVS control, CVS will not automatically add it to the repository. This behavior is to avoid extraneous files from being added to CVS control. (You can tell when a directory is under CVS control when it contains a CVS sub-directory with files such as Entries, Repository, and Root, none of which you should directly modify.)

You should not place compiler-generated or other auto-generated files under CVS control. This includes .class files and Javadoc files. If you place those filenames or patterns (e.g., *.class) in a .cvsignore file, then CVS will ignore those files. This simplifies CVS's update output in the presence of generated files (see this section).

To make CVS place a file under its control, first follow these instructions, then commit the file.

Eclipse

With Eclipse you do not need to worry about this step. Eclipse will automatically ask you if you want to add or ignore specific files when you run commit on the directory that contains the new files.

However, if you create a file or folder in an Eclipse project but via the command line (or any mechanism outside Eclipse), then you will need to "refresh" the package explorer for Eclipse to recognize the change. To do this, right click on the project name in the package explorer and select the "refresh" item.

Emacs

  1. Switch Emacs into CVS mode by running Alt-x cvs-examine or Alt-x cvs-update. (See Emacs CVS Mode.)
  2. Pressing 'a' will add an "Unknown" file into the repository. Or you can "mark" several files then press 'a'. Remember that you must then commit the file(s).

You will be asked to enter a description of the file(s) added. Do so, then press "enter".

Command Line

When adding a file, you must indicate to CVS whether it is a text/ASCII file (such as a .java file) or a binary file (such as a .jpg image) when invoking the cvs add filename command.

If you're are attempting to add a file to the directory ~/6.170/psets/psN/ directory, first you should cd to that directory:

cd ~/6.170/psets/psN 

Now you can add a text/ASCII file as follows:

cvs add RandomHello.java 

But if the file is binary, then you need to use a -kb flag:

cvs add -kb doc/screenshot.jpg 

Updating Files

CVS's "update" command updates your local copy of files to reflect changes made to the repository by other people (or by you when working on a different computer system).

In the first half of the class, the only changes made by people other than you will be made by the 6.170 staff when we are adding new psets to your repositories. During the final project, you will need to use update to see the changes your teammates make to your shared repository. If you work at home and on Athena, you will need to use update to propagate your changes between the two locations.

CVS usually does a good job of merging changes made to multiple checkouts (say, by different people or by you on your home computer and you on Athena), even if those changes are to different parts of the same file. However, if both people change the same line of a file, then CVS cannot decide which version should take precedence. In this case, CVS will signal a conflict during CVS update, and you must resolve the conflict manually. This can be an unpleasant task.

To minimize the possibility of conflicting changes being made simultaneously, you should update frequently and commit frequently.

Eclipse

In the Package Explorer window, right-click on a file or directory, and select Team » Update. If the selected item is not a directory, just that file will be updated; otherwise, everything inside the directory will be updated.

Emacs

Run Alt-x cvs-update, and enter ~/6.170/psets/psN when asked which directory to update or enter ~/6.170/psets if you are attempting to gain access to a new pset.

Command Line

To update your local copy, go to the root of your repository (~/6.170/psets) and run:

cvs update

This will display a list of files that have been updated. You should pay attention to the character that shows up in front of each file, as it indicates what sort of change was made to the file:

? Indicates that this file has not been added to the repository, so CVS has not edited it. This is an indication that the file should probably either be added to the repository or to a .cvsignore file in that directory.
U Indicates that this file has been updated successfully to match the latest copy in the repository.
M Indicates that your working copy of the file is different from the repository's latest version, but that merging the repository's copy with yours is successful.
C Indicates that there was a conflict when trying to merge your local copy of the file with the one in the repository. (See Resolving Conflicts for more information.)
P Indicates that this file has been patched which for all practical purposes is the same as updated. (The change to the file was "small enough" that a full update wasn't necessary.)

Committing Changes

After making changes to, adding, or removing files, you must "commit" your changes to CVS. This step will cause CVS to record your changes to the repository, so that your changes are backed-up and available to other people working on the repository, or to you when working on a different computer system.

In general, you should "update" your files before committing them. (And, if the update results in any conflicts, you should resolve them before committing.) If you forget to update, CVS will abort and remind you to update first.

It is a good idea to commit your changes frequently. It backs up your work, thus enabling you to revert to an earlier version of your code if you find yourself going down a wrong path. Also, when you are working with others, it minimizes conflicts.

Note: It is not possible for a commit to change the name of a file. If you want to change the name of one of your files, copy the file, cvs remove the old one, then cvs add the renamed file.

Emacs

  1. Switch Emacs into CVS mode by running Alt-x cvs-examine or Alt-x cvs-update. (See Emacs CVS Mode.)
  2. Pressing 'c' will commit a "Modified" or "Added" file. Or you can "mark" several files then press 'c' to commit all of them.

After adding the files, you will be prompted to provide a message. Provide a descriptive log message, then type Ctrl-c Ctrl-c.

Command Line

First, enter the directory in which the file(s) you wish to commit are located. For example:

cd ~/6.170/psets/psN

Then, you can commit a group of filename(s) to CVS by running:

cvs commit -m "a descriptive log message" filename(s)

If you omit the message flag -m "a descriptive log message" from your commit command, CVS will throw you into an editor where you can enter a message.

If you omit the filename(s) from your commit command, then CVS will recursively commit everything in the current directory. Be careful not to inadvertently commit files that you don't want to commit.

Eclipse

  1. Eclipse allows you to add files and commit changes in one step. Open the Java perspective (Window » Open Perspective » Other... » Java) if you're not already in it. Open the Package Explorer view if you don't already see it (Window » Open View » Package Explorer). Make sure before you commit that you've refreshed the current Package Explorer view by pressing F5, this will take into account all changes that did not occur in eclipse. In the Package Explorer window, right-click on psets in the directory structure (the one representing the entire project). Choose Team » Commit...:

    Screenshot: Commit menu option

  2. Depending on your settings, you may be prompted to add or ignore new files on your local file system that are not in the repository. If you choose to add these files, they will show up in the summary in the Commit files dialog with a small + sign next to their name. With every commit to CVS, you are recommended to append a comment describing changes you made since the last commit. If you ever need to look at old versions of your code, your commit messages will remind you of what happened between versions.

    Enter a descriptive commit comment, and click Finish. The lower panel shows you the list of files that you have modified and that will be committed to the CVS repository.

    Screenshot: Commit files dialog

Resolving Conflicts

When multiple people (or the same person on multiple machines) are working on the same file concurrently, CVS tries to merge the changes made by each person together as each person calls CVS update. However, sometimes CVS is unable to merge the files together when multiple people change the same line of a file. In this case, CVS will signal a conflict during the update. That person (possibly with help of the other parties) will be required to resolve the conflicts.

If some of your changes conflict with others' changes, cvs update will tell you so, and the source file will be changed to include both versions of any conflicting portions (yours and the one from the repository), in this format:

  <<<<<<< filename
  YOUR VERSION
  =======
  REPOSITORY'S VERSION
  >>>>>>> repository version's revision number

You must resolve the conflict by editing the file, removing the markers, and leaving whichever version of the code you prefer (or merging them by hand). (Searching for "<<<" until you've resolved all the conflicts is generally a good idea.)

Minimizing Merge Conflicts

CVS is no replacement for management! Coordination of work is important, even if you're working separately. You should minimize working on the same file at the same time if possible. If you do work on the same file, work on different portions. Modularizing code into multiple files often makes parallelizing work more efficient. You should always pass major design decisions by your teammates before implementing them, particularly if they involve interfaces that will affect their code.

When and how often should you commit? If you commit too often without sufficient testing, you may introduce bugs into the repository that will affect your teammates' work. However, if you commit too rarely, your teammates will be using outdated code, which may cause wasted effort and merge conflicts later.

There is no hard and fast rule, but one good rule of thumb is to make sure everything at least compiles before you check in. If you check in non-compiling code, your teammates will be very annoyed when they update (which is good practice) and they cannot compile the code any longer.

Another good rule of thumb (though this one is far more malleable) is that you should minimize leaving something uncommitted when you quit for the day. A lot can happen while you're not coding, and it's generally better to get your changes in working order and commit it before you leave. Large amounts of uncommitted code being committed all at once will result in much more conflicts than small amounts of code being committed often. Since the previous rule (of never checking in non-working code) is more important, this can be hard to accomplish if you're making big changes. Thus, it's often good to tackle one feature at a time, so you can finish each piece quickly and keep the repository up-to-date.

Coordinating your efforts with your teammates is, of course, the true key to minimizing merging hassles. Again, CVS is no replacement for management!

.cvsignore

There will often be files that you do not wish to check into CVS. These will include .class files and Javadoc files. However, CVS will, annoyingly, point out the existence of these files whenever you update. A solution is to tell CVS to ignore these files using a .cvsignore file. To do so, create a file named .cvsignore in the directory where the files to be ignored exist. Then, add the names of the files to be ignored to the .cvsignore file. Each file name should go on a separate line. You can also use * to match any file. For example, to ignore all the class files in the directory, add *.class to your .cvsignore file.

If you follow the steps above, you would need a .cvsignore file in every directory in which you want to ignore files. However, there may be certain types of files that you always want to ignore. You can do this by editing a global cvsignore file in your repository. To ignore all .class files, for instance, create the file ~/6.170/cvsroot/CVSROOT/cvsignore (with no dot preceding the file name), and write the pattern *.class in it.

Adding and Removing Directories

Command Line

You can add a sub-directory with:

  mkdir dirname
  cvs add dirname

cvs adding a sub-directory happens immediately, without the need to commit.

You cannot remove a sub-directory with CVS. (cvs remove cannot be used on a directory.) The best you can do is to cvs remove -f everything in that directory, then run cvs update -P to get rid of any empty sub-directories in your working directory (-P stands for "prune empty sub-directories"). Note that the -P option to update is added to your cvs commands by default via the ~/.cvsrc file created by the student-setup.pl script.

Tracking Changes

Eclipse

Eclipse conveniently marks files and directories that have changed since the last cvs update by preceding their names with a > sign in the Package Explorer. In addition, there are two features that allow you to track changes between your working copy and the repository's latest copy: Compare and Synchronize.

To compare a file with its latest version, right-click it in the Package Explorer and select Compare With » Latest from Head. If the files are different, a window will appear showing a side-by-side comparison of the two files.

To see a summary of differences between the local copy and the repository, right-click a file or directory in the Package Explorer and select Team » Synchronize with Repository. A window will appear that summarizes which files (if you selected a directory) have outgoing changes (changes you've made after updating), which have incoming changes (new revisions committed by others to CVS), and which have conflicts. Double-clicking one of these summarized items will bring up a Compare window for that file.

To view CVS commit logs and previous revisions to a file, right click it in the Package Explorer and select Team » Show Revision History. You will see a CVS Resource History window at the bottom panel. Double-clicking a row will allow you to read the corresponding revision.

Emacs

To see differences between the working copy and the repository's latest copy: Open the file whose history you are interested in then type C-x v =.

Another powerful way to see differences in Eamcs is Alt-x ediff-revisions. It can show changes you have made locally, or the differences between arbitrary versions of a file.

Command Line

To see the change log, which is a list of the messages used when checking in changes:

  cvs log filename

To see differences between the working copy and the repository's latest copy:

  cvs diff [filename] 

Omit filename to see differences for all files. Use the -r1.xx flag to compare with a particular revision, and use two -r flags to compare two versions with each other.

CVS Pitfalls

Some tips on avoiding common problems while using CVS:

CVS mail on commit

You should not need this until the final project.

You may wish your group to be notified whenever a group member commits. You can tell cvs to send email to the group, along with the files committed and the commit message.

  1. Check out the cvs administrative directory:
    cvs -d /mit/6.170/groups/seMMN/cvsroot co CVSROOT
    

    You may need to change the "-d" argument to your own cvs root. This will create a CVSROOT directory in the current directory containing the cvs configuration files for your repository.

  2. Enter the newly created directory:
    cd CVSROOT
  3. Edit commitinfo to add the following line at the bottom:
    DEFAULT /mit/6.170/bin/commit_prep -r
    

    This tells cvs to run the commit_prep script after each file has been committed to the repository.

  4. Edit loginfo to add the following line at the bottom:
    DEFAULT /mit/6.170/bin/log_accum.pl -s -m 'someone@somewhere.com' %s
    

    Change the "-m" argument to the address you want to send mail to. This tells cvs to run the log_accum.pl script after all the files have been committed. log_accum.pl will mail all the commit messages to the address specified with the "-m" arguments. You can pass multiple -m arguments, or you can just send it to your groups' mailing list ("seMMN@mit.edu").

  5. Run cvs ci to commit your changes to the administrative database