The simplest use pattern of cvs has four steps and it makes sense only if you are sure that
no one else is modifying the same files that you intend to modify:
- Check out the files you need.
- Modify some or all of these files.
- Create some new files. There are tricks to adding new directories: see below.
- Edit the release notes file.
- Commit your new files and changed files to the repository.
Before the commit, the changes you made are local to your working copy.
After the commit, anyone who checks out the code will get your changes.
The reason that this pattern is not recommended is that the "c" in cvs stands
for "concurrent"; cvs allows many people to check out the same files and work on them in parallel.
Therefore the following scenario is possible:
- You check out a file.
- Someone else checks out the same file;
- The other person changes the file and commits it back to the repository.
- You change the file and commit.
- The commit will fail with a message that your changes conflict
with previously committed changes.
If you have changed many files it is possible that some commits will succeed while
others fail.
In general this will leave the code base in broken state and it
is your job to fix it promptly.
The recommended pattern is:
- Check out the files you need.
- Modify some or all of these files.
- Create some new files.
- Make a backup of your work.
- Use cvs status and cvs update to check for and merge in changes made by others.
Resolve any conflicts this creates. See below.
- One final check to ensure that changes made by someone else do not conflict at
run-time with changes you made. This step is not needed if cvs update did not modify
you working copy.
- Rebuild your code and rerun your tests.
- If any of your tests fail, resolve the problem and restart at step 4.
- Edit the release notes file.
- Commit your changes and delete your backups.
It is possible that, after you checked out your code, someone else committed changes to
cvs. It is your job to check for this situation, to make sure that your changes do not
conflict with the other person's changes and, if they do, to resolve the conflicts.
Remember that cvs only knows about conflicts of the form "two people changed the same line";
it cannot tell that the combination of change by you and a change by some else,
perhaps in a different file, will cause incorrect program behaviour.
Cvs has a variety of tools to help you manage this situation.
The cvs history command will tell you who has made what changes recently.
The cvs status command will tell you which files are up to date, which files have
possible conflicts, and so on. And cvs update will try to bring your working copy up to
date with the head of the repository. These three commands are discussed in more detail below.
The emacs editor has a powerful and convenient interface to cvs status. To use cvs status by hand,
do the following:
- cvs status >& status.log
- grep File: status.log | grep -v Up-to-date
Presumably most files are up to date so this will shorten the list of things to look at.
- Each file will have
one of 10 possible status values.
- The 4 most frequently encountered status values are:
- Up-to-date: you have not modified your checked out file and no one else has commited
a change to it since you checked it out.
- Locally Modified: means that you modified the file and that no one else has checked in a change to it
since you checked it out.
- Needs Patch: means that you did not modify the file but someone else has checked in changes since you
checked out your code.
- Needs Merge: means that you have modified the file and someone else has also checked in changes since
you checked out your code.
- If there are no files with a status of "Needs Merge" then there are no conflicts that cvs can detect. So you
should
- cvs update -PdA >& update.log.
See below for details on the recommended options.
- Check the log file for errors; there should not be any but, if there are, it is your job to resolve them.
- Proceed to the "one final check" step in the recommended use pattern.
- If there are files with a status of "Needs Merge" then you need to investigate these files further.
- Look at status.log to learn the latest version in the repository.
- cvs diff -r vvv filepath
where vvv is the latest version in the repository, will show you the differences
between your working copy and the latest version in the repository. Remember this
for later.
- If you omit the "-r vvv", cvs diff will show the differences between your current working
copy and the version that you checked out. This is certainly useful but it is not
what you want for this case.
- You should be able to tell from the diff output if cvs is likely to be able to do a
successful merge.
- If you have not already done so, make a backup copy of those files in your working copy
that need merging.
- cvs update -PdA >& update.log .
See below for details on the recommended options.
- Check by hand that the merges worked properly; if they did not, it is your job to resolve them.
- Look in update.log to check for other errors; there should not be any but, if there are,
it is your job to resolve them.
- Proceed to the "one final check" step in the recommended use pattern.
If your commit fails because of conflicts, it is your job to fix it promptly. Very likely the cvs HEAD will
be in a broken state until you fix it.
In some cases the fix will be easy. Just edit your working copy, find the problem and fix it. Usually, however,
you will need to do more work. The following are some suggestions.
First, you should make a backup of the current state of your work. You can use cvs history to see who else
has recently worked on the offending file; they may have useful advice. You can use cvs diff to compare the
current HEAD with the version the other person started from; this will identify what the other person did.
You can use cvs diff to compare your working copy with the HEAD; this will show the conflicts plus your
other changes that are not conflicts. As a last resort, you can delete the offending file from your working copy
and re-checkout the HEAD of that file; after that you can, by hand, merge your changes into the HEAD
(this is why it is recommended to make a backup before you start the commit process).
Suppose that you have a checked out copy of the code on which you are working.
Then you are told that a directory has been deleted in the cvs head.
You might expect the
following command to remove the directory if you execute it in your top level
working directory:
cvs update -PdA
If the only files under that directory are files managed by cvs, then this
command will do exactly what you expect - it will remove the entire directory
tree rooted at the deleted directory.
However the mu2e build system, scons, interferes with this. When you run
scons, it leaves object files ( ending in .os ) in the same directory as the
corresponding source files (ending in .cc). Therefore the action of cvs
update with the
above options will be to:
- Traverse the directory tree rooted at the removed directory and remove
all files known to cvs.
- Files that are not under version control are not touched; this includes
.os files and any other files you may have created.
- If, after the above steps, any directories are empty, remove those
directories.
A typical result is that the .os files remain, along with all of the
directories on the path to them. If this happens, verify that the
directory tree rooted at the removed
directory does not contain files that you care about. If it does, move those
files elsewhere. Then, from the top level of your working directory,
/bin/rm -r -i <directory_name>
/bin/rm -r -i lib/libmu2e_<directory_name>*
If you are sure that all is OK, then use ^C to interrupt the command
and reissue it without the -i. The second step removes the libraries created
from the deleted code.
An alternative is always to do the following when you expect directories
to be removed by a cvs update:
find <directory_name> -name \*.os -exec rm {} \;
/bin/rm -r -i lib/libmu2e_<directory_name>*
cvs update -PdA
This removes all .os files and .so files before doing the update; so there
will be empty directories for the update to remove.
This sort of behaviour is common to most code management systems, which
never touch files that are not under their management.
- checking out files
- cvs co module
- cvs co -r tag module
- cvs co -D date module
- module: the name of a package or sub-package (e.g. Offline)
- tag: the name of a cvs tag (e.g. v0_0_3)
- date: for example "2010-04-27 01:30"
- The date option will select the most recent files with a commit date no
later than the date specified as the argument. I believe, but am not sure,
that the date is given in the time zone set on the computer in which you
execute the cvs command.
- Comparing your current working files to the version that was checked out.
This does not warn you about concurrent changes made by others.
- cvs diff filename(s)
- cvs diff
- The first version will compare the specified file(s). If any of the filenames
are directories, the cvs will recursively descend through the directory and compare
all files.
- The second version compares all files in the directory tree rooted at .
- There is also a syntax to diff your working against an arbitrary version in the
repository and a syntax to diff two different versions from the repository.
Consult the complete cvs documentation.
- commit your changes
- adding files
- There are separate instructions for adding a directory.
- Adding a new file is a three step process.
- Create the file(s)
- cvs add filename(s)
- cvs commit -m "Comment" filename(s)
- In the add and commit commands, you can use wildcards and/or you may
list several files on one line. Be careful with wildcards so that you do
not add or commit unintended files.
- If you accidentally add a file, simply skip the commit for that file.
- adding directories
- You cannot commit an empty directory. You must create a file in the directory first.
This is why some of the Mu2e directories are empty except for a file with a name like
placeholder.txt.
- The mantra for adding a directory is:
- mkdir directoryname
- cd directoryname
- Create one or more files
- cd ..
- cvs add directoryname
- cvs add directoryname/file1 directoryname/file2 ...
- cvs commit -m "Comment text." directoryname
- This will add and commit both the directory and all of the files that you added in step 6.
- removing files
- rm file(s)
- cvs remove file(s)
- cvs commit -m "Comment text" file(s)
- You must rm the file before issuing the cvs remove command.
- The remove is not final until the commit has been issued.
- removing directories
- cvs does not let you remove directories.
- However it does let you ignore any directories that are empty.
- cvs co -P Offline
- cvs update -PdA
- In both cases, the -P option says to prune empty directories.
- If you remove all ordinary files from a directory tree, leaving only
subdirectories, then the -P option will skip the full directory tree.
- updating
- check the history and the commit comments for a specific file
- some useful history commands
- cvs history -c -a -D "1 day ago" -z CDT
find all changes submitted to the repository by anyone in the past day
- cvs history -c -a -D "1 day ago" -f Mu2eG4/src -z CDT
find all changes submitted to Mu2eG4/src (or any other subdirectory)
by anyone in the past day
- cvs history -c -u user "1 day ago" -f Mu2eG4/src -z CDT
find all changes submitted by user to Mu2eG4/src in the past day
- The -z CDT argument tells cvs to print the dates in CDT time ( Central Daylight Time - Fermilab
summer time ). You can substitute other time zone abbreviations
- command line help
- cvs -H command e.g., cvs -H history
- Tagging. Only experts should make tags.
- cvs tag tag [files ...]
- cvs tag -c tag [files ...]
- cvs rtag tag module [module...]
- Here tag is the name of the tag, for example v1_2_3.
- "cvs tag" takes as arguments the names of checked out files whereas "rtag" takes
as arguments the names of modules in the repository.
- For cvs tag:
- If a named file is a directory, then cvs tag will work recursively
down through that directory.
- If no files are named, then cvs tag will work recursively down from
the current working directory.
- Before tagging, make sure that your current working copy is up to
date with the respository: the thing that is tagged is the
version at the head of the repository, not your working copy.
Another way to say this is that tag does not have an implied commit.
- The -c option will check the named files to make sure that all files are up
to date with the repository before tagging. If there are any problems, nothing is tagged.
- For cvs rtag:
- The tag is placed on files at the head of repository.
- Moving tags. On very, very rare occasions it may be necessary to move tag from one version of file to
another; perhaps you forgot to commit one file before tagging. The command to do this is:
cvs tag -r revision -F tag [files ...]
where revision is the internal cvs revsion number of the version which is to receive
the tag and where tag is the name of the tag to be moved. Never move a tag on code that
has been used to create results that are in any way "official"; if it is necessary to bug fix
production code, use a branch instead of moving tags.
- Checking the status of a file.
- cvs status filename
where filename is the name of a checked out file. This command will let you know
if the checked out version of the file is in sync with the repository,
if the file has been modified since you checked it out or if the repository version of the
file has been modified since you checked it out.
- Other information about a file
- cvs log filename
where filename is the name of a checked out file. This command will
print out information about all existing versions of the file, including all of the
comments made at commit-time. The printout includes the list of which revision numbers
belong in which tag.
- Importing a directory tree at the top level.
- Here is an example: you want to make a top level cvs package named G4BeamlineScripts wih 4
subdirecotries, named BeamFiles, Field, Geometry and Inputs.
- Cd to an empty directory in your own disk space; the name of this directory
is not important.
- mkdir the 4 subdirectories. As a precaution I usually add one file, named Placeholder.txt,
in each subdirectory; I do this because cvs sometimes does not like empty directories.
- cvs import -m "Starting a new package." G4BeamlineScripts empty start
- The name G4BeamlineScripts is important. It is the package name.
- The import command will import all files descended from the current working directory
and place them in the package G4BeamlineScripts; the subdirectory structure is preserved.
- The -m " " is the usual cvs comment.
- The last two arguments are the "vendor tag" and the "release tag". For purposes
of this example, the vendor-tag is not important; if we imported software from
another code management system, this would be the tag by which it is known to
that system. The release tag is the usual cvs tag; we can recover the original
files by cvs co release-tag-name.
- It is possible to import into a subdirecotry of a package:
cvs import -m "Starting a new package." G4BeamlineScripts/subdir empty start
In this case the imported files would be rooted at G4BeamlineScripts/subdir.
The version of cvs installed on both detsim and mu2egpvm* is 1.11.22. The full documentation
for this version is available as:
The documentation for many different versions of cvs is found at
http://cvsman.com.
On a unix machine, you can discover which version of cvs you are using with:
> cvs --version
- An excellent tutorial and reference are the CVS related chapeters in
"Open Source Development with CVS", by Karl Fogel.
- CVS article in Wikipedia
This file last modified Thursday, 15-Nov-2018 11:59:19 CST