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 toolsbin
contains scriptsdotfiles
contains, well dotfiles, eg..screenrc
emacs
contains my Emacs configurationsh
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)