31 December 2019

Using Subversion to version your home directory

Why version your home directory?

Aside from the usual benefits of version control: the ability to roll back changes, document the why of changes there's some additional benefits to doing it to your home directory:

  • quickly get your settings onto a new system
  • share configuration between environments
  • keep environment specific configuration versioning without dragging it along everywhere

Why Subversion? "

The basic reason is that I've been doing this for a very long time and the tool most people would, at least nowadays, immediately look to, namely Git, only just existed. It most definitely wasn't able to handle the repository structure I'll be describing.

So that being said, would I pick Git if I'd be settings this up from scratch on the current day? Most likely not because the layout relies very heavily on the use of an often abused Subversion feature: externals.

As far as I'm aware there is no functional alternative to this outside of subversion. Certainly, there are ways to get something similar set up in other version control systems. After all, the original idea I got from a blog post of someone doing something rather similar with CVS and using scripts to fill in the gaps. A similar approach most definitely could be made to work with Git, but Subversion supports it out of the box, so well, "right tool for the job" and all that.

Additional considerations

Clearly an approach like this favours tools that store their configuration in text files. While it is possible to use it on binary configuration files you do lose quite a few of the benefits of your version control system.

Trying to version, say, a Gnome configuration might not go so well.

Repository layout

While Subversion doesn't enforce a specific layout I did try to stay with the recommended structure.

The repository as it stands looks something like this:

 + branches
  |-- pc1
  |-- pc2
  |-- pcN
 + tags (empty)
 + trunk
  |-- home-base
  |-- home-full
  |-- home-minimal
  |- + modules
      |--  X
      |-- bin
      |-- dotfiles
      |-- emacs
      |-- sh

The branches are the highest level entities, these represent concrete machines, so pc1 could be my desktop, pc2 could be my laptop, etc.

Then one level down we have the optional "preconfigureds", these are intended as bases to branch concrete configurations from, but, of course, can be used as they stand.

And at the bottom we have repositories that group specific topics:

  • X configuration for X-specific tools
  • bin contains scripts
  • dotfiles contains, well dotfiles, eg. .screenrc
  • emacs contains my Emacs configuration
  • sh contains shell configuration files

There are more, but these should give some idea as to what goes into them on a general level.

Checkout structure

So let's see what this looks like on, for example my laptop:

[dragon@gentoo ~]% svn propget svn:externals
.common/bin             https://my-server/svn/home/trunk/modules/bin
.common/dotfiles        https://my-server/svn/home/trunk/modules/dotfiles
.common/emacs           https://my-server/svn/home/trunk/modules/emacs
.common/sh              https://my-server/svn/home/trunk/modules/sh
.common/vim             https://my-server/svn/home/trunk/modules/vim
.common/X               https://my-server/svn/home/trunk/modules/X

So Subversion will check out these externals to the directory listed in the first column, then I symlink them to their actual location:

bin -> .common/bin
.elisp -> .common/emacs/.elisp
.emacs -> .common/emacs/.emacs
.emacs-custom.el -> .common/emacs/.emacs-custom.el
.gitconfig -> .common/dotfiles/.gitconfig
.gnus.el -> .common/emacs/.gnus.el
.screenrc -> .common/dotfiles/.screenrc
.Xdefaults -> .common/X/.Xdefaults
.xsession -> .common/X/.xsession
.zlogout -> .common/sh/.zlogout
.zsh -> .common/sh/.zsh
.zshrc -> .common/sh/.zshrc

So the "preconfigureds" like home-base just contain the externals and symlinks that make sense for that "preset", at least in my mind.

So for the actual machine branches I start from either a preset, or an existing branch (eg. if I get a laptop for work I might base the branch for that off of my personal laptop branch, or vice-versa) to which I then add additional items specific to that branch (like window manager configuration files), either by adding additional externals or by adding files directly to the branch.

Extending global configuration

The most obvious problem with this setup is, what if you want to add machine specific settings to, say, your shell configuration file?

The most straightforward solution is to pick tools that are able to load extra configuration.

As an example the final line in my ~/.zshrc is this:

source ~/.zshrc.local

So my machine-specific files can contain a .zshrc.local file that will just append settings to my default .zshrc

The same happens with my Emacs configuration:

;; ---------------------------------------------------------------------------
;;;; Override with system specific settings
;; ---------------------------------------------------------------------------
(load "~/.emacs-local.el" t)
Tags: GNU/Linux Subversion Version control