RSS Amplifier

eshlox · Mar 26, 2026

Dotfiles backup with a bare Git repo

0
Sign in to vote or save

Przemysław Kołodziejczyk · eshlox

I have configuration files scattered across my home directory: Hammerspoon, Helix, tmux, lazygit, Ghostty, zsh, and more. I wanted a simple way to back them up to a private GitHub repo without symlinks, without extra tools, and without accidentally tracking my entire home directory.

A bare Git repo does exactly this. No dependencies, no wrapper tools, just Git.

This post is part of my terminal workflow series.

How it works

A normal git init creates a .git folder in the current directory and tracks everything under it. A bare Git repo separates the Git database from the working tree. The database lives in ~/projects/.dotfiles, the working tree is your entire $HOME, but it only tracks files you explicitly add. Everything else is invisible to it.

Setup

Step 1: Create the bare repo

git init --bare ~/projects/.dotfiles

Step 2: Add the alias

Add this to your ~/.zshrc:

alias dot='git --git-dir=$HOME/projects/.dotfiles --work-tree=$HOME'

Reload your shell:

source ~/.zshrc

Step 3: Hide untracked files

This is the key step. Without it, dot status would list every single file in your home directory.

dot config status.showUntrackedFiles no

Step 4: Create the remote repo

Create a private repo on GitHub (e.g. dotfiles), then:

Step 5: Add your config files

Only the files you explicitly add get tracked. Nothing else.

dot add ~/.zshrc

dot add ~/.config/tmux/tmux.conf

dot add ~/.config/helix/config.toml

dot add ~/.config/helix/languages.toml

dot add ~/.config/lazygit/config.yml

dot add ~/.config/ghostty/config

dot add ~/.hammerspoon/init.lua

dot add ~/Library/LaunchAgents/com.local.capslock-remap.plist

Commit and push:

dot commit -m "initial dotfiles"

dot push -u origin main

Step 6: One command to back up everything

Add this function to your ~/.zshrc:

dotpush() {

dot add -u

dot commit -m "update $(date +%Y-%m-%d)"

dot push

}

dot add -u stages changes only to files that are already tracked. It will never pick up new files, only updates and deletions to files you previously added. So if you create a new config file, you still need to dot add it once manually. After that, dotpush handles it.

Reload your shell:

source ~/.zshrc

From now on, run dotpush and all your tracked configs get committed and pushed with today’s date.

Adding new files later

Whenever you set up a new tool or change where a config lives:

dot add ~/.config/new-tool/config.toml

dot commit -m "add new-tool config"

dot push

After that first dot add, the file is tracked and dotpush will pick up future changes automatically.

Checking what is tracked

dot ls-files

This lists every file the repo knows about. Useful to verify you have not missed anything, or accidentally added something you should not.

To see what changed since the last commit:

dot status

dot diff

Restoring on a new Mac

Step 1: Clone the bare repo

Step 2: Add the alias

alias dot='git --git-dir=$HOME/projects/.dotfiles --work-tree=$HOME'

Step 3: Checkout

dot checkout

If this fails with an error about existing files (e.g. macOS creates a default .zshrc), back them up first:

mkdir -p ~/projects/.dotfiles-backup

dot checkout 2>&1 | grep "^\t" | sed 's/^\t//' | while read -r file; do

mkdir -p ~/projects/.dotfiles-backup/$(dirname "$file")

mv "$file" ~/projects/.dotfiles-backup/"$file"

done

dot checkout

This moves conflicting files to ~/projects/.dotfiles-backup so you can compare them later.

Step 4: Hide untracked files

dot config status.showUntrackedFiles no

Step 5: Verify

dot ls-files

All your configs are in place. Install your tools (brew install --cask hammerspoon, etc.) and everything picks up its config automatically.

Full restore script

For convenience, save this as a gist or keep it in your repo README. Run it on a fresh Mac after installing Git and setting up SSH keys:

git clone --bare [email protected]:YOUR_USERNAME/dotfiles.git ~/projects/.dotfiles

alias dot='git --git-dir=$HOME/projects/.dotfiles --work-tree=$HOME'

# Back up any conflicting files

mkdir -p ~/projects/.dotfiles-backup

dot checkout 2>&1 | grep "^\t" | sed 's/^\t//' | while read -r file; do

mkdir -p ~/projects/.dotfiles-backup/$(dirname "$file")

mv "$file" ~/projects/.dotfiles-backup/"$file"

done

dot checkout

dot config status.showUntrackedFiles no

echo "Dotfiles restored. Conflicts (if any) moved to ~/projects/.dotfiles-backup"

What not to track

Do not add files that contain secrets: SSH keys, API tokens, .env files. The repo is private, but secrets do not belong in Git. If you need to back up secrets, use a password manager or encrypted storage.

Also avoid large or machine-specific files like caches, logs, or anything under ~/Library that is not a LaunchAgent or a config plist you explicitly manage.

File locations

~/projects/.dotfiles/ # bare Git database (hidden, do not touch)

~/.zshrc # contains the dot alias and dotpush function

The whole point is that your config files stay exactly where they are. No symlinks, no special directories, no wrapper tools. Just Git.

Spot something wrong?

Found a typo, a broken link, or something that could be better? I would love to hear from you. Drop me a message and I will fix it.

Read the original on eshlox.net

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.