The Elegant Way to Manage Dotfiles: Bare Git Repository Method

If you’re a developer, you’ve probably accumulated a collection of carefully crafted configuration files over the years. Your .vimrc, .bashrc, .tmux.conf, and other dotfiles represent hours of tweaking and optimization. But how do you keep them in sync across multiple machines? How do you version control them without cluttering your home directory?

After trying various dotfiles management solutions—from complex frameworks to symlink farms—I discovered what might be the most elegant approach yet: using a bare Git repository.

The Problem with Traditional Approaches

Most dotfiles management solutions fall into a few categories:

  • Symlink managers: Create a separate directory and symlink files back to your home directory. Works, but creates complexity and potential broken links.
  • Specialized tools: GNU Stow, dotbot, chezmoi—all great tools, but they add another layer of tooling to learn and maintain.
  • Simple Git repo: Just put everything in a Git repo and manually copy files around. Simple but not automated.

What if there was a way to get all the benefits of Git version control without the complexity of additional tooling or symlinks?

Enter the Bare Repository Method

This technique uses a bare Git repository stored in a hidden folder (like ~/.cfg) combined with a simple alias. The genius is that your home directory becomes the working tree, allowing you to track files directly where they live.

Here’s how it works:

# The magic alias that makes everything work
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'

This alias tells Git to:

  • Use ~/.cfg as the Git directory (where Git stores its metadata)
  • Use your home directory as the working tree (where your files actually live)

Setting It Up

Starting Fresh

# Initialize the bare repository
git init --bare $HOME/.cfg

# Create the alias
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'

# Hide untracked files (important!)
config config --local status.showUntrackedFiles no

# Make the alias permanent
echo "alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'" >> $HOME/.bashrc

The showUntrackedFiles no setting is crucial—it prevents config status from showing every file in your home directory as untracked.

Adding Your First Files

config add .vimrc
config commit -m "Add vim configuration"
config add .bashrc
config commit -m "Add bash configuration"
config remote add origin git@github.com:yourusername/dotfiles.git
config push -u origin main

Installing on a New Machine

The real beauty of this system shines when setting up a new machine:

# Clone your dotfiles
git clone --bare git@github.com:yourusername/dotfiles.git $HOME/.cfg

# Set up the alias
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'

# Checkout your files
config checkout

If you have conflicting files, the checkout might fail. The solution is simple—back up the conflicts:

mkdir -p .config-backup && \
config checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | \
xargs -I{} mv {} .config-backup/{}

Then complete the setup:

config checkout
config config --local status.showUntrackedFiles no

Daily Usage

Once set up, managing your dotfiles becomes as natural as using Git:

# Check what's changed
config status

# Add a new config file
config add .tmux.conf
config commit -m "Add tmux configuration"

# Update existing files
config add .vimrc
config commit -m "Update vim keybindings"

# Push changes
config push

# Pull updates from another machine
config pull

Why This Method Rocks

No extra dependencies: Just Git, which you already have installed.

No symlinks: Files live exactly where they need to be. No broken links, no confusion.

Full Git power: Branches for different machines, commit history, easy rollbacks—everything Git offers.

Clean home directory: The .cfg folder is hidden and won’t interfere with other Git repositories in your home directory.

Easy sharing: Your setup script can be a simple one-liner that others can use.

Advanced Tips

Multiple Machine Configurations

Use branches for different setups:

# Create a branch for your work laptop
config checkout -b work-laptop
config add .work-specific-config
config commit -m "Add work-specific settings"
config push -u origin work-laptop

Automation Scripts

You can create installation scripts that others (or future you) can run:

#!/bin/bash
git clone --bare https://github.com/yourusername/dotfiles.git $HOME/.cfg
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'
mkdir -p .config-backup
config checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .config-backup/{}
config checkout
config config --local status.showUntrackedFiles no
echo "Dotfiles installed!"

Selective File Management

Since this is just Git, you can use .gitignore to exclude sensitive files or use config add -f to force-add specific files you want to track.

Conclusion

After using this method for managing my dotfiles, I can’t imagine going back to any other approach. It’s simple, powerful, and leverages tools I already know and use daily. No extra learning curve, no additional dependencies, just elegant simplicity.

The best part? When someone asks me how they can get my exact setup, I can give them a single command that will install everything perfectly. That’s the kind of developer experience we should all strive for.

Give this method a try—I think you’ll find it as elegant and practical as I have.


Want to see this in action? Check out my personal dotfiles setup on GitHub.