GNU Stow repo with git submodules

GNU Stow is a tool to manage symlinks from a single directory. That makes the tool a perfect candidte to keep configuration files (or dotfiles) in a VCS repository, such as a Git repo. This is where git submodules come in handy: oftentimes people maintain configurations as standalone repositories, e.g. configuration for Emacs. They can be plugged into a stow directory as a submodule.

Example stow directory

Let’s create a stow directory and initialise a git repository:

$ mkdir stow-example
$ cd stow-example/
$ git init
Initialized empty Git repository in /home/pilosus/git/stow-example/.git/

Now, let’s create a stow package for Emacs - a directory in a stow directory containing all the necessary files and directories to be installed into a target directory (which is $HOME or ~/). The package resides under emacs directory and will contain a .emacs.d directory as a git submodule:

$ mkdir emacs
$ cd emacs/
$ git submodule add git@github.com:pilosus/dot-emacs.git .emacs.d
Cloning into '/home/pilosus/git/stow-example/emacs/.emacs.d'...

Let’s add one more stow package as a git submodule, a ~/.clojure directory that contains tools and basic aliases for Clojure language:

$ cd ..
$ mkdir clojure
$ cd clojure/
$ git submodule add git@github.com:pilosus/dot-clojure.git .clojure
Cloning into '/home/pilosus/git/stow-example/clojure/.clojure'...

So now we have two stow packages, emacs and clojure installed as git submodules:

$ tree -R -a -L 2 -I '.git'
.
├── clojure
│   └── .clojure
├── emacs
│   └── .emacs.d
└── .gitmodules

Installing packages with Stow is as easy as this:

$ stow --target=$HOME emacs
$ stow --target=$HOME clojure

For bulk install */ can be used:

$ stow --target=$HOME --stow */

Stowing a package will create symlinks in the target directory ($HOME in this case) that reflect the hierarchy of files and directories in the package. E.g. for clojure package the hierarcy is the following:

$ cd clojure
$ tree -R -a
.
└── .clojure
    ├── deps.edn
    ├── .git
    ├── .gitignore
    ├── LICENSE
    ├── README.md
    └── tools
        ├── antq.edn
        ├── clj-watson.edn
        ├── new.edn
        └── tools.edn

It’s symlinked to a home directory that we used as a target directory when stowing:

$ ls -lth ~/.clojure
lrwxrwxrwx. 1 pilosus pilosus 29 Sep  5 15:51 /home/pilosus/.clojure -> git/stow-example/clojure/.clojure

Using git modules allows to support a standalone repository for configuration files so that they can be kept separately from the stow repo itself. This is a good solution given that a stow repo can contain sensitive configuration files such as inventory for Ansible or other authentication information.

Git submodules also allow to do all the normal git things such as checking out a certain commit or tag, pushing changes upstream, etc. That makes the package management easy and flexible.

My personal dotfiles

Here’s dotfiles, a repo with my personal config files organised with GNU/Stow.