Rich Kulawiec on 16 Nov 2017 10:08:27 -0800


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: [PLUG] Revision Control for the Rest of Us


Excellent idea.  And while it's clear that git has largely prevailed
over its competitors and predecessors, let me put in a word for RCS.

RCS has been around a very long time.  It's very stable -- and very limited.
There are all kinds of things it doesn't do, and of course, if you need to
do those things, you probably shouldn't use it.

But in its favor are two things: first, there are a lot of simple use
cases that it handles beautifully.  Second, you can learn it in ten minutes.

I use it to do revision control on my resume, on my mail system's
custom anti-spam rules, on my record collection (yes, I still own
records) (and I listen to them), and things like that.  Note that
RCS works at the level of individual files, which has the advantage
of keeping it simple and the disadvantage of not helping manage
collections of files.  (I do that with make(1).)  So emphasis
on the "simple" in "simple use cases".  While we *did* use this
for software development projects with thousands of files and many
developers back in the day, I wouldn't recommend it now unless there
was some very compelling reason.

I've used git, subversion, SCCS, CVS, and so on.  But I still rely on
RCS for certain tasks because it does the job quickly, efficiently,
accurately, and easily.  (Note also that if you start out with it,
and then it turns out you were wrong about the capabilities you needed,
you can switch to most other revision control systems fairly easily.)

Appended below is a quick how-to that really will have you using RCS
in ten minutes.  Less, if you read quickly. ;)

---rsk


RCS consists of about ten commands, each of which has quite a few options that
provide other capabilities as well -- but you only need to know a handful of
things to start using it quite effectively.

