Practical CVS Tutorial
The purpose of this tutorial is to provide practical examples of
how to use CVS during a typical source development cycle.
The complexity of the CVS interface and underlying structure tends
to prevent casual use of the system and prevents us from taking full
advantage of the multi-user, parallel development features.
With the examples provided in this tutorial, it will
be possible to use CVS as just another tool, simplifying back
porting bug fixes, maintainence of released packages, and safely
going about large scale code changes (such as a platform port,
architectural changes, or adding experimental code).
Those using CVS after this tutorial will be able to enjoy some
of its more mysterious benefits with as little trouble as possible.
Being a tutorial, or
'how to', only necessary procedures are provided. Those wishing
to understand the nuance, history, and detail of CVS are invited to
read the manual.
The Environment
Adding a package to your CVS repository.
If you have write permission to your CVSROOT, you may add a
module to the CVS repository using cvs import
.
(A cvs module is another word for a source package name. For instance,
"ibp", "lors", "libexnode" are all examples of modules currently in
the LoCI CVS repository. "Module name" or "package name" may be used
interchangably.)
UNIX> cvs import <package name> <vendor tag> <symbolic tagname>
The vendor tag
is not important for now. But you
must have something in its place.
CVS does not allow tag names to contain any spaces, dashes, or
periods. This is unfortunate, because we are used to
naming packages with dashes and periods: "lors-0.80", for instance.
However, this name would become lors_0_80, if it were to be
used as a tag name.
From the PWD of the package you wish to import into CVS
(not below the directory; in the directory), run the import command.
UNIX> cat source.c
In this example, I have a single C file named "source.c". CVS
prepends the package name to each file or
directory imported. The 'N' signifies that this file is new.
And, assuming you've not chosen a package name that is already in the
CVS repository, the command will exit without an error.
#include <stdio.h>
int main()
{
printf("Hello broccoli.\n");
}
UNIX> cvs import lilprogram loci lilprogram_0_0
N lilprogram/source.c
No conflicts created by this import
Retrieving a module from CVS
cvs checkout
.
Move to a directory where you wish to place the
working directory of your CVS package.
UNIX> cvs checkout lilprogram
In this example, CVS creates a directory named lilprogram, and
populates it with source.c. The 'U' means CVS has updated
this file. You may now change to the new directory and begin editing and
saving changes.
cvs checkout: Updating lilprogram
U lilprogram/source.c
Updating your working directory and Committing changes
Once you modify and save your source files, you will wish to
commit them back to the CVS repository eventually. Before
doing this, it is important to run cvs update
.
When you are working with more than one developer, they may
have committed changes since the last time you checked out or
updated your working directory. Update will first look in the
CVS repository and compare your files to those in CVS. If
there have been additions committed by others, then CVS
brings the files in your working directory up to date with the
content of the repository. If you try to commit your changes
before updating, CVS will warn
you if your code is not up to date with the code in the
repository. But, updating first saves you a step and allows you to
verify that your code still works with the updates of others.
In my lilprogram, I realize that broccoli is not a
friendly vegetable, and I would rather say "Go away" than
"Hello". I make this change, and run cvs update
.
UNIX> cvs update
The 'M' tells me that source.c was modified by me and no other
changes were made by anyone else. It is now safe commit the changes.
cvs update: Updating .
M source.cUNIX> cvs commit source.c
This commit introduces the first revision in the history of
source.c in the main branch.
Checking in source.c;
/Users/soltesz/cvsroot/example/source.c,v <-- source.c
new revision: 1.2; previous revision: 1.1
done
Branching and Tagging
So far, we have assumed that there is only one branch in CVS,
and every checkout, update or commit operates on the most
current revision in that branch (also known as the "head" of
the branch.)
One of the strengths of CVS is its ability to manage
independent, parallel development branches within a single module (or
package).
Examples when branching the versions of a package may be
beneficial are at the time of a package release, during code porting,
or when experimental features are added that may be unstable
or otherwise unsuitable for the main branch.
For releases, development can continue on the main branch, but if
several weeks later bugs
are discovered in the release, it may be difficult to recreate
a new release and add the bug fixes in the CVS repository.
As for porting and experimental features, once the new code is
stable, it can be brought back into the main branch.
The important point to recognize is that release maintenance and
experimental development can be done inside and with the assistance of
CVS, rather than outside of CVS.
To have an understanding of what happens when a branch is created
in CVS, imagine that a new directory is created in the repository
and a copy
is made of the main branch into this new directory. The
resulting two
directories are entirely independent of one another. Changes
committed to one will not show up in the other unless you ask
CVS to do this.
This procedure is idential to what you would end up doing if
you were adding a fix to a bug in a release available
only in the distributed TAR.GZ. You would unpack the archive
and add your fixes, entirely independent of the CVS
repository. When you do this, you must now return to the CVS
repository and make the fixes or changes a second time to
commit them. The difference is, of course, that CVS could manage the
separate directories and releases for you, allowing you to have a single
working directory for all of these tasks if you wish.
cvs rtag
expects the module name and the name of
the branch you are creating. Because the branch name is a
tag, the same rules for apply to branches as apply to tag names.
If I wished to create a branch in the lilprogram to allow me to
work on translating the message I would do this:
UNIX> cvs rtag -b lilprogram_0_1 lilprogram
After the branch command, there are two branch heads in the
repository. At this moment, they contain the same information.
However, each branch is now independent of the other.
cvs rtag: Tagging lilprogram
Once a branch has been created I can make my working
directory mirror either branch using cvs update
.
In the first example, the new branch is brought into the
user's working directory. This step is necessary before you
can commit changes or additions to this branch. Now you may
update and commit and CVS will know that you are working on
the lilprogram_0_1 branch.
UNIX> cvs update -r lilprogram_0_1
The 'Sticky Tag' is lilprogram_0_1, showing that it is in fact
from the lilprogram_0_1 branch.
cvs update: Updating .
UNIX> cvs status source.c
$ cvs status source.c
===================================================================
File: source.c Status: Up-to-date
Working revision: 1.2 Wed Jun 4 00:00:21 2003
Repository revision: 1.2 /Users/soltesz/cvsroot/lilprogram/source.c,v
Sticky Tag: lilprogram_0_1 (branch: 1.2.2)
Sticky Date: (none)
Sticky Options: (none)
To revert to the main branch:
UNIX> cvs update -A
The 'Sticky Tag' is now 'none', which is appropriate for the
head of the main branch.
cvs update: Updating .
UNIX> cvs status source.c
===================================================================
File: source.c Status: Up-to-date
Working revision: 1.2 Wed Jun 4 00:05:16 2003
Repository revision: 1.2 /Users/soltesz/cvsroot/lilprogram/source.c,v
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none)
Using cvs update -A
, files in your working directory
will be updated or removed
when needed. As long as you have committed any changes you've
made to the other branch, nothing will be lost.
My second committed change will be to replace broccoli with
celery.
UNIX> cat source.c
The head of the main branch is now different from the head of
the lilprogram_0_1 branch. I can verify this by updating my
working directory to the lilprogram_0_1 branch and checking
source.c
#include <stdio.h>
int main()
{
printf("Go away celery.\n");
}
UNIX> cvs commit
$ cvs ci source.c
Checking in source.c;
/Users/soltesz/cvsroot/lilprogram/source.c,v <-- source.c
new revision: 1.3; previous revision: 1.2
done
UNIX> cvs tag liltag_0_0
cvs tag: Tagging .
T source.cUNIX> cvs update -r lilprogram_0_1
cvs update: Updating .
U source.c
UNIX> cat source.c
#include <stdio.h>
int main()
{
printf("Go away broccoli.\n");
}
Merging Branches
In the lilprogram_0_1 branch, I will translate my message to
German, and commit this change.
UNIX> cat source.c
While there may be more than one branch, it may ultimately be
desirable to bring these branches back together. For
instance, if the branch held the code needed to run on
windows, this branch could be merged with the main branch, and
any conflicts resolved.
#include <stdio.h>
int main()
{
printf("Gehen brokkoli weg.\n");
}
UNIX> cvs ci source.c
Checking in source.c;
/Users/soltesz/cvsroot/lilprogram/source.c,v <-- source.c
new revision: 1.2.2.1; previous revision: 1.2
done
To do this, make sure your working directory is the head of
the main branch (or which ever branch you're merging into).
Using cvs update -j <branchname>
, where
branchname is the name used during the 'rtag' command. Where
there are conflicts CVS will print a warning, and surround the
differences in the file with identifying marks.
If I wish to merge the branches of the lilprogram, this is the
message and content of source.c:
UNIX> cvs update -A
It is the user's responsibility to resolve these conflicts
before committing the file back to the main branch.
In this example, I might make the message printed conditional
on a command line argument or simply combine the messages into
a single print statement.
cvs update: Updating .
U source.c
UNIX> cvs update -j lilprogram_0_1
cvs update: Updating .
RCS file: /Users/soltesz/cvsroot/lilprogram/source.c,v
retrieving revision 1.2
retrieving revision 1.2.2.1
Merging differences between 1.2 and 1.2.2.1 into source.c
rcsmerge: warning: conflicts during merge
UNIX> cat source.c
#include <stdio.h>
int main()
{
<<<<<<< source.c
printf("Go away celery.\n");
=======
printf("Gehen brokkoli weg.\n");
>>>>>>> 1.2.2.1
}