It’s become quite common to see ‘dotfiles’ repositories across GitHub, and for good reason. Versioning your home directory is a great way to maintain backups of your dotfiles, share with others, learn from others, and makes configuring a new machine a bit easier. Many users, however, don’t actually version their home directory. A common approach is to have a dotfiles repo somewhere (let’s say
~/dotfiles) and use a number of different ways to get the actual dot-files into the home directory itself: manually copy, manually symlink, or scripting the copy/symlink.
I’m not a fan of the copy approach. Any changes made to the actual dotfiles must to be manually replicated in the repo. I’m also not a fan of the symlink approach. It solves the problem of modifications but doesn’t account for new files. So why don’t we version the home directory itself?
I’m sure there are some good reasons not to version the home directory, and I’d love to hear them. I can’t think of concrete examples where having a ~/.git directory is a bad idea, but I’m sure there are a few. Regardless, here’s my current setup.
My dotfiles repo is cloned under
~/dotfiles. Git has a setting (
core.worktree) that configures where the working tree should be checked out. It expects a path that can be either absolute or relative to the ‘.git’ directory. To accomplish this, from my home directory:
$ git clone --no-checkout https://github.com/jasonkarns/dotfiles.git $ git config core.worktree="../../" $ git checkout master
This will do a normal git checkout, but instead of checking out the
master branch to ‘~/dotfiles’, it checks out to the home directory itself. The repo itself (‘~/dotfiles’) is completely empty, except for the ‘.git’ directory.
- Any changes to my dotfiles are known to git
- New files are known to git
- The home directory itself is not a git repo, yet it’s fully versioned.
- git commands cannot be run under ~
- git status is not displayed in my command prompt ($PS1) under ~
- To manage the dotfiles repo and run git commands, I must be in ‘~/dotfiles’. (I like the forced context switch.)
This setup has worked well for me. I have the benefits of a versioned home directory, without the annoyance of it being an actual repo (like seeing git status info in my command prompt).
There is one major complication: submodules. I use Vundle to manage my Vim plugins. Vundle itself is a git submodule under ‘dotfiles/.vim/bundle’. In order to run
git submodule update, git requires that I be in the repo (duh) but also that I be in the working tree root. Since my working tree root is not in the repo, I get an error:
$ git submodule update fatal: Not a git repository (or any of the parent directories): .git
To get around this, git has a feature wherein a plaintext file named ‘.git’ is placed in the root of the working tree. It contains just a single line:
gitdir: /path/to/actual/repo/.git. While this file exists, the home directory itself becomes, for all intents and purposes, a proper git repo. I can run git commands directly from the home directory and even my command prompt picks up the git status info.
So, with ‘~/.git’ containing
gitdir: /Users//dotfiles.git, I am able to properly run
git submodule update and everything works! Of course, as long as this ‘.git’ file exists, my home directory is essentially a proper git repo, so once I’ve run any necessary commands, I simply delete the ‘.git’ file, and now I’m back to a plain, non-repo home directory!