- Published on
Tutorial 2: How to manage dotfiles using GNU Stow
- Authors

- Name
- Shibi Suriya
Now that you know what dotfiles are, the benefits you can experience by managing them & how to manage them by moving them into a git repository & manually symlinking them to their original location using the ln command, we will be improving our existing setup from tutorial 1 using GNU Stow - a neat tool that automates the process of creating & removing symlinks.
We will first revisit our existing setup, learn what GNU Stow is, its quirks and then integrate it into our existing setup.
Existing setup
We have created a git repo at ~/Desktop/dotfiles using the git init command & moved all of our dotfiles into it. We are then symlinking each dotfile to its original location using the ln command. For example, the bash shell looks for .bashrc (bash's configuration file) at ~ (user's home directory - /user/<username>).
cd ~/Desktop/dotfiles
ls -a
. .. .bashrc keybindings.json
ln -s .bashrc ~/
Since we have multiple dotfiles in our git repository, typing & executing the ln command for each dotfile is annoying! So we have written the ln commands in a shell script - symlink.zsh.
To symlink all of your dotfiles to their original location,
./symlink.zsh
We will be replacing symlink.zsh (a shell script that symlinks each dotfile to its original location) with GNU Stow in this tutorial.
Tutorial
Install GNU Stow,
On macOS, using Homebrew,
brew install stowOn Debian/Ubuntu based distros, using apt,
sudo apt install stow
Assuming your dotfiles repo is located at ~/Desktop/dotfiles on your computer,
Let's remove
symlink.zsh- a shell script that symlinks our dotfiles to their original location. As mentioned earlier we will be replacingsymlink.zshwith GNU Stow.cd ~/Desktop/dotfiles rm symlink.zshCreate Stow packages.
Let's create Stow packages first, I will explain what Stow packages are later.
cd ~/Desktop/dotfiles ls -a . .. .bashrc keybindings.json kitty.confWe have 3 dotfiles in our repo,
.bashrcis bash shell's configuration file, bash looks for.bashrcat~- the user's home directory (/Users/<username>).kitty.confis kitty's (a terminal emulator) configuration file, kitty looks forkitty.confat~/.config/kitty/.Visual Studio Code stores its keyboard shortcuts in
keybindings.json, it looks forkeybindings.jsonat~/Library/Application Support/Code/Userin macOS and at~/.config/Code/Userin Linux.
To create stow packages,
Create a directory named
bashin your dotfiles repo & move.bashrcinto it.Create a directory named
vscodein your dotfiles repo & movekeybindings.jsoninto it.Create a directory named
kittyin your dotfiles repo, inside thekittydirectory create a directory named.config, inside the.configdirectory create a directory namedkitty& movekitty.confinto it.
Your dotfiles repo's directory structure should look something like this,
~/Desktop/dotfiles ❯ tree -a . ├── bash │ └── .bashrc ├── kitty │ └── .config │ └── kitty │ └── kitty.conf └── vscode └── keybindings.json 6 directories, 3 filesThe root directory of your dotfiles repo is called the 'Stow directory' (
~/Desktop/dotfiles) and its subdirectoriesbash,vscode&kittyare called 'Stow packages'.We have 3 stow packages now, the command to stow (to create symlinks) packages to their target location is,
stow <package-name> -t <target-directory>Let's first stow the packages
bash&kitty,~/Desktop/dotfiles ❯ tree -a kitty bash kitty └── .config └── kitty └── kitty.conf bash └── .bashrc 4 directories, 2 filesstow bash kitty -t ~Observe the directory structure of the stow packages & the locations where the symlinks are created,
.bashrcwill be symlinked to~/.bashrc.kitty.confwill be symlinked to~/.config/kitty/kitty.conf.
GNU Stow uses the directory structure of the stow package & the path supplied to
-tto determine where the symlinks needs to be created.So, if your stow package's directory structure is,
~/Desktop/dotfiles ❮ tree a a └── b └── c └── d.txt 3 directories, 1 fileAnd the path supplied to
-tis~/Desktop, then the filed.txtwill be symlinked to~/Desktop/a/b/c/d.txt.Stow the
vscodepackage,stow vscode -t "~/Library/Application Support/Code/User" # In macOS. stow vscode -t "~/.config/Code/User" # In Linux.As mentioned earlier Visual Studio Code expects
keybindings.jsonat~/Library/Application Support/Code/Userin macOS & at~/.config/Code/Userin Linux.
The confusing parts in most of the popular tutorial on 'How to manage dotfiles using GNU Stow?'
In this section we will explore some of the bad practices used in most of the popular tutorial on 'How to manage dotfiles using GNU Stow?'.
Users that keep all of their dotfiles in the dotfiles repo's root directory instead of arranging them as Stow packages
You can cd into a stow package & run stow . -t <target-directory> to stow a Stow package instead of mentioning the Stow package's name in the stow command.
So instead of,
cd ~/Desktop/dotfiles # Root directory of the dotfiles git repo.
stow bash -t ~
# Have mentioned the name of the stow
# package in the command itself - bash.
you can use,
cd ~/Desktop/dotfiles/bash # The subdirectory `bash` is a stow package.
stow . -t ~
# GNU Stow assumes that the current working directory is the Stow package.
The point is GNU Stow can't work without Stow packages, most of the popular tutorial on 'How to manage dotfiles using GNU Stow?' available online are confusing because they don't arrange their dotfiles as Stow packages instead they place all of their dotfiles in the root directory of their dotfiles git repo & run the stow . -t ~ command, in this case GNU Stow treats the root directory of the dotfiles repo as the Stow package.
So instead of arranging your dotfiles as Stow packages,
~/Desktop/dotfiles
❯ tree -a kitty bash
kitty
└── .config
└── kitty
└── kitty.conf
bash
└── .bashrc
4 directories, 2 files
You can place all of your dotfiles in the root directory of your dotfiles repo,
❯ tree -a
.
├── .bashrc
└── .config
└── kitty
└── kitty.conf
3 directories, 2 files
& run the stow . -t ~ command,
.bashrc will get symlinked to ~/.bashrc & kitty.conf will get symlinked to ~/.config/kitty/kitty.conf. Some GNU Stow users prefer to mention the target directory in the .stowrc file (placed at dotfiles repo's root directory) instead of mentioning the target directory using the -t option.
The default 'target directory' is the parent directory of the 'Stow directory'. That is, if you run the command stow . from ~/Desktop/dotfiles (the Stow directory in this case) then ~/Desktop is the target directory. Some GNU Stow users prefer to clone their dotfiles repo at ~ so that they don't have to mention the target directory using the -t option!
So, lets move our dotfiles repo from ~/Desktop/dotfiles to ~.
mv ~/Desktop/dotfiles ~
cd ~
stow bash
# No need to mention the target directory using the `-t` option
# because the parent directory is the default target directory (`~` in this case).
The command to unstow packages (to remove the symlinks) is,
stow -D <package-name> -t <target-directory>To unstow
kitty&bash,stow -D kitty bash -t ~To unstow
vscode,stow -D vscode -t "~/Library/Application Support/Code/User" # In macOS. stow -D vscode -t "~/.config/Code/User" # In Linux.Putting it all together.
We have 2 problems,
We can't remember all the stow package names & their target directory.
The target directory of some Stow packages depend on the operating system.
To solve these 2 problems let's write a shell script that determines the computer's operating system & stows/unstows all of the stow packages to their target location.
Create a shell script,
touch stow.zsh chmod +x stow.zshPaste the code given below in
stow.zsh,#!/bin/zsh unstow=false for arg in "$@"; do if [[ "$arg" == "-D" ]]; then unstow=true fi done stow_unstow_package() { local package_name=$1 local target=$2 if [[ "$unstow" == true ]]; then echo "Unstowing package: $package_name" stow -t "$target" -D "$package_name" else echo "Stowing package: $package_name" stow -t "$target" "$package_name" fi } packages=() if [[ "$(uname)" == "Darwin" ]]; then # For macOS packages=("bash" "kitty") vscode_settings_dir="$HOME/Library/Application Support/Code/User" else # For Linux packages=("bash" "kitty") vscode_settings_dir="$HOME/.config/Code/User" fi if [ -d "$vscode_settings_dir" ]; then stow_unstow_package "vscode" "$vscode_settings_dir" fi for package in "${packages[@]}"; do stow_unstow_package "$package" "$HOME" doneTo stow your dotfiles,
./stow.zshTo unstow your dotfiles,
./stow.zsh -D
You can use a bare git repository to manage your dotfiles if you don't prefer creating symlinks.