Managing dotfiles can be tricky when you have multiple machines. Fortunately, there’s a beautifully simple tool that makes this easy: GNU Stow. If you have dotfiles that you want to share across multiple machines, or manage revisions, GNU Stow will make it easy.
Here you’ll learn about how to use GNU Stow to manage your dotfiles. I’ll start with a simple example, explain in detail how it works, and finally demonstrate a recommended workflow for using GNU Stow to manage your own dotfiles.
From the website:
GNU Stow is a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in the same place.
While GNU Stow is an excellent tool for package management, its real beauty lies in the simple design that makes it adaptable to other uses. Despite not being designed specifically for dotfiles, it is a perfect tool for the job.
GNU Stow is available nearly everywhere, and can probably be installed with your favourite package manager as the package stow. I use it on OpenBSD, FreeBSD, Debian, and macOS (available via MacPorts and HomeBrew).
Note that GNU Stow does not work on Windows. There exist some alternatives such as Eric Subach’s Stow Lite, but I do not have enough experience with such alternatives to comment on their suitability.
Let’s get started with a simple example. You probably already have a .gitconfig in your home directory, so let’s start by stow-ing that.
Create a new home for your git-related dotfiles:
$ mkdir -p ~/dotfiles/git
Now move your .gitconfig
there:
$ mv ~/.gitconfig ~/dotfiles/git
Get it back using stow:
$ cd ~/dotfiles
$ stow git
Behold, your git config:
$ cd ~
$ ls -l .gitconfig
lrwxr-xr-x 1 srbaker srbaker 23 Jun 13 16:00 .gitconfig -> dotfiles/git/.gitconfig
If you’re not quite sure what running stow will do, you can find out by enabling verbose output, and telling it not to perform operations: stow --verbose --no
and it will display what it would have done, without making any changes.
By now you have all of the knowledge necessary to manage all of your dotfiles using stow using the pattern from above. In the following sections, you’ll learn details about how it works. If you would like to get started immediately, you can just skip to the recommended workflow section at the end and learn the details later (if needed).
The basic premise of GNU Stow is that it takes files in multiple directories, and manages symlinks to make them appear in one directory.
When invoked with a directory as an argument, stow simply changes into that directory, and creates a symlink for everything it contains to the parent directory. When invoked with many directories as arguments, it does this for each directory listed.
Take this example:
$ pwd
/home/srbaker/stow-example/stow
$ ls -R
bar foo
./bar:
barfile
./foo:
foofile
If we run stow bar
, we will see that barfile
appears the in the parent directory.
$ ls ..
barfile stow
This is everything you need to know to make full use of stow. By default, GNU Stow is smart enough to do the right thing with files that you probably don’t want included, handling directories, and identifying conflicts.
GNU Stow can ignore files that you don’t wish to have stow
-ed. You can tell stow
to ignore files on the command line using the --ignore
argument, and supplying a regular expression. If we want to use stow, but ignore files named foo we could do so like so:
stow --ignore=foo *
Likewise, if we wanted to ignore all files starting with the word foo we could use a regular expression: stow --ignore=foo.* *
.
It would be cumbersome to add the --ignore
argument to every single run. To solve this, stow
will read an ignore list from .stow-local-ignore
in the current directory, as well as a global .stow-global-ignore
in your home directory.
By default, when neither a local nor global ignore list exists, GNU Stow will use its default ignore list which includes entries for version control related files, emacs backup and autosave files, and README
, LICENSE
, and COPYING
. You can look at the GNU Stow documentation for the default ignore list.
When stow
-ing files, directories will be handled as well. I use this trick for managing my ~/bin
directory, which you will see in the recommended workflow section below.
Consider that you have a directory foo that contains a file foofile and a directory qux which itself contains a file fooquxfile, as demonstrated here:
stow-example$ find foo
foo
foo/foofile
foo/qux
foo/qux/quxfile
Running stow foo
will create links to both the file foofile
and the directory qux
:
stow-example$ ls -l ..
total 4
lrwxrwxrwx 1 srbaker srbaker 24 Nov 13 16:49 foofile -> stow-example/foo/foofile
lrwxrwxrwx 1 srbaker srbaker 20 Nov 13 16:49 qux -> stow-example/foo/qux
drwxr-xr-x 4 srbaker srbaker 4096 Nov 13 16:42 stow-example
You can see that foofile
is appropriately a link to foo/foofile
and qux
is a link to the directory foo/qux
.
But what if in addition to foo
containing a directory qux
as described above, you also have a directory bar
which has a qux
directory of its own? From the example above, we can see that the stow
-ed qux
is already a symlink to foo/qux
. Where will bar/qux
live?
Fortuantely, stow
does the right thing:
stow-example$ stow bar
stow-example$ ls -l ..
total 8
lrwxrwxrwx 1 srbaker srbaker 24 Nov 13 16:54 barfile -> stow-example/bar/barfile
lrwxrwxrwx 1 srbaker srbaker 24 Nov 13 16:54 foofile -> stow-example/foo/foofile
drwxr-xr-x 2 srbaker srbaker 4096 Nov 13 16:54 qux
drwxr-xr-x 4 srbaker srbaker 4096 Nov 13 16:42 stow-example
The qux
directory that stow
created is now a directory of its own:
stow-example$ ls -l ../qux
total 0
lrwxrwxrwx 1 srbaker srbaker 31 Nov 13 16:54 quxfile -> ../stow-example/foo/qux/quxfile
lrwxrwxrwx 1 srbaker srbaker 32 Nov 13 16:54 quxfile2 -> ../stow-example/bar/qux/quxfile2
When stow
creates a directory and links the contents from multiple sources inside on the second run, it’s called tree folding. stow
has noticed that the directory qux
is in two different sources, and folds them into the same tree. GNU Stow calls the reverse of this operation tree unfolding.
The default beahviour of GNU Stow covers most use cases without even displaying output. In most cases, it just does the right thing. Since files are being linked around the filesystem, it’s possible that stow
will be asked to put two files with the same name in the same place. Consider two source directories foo
and bar
which both have a file called bazfile
inside:
stow-example$ stow *
WARNING! stowing foo would cause conflicts:
* existing target is stowed to a different package: baz => stow-example/bar/baz
All operations aborted.
In this case stow
has recognized that there is a conflict, and refuses to make any changes.
A stow
invocation that would overwrite an existing file also results in a conflict warning that aborts all operations. Consider that our parent directory (the target) already has a file called foofile
and we try to stow the foo
directory containing foofile
:
stow-example$ stow foo
WARNING! stowing foo would cause conflicts:
* existing target is neither a link nor a directory: foofile
All operations aborted.
This very careful default behaviour means that running stow
is always a completely safe operation: no files will be moved or overwritten unless it can be done non-desctructively.
Using GNU Stow to manage your dotfiles is made infinitely better by storing your dotfiles in version control. By doing this, you will have history of your edits, and you can use existing tooling to share your dotfiles across machines.
If you keep your dotfiles in a git repository, setting up a new machine is as easy as:
git clone my-git-host:dotfiles.git && cd dotfiles && stow *
The machine you’re sitting at right now probably already has your preferred dotfiles, so you can get started immediately.
$ git init dotfiles
Now, for each program you have dotfiles for, move them into a directory inside your dotfiles working copy. For example, if you want to stow your git and bash dotfiles, you might do the following:
$ mkdir git
$ mv .git* dotfiles/git
$ mv .bash_profile .bashrc .bash_aliases dotfiles/bash
$ cd dotfiles && stow *
Whenever you make changes to your dotfiles in place, you will need to remember to commit those changes. If you add new files, you will have to remember to re-run stow
: cd ~/dotfiles && stow *
. If you’ve removed files since the last run, you should re-stow
: cd ~/dotfiles && stow -R *
.
Finally, if you decide that you would like to un-stow
all of your dotfiles for whatever reason, you can cd ~/dotfiles && stow -D
.
I hope this introduction to dotfile management with GNU Stow has been helpful. If you find any errors, or have any questions, I am more than happy to respond to email.