"Linux Gazette...making Linux just a little more fun!"


Introduction to RCS

By Håkon Løvdal


Do you find yourself having lots of different unsorted and more or less old backup files lying around when working on something, but do not dare to delete any of them because you might need to go back and find out what changes you have made compared to your current version ?

Would you like to get all those backups out of the way (without reducing the number of backups), have them sorted and systemated, and perhaps even with some sort of documentation like exactly when changes where made, by who (when several persons are involved), and a few lines that describes the changes which can be the input of an automatically made change-log ?

In that case read on because RCS will do that for you.


Lets have a look at an example (a traditional hello world program) of what RCS can do:

        (hlovdal) localhost:/tmp/rcstest>ls -l
        total 0
        (hlovdal) localhost:/tmp/rcstest>cat > hello.c
        /*
         * $Id$
         *
         * $Log$
         */
        main(){
        printf("hello world");
        }
        (hlovdal) localhost:/tmp/rcstest>gcc -o hello hello.c
        (hlovdal) localhost:/tmp/rcstest>./hello
        hello world(hlovdal) localhost:/tmp/rcstest>ls -l
        total 5
        -rwxrwx---   1 hlovdal  hlovdal      3928 Jun 28 01:01 hello
        -rw-rw----   1 hlovdal  hlovdal        60 Jun 28 01:00 hello.c
        (hlovdal) localhost:/tmp/rcstest>

(The two $-tags in the comment are for automatically documentation, more about those later)

Our hello world program works now, so we would like to save it in it's current state before making any changes to it. This is done by running ci, Check In, on the source file. That is, the source file is put into the RCS database. When the file is checked in, it is by default also removed from the current directory.

        (hlovdal) localhost:/tmp/rcstest>mkdir RCS
        (hlovdal) localhost:/tmp/rcstest>ci hello.c
        RCS/hello.c,v  <--  hello.c
        enter description, terminated with single '.' or end of file:
        NOTE: This is NOT the log message!
        >> A plain simple hello world program
        >> .
        initial revision: 1.1
        done
        (hlovdal) localhost:/tmp/rcstest>ls -l
        total 5
        drwxrwx---   2 hlovdal  hlovdal      1024 Jun 28 01:02 RCS
        -rwxrwx---   1 hlovdal  hlovdal      3928 Jun 28 01:01 hello
        (hlovdal) localhost:/tmp/rcstest>

By looking into the RCS directory we can now see that there is a file with the same name as our program with an extra extension ",v". (By omitting the RCS directory the file would be in the current directory) This file now holds the source plus some additional information, and will later on contain the source for all versions. The rcs file is not particularly interesting to look at directly:

        (hlovdal) localhost:/tmp/rcstest>cat RCS/hello.c,v
        head    1.1;
        access;
        symbols;
        locks; strict;
        comment @ * @;


        1.1
        date    97.06.28.01.03.43;      author hlovdal; state Exp;
        branches;
        next    ;


        desc
        @A plain simple hello world program
        @


        1.1
        log
        @Initial revision
        @
        text
        @/*
         * $Id$
         *
         * $Log$
         */
        main(){
        printf("hello world");
        }
        @
        (hlovdal) localhost:/tmp/rcstest>

This our first version of the hello world program sort of worked, but it lacks an ending newline and the source isn't pretty. Lets fix that. The source was moved when it was checked in, so we must use co, Check Out, to copy the source out of the RCS database.

        (hlovdal) localhost:/tmp/rcstest>co hello.c
        RCS/hello.c,v  -->  hello.c
        revision 1.1
        done
        (hlovdal) localhost:/tmp/rcstest>ls -l
        total 6
        drwxrwx---   2 hlovdal  hlovdal      1024 Jun 28 01:02 RCS
        -rwxrwx---   1 hlovdal  hlovdal      3928 Jun 28 01:01 hello
        -r--r-----   1 hlovdal  hlovdal       189 Jun 28 01:04 hello.c
        (hlovdal) localhost:/tmp/rcstest>

Note that co by default fetches the source read-only. This is usually not what we want, so in order to get the source writable we use the "-l" option to mark the file locked for others.

        (hlovdal) localhost:/tmp/rcstest>co -l hello.c
        RCS/hello.c,v  -->  hello.c
        revision 1.1 (locked)
        done
        (hlovdal) localhost:/tmp/rcstest>ls -l
        total 6
        drwxrwx---   2 hlovdal  hlovdal      1024 Jun 28 01:02 RCS
        -rwxrwx---   1 hlovdal  hlovdal      3928 Jun 28 01:01 hello
        -rw-r-----   1 hlovdal  hlovdal       197 Jun 28 01:05 hello.c
        (hlovdal) localhost:/tmp/rcstest>

