GNU Stow
Manage dotfiles as symlink farms — one repo, multiple machines, instant propagation.
Basic Operations
Stow a package — creates symlinks from package dir into target
cd ~/atelier/_projects/personal/dots-quantum && stow -t ~ hyprland
The package directory structure mirrors the target. hyprland/.config/hypr/hyprland.conf becomes ~/.config/hypr/hyprland.conf.
Unstow a package — removes symlinks
cd ~/atelier/_projects/personal/dots-quantum && stow -D -t ~ hyprland
Restow — unstow then stow (use after restructuring a package)
cd ~/atelier/_projects/personal/dots-quantum && stow -R -t ~ hyprland
Dry run — preview what stow would do without making changes
cd ~/atelier/_projects/personal/dots-quantum && stow -n -v -t ~ hyprland
-n simulates, -v shows each symlink operation.
Stow Multiple Packages
Stow several packages at once
cd ~/atelier/_projects/personal/dots-quantum && stow -t ~ tmux nvim git bash
Stow all packages in a directory (careful — includes non-stow dirs)
cd ~/atelier/_projects/personal/dots-quantum && stow -t ~ */
Some directories are NOT stow packages (e.g., hosts/, gpg/). Stow them individually.
|
Verification
Verify a package is stowed — symlink should point into dots-quantum
ls -la ~/.config/hypr/hyprland.conf
Check which packages are stowed vs missing
cd ~/atelier/_projects/personal/dots-quantum
for pkg in */; do
pkg="${pkg%/}"
[[ "$pkg" == "hosts" || "$pkg" == "gpg" ]] && continue
sample=$(find "$pkg" -type f | head -1)
[[ -z "$sample" ]] && continue
target="$HOME/${sample#$pkg/}"
if [[ -L "$target" ]]; then
echo "✓ $pkg"
else
echo "✗ $pkg — needs: stow -t ~ $pkg"
fi
done
Find broken symlinks in home directory
find ~ -maxdepth 4 -xtype l 2>/dev/null | head -20
Conflict Resolution
Stow refuses to overwrite existing files — adopt them first
stow --adopt -t ~ tmux
--adopt moves the existing file INTO the stow package, replacing it with a symlink. Then git diff in dots-quantum shows what changed.
--adopt overwrites the package file with the target’s version. Review the diff before committing.
|
Force stow when target exists as a directory (not a symlink)
# Remove the real directory first, then stow
rm -rf ~/.config/nvim
cd ~/atelier/_projects/personal/dots-quantum && stow -t ~ nvim
Package Structure
Correct package layout — mirrors target directory structure
# Package: hyprland/
# hyprland/.config/hypr/hyprland.conf → ~/.config/hypr/hyprland.conf
# hyprland/.config/hypr/hypridle.conf → ~/.config/hypr/hypridle.conf
tree hyprland/
NOT a stow package — per-host config containers
# hosts/ and gpg/ are NOT stow packages
# hosts/<hostname>/env-gpu.conf is manually symlinked:
ln -sf ~/atelier/_projects/personal/dots-quantum/hosts/p16g/env-gpu.conf ~/.config/hypr/env-gpu.conf
dots-quantum Workflow
New machine setup — stow core packages
cd ~/atelier/_projects/personal/dots-quantum
for pkg in bash git tmux nvim hyprland waybar foot claude shell; do
stow -v -t ~ "$pkg"
done
After editing a config — commit in dots-quantum, push
cd ~/atelier/_projects/personal/dots-quantum
git add -A && git commit -m "feat(hyprland): add Zoom window rules"
git push origin main
Changes are live immediately — the symlink means the running config IS the repo file.
Ignoring Files
Create
.stow-local-ignore in a package to exclude files# hyprland/.stow-local-ignore
README.md
\.git
Stow uses Perl regex. Default ignores: .git, CVS, .svn, README.*.
See Also
-
Git Basics — version control for dotfiles
-
Permissions — symlink ownership and mode