IAnswerable

Manage Your Dotfiles So a New Machine Takes Minutes

13 Apr 2026

The standard advice is to symlink dotfiles from a repo into your home directory with a script. That works until the script itself breaks, or you forget which files it manages. A bare git repo skips the symlinks and the script entirely — your home directory is the working tree.

Set it up

git init --bare "$HOME/.dotfiles"
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no

That last line is the one people miss, and without it dotfiles status lists every single file in your home directory as untracked — useless noise. With it, dotfiles status only ever shows files you’ve already added.

Add the alias permanently to your shell config:

echo "alias dotfiles='git --git-dir=\$HOME/.dotfiles/ --work-tree=\$HOME'" >> ~/.zshrc

Use it exactly like git, just with a different name

dotfiles add ~/.zshrc ~/.gitconfig ~/.config/nvim/init.lua
dotfiles commit -m "add zsh and nvim config"
dotfiles push

No symlinks, no stow, no separate directory you have to remember to sync. The files live where every tool expects them, and dotfiles is just git pointed at a different .git directory (--git-dir) with your home directory as the working tree (--work-tree) instead of a repo subfolder.

Set up a remote

dotfiles remote add origin git@github.com:you/dotfiles.git
dotfiles push -u origin main

Use a private repo unless you’ve audited every file for tokens, API keys, and internal hostnames. ~/.aws/credentials living two directories over from ~/.zshrc is exactly the kind of thing that ends up in a commit by accident with dotfiles add . — never run add . with this setup, always add files by explicit path.

Bootstrap a new machine

git clone --bare git@github.com:you/dotfiles.git "$HOME/.dotfiles"
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles checkout

The checkout step fails if any of the files it’s about to place already exist — a fresh macOS install always has a stock .bashrc or .zshrc sitting there. Back the conflicting files up and retry:

mkdir -p ~/.dotfiles-backup
dotfiles checkout 2>&1 | grep -E "^\s+" | awk '{print $1}' | \
  xargs -I{} mv {} ~/.dotfiles-backup/{}
dotfiles checkout

That grabs the list of conflicting paths straight from git’s own error output and moves them aside in one pass, so you don’t do it file by file.

What to actually track

Track configs, not secrets and not machine-specific state:

Don’t track: .ssh/ private keys, .aws/credentials, anything with a token in it, shell history files, or per-machine state like .zsh_sessions.

Handle machine-specific differences without branching

# in .gitconfig
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

Git’s own includeIf handles the common case — a different email for work repos — without you maintaining separate dotfiles branches per machine. For anything more divergent, gate it in the shell config itself:

if [[ "$(hostname)" == "work-laptop" ]]; then
  export JAVA_HOME=/usr/lib/jvm/work-jdk
fi

A symlink script is more code to maintain, and it’s one more thing that can be out of date or broken on a machine you haven’t touched in months. The bare-repo approach has zero moving parts beyond git itself, which is already installed everywhere you’d want your dotfiles anyway.