Last updated: Wed May 01 2024

Storing dotfiles in git

Rationale

Storing dotfiles in a git repository is fairly easy. While it's straightforward to simply initialise a git repository directly within your home directory, this presents problems with managing ignore lists and potentially opens the door to interaction from other git repositories stored within your home directory. Alternatively you could have an isolated git repository elsewhere and symlink your home directory's dotfiles to that directory, but that's pretty ugly.

A slightly modified technique is to use a "bare" git repository in a sub-directory somewhere off $HOME, and then your dotfiles in $HOME are literally the same as the files in git. I use $HOME/.dotfiles and alias the git command with the necessary parameters to a new dotfiles command, specifically for interacting with the dotfiles repository.

dotfiles is literally git

Setup

All we needs is a git repository initialised somewhere, treating $HOME as its root.

git init --bare $HOME/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no

This will give you a git repository in ~/.dotfiles and a dotfiles command in your current terminal to work with it.

To make the dotfiles command permanent, you will need to add it to your .zshrc (shown) or .bashrc

echo "alias dotfiles='git --git-dir=\$HOME/.dotfiles/ --work-tree=\$HOME'" >> $HOME/.zshrc

The echoed string has $HOME escaped to prevent it being expanded before being written into .zshrc, which will work fine locally but may cause problems when you share this across different machines.

You can then interact with this like any other git repository:

dotfiles status
# No files will be shown as we are using status.showUntrackedFiles=no

dotfiles add ~/.zshrc
dotfiles commit -m "Add .zshrc to dotfiles"

# Add a remote
dotfiles remote add origin git@mygithost.com:myname/dotfiles.git
dotfiles push

Restoring on another machine

Restoring this on another machine is not quite trivial because git won't overwrite existing files.

This is a short script that will move existing files to ./.dotfiles-backup/ (relative to the current working directory) and then restore the dotfiles from the repository.

Assuming you have your .zshrc checked in, you won't need to set the dotfiles alias again.

#!/usr/bin/env bash

git clone --bare git@mygithost.com:myname/dotfiles.git $HOME/.dotfiles

function dotfiles {
   git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME $@
}

mkdir -p .dotfiles-backup
dotfiles checkout

if [ $? = 0 ]; then
  echo "Checked out dotfiles"
  else
    echo "Couldn't check out dotfiles due to existing files. Backing up existing files to .dotfiles-backup"

    dotfiles checkout 2>&1 | grep -E "^\s+\S+" | awk {'print $1'} | xargs -d $'\n' sh -c 'for arg do echo "Backing up $arg"; mkdir -p .dotfiles-backup/"$arg"; mv "$arg" ".dotfiles-backup/$arg"; done' _

    echo 'Backed up'
fi;

dotfiles checkout
dotfiles config --local status.showUntrackedFiles no

echo 'Done'