- Getting started
Follow the excellent directions at http://rhex.sourceforge.net/developers.html
to set up a SourceForge account, and then follow the directions
at http://rhex.sourceforge.net/cvs_howto.html
to enable SourceForge CVS access and set up the environment variables
CVSRSH and CVSROOT. You may also want to set the environment
variable "EDITOR" to "emacs" (or your favorite
editor: This will be the editor CVS starts for you to write log
messages, it defaults to vi).
- CVS Overview
CVS is the Concurrent Versions System. In essence, there is
a central repository of the code and a history of all changes
made to the code, in this case managed by SourceForge. Users
check local copies of the code from the repository, modify it,
and then commit those changes back to the repository. Other users
can "update" their local copies to propagate new changes
out from the repository. Unlike most other source code control
systems, CVS is designed to not use "exclusive" locks
on files, i.e., when user A checks out code and makes changes,
user B is still free to check out the same code and make changes.
The commitment and update process is designed to merge these
changes together. CVS does a credible job of merging changed
files together, and is fairly conservative about warning about
"conflicts," i.e., when two incompatible changes have
been made to the same piece (usually line) of code.
So, when do you commit? The simple answer is often. Do not
think of a CVS commitment as setting something in stone, but
rather as a lightweight bookmarking of your development. Every
time you make a bug fix, or even get code to the stage of initial
testing, you should commit and write log messages describing
the changes you have made. The more frequently you commit, the
more fine grained the "history" of your code will be
and the easier it will be to track the progress of the project
and see where bugs happen. For example, I frequently perform
the following operation: debug a new feature on a work-bench
development platform to a certain extent, commit those changes,
and use CVS update to propagate the changes to my robot platform,
fix the bugs that crop up when dealing with "real"
hardware, and then commit those changes. In this example, I knew
what I committed from the work-bench was not "working,"
but by using CVS I avoid the pitfalls of manual code transfer,
and get a history of both my new features, and what it took to
get the new features to actually work on the hardware. My only
rule of thumb is to only commit things that compile.
- Basic CVS usage
All CVS operations are performed through the "cvs"
program. For example, to initially check out the entire RHexLib
core library, you would do
-
- # cvs checkout RHexLib
This will check out all files and subdirectories rooted at
RHexLib in the repository. In our case, we are accessing our
SourceForge maintained CVS repository via SSH, and thus every
CVS operation will require you to enter your SourceForge password.
Once you have checked out a copy of the source trees you desire,
99% of your CVS operations will involve the following three commands
cvs update |
Propagate any changes made in the repository to here |
cvs commit |
Propagate any changes made here to the repository |
cvs diff |
Print any changes made here since the last update |
All three of these commands with no arguments will operate
recursively on all files rooted at the current directory. You
can give all of these commands file or subdirectories names to
restrict them to only work on those files or subdirectories.
As the "cvs update" command progresses in propagating
changes from the repository to the local source tree, it prints
out various status lines for files that it is performing operations
on. Each status line starts with a letter code. The codes you
should be watching are "M", which means that there
are changes in the local code which it has accounted for, "U"
or "P" which means that updates or patches have been
made to the local code due to changes in the repository, and
most importantly, "C" which means there is a conflict.
Any time you see a "C" you should go to that file and
search for ">>>>>>>" and "<<<<<<<"
which is how CVS will have delineated the conflict between the
local code and the repository code. These conflicts are usually
easily resolved.
"cvs update" by itself will only check out new files,
it will not check out new directories. To force cvs to checkout
new directories in an update that others may have added do,
-
- # cvs update -d
When you want to commit your changes to the repository, use
"cvs commit." An editor should start up in which you
will write a log message detailing the changes you have made.
Hopefully, you are committing frequently enough that these log
messages can be both detailed and short. For one-line log messages,
if you want to avoid starting an editor, you can use the "-m"
option, e.g.,
-
- # cvs commit -m "Small change" foo.cc
I tend to use the recursive abilities of commit rather sparingly,
i.e., when I have made large sweeping changes. Normally, I commit
file by file (or at least directory by directory) in order to
have log messages tightly coupled with pertinent files.
"cvs diff" is probably one of the most useful commands
in CVS. With it you can see exactly the changes made since the
last update. You can also give it absolute dates, i.e.,
-
- # cvs diff -D "10/28/01"
to see all changes made since a certain date, or you can give
looser dates,
-
- # cvs diff -D "last week"
One useful variant is
-
- # cvs diff -D now
which will give you the difference between your local source
code and the latest stuff in the repository. By default a "cvs
diff" with no arguments gives you the difference between
your local source code and the versions as they existed at the
time of the last update. "cvs diff -D now" gives you
a "preview" of the changes that will be incorporated
in a "cvs update."
The last two cvs commands that will fill out most of your
repertoire are
cvs add <filename> |
Add a file or directory |
cvs remove <filename> |
Remove a file or directory |
"cvs add" takes the name of the file or directory
to be added to the repository. The addition will not actually
be propagated to the repository until you do a cvs commit. Similarly,
"cvs remove" tags a file to be removed from the repository.
As a safeguard, "cvs remove" will only work on files
that you have physically removed with "rm." As with
additions, removals do not take effect in the repository until
you do a cvs commit.
- Sharing a CVS local tree
The previous section shows the standard case in which user
maintain their own copies of a source tree. CVS expects this
to be the standard case and "caches" the repository
root in administrative files (specifically CVS/Root) in each
directory of your source tree. Later invocations of CVS will,
by default, ignore your CVSROOT environment variable and
use this cached value.
Unfortunately, this presents problems when multiple users
are sharing the same source tree, as might be the case on board
one of the robots. Since we are using SSH access to a remote
repository, the name of the user who first checked out the code
is embedded in the cached repository root, and every time a CVS
operation is performed, the default behavior is to ask for the
password of that initial user.
To avoid this problem when using shared source trees, you
should use the initial "-d" option to override CVS's
default behavior, i.e.,
-
- # cvs -d $CVSROOT update
will force CVS to use your CVSROOT environment variable for
the root, and thus your password will be requested rather than
the initial user's. I would suggest aliasing cvs to "cvs
-d $CVSROOT" on platforms for which code sharing will be
common.
- More...
We are just scratching the surface on what CVS can do in this
introduction. For more information you can look at the man page,
or if you live in emacs, I find the info page on CVS very helpful
(M-X info, look for CVS). In addition there are a variety of
web resources available, such as
You may especially want to check out "cvs log,"
for printing out the log messages, and "cvs tag," for
creating and using symbolic tags (as a usage example, for RHexLib
you could do "cvs diff -r rel-1-0" to see all changes
made since Uluc tagged release 1.0).