Managing DotFiles with a “bare” Repository

Reading Time: 5 minutes

Note: This is probably only of use to those using “Unix-like” systems such as OS X and Linux. It may or may not work on Windows, using Git Bash. Probably not useful on Windows, otherwise.

In this post, we’ll be covering a few things. First up is the concept of “dotfiles,” the configuration files you keep in your home directory that provide customization and configuration.

After that, we’ll take a look at some strategies for maintaining these configuration files using Git, the version control package that powers the Open Source movement.

Finally, we’ll be looking at so-called “bare” repositories as well as some clever tweaks that you’ll be able to apply not only to your dotfiles, but to anywhere you might want to leverage Git for files that may not live inside a traditional repository.

First up, the Dotfiles

On a Unix/Linux system, when you don’t want to see a file by default, you can hide it from view by starting its filename with a “dot,” or period character. This is a convenience, not a security measure–it’s a way of keeping your view of a directory relatively uncluttered.

For example, if I take a look at my home directory using ls, here’s what I see:

$ ls                                                               
AppData Documents Music Pictures Templates
bin Downloads node_modules projects Videos
Desktop ember-quickstart package-lock.json Public

But that’s not the complete list of files in that directory. There are a lot more, visible if you use the -a flag:

$ ls -a

drwxr-xr-x. 4 jim jim 4.0K Nov 9 14:00 .ansible
drwxr-xr-x. 3 jim jim 4.0K Jan 16 15:06 AppData
drwxrwxr-x. 6 jim jim 4.0K Dec 9 11:22 .atom
-rw-------. 1 jim jim 593 Nov 2 09:52 .bash_history
-rw-r--r--. 1 jim jim 18 Oct 8 09:41 .bash_logout
-rw-r--r--. 1 jim jim 141 Oct 8 09:41 .bash_profile
-rw-r--r--. 1 jim jim 389 Nov 2 00:21 .bashrc
drwxr-xr-x. 3 jim jim 4.0K Jan 6 15:06 bin
drwxrwxr-x. 3 jim jim 4.0K Jan 12 11:33 .byobu
drwx------. 23 jim jim 4.0K Jan 14 12:25 .cache
drwx------. 21 jim jim 4.0K Dec 19 12:58 .config
drwxr-xr-x. 4 jim jim 4.0K Dec 19 14:58 Desktop
drwxr-xr-x. 2 jim jim 4.0K Nov 2 00:01 Documents
drwxr-xr-x. 4 jim jim 4.0K Jan 16 15:06 Downloads
-rw-rw-r--. 1 jim jim 85 Dec 10 14:07 .gitconfig
-rw-r--r--. 1 jim jim 12 Dec 10 14:08 .gitignore
drwx------. 3 jim jim 4.0K Nov 19 08:39 .gnome
drwxr-xr-x. 3 jim jim 4.0K Dec 19 17:32 .grip
-rw-------. 1 jim jim 10K Jan 16 19:15 .ICEauthority
drwx------. 2 jim jim 4.0K Dec 15 11:01 .irssi
drwx------. 5 jim jim 4.0K Jan 6 18:36 .local
drwxr-xr-x. 6 jim jim 4.0K Nov 2 00:10 .mozilla
drwxr-xr-x. 2 jim jim 4.0K Nov 2 00:01 Music
drwxr-xr-x. 115 root root 4.0K Jan 14 12:24 node_modules
drwxr-xr-x. 6 jim jim 4.0K Dec 8 09:50 .npm
drwxr-xr-x. 6 jim jim 4.0K Dec 8 09:47 .nvm
drwxr-xr-x. 11 jim jim 4.0K Dec 27 14:06 .oh-my-zsh
-rw-r--r--. 1 root root 30K Jan 14 12:24 package-lock.json
drwxr-xr-x. 2 jim jim 4.0K Dec 9 11:22 Pictures
drwxrw----. 3 jim jim 4.0K Nov 2 00:01 .pki
drwxr-xr-x. 4 jim jim 4.0K Dec 8 10:57 projects
drwxr-xr-x. 2 jim jim 4.0K Nov 2 00:01 Public
drwx------. 2 jim jim 4.0K Jan 11 10:40 .ssh
drwxr-xr-x. 2 jim jim 4.0K Nov 2 00:01 Templates
drwxr-xr-x. 2 jim jim 4.0K Nov 2 00:01 Videos
drwxrwxr-x. 3 jim jim 4.0K Nov 2 00:20 .vim
-rw-------. 1 jim jim 21K Jan 12 11:34 .viminfo
-rw-rw-r--. 1 jim jim 4.1K Dec 13 10:33 .vimrc
drwxr-xr-x. 3 jim jim 4.0K Dec 9 16:37 .vscode
-rw-r--r--. 1 jim jim 215 Jan 6 15:02 .wget-hsts
-rw-------. 1 jim jim 24K Jan 17 13:38 .zsh_history
-rw-------. 1 jim jim 6.3K Dec 4 14:13 .zsh_history.corrupt
-rw-r--r--. 1 jim jim 3.4K Jan 12 11:34 .zshrc

