1.1 What is CVS?
CVS (Concurrent Versions System) is a utility used to keep several versions of a set of files, and to allow several developers to work on a project together. It allows developers to see who is editing files, what changes they made to them, and when and why that happened.
Many programming projects (both open-source and proprietary) use CVS nowadays. Ever wonder why there seem to be directories called 'CVS' in a source package you just downloaded? That's the developer who packaged it being lazy :) Ever see one of these :
$Id: cvs-tutorial.txt,v 1.8 2000/07/21 20:31:40 doc Exp $in a source file, and wonder what it means? I'll be telling you how to decipher such things, and how to make them appear in your project source files later.
CVS, along with the likes of Bugzilla, are some of the tools that you use once, and never stop using. Trust me. You'll love it.
1.2 Why should I use CVS?
Because it rocks hard. Ever want to work on a project with others, except maintaining a working codebase is a bloody nightmare? CVS does that for you. Ever want to see who made a particular change to a file, so you can batter them round the head and shoulders with a trout and demand they explain themselves? CVS does that. Ever want to maintain a complete history of all changes to all source file since the project's inception? CVS does that, too.
1.3 Where can I get CVS?
CVS can be got in quite a few places. The primary site is http://www.cyclic.com/. It's available in RPM and DEB as well, I believe. FreeBSD comes with it installed as part of the base install.
You can also get windows/dos versions of the client, both with and without a GUI. I recommend WinCVS for windows, for those of you who demand a GUI, and cervisia and linCVS for your preferred UNIX. Of course, this tutorial only covers command line usage :)
S. 2 - Down to Business then
2.1 How does CVS work?
CVS can either work locally, or via the network. I'll be covering network usage in this tutorial, as it seems to be the most commonly used, and local usage is identical, except for setup, and environment variables used.
CVS works by keeping RCS versions of each file in a directory in a central repository on a machine (the 'CVS Server', if you will.). It handles requests from clients to make amendments, or retrieve past versions (or "revisions") of files. It also allows files to be 'tagged' (i.e. 'marked' as being in a certain state, i.e. 3_2_RELEASE :)). It also caters for conflict resolution if several changes are committed at once.
2.2 Terminology
- 'Module' - a particular set of files kept in CVS.
- 'Repository' - Location on CVS server where modules are kept.
- 'Revision' - A certain version of a file.
- 'Tag' - A certain milestone in a file or module's development.
- 'Branch' - A 'fork' of the module.
2.3 Getting Started
The first thing to do is to install CVS itself. It's a pretty standard package. You can use the RPM or the .DEB, or if you're running FreeBSD you're sorted anyway. I'm sure there's a "Installing-packages-HOWTO" out there somewhere. I won't patronise the rest of you :)
What you need to do first is to set up your CVS repository (ref. 2.2). You should pick a place that's got plenty of space, allowing for several times the size of your actual source package (remember, we're storing multiple versions, and logs!). You can then set your $CVSROOT environment varible to this directory. We're working in local mode for now.
~ root@sprocket $ export CVSROOT=/cvs
Now we need to actually create the repository.
This couldn't be simpler. Just find your location ( in this case, /cvs) and go:
~ root@sprocket $ cvs init
This creates the repository, and the special module containing the config files for this Repository ( called 'CVSROOT').
We might want to configure some settings in the config files, so what we do is check out a working copy of the CVSROOT module.
*Rule #1 of keeping CVS happy : DO NOT EDIT FILES IN THE REPOSITORY!*
So, we check out the CVSROOT module:
~ root@sprocket $ cvs checkout CVSROOT
cvs checkout: Updating CVSROOT
U CVSROOT/checkoutlist
U CVSROOT/commitinfo
U CVSROOT/config
U CVSROOT/cvswrappers
U CVSROOT/editinfo
U CVSROOT/loginfo
U CVSROOT/modules
U CVSROOT/notify
U CVSROOT/rcsinfo
U CVSROOT/taginfo
U CVSROOT/verifymsg
~ root@sprocket $ cd CVSROOT
~/CVSROOT root@sprocket $ ls
CVS commitinfo cvswrappers loginfo notify taginfo checkoutlist config editinfo modules rcsinfo verifymsg ~/CVSROOT root@sprocket $
"Checking out" a module basically means you retrieve a working copy of it from CVS to your hard disk.
The only config file you might want to edit right now is 'config'. Don't worry too much about the commands you see here, they'll be explained in more detail later.
We mark ourselves as being an editor of this file:
~/CVSROOT root@sprocket $ cvs edit config
And then we edit the file. The only configuration directive you might want to change now is 'PreservePermissions', which determines wether file permissions are preserved in the repository. You might want to turn this off if you're going to have any WIN32 clients connecting to the CVS server. :)
Once we finish editing the file, we save it, and commit our changes to the file:
~/CVSROOT root@sprocket $ cvs commit config
We get presented with an editor (as per our $EDITOR environment variable), where we can enter a log message. This message describes what you did to the file, why, what bug number it resolves, etc. Enter a short log message.
Once the editor is closed, we see the file being committed to the local CVS repository.
/tmp/cvslQ1928: 7 lines, 292 characters.
Checking in config;
/cvs/CVSROOT/config,v <-- config
new revision: 1.2; previous revision: 1.1
done
cvs commit: Rebuilding administrative file database
~/CVSROOT root@sprocket $
'config' is now at version 1.2, and your CVS server is basically configured for simple use locally.
Mark yourself as no longer editing the 'config' file.
~/CVSROOT root@sprocket $ cvs unedit config
2.4 Restricting Access
Next thing you might want to do is only allow specific users (i.e. your developers) to write to the CVS repository. Easy enough. Go back to your working copy of the CVSROOT module (or check it out again if you've wiped it). To allow only certain users write access to the repository, place their usernames in a file called 'writers' in the CVSROOT module.
~/CVSROOT root@sprocket $ cat >> writersWe then need to add this file to the module, like so:
doc
fred
someguy
youthere
^D
~/CVSROOT root@sprocket $
~/CVSROOT root@sprocket $ cvs add writersDon't commit yet. Append the name 'writers' to the end of the 'checkoutlist' file. This lets CVS know it has a new config file!
cvs add: scheduling file `writers' for addition
cvs add: use 'cvs commit' to add this file permanently
~/CVSROOT root@sprocket $
~/CVSROOT root@sprocket $ cvs edit checkoutlistNow, we can commit our changes to both files at once, by specifying them both on the command line. Also, I'm using the -m switch here, instead of having to use an editor. Useful if the comment is short.
~/CVSROOT root@sprocket $ cat >> checkoutlist
writers
^D
~/CVSROOT root@sprocket $
~/CVSROOT root@sprocket $ cvs commit -m /Now, only 'doc', fred', 'someguy', and 'youthere' can write to this CVS repository.
"Added writers file, restricting access to
developers" /
checkoutlist writers
Checking in checkoutlist;
/cvs/CVSROOT/checkoutlist,v <-- checkoutlist
new revision: 1.2; previous revision: 1.1
done
RCS file: /cvs/CVSROOT/writers,v
done
Checking in writers;
/cvs/CVSROOT/writers,v <-- writers
initial revision: 1.1
done
cvs commit: Rebuilding administrative file database
~/CVSROOT root@sprocket $
Your CVS server is now ready for use.
2.5 Network Setup
What we want to do now is set up access to the CVS server over the network. This is quite easily done, as the CVS 'pserver' runs from inetd.
Make sure the following line is in /etc/services
cvspserver 2401/tcp #CVS network serverAnd then add something like the following to /etc/inetd.conf
cvspserver stream tcp nowait root /usr/bin/cvs cvs --allow-root=/cvs pserver(Substitute /usr/bin/cvs for wherever 'cvs' is on your system, and the argument to --allow-root to the location of your Repository.).
Restart inetd. The CVS pserver should now be running.
You can do a quick test of the server as a normal user :
/usr/home/doc doc@sprocket $ cvs -d :pserver:doc@sprocket:/cvs loginIf it accepts your login password, then your pserver is up and running!
(Logging in to doc@sprocket)
CVS password:
/usr/home/doc doc@sprocket $
S. 3 - Basic Usage of CVS
3.1 The CVSROOT variable
This is an environment variable used to specify the location of the repository we're doing operations on. For local use, it can be just set to the directory of the repository. For use over the network, it must be of the form :
:pserver:<username>@<host>:/cvsdirectorySo, if user 'doc' wants to connect to the CVS server on 'sprocket', and the CVS repository on sprocket is in /cvs, then doc's CVSROOT environment variable would look like :
:pserver:doc@sprocket:/cvsWe will be concentrating on network use from now on.
3.2 Logging in
If we're working over the network, we need to log into the CVS server to perform operations. This is done with the 'cvs login' command.
~ doc@consortium $ export CVSROOT=:pserver:doc@sprocket:/cvs
~ doc@consortium $ cvs login
(Logging in to doc@sprocket)
CVS password:
~ doc@consortium $
3.3 Importing Existing Code
You may have existing code you want to import into CVS. This is easy to do. Decide on a module name for the project. In this example, i have a project called 'MyProject'. I want to import it into a module called 'MyProject'. Change directory into the root of the directory you want to add to CVS, and go:
~/prog/MyProject doc@consortium $ cvs import MyProject SillySoft START
The arguments are : cvs import <module name> <vendor tag> <initial tag>
The vendor tag can be just a string that identifies your coding group/software company. The initial tag is usually START. It's not important for now.
After you do this, you enter a log message. The current directory is then recursively imported into the 'MyProject' CVS module, like so:
/tmp/cvsWU2948: 5 lines, 244 characters.Your current directory is now imported into CVS as the 'MyProject' module. Note that you need to create a separte checkout of the module again if you want to poerform CVS operations on it.
N MyProject/Makefile
N MyProject/README
cvs server: Importing /cvs/MyProject/src
N MyProject/src/main.c
N MyProject/src/blah.c
N MyProject/src/somethingelse.c
cvs server: Importing /cvs/MyProject/doc
N MyProject/doc/manual.txt
N MyProject/doc/manual.html
N MyProject/doc/manual.pdf
No conflicts created by this import
~/prog/MyProject doc@consortium $
3.4 Checking out a Module
In order to do CVS operations (adding file, amending files, etc.) on a module, you need to 'check out' a working copy of the module to your machine. You can then do operations in the files on this working copy. Say we want to checkout a working copy of our 'MyProject' module, we can do it like this:
~/cvs doc@consortium $ cvs checkout MyProjectNote that you can check out an individual directory of a module as well. Say we wanted just the src/ directory of the 'MyProject' module, we could do that with:
cvs server: Updating MyProject
U MyProject/Makefile
U MyProject/README
cvs server: Updating MyProject/doc
U MyProject/doc/manual.html
U MyProject/doc/manual.pdf
U MyProject/doc/manual.txt
cvs server: Updating MyProject/src
U MyProject/src/blah.c
U MyProject/src/main.c
U MyProject/src/somethingelse.c
~/cvs doc@consortium $
~/prog/MyProject doc@consortium $ cvs checkout MyProject/src
This will checkout the src/ directory of the MyProject module. You now have a working copy of the module.
3.5 Adding A File
If you have a new file in your working directory that you want to add to the CVS module, you need to 'add' the file to the module, using 'cvs add'.
~/cvs/MyProject doc@consortium $ cvs add INSTALLNotice that we ran "cvs commit" on the file after we ran "cvs add". This commits our changes to the CVS server. It doesn't commit automatically just in case you want to do several amendments to your current working directory, which can then be done in a batch by doing a simple "cvs commit" (with no file arguments).
cvs server: scheduling file `INSTALL' for addition
cvs server: use 'cvs commit' to add this file permanently
~/cvs/MyProject doc@consortium $ cvs commit INSTALL
<edit log file>
RCS file: /cvs/MyProject/INSTALL,v
done
Checking in INSTALL;
/cvs/MyProject/INSTALL,v <-- INSTALL
initial revision: 1.1
done
~/cvs/MyProject doc@consortium $
3.6 Updating your Working Copy
Several other developers might be working on the same module as you at any one time. It's a good idea to regularly update your working copy of the module to include the changes made by others working on the module. (It's especialy a good idea to update just before you change anything!). This is simple enough to do.
~/cvs/MyProject doc@consortium $ cvs update -dI use the -d switch, which creates files in the working copy that didn't exist before, and that have appeared in the repository (i.e. have been added by someone else).
cvs server: Updating .
cvs server: Updating doc
cvs server: Updating src
U src/main.c
U src/somethingelse.c
~/cvs/MyProject doc@consortium $
The 'U' before the files mentioned indicates they have been 'U'pdated. Other letters that may appear here are 'P' for new files, 'M' for files you've modified locally and not committed, and 'C' for files you've updated locally, and that have had different changes done by someone else. (This is called a "Conflict", I'll go into resolving them in another tutorial in detail, for the moment just try to merge the 2 sets of changes manually).
3.7 Editing a file
There are several steps to editing a file. Some are optional, such as "cvs edit" and locking files.
The first thing we want to do is update the working copy of the file.
~/cvs/MyProject/src doc@consortium $ cvs update main.cThe copy we have is up-to-date, good.
~/cvs/MyProject/src doc@consortium $
We then want to mark ourselves as an editor of this file. The "cvs edit" command looks after this.
~/cvs/MyProject/src doc@consortium $ cvs edit main.cThis tells the CVS server that we're editing the file. This can be useful for if other people want to know who's editing the file. (They can run "cvs editors" on the file.) Like so:
~/cvs/MyProject/src doc@consortium $
/usr/home/fred/demo/MyProject/src fred@scrappy $ cvs editors main.cFred can then come thump me over the head and ask me what changes I plan to make to the file. "cvs edit" is, of course, optional.
main.c doc Fri Jul 21 20:18:38 2000 GMT consortium.thehouse
/usr/home/doc/cvs/MyProject/src
/usr/home/fred/demo/MyProject/src fred@scrappy $
If you want exclusive access to the file (i.e. you're doing major restructuring of it), you can lock it, by running "cvs admin -l" on it. (Of course, when you're finished editing the file, run "cvs admin -u" on it. The failure to do this can be the cause of...accidents. This is how developers get brutally beaten. This is also why only CVS users that are in the UNIX group called 'cvsadmin' on the CVS server itself can lock files.)
Okay, you're now ready to edit the file. Go ahead and edit it.
When you're finished editing, you want to commit your changes. Run "cvs commit" on the file.
You then want to tell the CVS server you're no longer editing the file. run "cvs unedit" on it.
Your changes have now been added to the repository.
3.8 Deleting a File
If a file in the repository is no longer needed (i.e. some code or documentation has become redundant), you might want to remove the file. You need to delete the actual file from your working copy first. Then, just run "cvs delete" on it, then "cvs commit" to commit the delete.
~/cvs/MyProject/src doc@consortium $ rm blah.c
~/cvs/MyProject/src doc@consortium $ cvs delete blah.c
cvs server: scheduling `blah.c' for removal
cvs server: use 'cvs commit' to remove this file permanently
~/cvs/MyProject/src doc@consortium $ cvs commit blah.c
<edit log file>
Removing blah.c;
/cvs/MyProject/src/blah.c,v <-- blah.c
new revision: delete; previous revision: 1.1.1.1
done
~/cvs/MyProject/src doc@consortium $
S 4. Advice, tips, funky things
4.1 Getting that Weird String thing from 1.1 (or: The CVS ID)
Cooldn't be simpler. Just put the string $Id$ anywhere in a source file, and when you commit the file to CVS, it's converted to something of the form :
$Id: cvs-tutorial.txt,v 1.8 2000/07/21 20:31:40 doc Exp $
-
The String here consists of:
- The Name of the RCS file, and path in the repository.
- The Version number of the file.
- The date and time the file was last committed.
- The last CVS user to commit changes to the file.
4.2 Other Weird String Things.
Here's a list of other keywords that can be included in files, similar to $Id$ above.
$Author$ | - The last CVS user to commit the file. |
$Date$ | - The date of the last commit. |
$Name$ | - The tag name |
$Log$ | - All Log Messages from commits to this file (note: old log messages are kept as well as the latest one). |
$RCSfile$ | - The RCS name of the file (without a path). |
$Revision$ | - The Revision (or Version) number. |
$Source$ | - The full pathname of the RCS file in the repository. |
$Log: cvs-tutorial.txt,v $
Revision 1.8 2000/07/21 20:31:40 doc
Added final Examples, Initial Draft
Revision 1.7 2000/07/21 20:06:46 doc
Content done. Just need to add last few examples
Revision 1.6 2000/07/09 18:03:04 doc
Demonstrating Log KeyWord
4.3 Adding CVS users that don't exist as UNIX users.
The CVS server can be made to authenticate from a password file in the CVSROOT module we tweaked earlier. All you need to do is add a file into the CVSROOT module. So, go into your local checkout of CVSROOT, or check it out again if you don't have a working copy. create a file in there, called 'passwd'. Entries in this file should be of the form :
desiredusername:EnCrYpTeDpAsSwOrD:realuserThe fisrts entry in this colon-separted list is the desired username to allow. The second entry is the password for that user, done in standard unix crypt() format. The third entry is the real UNIX user on the server that file operations will be done as (so, you can set up a user that can write to just the repository and nothing else). If you have apache installed, there's a utility called 'htpasswd' which can generate the first 2 fields for you. You can then add the tird field manually.
Once you have the 'passwd' file added, add it with "cvs add". To tell the CVS server to read this file, you can append the name of the file to the 'checkoutlist' file in the CVSROOT module. Once you've done this, you can then commit your changes with 'cvs commit'.
~/cvs/CVSROOT doc@consortium $ echo "passwd" >> checkoutlistThis leads me to the next, and final topic for now...
~/cvs/CVSROOT doc@consortium $ cvs add passwd
cvs server: scheduling file `passwd' for addition
cvs server: use 'cvs commit' to add this file permanently
~/cvs/CVSROOT doc@consortium $ cvs commit -m "added passwd file"
cvs commit: Examining .
Checking in checkoutlist;
/cvs/CVSROOT/checkoutlist,v <-- checkoutlist
new revision: 1.6; previous revision: 1.5
done
Checking in passwd;
/cvs/CVSROOT/passwd,v <-- passwd
initial revision: 1.1
done
cvs server: Rebuilding administrative file database
~/cvs/CVSROOT doc@consortium $
4.4 Anonymous Access
Anonymous access is easy to do. Just set up the 'passwd' file as above, with the following entry in it:
anonymous::anonuser'anonuser' is a real user on the server that doesn't have write access to the CVS repository. This then allows people to log into the CVS server as 'anonymous', with no password, and have read-only access.
Bye.
That's all for now. If anyone wants to go into tagging, branching etc. I may do another tutorial later.
- DoC