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.
What is GNU Stow?
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.
A Simple Example
Let’s get started with a simple example. You probably already have a
.gitconfig in your home directory, so let’s start by
Create a new home for your
$ mkdir -p ~/dotfiles/git
Now move your
$ mv ~/.gitconfig ~/dotfiles/git
Get it back using
$ 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).
How It Works
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
barfile will appear 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
COPYING. You can look at the GNU Stow documentation for the default ignore list.
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
stow foo will create links to both the file
foofile and the directory
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
qux is a link to the directory
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
qux is already a symlink to
foo/qux. Where will
stow does the right thing:
stow-example$ stow bar srbaker@carbon:~/tmp/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
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
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
bar which both have a file called
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.
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
foo directory containing
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 VCS 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
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.