...making Linux just a little more fun!

<-- prev | next -->

SVN Hackery: Versioning Your Linux Configuration

By Stephen Shirley

Basic Premise

"So, let's assume that you're already familiar with SVN..." This might have made a good start for an article that deals with refining and sharpening your SVN-Fu and making you into an SVN Guru. Stephen's article, however, started life as a tip posted to ILUG - and gained a new lease on life when Rick Moen, a member of LG's staff and an ILUG regular, forwarded it to me for possible inclusion in LG. At first, I didn't see this idea as being all that revolutionary - but after an email exchange with Stephen where he explained the finer points to me [1], I came to appreciate it for what it is.

SVN (Subversion, also known as "a compelling replacement for CVS") is a central part of the LG publication process; after using it for a couple of years and exploring its various features, I'm definitely a fan. However, SVN is not an end in itself but an important building block, a flexible tool. In the case described here, it's used to provide a series of versioned snapshots of your system - something that would allow you to "roll it back" to a known-good version (creating a cron job that would take those snapshots on a regular basis would, of course, be an obvious complement to this.) In my opinion, that makes a very nice addition to a system administrator's toolkit - one that's likely to save you untold grief in a number of situations. That's the kind of ideas and systems I like to expose to our readers; that, to me, is what makes Linux "just a little more fun".
-- Ben Okopnik, Editor-in-Chief


So you have all of /etc stored in your SVN repository (say, at file:///root/svn/etc). That works nicely: it's trivially easy to see if any files have changed, you can revert to any previous version, sleep well at night - just great. Then you decide that you should also store /boot in SVN (say, at file:///root/svn/boot). Also great. However, now in order to check if anything has changed, you have to do:

   cd /etc; svn status
   cd /boot; svn status

If you add any more top-level directories to SVN, it quickly becomes a pain to do this. So, you want to store / in SVN also (with the appropriate "svn:ignore" properties set so that all the non-SVN'd top-level dirs don't show up in 'svn status'). If, however, you now try

   svn co file:///root/svn/ /

it wont work as SVN will try to also check out /etc and /boot, and will complain that those directories already exist. Hurm. If you try the non-recursive checking

   svn co -N file:///root/svn/ /

it will check out file:///root/svn/ into / alright, but /etc and /boot will show up as unknown in "svn status":

   fluff# svn status /
   ?    /boot
   ?    /etc

Fixing this requires some minor hackery of the .svn/ special directory. If you open /.svn/entries in a text editor, youll see something like:

<?xml version="1.0" encoding="utf-8"?>
<wc-entries
  xmlns="svn:">
<entry
  committed-rev="226"
  name=""
  committed-date="2006-08-17T22:34:00.835159Z"
  url="file:///root/svn"
  last-author="root"
  kind="dir"
  uuid="18f6e95b-a6ff-0310-910f-8823210a8ec4"
  revision="226"/>
</wc-entries>

It contains a single <entry ... /> tag, with the name attribute set to "". This is the SVN entry for '/' itself. If you open '/etc/.svn/entries' in a text editor, you'll find a very similar <entry... name=""... /> tag:

<entry
  committed-rev="25"
  name=""
  committed-date="2005-09-22T20:33:37.949298Z"
  url="file:///root/svn/etc"
  last-author="root"
  kind="dir"
  uuid="18f6e95b-a6ff-0310-910f-8823210a8ec4"
  revision="25"/>

So, to make SVN consider /etc to be part of the / working directory, what you need to do is this: copy the above <entry.../> tag from /etc/.svn/entries into /.svn/entries, placing it just before the closing </wc-entries> tag. Then, change the 'name' attribute from "" to "etc". The complete finished /.svn/entries file should look like this:

<?xml version="1.0" encoding="utf-8"?>
<wc-entries
  xmlns="svn:">
<entry
  committed-rev="226"
  name=""
  committed-date="2006-08-17T22:34:00.835159Z"
  url="file:///root/svn"
  last-author="root"
  kind="dir"
  uuid="18f6e95b-a6ff-0310-910f-8823210a8ec4"
  revision="226"/>
<entry
  committed-rev="25"
  name="etc"
  committed-date="2005-09-22T20:33:37.949298Z"
  url="file:///root/svn/etc"
  last-author="root"
  kind="dir"
  uuid="18f6e95b-a6ff-0310-910f-8823210a8ec4"
  revision="25"/>
</wc-entries>

Now when you check the SVN status of /, it no longer considers /etc to be a foreign object:

   fluff# svn status /
   ?    /boot

Bingo. If you repeat the process again for /boot (i.e. copy the <entry... name=""... /> tag from /boot/.svn/entries into /.svn/entries before the closing </wc-entries> tag, and set the name attribute to be "boot") and any other applicable dirs; SVN will then start treating them as proper checked-out subdirs of the working dir.

As your reward, a single 'svn status /' will now check all of the top-level dirs that are stored in Subversion, making it much easier to keep track of things.


[1] Our exchange went something like this:

Ben > > Why not just do 'svn status /etc /boot', or even a little script that
Ben > > would read the list of directories in SVN and report on them? E.g.,
Ben > > 
Ben > > -------------------------------------------------------------------
Ben > > #!/bin/bash
Ben > > 
Ben > > cd /root/svn
Ben > > svn status `echo *|sed 's|\<|/|g'`
Ben > > -------------------------------------------------------------------
>
Steve > For what i explicitly stated, yes, that's a neat enough solution.
Steve > Other solutions that were posted in response on the ILUG list involved
Steve > using the "svn:externals" property on / to achieve roughly the same
Steve > thing. The downside to doing this is that you lose the benefit of a
Steve > cohesive working directory. You can't do atomic commits (you have to
Steve > do one per /, /etc and /boot instead). It also doesn't work with
Steve > branching etc. And of course you have to remember to run a script
Steve > rather than just 'svn status' when you're working on stuff.
Steve > 
Steve > All in all, my solution is probably overkill for most people, i just
Steve > thought i'd document it seeing as i discovered it was possible with a
Steve > moderate amount of effort, and it makes things nice and elegant for
Steve > me, even when i set up svn for /etc before realising that i want it
Steve > for other top-level dirs too.
Steve >
Steve > By all means publish it if you think it's genuinely useful to others,
Steve > but i'd perfectly understand if you don't -)

Talkback: Discuss this article with The Answer Gang


[BIO]

Stephen 'Captain Pedantic Pants' Shirley is a quiet student[0] with an appetite for sanely configured, maintainable systems. He was first introduced to Linux in 1996 with RedHat 4.1, and has since progressed through SuSE, Mandrake and Debian, seeking enlightenment. He is now involved in the Ubuntu project, and is the head admin of the University of Limerick computer society (skynet.ie). Currently finishing a MSc through research in the aforementioned UL, he's looking forward to a fulfilling life of bending Unix boxen to his will, whilst striking down inaccuracies wherever he finds them.

[0] It's always the quiet ones...


Copyright © 2006, Stephen Shirley. Released under the Open Publication license unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 131 of Linux Gazette, October 2006

<-- prev | next -->
Tux