Source Code Management

There are two "standard" source code management systems available on Unix. These are SCCS (Source Code Control System) and RCS (Revision Control System).

SCCS is included with many versions of Unix (SunOS and AIX included). RCS is public-domain software that must be obtained and installed.

SCCS and RCS support many similar features. RCS appears to have a superior implementation, therefore we will focus on RCS.

RCS Preliminaries



RCS -- Checking Source Files In and Out



RCS Version Trees

As a source file undergoes edits by programmers, a sequence of revisions is built up. E.g.
	1.1  1.2  1.3  1.4  1.5  2.1  2.2  2.3  3.1
However, it is more than possible that the software developers need to produce multiple versions of the product. E.g., there may be the Windows version, the Unix version ... There may be the domestic North American version and the export version. The possibilities are endless.
A new version (as opposed to a revision of an existing version) causes the revision sequence to fork.

E.g., if the revision sequence

	1.1  1.2  1.3  1.4  1.5  2.1  2.2  2.3  3.1
corresponds to the Unix version of the software, and we now want to develop the Windows version, we would pick one of the Unix revisions as being a base for the new version. Let's suppose that we pick revision 2.2 for this purpose.

We would check out revision 2.2 for browsing ...

		co -r2.2 foo.c 
we would change the file permissions on foo.c so that it is no longer flagged as read-only ...
		chmod 644 foo.c 
we would edit the file to make it Windows specific, and then we would simply check in the file with a longer revision number ...
		ci -r2.2.1 foo.c 
This creates a new version which has revision number 2.2.1.1.

This procedure gives us version trees.

Tichy describes other scenarios where version trees are useful. For example, suppose that we have been selling a software product, and that there have been many releases of the product. Some customers are using revision 1.1, others are using 1.2, and so on. The revision tree might be as follows:

	1.1  1.2  1.3  2.1  2.2  3.1  3.2  3.3 
Now suppose that a customer reports a bug in revision 1.3 of the product. What we should do is check-out 1.3, fix the bug, and check in the new code as revision 1.3.1.1. That is:
		co -r1.3 code.c
		chmod 644 code.c
		... edit code.c
		ci -r1.3.1 code.c
We cannot check the revised code in as 1.4 to be inserted between revisions 1.3 and 2.1 in our version tree because revisions 2.1 were not derived from 1.4 (and do not include our new bug fix).

Of course, we may later wish to take the bug fix of 1.3.1.1 and make that same bug fix to the 2.1, 2.2 and later revisions of the code. (The rcsmerge program has been provided for exactly that purpose.)

Implementation of RCS



Additional Features of RCS

Various RCS Issues



Symbolic Version Names

Each file under control of RCS follows an independent revision history. If we construct a version of a program for distribution, we might find that the program is composed from revision 3.2 of main.c, revision 2.7 of foo.c, revision 1.2 of foo.h, ... etc.

One way of eliminating the confusion would be to keep all revision numbers in lock-step. Whenever we update foo.c from 2.6 to 2.7, then main.c (and all other files) are renumbered as 2.7 too.

RCS provides a better solution. It allows you to attach symbolic version names to file revisions.

An Example
Suppose that we wish to release version 6.0beta of our new word processing program.

For each file used in the released product, we could execute a command like this:

		rcs -r2.7 -nV6.0beta foo.c 
This attaches the symbolic version name V6.0beta to revision 2.7 of foo.c.

[ Alternatively, the symbolic name can be attached when a file is checked back in, for example:

		ci -nV6.0beta foo.c 
Also, one can move the symbolic version name from one revision to another. Executing
		rcs -r2.8 -nV6.0beta foo.c 
would transfer the label to revision 2.8.]

If you wish to create the released version from the latest revisions of all the files, there is a simple short-cut. One just needs to execute:

		rcsclean
		rcsfreeze -nV6.0beta 
The rcsclean command automatically checks any files back in that you have checked out for update. (Otherwise RCS will use the latest revisions that it knows about.) The rcsfreeze command then attaches the symbolic label to every file.

To check out one or more files corresponding to a symbolic name, a command like this is used:
		co -sV6.0beta foo.c bar.c 
And to check out that version of all the files, we can use the command:
		co -sV6.0beta RCS/*,v 


Using make With RCS

Ideally, we would like to use make to build our program from the files that are managed by RCS. This means that make should be told to check out missing files from RCS automatically.

The public-domain version of make distributed by the Free Software Foundation has support for RCS built in. The standard make does not. To use the standard make, we must add additional rules to our Makefile. Here is an example:

		## Example Makefile for use with RCS

		RCSFLAGS =
		CFLAGS = -O
		CC = cc

		OBJS = foo.o main.o

		.DEFAULT:
			co $(RCSFLAGS) $<

		myprog: $(OBJS)
			$(CC) $(CFLAGS) -o foo $(OBJS)

		clean:
			rcsclean
			rm -f a.out core *.o

		foo.o: foo.h foo.c
			$(CC) $(CFLAGS) -c foo.c

		main.o: foo.h main.c
			$(CC) $(CFLAGS) -c main.c
The .DEFAULT name is a special make target. It is a rule to be applied if a needed file is missing (and there are no explicit or implicit rules that can be invoked to create the missing file). In our case, when a file is missing from the current directory, we want to check it from RCS.

Unfortunately, the .DEFAULT rule does not interact with implicit rules for compiling `.c' files in the manner that we would desire. We cannot just put dependencies in the form:

		foo.o: foo.h

		main.o: foo.h
into the Makefile and rely on make to know that foo.o depends on foo.c (and main.o on main.c). Make would check out only foo.h and then think it was finished.

[An implicit rule such as

		.c.o:
			$(CC) $(CFLAGS) $(CPPFLAGS) -c $<: 
only kicks in if the .c file can be found in the current directory.]

Therefore we have to force make to check out foo.cwhen foo.o is the target, and having shown the explicit dependency on foo.c, we have to show the C compilation command as well.

 




Previous Section Back to Index