As regular readers will know, I’m a big fan of version control for software projects. You may also know that I’ve recently started to use RCS for managing single files (shell/Python scripts, CGI scripts etc.). Tonight, I’d like to present my simple workflow for version controlling configuration files, also using RCS.

The Problem

We Linux users frequently have to create and edit config files. Here is a list of some of mine:

.vimrc
.nanorc
/etc/smtpd.conf
/etc/aliases
/etc/hosts
/etc/apache2/*
/etc/apt/sources*
...

Up until recently, I’d usually rename a config file to <filename>.bak in case something went horribly wrong. Come to think of it, that’s pretty much always the advice you’ll be given online.

Of course, there’s a better, more robust way of keeping track of changes you’ve made, with the ability to roll back to any previous revision. But, it does take a little bit of discipline!

The Solution

Let’s say you need to edit your .nanorc

simon@computer:~$ mkdir RCS
mkdir: cannot create directory ‘RCS’: File exists

OK, good. I’ve already got a repository setup in my home directory. Let’s open it up and see what it contains:

nano .nanorc
 GNU nano 6.2                         .nanorc                                  



















                                [ Read 0 lines ]
^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

An empty file which means the file either didn’t exist or was empty. Let’s get to work. I’ll add the settings I want, plus the RCS metadata so I can see the current version number and the date it was checked in:

  GNU nano 6.2                         .nanorc                           I   S  
#=====================================================================
# $Revision$
# $Date$
# $Author$
#======================================================================

set autoindent
set tabsize 4
set tabstospaces
set softwrap









                               [ Read 10 lines ]
^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

Now let’s checkin the file

simon@computer:~$ ci -l .nanorc 
RCS/.nanorc,v  <--  .nanorc
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> First checkin 
>> .
initial revision: 1.1
done
simon@computer:~$ 

Let’s have a look at the file

simon@computer:~$ cat .nanorc 
#=====================================================================
# $Revision: 1.1 $
# $Date: 2023/08/25 19:11:01 $
# $Author: simon $
#======================================================================

set autoindent
set tabsize 4
set tabstospaces
set softwrap

So, three things are immediately evident:

1. The file is version controlled
2. I can see how many revisions have been made to the file
3. I can see the datetime of the last revision. Nice.

I read somewhere that it’s a good idea to add the .nanorc files from the /usr/share/nano/* directory to enable syntax highlighting for lots of filetypes. Let’s add that to my .nanorc file:

  GNU nano 6.2                         .nanorc                           I   S  
#=====================================================================
# $Revision: 1.1 $
# $Date: 2023/08/25 19:11:01 $
# $Author: simon $
#======================================================================

set autoindent
set tabsize 4
set tabstospaces
set softwrap

include "/usr/share/nano/*.nanorc"







                               [ Read 13 lines ]
^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

Now I’ll checkin the change

simon@computer:~$ ci -l ./.nanorc 
./RCS/.nanorc,v  <--  ./.nanorc
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Add all .nanorc files from /usr/share/nano/*
>> .
done
simon@computer:~$ 

If I accidently try and checkin an unchanged file, RCS will say:

simon@computer:~$ ci -l ./.nanorc 
./RCS/.nanorc,v  <--  ./.nanorc
file is unchanged; reverting to previous revision 1.2
done

If you’re ever unsure you can always run rlog:

simon@computer:~$ rlog ./.nanorc 

RCS file: ./RCS/.nanorc,v
Working file: ./.nanorc
head: 1.2
branch:
locks: strict
        simon: 1.2
access list:
symbolic names:
keyword substitution: kv
total revisions: 2;     selected revisions: 2
description:
First checkin
----------------------------
revision 1.2    locked by: simon;
date: 2023/08/25 19:20:20;  author: simon;  state: Exp;  lines: +6 -3
Add all .nanorc files from /usr/share/nano/*
----------------------------
revision 1.1
date: 2023/08/25 19:11:01;  author: simon;  state: Exp;
Initial revision
=============================================================================

Yep, 1.2 is the latest revision. Another option is to run rcsdiff:

simon@computer:~$ rcsdiff ./.nanorc 
===================================================================
RCS file: ./RCS/.nanorc,v
retrieving revision 1.2
diff -r1.2 ./.nanorc

No changes are shown (as no changes have been made).

What if we have changes? I’ll add the setting to show line numbers (without checking the file in):

simon@computer:~$ rcsdiff -u ./.nanorc 
===================================================================
RCS file: ./RCS/.nanorc,v
retrieving revision 1.2
diff -u -r1.2 ./.nanorc
--- ./.nanorc   2023/08/25 19:20:20     1.2
+++ ./.nanorc   2023/08/25 19:29:15
@@ -8,6 +8,7 @@
 set tabsize 4
 set tabstospaces
 set softwrap
+set linenumbers

 include "/usr/share/nano/*.nanorc"

I’ve now run ci -l .nanorc but decide I don’t really need to see to see line numbers. I want to go back to the revision before I added that line:

simon@computer:~$ co -l -r1.2 ./.nanorc 
./RCS/.nanorc,v  -->  ./.nanorc
revision 1.2 (locked)
writable ./.nanorc exists; remove it? [ny](n): y
co: ./RCS/.nanorc,v: warning: You now have 2 locks.
done

Let’s see the revision of the file now:

simon@computer:~$ cat .nanorc 
#=====================================================================
# $Revision: 1.2 $
# $Date: 2023/08/25 19:20:20 $
# $Author: simon $
#======================================================================

set autoindent
set tabsize 4
set tabstospaces
set softwrap

include "/usr/share/nano/*.nanorc"

Ah good, we’ve rolled back to where we were. And that’s all there is to it. Here is the new workflow

The New Workflow

1. mkdir RCS in the working directory
2. Add RCS metadata to the file if it doesn’t exist
3. Make changes to the config file
4. Checkin the changes (ci -l myfile.conf)
5. If you make a mistake or dislike a setting, co -l -r1.?

Some nice features would be to automatically create the RCS directory if it doesn’t exist and to have all these RCS directories backed up. That’s for another post though…