Dotfile setups tend to start as a repo full of files plus a shell script
that copies them into $HOME. The copies drift from the repo, the
script grows special cases, and eventually nobody is sure which version
is real.
GNU Stow removes the copy step. One directory per tool, and stow <tool>
symlinks its contents into the parent directory. Editing the repo edits
the live config, because they are the same file.
The layout
Stow’s model is simple once you see it: each package directory’s
contents are mirrored into the parent of the stow directory. So with the
repo at ~/.dotfiles, the target is ~:
~/.dotfiles/
├── git/
│ └── .gitconfig → ~/.gitconfig
├── zsh/
│ ├── .zshrc → ~/.zshrc
│ └── .zsh/ → ~/.zsh/
└── nvim/
└── .config/nvim/ → ~/.config/nvim/
Note the intermediate directories are part of the package. nvim/.config/nvim/
is what makes the symlink land at ~/.config/nvim. Then:
cd ~/.dotfiles
stow git zsh nvim
Adding a new tool is a directory and one command. Removing one is
stow -D <tool> — the symlinks go, the repo keeps the config.
Conflicts
The one thing that will bite you: Stow refuses to overwrite a real file.
WARNING! stowing git would cause conflicts:
* existing target is neither a link nor a directory: .gitconfig
All operations aborted.
That is Stow declining to destroy a config you may not have copied anywhere. Two ways out, and the choice matters:
stow --adopt git— moves the existing~/.gitconfiginto the package, then links it back. Your live config wins, and the repo now contains it. Checkgit diffimmediately after: adopt overwrites the repo’s version, so this is exactly when you find out whether the two had diverged.- Delete the target first —
rm ~/.gitconfig && stow git. The repo’s version wins. Only do this once you have confirmed the live file has nothing you want.
Both are fine. Picking the wrong one silently is not, so look at the file before deciding.
Folding, and the surprise it causes
When a target directory does not exist, Stow symlinks the directory
rather than creating it and linking each file — this is called folding.
It keeps the tree tidy, but it means ~/.config/nvim is a link to the
package, so anything written into it by another program lands in your
repo.
For config directories where a tool writes its own state alongside your
config, that is not what you want. stow --no-folding <tool> creates
real directories and links only the files.
Why it holds up
Deploying config is a solved problem the moment you stop copying files.
A new machine is git clone plus one stow per package. There is no
sync step to forget, no “did I update the repo after fixing this?”
because there is only one file.
The catch is that Stow does exactly one thing: it makes symlinks. It will not install packages, set macOS defaults, or bootstrap a machine. That is a feature — those belong in their own script, where you can read them — but it does mean Stow is the deployment layer of a dotfiles setup, not the whole thing.