Introduction to RCS


I’ve resurrected this text from my old UKFSN site because a couple of people asked me to, but I wouldn’t recommend using RCS to anyone at this point. It is mainly to help people who need to understand RCS because they have no choice, for example Gentoo users who are stuck with dispatch-conf (or so my mails tell me).


This introduction is aimed at people with no experience of a VCS at all, but who do have a need for an easy to use and non-intrusive way to backup, store and annotate configuration files or 5000 file C source trees. This text is by no means an in-depth RCS usage manual, it is a short and fast way to get working with RCS.

RCS is great for small projects or managing the wealth of configuration files in /etc. It provides a way to reduce the need for file backups(without losing any backup quality or quantity), to annotate changes made to a file, to allow other people to work on a file and an easy method to package patch/diff files for distribution.

If you want more in-depth information about using RCS, or when you find you need more functionality from RCS, there is a HOW-TO at The Linux Documentation Project (direct link) and fantastic documentation is provided with the RCS package.

Diving in

RCS is that simple to use that I will jump straight in to an example.

$ mkdir RCS

The RCS directory holds the RCS control files, they contain all the data RCS needs to work.

$ cat > <<EOF
#! /bin/sh
# $Id$
echo "Hello World!"
exit 0

$ ci
RCS/,v  <--
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> /bin/sh "Hello World!" example
>> .
initial revision: 1.1

The command ci (check in) is where the magic of RCS takes place. We have decided we want to make a snapshot of our file,, and use ci to add the file to revision control.

$ ls

The file we checked in appears to have disappeared, obviously it hasn’t but the default behaviour of RCS is to remove the file we check in. You can choose to keep a working copy with ci -u or ci -l (covered below).

$ ls ./RCS/,v

In the RCS directory a file now exists with the same name as our script plus a ,v. This is the file RCS uses to store all of its data in.

$ co
RCS/,v  -->
revision 1.1
$ ls -l ./
drwxr-xr-x    2 james    james        1024 Nov  4 11:29 RCS
-r--r--r--    1 james    james          95 Nov  4 11:29

The command co (check out) pulls a current version(by default) from the RCS repository and places it in the current directory. Default behaviour is to check out a read-only version of the file.

$ co -l
./RCS/,v  -->  ./
revision 1.1 (locked)

The -l option to co (and also ci ) is used to lock the file. This file now becomes a working file, which is writable, and it also means other people can’t edit it until you have released it or checked it in again.

Blindly using the -l option to ci/co is not advisable, you should get in to the habit now of only locking files you are working on. RCS uses locking to block other users from checking in changes and will cause much grief if you use RCS on multi-user projects. It is much better to use -u (or unlocked) when you ci in new files/changes this way you will have access to a read-only version of the file and it will allow other users to edit it.

$ cat
#! /bin/sh
# $Id:,v 1.1 2002/11/04 11:29:48 james Exp james $
echo "Hello World!"
exit 0

If you remember from the original file it contained $Id$ on a commented line, this $Id$ is a keyword used by RCS that is substituted with information about the current file. In this example the tag $Id$ is expanded to read the filename, the revision number, the date and time of the check in, the person who checked in the file, the state of the file and the owner of the lock(if any).

There are many keywords available, including:

  • $Author$ - the name of the person who did the check in

  • $Header$ - the same as $Id$ but including the path for the file

  • $Log$ - a full changelog for file from the ci annotations

Although using $Log$ seems like a good idea it does mean the size of the file is increased a huge amount. All the changelog data is available with the rlog command at any time and without filling the source file.

$ sed -ie 's/World/${USER}/'
$ cat
#! /bin/sh
# $Id:,v 1.1 2002/11/04 11:29:48 james Exp james $
echo "Hello ${USER}!"
exit 0
$ sh ./
Hello james!

So we have decided to make some changes to the file, and then tested it works.

$ rcsdiff
RCS file: RCS/,v
retrieving revision 1.1
diff -r1.1
< echo "Hello World!"
> echo "Hello ${USER}!"
TEST$  rcsdiff -u
RCS file: RCS/,v
retrieving revision 1.1
diff -u -r1.1
--- 2002/11/04 11:57:51     1.1
+++ 2002/11/05 03:52:14
@@ -1,4 +1,4 @@
 #! /bin/sh
 # $Id: myscript,v 1.1 2002/11/04 11:29:48 james Exp james $
-echo "Hello World!"
+echo "Hello ${USER}!"
 exit 0

