Old School Version Control
Since I started using Mercurial a few years back, I’ve realised the value in having everything important under version control. The thing is, tools like Mercurial and Git don’t seem to be quite the right tool when you’re wanting to manage single files.
Enter RCS
RCS dates back to 1982, and was last updated in 2022 (still active). I believe that one of RCS’s perceived weaknesses can actually be a strength: it was designed to work with single files. Using it is very easy. Let’s get started!
The first thing I do is add some variables that RCS will populate for us. I’ll add a bit of metadata to our fake program my_program
# $Date$
# $Revision$
# $Author$
These will be replaced when we “checkout” our file from RCS. But first we need to “checkin” our initial revision.
# $Date$
# $Revision$
# $Author$
This is the first version of my file. I've done a small amount of work that I want to save.
Let’s check in our first version.
simon@computer:~/tmp/my_script$ mkdir RCS
simon@computer:~/tmp/my_script$ ll
total 16K
drwxrwxr-x 3 simon simon 4.0K 2023-02-11 11:16 .
drwxrwxr-x 18 simon simon 4.0K 2023-02-11 11:09 ..
-rw-rw-r-- 1 simon simon 128 2023-02-11 11:15 my_program
drwxrwxr-x 2 simon simon 4.0K 2023-02-11 11:16 RCS
simon@computer:~/tmp/my_script$ ci my_program
RCS/my_program,v <-- my_program
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> Version 1 of my_program
>> .
initial revision: 1.1
done
So we’ve performed our first commit. Let’s see the state of our working directory
simon@computer:~/tmp/my_script$ tree
.
└── RCS
└── my_program,v
1 directory, 1 file
As you can see, our original file has been destroyed! We now have one file in the RCS directory my_program,v
. That file is our “versioned” file. Let’s take a look at it
simon@computer:~/tmp/my_script$ cat RCS/my_program,v
head 1.1;
access;
symbols;
locks; strict;
comment @# @;
1.1
date 2023.02.11.11.16.25; author simon; state Exp;
branches;
next ;
desc
@Version 1 of my_program
@
1.1
log
@Initial revision
@
text
@# $Date$
# $Revision$
# $Author$
This is the first version of my file. I've done a small amount of work that I want to save.
@
simon@computer:~/tmp/my_script$
As you can see, there’s quite a bit of information in there, along with the text of my_program
To retrieve our file and do some work, we need to check it out, with a lock, to prevent other people working on it at the same time as us (to avoid conflicts).
simon@computer:~/tmp/my_script$ co -l ./my_program
./RCS/my_program,v --> ./my_program
revision 1.1 (locked)
done
Let’s see our working directory state now
simon@computer:~/tmp/my_script$ tree
.
├── my_program
└── RCS
└── my_program,v
1 directory, 2 files
Great, the file is back! Let’s take a look at the contents
simon@computer:~/tmp/my_script$ cat my_program
# $Date: 2023/02/11 11:16:25 $
# $Revision: 1.1 $
# $Author: simon $
This is the first version of my file. I've done a small amount of work that I want to save.
As I said earlier, the dollar variables have been expanded by RCS. Let’s make another change and see what happens.
# $Date: 2023/02/11 11:16:25 $
# $Revision: 1.1 $
# $Author: simon $
This is the first version of my file. I've done a small amount of work that I want to save.
I've simply added a blank newline and this line now.
Running rcsdiff
will show us unsaved changes. Use the -u
option to get unified diff output.
simon@computer:~/tmp/my_script$ rcsdiff -u ./my_program
===================================================================
RCS file: ./RCS/my_program,v
retrieving revision 1.1
diff -u -r1.1 ./my_program
--- ./my_program 2023/02/11 11:16:25 1.1
+++ ./my_program 2023/02/11 11:29:41
@@ -4,4 +4,6 @@
This is the first version of my file. I've done a small amount of work that I want to save.
+I've simply added a blank newline and this line now.
+
simon@computer:~/tmp/my_script$
All looks good, so we’ll commit our changes.
simon@computer:~/tmp/my_script$ ci ./my_program
./RCS/my_program,v <-- ./my_program
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Did a bit of work
>> .
done
Very easy and painless, eh? Once again, our file will have been destroyed so we need to check it out again to either work on it (with the -l
option to have a locked version of the file) or without a lock for a read only version.
simon@computer:~/tmp/my_script$ co ./my_program
./RCS/my_program,v --> ./my_program
revision 1.2
done
simon@computer:~/tmp/my_script$ cat my_program
# $Date: 2023/02/11 11:31:42 $
# $Revision: 1.2 $
# $Author: simon $
This is the first version of my file. I've done a small amount of work that I want to save.
I've simply added a blank newline and this line now.
The date and revision have been updated and our most recent changes are there. Because we didn’t lock our file, it’s readonly.
GNU nano 6.2 my_program
# $Date: 2023/02/11 11:31:42 $
# $Revision: 1.2 $
# $Author: simon $
This is the first version of my file. I've done a small amount of work that I w>
I've simply added a blank newline and this line now.
[ File 'my_program' is unwritable ]
^G Help ^O Write Out ^W Where Is ^K Cut ^T Execute ^C Location
^X Exit ^R Read File ^\ Replace ^U Paste ^J Justify ^/ Go To Line
GNU Nano tells us at the bottom that the file is unwritable. Fair enough.
We’ll checkout an editable version now with co -l
simon@computer:~/tmp/my_script$ co -l ./my_program
./RCS/my_program,v --> ./my_program
revision 1.2 (locked)
done
If we want to view our changes rlog
will happily tell us
simon@computer:~/tmp/my_script$ rlog ./my_program
RCS file: ./RCS/my_program,v
Working file: ./my_program
head: 1.2
branch:
locks: strict
simon: 1.2
access list:
symbolic names:
keyword substitution: kv
total revisions: 2; selected revisions: 2
description:
Version 1 of my_program
----------------------------
revision 1.2 locked by: simon;
date: 2023/02/11 11:31:42; author: simon; state: Exp; lines: +5 -3
Did a bit of work
----------------------------
revision 1.1
date: 2023/02/11 11:16:25; author: simon; state: Exp;
Initial revision
=============================================================================
And that’s really all there is to it (for my purposes anyway). Take a look at the excellent manual for everything else you can do with RCS.
I’m sure you can see the value of RCS for shell scripts, single Python files, etc.