The basic idea of RCS is that it keeps the most current revision of a file as
well as a set of "deltas" -- one for each previous revision -- which enable it
to do/undo changes on the fly. All of these are stored in a single file, which
has a format known to RCS; by default, these files are suffixed ",v".
(You shouldn't even think about editing the ,v file.)


Let's say for example you're working on a file called "foo.sh". If you check
it into the RCS system like this:

    ci foo.sh

(Note: ci == "check-in". You now know an RCS command.)

You will get this message:

    enter description, terminated with single '.' or end of file:
    NOTE: This is NOT the log message!
    >>

RCS is asking you for a description of the file.   The reason it's
reminding you, pointedly, that this isn't a log message is that you
are not checking in a revision to the file: you are checking in
the original file.  So let's give it a description:

    >> An astoundingly clever shell script
    >> .

(The single "." tells RCS you're done describing the file.)

If you run "ls", you'll notice that you now have a file called "foo.sh,v" and
that you no longer have a file "foo.sh". Don't panic. You can get your script
back with:

    co foo.sh

which will yield this:

    foo.sh,v --> foo.sh
    revision 1.1
    done

This is RCS's way of telling you that it has extracted revision 1.1 from
foo.sh,v for you. Why 1.1? Because it's branch 1, revision 1 and because
that's the numbering RCS chooses by default.

(Note: co == "check-out". Now you know two RCS commands.)

The "foo.sh,v" file is called the "delta file" in RCS terms. What's in the
"foo.sh,v"? Well, the entire contents of foo.sh, plus some information that
RCS uses to keep track of who checked it in, when it was checked in, etc. You
can ask the RCS system to tell you everything it knows about "foo.sh,v" by
doing this:

    rlog foo.sh

(rlog == "RCS log". Now you know three RCS commands.)

This will play back the entire change history of the file -- which of course
isn't all that interesting at this point, because all you've done is create one
revision.

Now suppose you want to edit "foo.sh" to add a new routine. You'll notice that
the copy you currently have isn't writable. That's because "foo.sh" is not
"locked" by you. The idea is that in order to edit a file you have to check it
out -- locked -- so that nobody else can edit it while you're doing so. To do
this, you do a:

    co -l foo.sh

(co -l == "check-out AND lock")

Assuming that nobody else has beaten you to it, you now have a writable copy of
"foo.sh" that you can edit to your heart's content. Anyone else trying to do a
"co -l foo.sh" will be told that you currently have it locked.

Now you might say "but I'm the only one who will ever..." and you know,
you're right.  This is still useful, though, because it defends you from
your own mistakes: if you go to check out and edit foo.sh and discover
that it's already locked, then that's probably because you checked it
out a while ago, edited it, and forgot to check in your revisions.
Maaaaaaybe you might want to figure out what happened before you proceed.


When you're done editing, you can check the new version in -- and unlock the
file -- by doing a:

    ci -u foo.sh

(ci -u == "check-in AND unlock")

RCS will prompt you for a description of what you did -- this is a good place
to record your comments on the changes, such as "added a new routine foobar()
which sorts an array". You'll note that RCS also automatically increments the
version number on the file; it numbered your original version 1.1, and will
number this one 1.2.

Suppose you come along later and can't remember exactly what changes you made
between 1.1 and 1.2. Well, if you do a:

    rcsdiff -r1.1 -r1.2 foo.sh

RCS will show you just what is different between those two versions. This works
for non-sequential revisions, e.g.:

    rcsdiff -r1.2 -r1.9 foo.sh

will show all the differences between those two versions.

(Now you know four RCS commands.)

RCS has a lot of features, but frankly, all you probably really have to know
to get started is:

  * co -l (Check out/lock a file so you can edit it)
  * ci -u (Check in/unlock a file to save changes)
  * rlog (Read revision history of a file)
  * rcsdiff (Look at specific changes between revisions)

Most of these commands have a number of options, which you can read
about in their manual pages.  Some of those are more useful than others.

RCS also does branching and merging (rcsmerge is a *great* little tool) and
symbolic version naming and all kinds of stuff. None of which are needed for
nearly all simple use cases.

A couple things to be careful of: if RCS complains about not wanting to
overwrite a file, it's probably a good idea to find out what's going on before
telling it to go ahead; you may have just asked it to blow away your careful
changes. The first thing you probably want to do is "rcsdiff" to figure out
what's where.

Second, and I probably should have put this in sooner, if the current working
directory has a subdirectory named "RCS", then the ,v files will go there.
Very handy...keeps the directory uncluttered. Example:

    % ls
    foo.sh RCS

    % ls RCS
    foo.sh,v

Third, GNU make is RCS-aware.  That is, it has built-in rules that allow it
to figure out that if you say "make foo.sh", and foo.sh does not exist,
but foo.sh,v or RCS/foos.sh,v does, it should invoke "co" to create the file.

Appended below are the abridged PUCC (Purdue University Computing Center)
pocket guide entries for some of the RCS-related commands.  This just
scratches the surface: for more information, see the manual pages for
each command.  

-------------------------------------------------------------------------------
ci [<options>] <file(s)>

Check in RCS files.

    -l[<rev>] Perform checkout with locking after checkin.
    -u[<rev>] Perform checkout without locking after checkin.
    -m<msg> Use <msg> as the log message for all files checked in.

-------------------------------------------------------------------------------
co [<options>] <file(s)>

Check out RCS files.

    -r[<rev>] Retrieve latest revision whose number is less than or equal to
    <rev>.
    -l[<rev>] Lock checked out revision.
    -u[<rev>] Unlock the retrieved revision (default: latest).
    -f[<rev>] Force overwriting of the working file.

-------------------------------------------------------------------------------
rlog [-b] [-h] [-t] [-d<dates>] [-l[<lockers>]] [-r<revisions>] [-s<states>]
[-w[<logins>]] [-L] [-R] <file(s)>

Print log messages and other information about RCS files.

Rlog prints the intersection of the revisions selected with the options -d, -l,
-s, -w, intersected with the union of the revisions selected by -b and -r.

    -b Print information about revisions on default branch.
    -h Print using brief format.
    -t Same as -h, plus descriptive text.
    -r<revisions> Restrict report to specified comma-separated <revisions>. A
    range of revisions "r1-r2" means revisions from <r1> to <r2> on the same
    branch.
    -L Ignore RCS files that have no locks set.
    -R Only print name of RCS file.

-------------------------------------------------------------------------------
rcsdiff [-b] [-cefhn] [-q] [-r<rev1>] [-r<rev2>] <file(s)>

Compare two RCS revisions.

Rcsdiff runs diff to compare two RCS revisions. If both <rev1> and <rev2> are
omitted, rcsdiff compares the latest revision on the trunk with the working
file. If only <rev2> is omitted, rcsdiff compares <rev1> with the working file.
The -b, -c, -e, -f, and -h options have the same effect as with diff.

    -n Generate an RCS format edit script.

-------------------------------------------------------------------------------
rcs [<options>] <file(s)>

Change RCS file attributes or create RCS files.

    -l[<rev>] Lock specified revision. If <rev> omitted, lock latest revision
    on trunk.
    -u[<rev>] Unlock specified revision. If <rev> is omitted, unlock latest
    lock held by invoker.
    -L Set locking to strict.
    -U Set locking to non-strict.
    -o<range> Delete revisions specified by <range>. A range
    "r1-r2" specifies revisions from <r1> to <r2>. If one of the endpoints of
    the range is omitted, the range extends to the end of the branch.

-------------------------------------------------------------------------------
rcsmerge -r<rev1> [-r<rev2>] [-p] <file>

Merge RCS revisions.

Rcsmerge finds the differences between <rev1> and <rev2> and modifies <file> to
reflect these differences. <Rev1> may not be omitted; if <rev2> is omitted, the
latest revision on the trunk is assumed. Rcsmerge prints a warning if an
overlap occurs.

    -p Write result to stdout instead of overwriting <file>.

___________________________________________________________________________
Philadelphia Linux Users Group         --        http://www.phillylinux.org
Announcements - http://lists.phillylinux.org/mailman/listinfo/plug-announce
General Discussion  --   http://lists.phillylinux.org/mailman/listinfo/plug