Move your dotfiles to version control

Back up or sync your custom configurations across your systems by sharing dotfiles on GitLab or GitHub.
450 readers like this.
Filing papers and documents

There is something truly exciting about customizing your operating system through the collection of hidden files we call dotfiles. In What a Shell Dotfile Can Do For You, H. "Waldo" Grunenwald goes into excellent detail about the why and how of setting up your dotfiles. Let's dig into the why and how of sharing them.

What's a dotfile?

"Dotfiles" is a common term for all the configuration files we have floating around our machines. These files usually start with a . at the beginning of the filename, like .gitconfig, and operating systems often hide them by default. For example, when I use ls -a on MacOS, it shows all the lovely dotfiles that would otherwise not be in the output.

dotfiles on master
➜ ls
README.md  Rakefile   bin    	misc   	profiles   zsh-custom

dotfiles on master
➜ ls -a
.          	.gitignore 	.oh-my-zsh 	README.md  	zsh-custom
..         	.gitmodules	.tmux      	Rakefile
.gemrc     	.global_ignore .vimrc     	bin
.git       	.gvimrc    	.zlogin    	misc
.gitconfig 	.maid      	.zshrc     	profiles

If I take a look at one, .gitconfig, which I use for Git configuration, I see a ton of customization. I have account information, terminal color preferences, and tons of aliases that make my command-line interface feel like mine. Here's a snippet from the [alias] block:

87   # Show the diff between the latest commit and the current state
88   d = !"git diff-index --quiet HEAD -- || clear; git --no-pager diff --patch-with-stat"
89
90   # `git di $number` shows the diff between the state `$number` revisions ago and the current state
91   di = !"d() { git diff --patch-with-stat HEAD~$1; }; git diff-index --quiet HEAD -- || clear; d"
92
93   # Pull in remote changes for the current repository and all its submodules
94   p = !"git pull; git submodule foreach git pull origin master"
95
96   # Checkout a pull request from origin (of a github repository)
97   pr = !"pr() { git fetch origin pull/$1/head:pr-$1; git checkout pr-$1; }; pr"

Since my .gitconfig has over 200 lines of customization, I have no interest in rewriting it on every new computer or system I use, and neither does anyone else. This is one reason sharing dotfiles has become more and more popular, especially with the rise of the social coding site GitHub. The canonical article advocating for sharing dotfiles is Zach Holman's Dotfiles Are Meant to Be Forked from 2008. The premise is true to this day: I want to share them, with myself, with those new to dotfiles, and with those who have taught me so much by sharing their customizations.

Sharing dotfiles

Many of us have multiple systems or know hard drives are fickle enough that we want to back up our carefully curated customizations. How do we keep these wonderful files in sync across environments?

My favorite answer is distributed version control, preferably a service that will handle the heavy lifting for me. I regularly use GitHub and continue to enjoy GitLab as I get more experienced with it. Either one is a perfect place to share your information. To set yourself up:

  1. Sign into your preferred Git-based service.
  2. Create a repository called "dotfiles." (Make it public! Sharing is caring.)
  3. Clone it to your local environment.*
  4. Copy your dotfiles into the folder.
  5. Symbolically link (symlink) them back to their target folder (most often $HOME).
  6. Push them to the remote repository.

* You may need to set up your Git configuration commands to clone the repository. Both GitHub and GitLab will prompt you with the commands to run.

Creating a new project on GitLab

Step 4 above is the crux of this effort and can be a bit tricky. Whether you use a script or do it by hand, the workflow is to symlink from your dotfiles folder to the dotfiles destination so that any updates to your dotfiles are easily pushed to the remote repository. To do this for my .gitconfig file, I would enter:

$ cd dotfiles/
# Be sure to adjust the user to your user, which you can
# find in the path or by running the following: 
$ echo $USER
mbbroberg
$ pwd
/Users/mbbroberg/Develop/dotfiles
# Be sure to use an absolute path
$ ln -nfs /Users/mbbroberg/Develop/.gitconfig /Users/mbbroberg/.gitconfig

The flags added to the symlinking command offer a few additional benefits:

  • -s creates a symbolic link instead of a hard link
  • -f continues with other symlinking when an error occurs (not needed here, but useful in loops)
  • -n avoids symlinking a symlink (same as -h for other versions of ln)

You can review the IEEE and Open Group specification of ln and the version on MacOS 10.14.3 if you want to dig deeper into the available parameters. I had to look up these flags since I pulled them from someone else's dotfiles.

You can also make updating simpler with a little additional code, like the Rakefile I forked from Brad Parbs. Alternatively, you can keep it incredibly simple, as Jeff Geerling does in his dotfiles. He symlinks files using this Ansible playbook. Keeping everything in sync at this point is easy: you can cron job or occasionally git push from your dotfiles folder.

Quick aside: What not to share

Before we move on, it is worth noting what you should not add to a shared dotfile repository—even if it starts with a dot. Anything that is a security risk, like files in your .ssh/ folder, is not a good choice to share using this method. Be sure to double-check your configuration files before publishing them online and triple-check that no API tokens are in your files.

Where should I start?

If Git is new to you, my article about the terminology and a cheat sheet of my most frequently used commands should help you get going.

There are other incredible resources to help you get started with dotfiles. Years ago, I came across dotfiles.github.io and continue to go back to it for a broader look at what people are doing. There is a lot of tribal knowledge hidden in other people's dotfiles. Take the time to scroll through some and don't be shy about adding them to your own.

I hope this will get you started on the joy of having consistent dotfiles across your computers.

What's your favorite dotfile trick? Add a comment or tweet me @mbbroberg.

I'm happiest at a microphone
Matt was an EMC storage expert, VMware vExpert, and former fan of other proprietary technologies. He now focuses on open source and DevRel adoption.

7 Comments

An interesting article, Matt, thanks! I was glad to see "what not to share".

While most of my dot files hold no secrets, as you note some do - .ssh, .gnupg, .local/share among others... could be some others. Thinking about this, my dot files are kind of like my sock drawer - plenty of serviceable socks there, not sure I would want to share them! Anyway a neat idea.

Instead of linking your dotfiles, give YADM a try: https://yadm.io

It wraps the git command and keeps the actual git repository in a subdirectory.

Check out https://github.com/twpayne/chezmoi. It allows you to store secrets securely, too. Disclaimer: I'm the author of chezmoi.

Instead of keeping on monolithic and overloaded repository, it would make more sense to split the repos up using vcsh: https://github.com/RichiH/vcsh

This has the added benefit of allowing you to e.g. split up work and personal SSH config etc.

on my mac, when I tried to symlink my .gitconfig using `ln -nfs .gitconfig $HOME/.gitconfig` from my dotfiles directory, I ended up with `/Users/bjorn/.gitconfig -> ./.gitconfig` in my home directory, which is actually a link to itself, and broke git entirely. Using a full path (e.g. `ln -nfs ~/dotfiles/.gitconfig ~/.gitconfig`) did the right thing and gave me `/Users/bjorn/.gitconfig -> /Users/bjorn/dotfiles/.gitconfig`

I think there is a bug here due to relative pathways when we need absolute. We want both paths to be absolute like you show above. I'll correct this now -- thanks for bringing it up!

In reply to by Bjorn (not verified)

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.