Manage Your Dotfiles Like a Superhero
This post is also a YouTube video! Check it out here.
I'm going to assume you are reading this because:
-
you have one or more dotfiles lying around and you don't know how to organize them.
-
you heard that you can save dotfiles for later use in case your computer explodes or something.
-
you've seen other people's dotfiles on GitHub, and they look so damn cool.
-
you like superheros.
The thing about programming, or anything you try to teach yourself for that matter, is that when you start, you don't know what you don't know.
Dotfiles are a perfect example. You don't know about them until either someone
tells you they are thing, or you fall down a rabbit hole and find yourself
setting the $PATH
variable in your .bashrc
.
Eventually you go from not knowing about dotfiles, to really caring about them. You realize just how essential they are for your workflow. How having a good relationship with them can accelerate your ability to solve problems.
And most important, you realize just how picky you really are, and how your dotfiles are a perfect outlet for such pickiness.
What you're probably doing
If you are not managing your dotfiles in any way, then they are probably strewn
about your home directory. You probably have a file for your shell, say a
.bashrc
or a .zshrc
. You probably have a .vimrc
or something like that.
You might even have a .gitconfig
.
In this scenario your dotfiles are ephemeral. They exist as long as your machine exists. And when your machine goes down for the long nap, so will your dotfiles. You don't want to be that girl or guy setting up a new machine from scratch every single time. It sucks. I've been there. Avoid at all costs!
What you should be doing
Your dotfiles should be stored in such a way that you can access them anywhere
at any time. If something happens to your current machine, or you need to setup
another one, you're a git clone
away from an identical development
environment. That is the dream.
You may have come across resources like Atlassian's How to store dotfiles, or Yet Another Dotfiles Manager. I've tried them both. They work, but they're a little involved.
Instead I recommend you use GNU Stow, a utility that lets you symlink files from different parts of your machine and make them appear as if they are installed in the same place. This will make sense soon.
How to do it
Step one is to create a .dotfiles
directory in your home directory. For
example, let's say Alice's home directory is:
/Users/alice
Alice will create her .dotfiles
directory here:
mkdir /Users/alice/.dotfiles
Next, you'll need to get your hands on the stow
package. Luckily it is a
pretty ubiquitous piece of software, so most package managers should make it
available to you. For MacOS users you can use
homebrew
:
brew install stow
The third step is to move existing dotfiles to the .dotfiles
directory, and/or
create new ones depending on your scenario. This step requires some explanation
of how stow
works.
Understanding stow
stow
has a few key concepts that, when understood, should make this entire
process a breeze. These concepts can be found in the
Terminology documentation:
- A package is a folder containing related dotfiles.
- A stow directory is a folder containing one or more packages.
- A target directory is the location in which a package's contents will be symlinked.
What this will look like in practice is:
target-directory
├── stow-directory
│ ├── package-1
│ │ └── .dotfile-1
│ ├── package-2
│ │ └── .dotfile-2
│ └── .dotfile-3
Let's use Alice again as an example. Alice's .dotfiles
directory is located
at:
/Users/alice/.dotfiles
The .dotfiles
directory is her stow directory. Currently it's empty. She
will need to create one or more packages within it to house her dotfiles.
Alice has the following dotfiles:
.bashrc
.vimrc
.gitconfig
Since each file belongs to a different "package", bash
, vim
and git
respectively, Alice will create a package for each:
mkdir bash
mkdir vim
mkdir git
Now, Alice can move each dotfile into their respective package directory. As a
result, the .dotfiles
directory will look like this:
.dotfiles
├── bash
│ └── .bashrc
├── vim
│ └── .vimrc
└── git
└── .gitconfig
.dotfiles
is the stow directory. bash
, vim
and git
are package
directories. Their contents are the dotfiles themselves. Now, all that's left is
to use the stow
command to target each package inside of the stow directory:
stow bash
stow vim
stow git
The commands above will symlink the contents of each package to the target directory.
But what is the target directory?
With stow
, the target directory is one directory above the stow directory. In
Alice's case, if her stow directory is:
/Users/alice/.dotfiles
Then her target directory is:
/Users/alice
Let me explain these symlinks a little bit better. When you run
stow <package>
, the stow
utility will create the least amount of symlinks
necessary to mirror the contents of the package to your target directory. If
your package had a single dotfile, stow
will create a single symlink:
/Users/alice
├── .dotfiles
│ ├── vim
│ │ └── .vimrc
├── .vimrc -> .dotfiles/vim/.vimrc
The arrow (->) represents a symlinked file pointing to its original source.
This might seem obvious to some, but what happens if you have a subfolder within
a package? For instance, it's common for Vim users to not only have a .vimrc
file in their home directory, but also a .vim
directory with scripts and
plugins. How would you stow that?
/Users/alice
├── .dotfiles
│ ├── vim
│ └── .vimrc
│ └── .vim
| └── script.vim
├── .vimrc -> .dotfiles/vim/.vimrc
├── .vim -> .dotfiles/vim/.vim
| └── script.vim
This gives you a ton of flexibilty, because very often command line tools like
git
and vim
use more than dotfiles stored in the home directory. They have
their own directories and subdirectories, and being able to model this out
within a package is really powerful.
Once you can visualize how stow
mirrors a package's contents to the target
directory, structuring your .dotfiles
directory will be easy peezy lemon
squeezy.
One more example
The last example I'll give should really drive stow
symlinks home. Let's say
Alice is a vim
user, but instead of traditional vim she uses
Neovim. Neovim doesn't use a .vimrc
file in her home
directory, but instead an init.vim
file which is located at:
/Users/alice/.config/nvim/init.vim
The init.vim
file is heavily nested. If Alice wants to stow this, how would
she do it?
It's pretty simple actually. In her .dotfiles
directory she creates a package
called nvim
. It would look like this:
.dotfiles
├── nvim
│ ├── .config
│ └── nvim
| └── init.vim
The folder structure starting from .dotfiles/nvim
is the same structure that
is expected in Alice's home directory. Now she just needs to stow the nvim
directory:
stow nvim
And the result will be:
/Users/alice
├── .dotfiles
│ ├── nvim
│ └── .config
| └── nvim
| └── init.vim
├── .config -> .dotfiles/nvim/.config
Storing dotfiles in git
The .dotfiles
directory is just like any old directory on your machine. You
can git init
and git push
up to your git repository of choice:
git init
git add .
git commit -m "storing initial dotfiles"
git push
You can add a .gitignore
file to prevent unwanted configs from being pushed to
your remote repository, and a README
to illustrate how it all works.
Conclusion
I hope this has helped you gain a better understanding of how to manage your dotfiles. This is by no means the only way to do it, but it is my preferred way.
You can really take this as far as you want to go. For instance, I am planning
on creating an install
script that can be run on new machines. This script
will install a bunch of dependencies like node
and the like, stow my dotfiles
so the symlinks are created, etc.
If you want to take a peak at my dotfiles, you can see them here. They're still a work in progress, but it should be a good resource for learning.
If you made it to the end, thanks for reading! Have questions? See a mistake? Just want to say hi? Reach out to me on [Twitter].