Managing Dotfiles with Git and GNU Stow

A practical dotfiles workflow with Git and GNU Stow, including safe adoption, host-specific state, updates, and rollback.

Herdr agent workspace managed as part of the dotfiles setup

Dotfiles begin as a few hidden files in a home directory. Over time they become a record of how a machine works: shell behavior, editor settings, Git defaults, terminal themes, and small automation scripts.

Git gives that history a home. GNU Stow turns files in a repository into symlinks at the paths applications expect.

The basic model

Suppose a repository contains:

.dotfiles/
└── .config/
├── fish/
└── nvim/

Running Stow from the repository creates links under the target directory, normally the parent of the repository. The files stay versioned in .dotfiles; applications read them through ~/.config.

My repository uses a single package rooted at ~/.dotfiles:

Terminal window
cd ~/.dotfiles
stow -Rv .

The -R option restows the package, and -v shows what changes. Before applying an unfamiliar repository, use simulation mode:

Terminal window
stow -nRv .

Read the output. Do not blindly replace existing configuration.

Adopt existing files carefully

If ~/.config/nvim already exists, Stow may report a conflict. Back it up first:

Terminal window
mv ~/.config/nvim ~/.config/nvim.before-stow

Then restow and compare the old files deliberately. GNU Stow also has an --adopt option, but it moves existing files into the package. That is powerful and easy to misuse. Run it only with a clean Git worktree so the resulting diff shows exactly what was adopted.

Keep local state out of Git

A dotfiles repository should describe configuration, not every changing value on a machine.

My current Hue theme switcher stores the active mood under ~/.local/state/hue-theme/current. Neovim language profile selection also lives in its state directory. Secrets, caches, plugin downloads, generated histories, and machine credentials stay outside the repository.

For host differences, prefer small OS-specific files or environment variables over copying the entire configuration. The repository currently separates Fish configuration for macOS, Linux, and Windows.

Update as a transaction

A calm update loop looks like this:

Terminal window
cd ~/.dotfiles
git status --short
git pull --ff-only
stow -nRv .
stow -Rv .

Check the worktree before pulling because local configuration is easy to edit accidentally through a symlink. Commit intentional changes first.

My stow_setup.sh also verifies shared agent rules, checks generated Hue integrations, rebuilds Bat’s theme cache when needed, and configures Tide. Those steps are specific to this repository; plain Stow does not manage generated assets or application caches.

Rollback stays understandable

To remove the managed links without deleting the repository:

Terminal window
cd ~/.dotfiles
stow -Dv .

Git can restore an earlier configuration, while Stow can recreate its links. Keeping those two responsibilities separate is the reason the setup remains easy to reason about.

Source: my current dotfiles, GNU Stow manual.

Read more

All posts
  1. Neovim open beside terminal tools in the dotfiles desktop setup
  2. Lazy.nvim plugin manager using the shared Huế theme
  3. Abstract paths converging around a luminous Astra core
  4. Glowing geometric rules transforming into structured JSON objects