By looking at the hello.c file we see that now some values have been inserted into $Id$ and $Log$.

        (hlovdal) localhost:/tmp/rcstest>cat hello.c
        /*
         * $Id: hello.c,v 1.1 1997/06/28 01:03:43 hlovdal Exp hlovdal $
         *
         * $Log: hello.c,v $
         * Revision 1.1  1997/06/28 01:03:43  hlovdal
         * Initial revision
         *
         */
        main(){
        printf("hello world");
        }
        (hlovdal) localhost:/tmp/rcstest>vi hello.c
        ...

We makes a few changes. Exactly what was changed can be examined with the program rcsdiff.

        (hlovdal) localhost:/tmp/rcstest>rcsdiff hello.c
        ===================================================================
        RCS file: RCS/hello.c,v
        retrieving revision 1.1
        diff -r1.1 hello.c
        9,10c9,14
        < main(){
        < printf("hello world");
        ---
        >
        > #include <stdio.h>
        >
        > int main(int argc, char *argv[]){
        >       printf("hello world\n");
        >       return 0;
        (hlovdal) localhost:/tmp/rcstest>

The rcsdiff program is just a front end for ordinary diff, so it accepts all the options to diff, for example "-u".

        (hlovdal) localhost:/tmp/rcstest>rcsdiff -u hello.c
        ===================================================================
        RCS file: RCS/hello.c,v
        retrieving revision 1.1
        diff -u -r1.1 hello.c
        --- hello.c     1997/06/28 01:03:43     1.1
        +++ hello.c     1997/06/28 01:05:21
        @@ -6,6 +6,10 @@
          * Initial revision
          *
          */
        -main(){
        -printf("hello world");
        +
        +#include <stdio.h>
        +
        +int main(int argc, char *argv[]){
        +       printf("hello world\n");
        +       return 0;
         }
        (hlovdal) localhost:/tmp/rcstest>

This version looks good, so we want to save it with Check In. By giving option "-l", ci runs a implicit "co -l" so that the source file remains checked out. When ci is run we are asked to enter a log description of our changes. This log description is inserted into $Log$.

        (hlovdal) localhost:/tmp/rcstest>ci -l hello.c
        RCS/hello.c,v  <--  hello.c
        new revision: 1.2; previous revision: 1.1
        enter log message, terminated with single '.' or end of file:
        >> Fixed main prototype, inserted a missing newline and a missing #include
        >> .
        done
        (hlovdal) localhost:/tmp/rcstest>cat hello.c
        /*
         * $Id: hello.c,v 1.2 1997/06/28 01:07:23 hlovdal Exp hlovdal $
         *
         * $Log: hello.c,v $
         * Revision 1.2  1997/06/28 01:07:23  hlovdal
         * Fixed main prototype, inserted a missing newline and a missing #include
         *
         * Revision 1.1  1997/06/28 01:03:43  hlovdal
         * Initial revision
         *
         */

        #include <stdio.h>

        int main(int argc, char *argv[]){
                printf("hello world\n");
                return 0;
        }
        (hlovdal) localhost:/tmp/rcstest>gcc -o hello hello.c
        (hlovdal) localhost:/tmp/rcstest>./hello
        hello world
        (hlovdal) localhost:/tmp/rcstest>


In short RCS is this simple to use:

  1. "mkdir RCS"
  2. Insert $Id$ and $Log$ into one comment. Optional, but nice to have. (NB! Note that $Log$ does *not* include previous versions. If $Log$ is inserted after some time only that and later versions will end up in the log. It is therefore smart to have $Log$ in the file from the start)
  3. Edit the file
  4. Optionally run rcsdiff when you want to see what changes you have made since last check in.
  5. Run "ci -l" each time you want to save what you have done so far.
  6. Repeat 3 to 5.

For more info on RCS look at the rcsintro(1) man page.

Here in this example RCS is used on C source, but RCS can be used on many other things. Config files in /etc are for example excellent candidates of being put under RCS control.

RCS is one method of version control. Two others are SCCS and CVS. CVS (Concurrent Versions System) is a further development of RCS intended to be used on larger software projects. For example most (?) BSD clones are distributed and developed using CVS.

SCCS (Source Code Control System) is an old proprietary system which few (if any) uses. I think SCCS and RCS have a somewhat a similar relation as traditional compress vs gzip.


Copyright © 1997, Håkon Løvdal
Published in Issue 20 of the Linux Gazette, August 1997


[ TABLE OF CONTENTS ] [ FRONT PAGE ]  Back  Next