There are both dot files and dot folders in that list. (Hiding a directory is done the same way, just prepend its name with a dot.)

Many of these are configuration files and directories, especially for command line utilities. The Files .zshrc and .zsh_history and the folder .oh-my-zsh for example, are the configuration files that hold all of my customization directives for ZSH, the shell I use, as well as the incredible Oh My Zsh.

To edit files, I use Vim, also with a ton of customizations found in the files .vimrc , .viminfo and the directory .vim

These files are what make your working environment your own. When you’ve gotten them just how you like, you don’t want to lose them, or have them get out of sync with other machines you may work on. It makes sense to keep them in a structured version control system.

Git to the rescue.

If you’re reading this, you’re probably familiar with Git. You may have cloned a repository from github.com or even created and shared repositories of your own.
Typically, you’ll clone a project, which will exist as a directory filled with source code that you’ll edit or compile or whatever. When you make changes, you can commit them back up to Github or BitBucket, or somewhere else.
Git’s great–you can work with highly distributed teams of collaborators or work offline. You can, (if you’ve set it up correctly,) jump back to any save point and step through every commit.
When you’re a single user, you can use it to sync your project across many machines, which makes it ideal for synchronizing your dotfiles.
What always stopped me from doing this is that I didn’t want to put my whole home directory into Git – there’s a lot of stuff I don’t want to sync and the only way I knew how to exclude it was by explicitly listing it in the “Git Ignore” file, which seemed like too much work.
What some people started doing was to create a git repo, one level down, that contained the files, along with a script that would create symbolic links from that directory to your home directory. To me, this seemed an inelegant solution.

An Elegant Solution

A while back, I decided to revisit the problem and did some Googling, eventually coming across a post on Ycombinator:

========= Start of Quoted post ==========

StreakyCobra on Feb 10, 2016
I use:

    git init --bare $HOME/.myconf
    alias config='/usr/bin/git --git-dir=$HOME/.myconf/ --work-tree=$HOME'
    config config status.showUntrackedFiles no

where my ~/.myconf directory is a git bare repository. Then any file within the home folder can be versioned with normal commands like:

    config status
    config add .vimrc
    config commit -m "Add vimrc"
    config add .config/redshift.conf
    config commit -m "Add redshift config"
    config push

And so on…

No extra tooling, no symlinks, files are tracked on a version control system, you can use different branches for different computers, you can replicate you configuration easily on new installation.

========= End of quoted post ==========

Let’s take a look at those lines that do the work. First, a bare Git repo is created in your home directory:

 git init --bare $HOME/.myconf

That’s simple enough, but what about that next line?

alias config='/usr/bin/git --git-dir=$HOME/.myconf/ --work-tree=$HOME'

This part is really, really cool–it creates a new command called config that you use just like the command git, that adds some special options that apply only to the dotfiles problem.
It’s a wrapper for Git that says that the working directory is in ~/.myconf, but the actual versioned files live in your home directory.
Read that a couple times until you completely understand the sheer awesome power of what that simple line does.
Elegant AF.

The last line uses our newly-minted command to tell git to ignore files that we haven’t explicitly added, something that makes a lot of sense, given how much junk gets stashed in $HOME:
config config status.showUntrackedFiles no

Anyway, what was supposed to be a quick post has grown a bit, so I’m going to go ahead and publish it. Let me know if you have any questions or see any errors.

Please note! Your .ssh directory is kind of special in that while you may want to back up your encryption keys, doing so on a site such as Github is a tremendously bad idea.
Just don’t.
Feel free to back up
.ssh/config though, if you’ve customized it.

Leave a Reply