Keep your home dir in Git with a detached working directory
Monday, June 22nd, 2015
Many posts have been written on putting your homedir in git. Nearly everyone uses a different method of doing so. I’ve found the method I’m about to describe in this blog post to work the best for me. I’ve been using it for more than a year now, and it hasn’t failed me yet. My method was put together from different sources all over the web; long since gone or untracable. So I’m documenting my setup here.
So, what makes my method better than the rest? What makes it better than the multitude of pre-made tools out there? The answer is: it depends. I’ve simply found that this methods suits me personally because:
- It’s simple to implement, simple to understand and simple to use.
- It gets out of your way. It doesn’t mess with repositories deeper in your home directory, or with any tools looking for a .git directory. In fact, your home directory won’t be a git repository at all.
- It’s simple to see what’s changed since you last committed. It’s a little harder to see new files not yet in your repository . This is because by default everything is ignored unless you specifically add it.
- No special tools required, other than Git itself. A tiny alias in your .profile takes care of all of it.
- No fiddling with symlinks and other nonsense.
How does it work?
It’s simple. We create what is called a “detached working tree“. In a normal git repository, you’ve got your
.git dir, which is basically your repository database. When you perform a checkout, the directory containing this
.git dir is populated with files from the git database. This is problematic when you want to keep your home directory in Git, since many tools (including git itself) will scan upwards in the directory tree in order to find a
.git dir. This creates crazy scenario’s such as Vim’s CtrlP plugin trying to scan your entire home directory for file completions. Not cool. A detached working tree means your
.git dir lives somewhere else entirely. Only the actual checkout lives in your home dir. This means no more nasty
An alias ‘
dgit‘ is added to your
.profile that wraps around the
git command. It understands this detached working directory and lets you use git like you would normally. The dgit alias looks like this:
alias dgit='git --git-dir ~/.dotfiles/.git --work-tree=$HOME'
Simple enough, isn’t it? We simply tell git that our working tree doesn’t reside in the same directory as the .git dir (~/.dotfiles), but rather it’s our directory. We set the git-dir so git will always know where our actual git repository resides. Otherwise it would scan up from the curent directory your in and won’t find the .git dir, since that’s the whole point of this exercise.
Setting it up
Create a directory to hold your git database (the
$ mkdir ~/.dotfiles/ $ cd ~/.dotfiles/ ~/.dotfiles$ git init .
Create a .gitifnore file that will ignore everything. You can be more conservative here and only ignore things you don’t want in git. I like to pick and choose exactly which things I’ll add, so I ignore everything by default and then add it later.
~/.dotfiles$ echo "*" > .gitignore ~/.dotfiles$ git add -f .gitignore ~/.dotfiles$ git commit -m "gitignore"
Now we’ve got a repository set up for our files. It’s out of the way of our home directory, so the .git directory won’t cause any conflicts with other repositories in your home directory. Here comes the magic part that lets us use this repository to keep our home directory in. Add the dgit alias to your .bashrc or .profile, whichever you prefer:
~/.dotfiles$ echo "alias dgit='git --git-dir ~/.dotfiles/.git --work-tree=\$HOME'" >> ~/.bashrc
You’ll have to log out and in again, or just copy-paste the alias defnition in your current shell. We can now the repository out in our home directory with the
~/.dotfiles$ cd ~ $ dgit reset --hard HEAD is now at 642d86f gitignore
Now the repository is checked out in our home directory, and it’s ready to have stuff added to it. The
dgit reset --hard command might seem spooky (and I do suggest you make a backup before running it), but since we’re ignoring everything, it’ll work just fine.
Everything we do now, we do with the
dgit command instead of normal git. In case you forget to use
dgit, it simply won’t work, so don’t worry about that.
dgit status shows nothing, since we’ve gitignored everything:
$ dgit status On branch master nothing to commit, working directory clean
We add things by overriding the ignore with
$ dgit add -f .profile $ dgit commit -m "Added .profile" [master f437f9f] Added .profile 1 file changed, 22 insertions(+) create mode 100644 .profile
We can push our configuration files to a remote repository:
$ dgit remote add origin ssh://email@example.com:dotfiles $ dgit push origin master * [new branch] master -> master
And easily deploy them to a new machine:
$ ssh someothermachine $ git clone ssh://firstname.lastname@example.org:dotfiles ./.dotfiles $ alias dgit='git --git-dir ~/.dotfiles/.git --work-tree=$HOME' $ dgit reset --hard HEAD is now at f437f9f Added .profile
Please note that any files that exist in your home directory will be overwritten by the files from your repository if they’re present.
This DIY method of keeping your homedir in git should be easy to understand. Although there are tools out there that are easier to use, this method requires no installing other than Git. As I’ve stated in the introduction, I’ve been using this method for more than a year, and have found it to be the best way of keeping my home directory in git.