The command rcsdiff supplies an easy way to check changes in a file. Using rcsdiff filename will output a generic context-free diff(and a small RCS header to stderr ), or you can pass normal diff options to rcsdiff. In the second rcsdiff command the diff option -u is given to tell rcsdiff it should output a unified context diff.

$ ci -u ./
./RCS/,v  <--  ./
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Changed World to ${USER} to give a more personal feeling
>> .

So now we have seen the changes made to the file, and are happy with them, we check in our new revision. RCS asks for a log entry, this will make our changelog output later. You can cancel the check in using the normal C-c (control C) method.

$ cat
#! /bin/sh
# $Id: myscript,v 1.2 2002/11/04 11:34:21 james Exp james $
echo "Hello ${USER}!"
exit 0

You can see above that when RCS checked in the new revision it also updated the $Id$ tag.

$ rlog ./

RCS file: ./RCS/,v
Working file: ./
head: 1.2
locks: strict
access list:
symbolic names:
keyword substitution: kv
total revisions: 2;     selected revisions: 2
revision 1.2
date: 2002/11/05 04:01:13;  author: james;  state: Exp;  lines: +2 -2
Changed World to ${USER} to give a more personal feeling
revision 1.1
date: 2002/11/04 11:57:51;  author: james;  state: Exp;
Initial revision

The command rlog provides quick access to revision history for files, it accepts multiple files per command line(using normal shell wild-carding) and provides all the information RCS has on a file. Should you ever need to only know the changes that were made to the current revision you can use the -r option as in rlog -r filename. You can also check changes between revisions of files using the command like rlog -r1.1,1.2 filename.

The -r option of RCS is one of its most powerful, it is available in all the commands and shares the same semantics throughout. If -r is used with ci it forces a bump, for example ci -r1.7 filename will force RCS to check in filename as revision 1.7. Used with co you can pull any revision of the file from RCS history. Used with rcsdiff you can create a diff between any revision under RCS, for example rcsdiff -r1.1,1.8 -u filename will output a unified context diff of the changes from revision 1.1 to 1.8.

RCS really is that simple to use, it does have many more options that are not covered here(see the man pages) but the power of RCS is how simple it is to use. It takes almost no time to setup, and probably less time then you currently spend on arranging backups. The command syntax is simple, and stable across the separate commands. It provides an immensely powerful way to control configuration files, source code, even revisions of binary files and of course silly little shell Hello World examples.


To recap on RCS usage

  • Make the RCS directory.

  • Insert RCS tags, such as $Id$, in to your original files to help you keep track. - Edit your files.

  • Use ci to commit your revisions to the RCS history and annotate changes made. You can also use rcsdiff to see what changes you have made, maybe to help you build your changelog information.


rcsi screenshot

There are many tools available that can help you to manage your RCS files, including the RCS status monitor rcsi and blame RCS file annotator.

rcsi will display information about the files within a directory.

The screenshot to the right shows rcsi in use on a sample partially RCS controlled directory. All the information it contains should be fairly self explanatory, and even if it isn’t the package comes with a comprehensive man page and README.

1.2          (root     21-Aug-05):             eval find . -xdev -depth ${exceptions}   -type d -empty -exec rmdir '{}' \\';'
1.2          (root     21-Aug-05):             eend 0
1.2          (root     21-Aug-05):         else
1.1          (root     16-Jul-05):             ebegin "Cleaning /tmp directory"
1.4          (root     21-Jan-06):             {
1.2          (root     21-Aug-05):                 rm -f /tmp/.X*-lock /tmp/esrv* /tmp/kio* /tmp/jpsock.* /tmp/.fam*
1.2          (root     21-Aug-05):                 rm -rf /tmp/.esd* /tmp/orbit-* /tmp/ssh-* /tmp/ksocket-* /tmp/.*-unix
1.4          (root     21-Jan-06):                 # Make sure our X11 stuff have the correct permissions
1.4          (root     21-Jan-06):                 mkdir -p /tmp/.{ICE,X11}-unix

The above excerpt is a sample of the output from blame being run against a config file which is maintained using RCS by Gentoo’s dispatch-conf tool. It allows you to simply see which revision introduced a change to a specific line. You can also choose to annotate specific RCS revisions using the --revision option, or specific dates with --date option. blame has also has a very comprehensive manual page included with it which you should read if you want to enjoy its full power.

There are many other tools available which use RCS as a backend, and as long as you can access the RCS data files blame can help to understand what is happening with them too.

If you know of any interesting RCS uses please drop me a mail, and I hope this short text has been helpful to you.