14 Mar 2021  guides

Managing Dotfiles with Git and GNU Stow

Managing dotfiles can be tedious process, considering that configuration files for everything from ansible to zsh are located in many different places in the home directory.

Recently, I discovered GNU Stow  which is an intelligent symbolic linker (written in Perl). From Stow’s manpage, it’s official description is “a symlink farm manager which takes distinct sets of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in a single directory tree”.

While Stow’s intended purpose was not to manage dotfiles, it’s features naturally lended it to being a superior choice in this regard compared to other labor-intensive methods.

Installing GNU Stow

You can download releases for GNU Stow from http://ftp.gnu.org/gnu/stow  and for this guide I’ll be using the latest version which is 2.3.1 at the time of writing. The first step is to download and extract stow:

wget http://ftp.gnu.org/gnu/stow/stow-latest.tar.gz && tar -xvzf stow-latest.tar.gz

The binary for Stow has to be compiled and does require Perl, but Perl comes pre-packaged on all major distributions these days. The commands to do this are:

cd stow-2.3.1
sudo sh configure && make

If an error message about missing Test::Output module is displayed, this must be installed using CPAN, otherwise the Stow binary won’t work. Use the following commands to achieve this:

cd # go back to $HOME

sudo cpan

# you may be prompted for default settings, choose [yes]

install Test::Output


After installing the Test::Output module, delete the stow-2.3.1 folder and extract the archive again and rerun the build commands:

rm -rf stow-2.3.1

tar -xvzf stow-latest.tar.gz

cd stow-2.3.1

sudo sh configure && make

If all goes well you’ll have a binary called stow in the bin directory. You’ll need to copy this binary to your PATH:

Copy binary to PATH:

sudo cp bin/stow  /usr/bin

If you don’t know where to copy the binary, you can list folders that correspond to the PATH variable using the command echo "$PATH" — the listed directories will be separated by a colon.

Once you’ve copied the binary, verify that Stow works:

stow --version

That command should output the version number of Stow. If an error message is returned that resemlbes the following:

Can't locate Stow.pm in @INC (you may need to install the Stow module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.30.0 /usr/local/share/perl/5.30.0 /usr/lib/x86_64-linux-gnu/perl5/5.30 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.30 /usr/share/perl/5.30 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) at /usr/bin/stow line 463.
BEGIN failed--compilation aborted at /usr/bin/stow line 463.

Then you’ll have to install the Stow Perl module in order to fix this:

sudo cpan install Stow

# and now Stow should execute without dependency errors

stow --version

Configuring Stow

You can read the Stow manpages  for more information, but in a nutshell Stow uses “package” folders whose directory structure correspond to the parent directory that the stow command is run from.

Basically, the workflow involves the following steps:

  1. Create a directory where stow will store (stow) all the symlinks, e.g. ~/stow
  2. Create subdirectories known as “packages” where stow will symlink data, e.g. ~/stow/zsh and ~/stow/git
  3. Understand that whenever Stow is invoked in a directory it will target the parent directory when creating symlinks within packages, e.g. if you run stow zsh in ~/stow then ~/.zshrc will be symlinked to ~/stow/zsh/.zshrc

Real-World Example

To illustrate the above workflow concepts, I’ll show you a basic example of how I use Stow to keep a few configuration files stored in one place. These are some of the files that I’ll want to manage centrally:


I’m going to keep all of them in a directory called stow in my home folder. Within this directory, I’ll create subdirectories that are known as Stow “packages” and can be given any name, as they exist for organizational purposes only. Below are the “package” folders that I created:

mkdir ~/stow
cd ~/stow

mkdir audacious
mkdir gnome
mkdir git
mkdir zsh

Next, I’ll move (not copy) the files from their original directories into the central location.

Apart from moving and not copying, the most important step is to match the structure of the parent directory transparently.

To illustrate this, below are the commands that I used:

cd ~/stow

mkdir audacious/.config/audacious
mkdir gnome/.config

mv ~/.config/audacious/config  ~/stow/audacious/.config/audacious
mv ~/.config/extensions-sync.json  ~/stow/gnome/.config/extensions-sync.json
mv ~/.gitconfig  ~/stow/git/.gitconfig
mv ~/.zshrc  ~/stow/zsh/.zshrc

And, the final locations of the files that were moved:


As you can see, the directories and files in each “package” directory are mapped as if they were children of the home (~/) parent directory.

Now that the files and directories are in place, the next step is to invoke the stow command from within the ~/stow directory, specifying each “package” directory by name in order to create the symbolic links:

cd ~/stow

stow audacious
stow gnome
stow git
stow zsh

After those commands are run, the old locations of the files that were copied now become symbolic links to their mapped location in the ~/stow directory, within their respective “package” subdirectories.

If I run an l command in my home directory for example, the .zshrc file will be shown with the syntax .zshrc -> stow/zsh/.zshrc.

Version Control with Git

Now that my files are in a central location, it’s trivial to schedule a backup job to archive and upload Stow’s directory for safekeeping. It’s also a straightforward option to use Git to keep track of changes, for example:

cd ~/stow

git init
git remote add origin https://gitlab.com/oedmarap/stow.git
git add --all
git commit -m "GNU Stow is awesome!"
git push -u origin master

Webmentions & Comments

Copyright © Paramdeo Singh. Built with Jekyll and ❤ in Guyana. All Rights Reserved.

Last Site Build on Tue, 13 Apr 2021 16:55:04 -0400

1MB Club Badge