I've had the same dotfiles repository since 2019. Seven years of shell configs, vim tweaks, and git aliases that I copy to every machine I touch. If you're not managing your dotfiles in git, you're doing it wrong, and I'll die on this hill.

The idea is simple: put every config file you care about in one repo, symlink them into place, and never manually configure a machine again. Here's how I do it.

Terminal dark screen
One .bashrc to rule them all.

Why Dotfiles Matter

Every time I SSH into a fresh server and don't have my aliases, I feel physically uncomfortable. Where's my `gs` for `git status`? Where's my `lla` for `ls -la`? It's like walking into someone else's kitchen and not knowing where the knives are.

Dotfiles are your digital muscle memory. They're the reason you type `dc` instead of `docker-compose` and `gco` instead of `git checkout`. Without them, every new machine is a productivity tax.

Keyboard and code
Because clicking through GUIs is for people with too much time.

The Repository Structure

Here's what my dotfiles repo looks like.

dotfiles/
├── bash/
│   ├── .bashrc
│   ├── .bash_aliases
│   └── .bash_prompt
├── git/
│   ├── .gitconfig
│   └── .gitignore_global
├── vim/
│   └── .vimrc
├── tmux/
│   └── .tmux.conf
├── ssh/
│   └── config
├── install.sh
└── README.md

Each directory holds one tool's config. One tool, one directory. No interleaving, no guessing which file does what.

The Install Script

This is the only file that matters. It creates symlinks from the repo to where the system expects configs. Here's mine.

#!/bin/bash

DOTFILES_DIR="$HOME/dotfiles"
BACKUP_DIR="$HOME/dotfiles_backup"

# Backup existing files
mkdir -p "$BACKUP_DIR"

# Files to symlink
files=(
    "bash/.bashrc"
    "bash/.bash_aliases"
    "bash/.bash_prompt"
    "git/.gitconfig"
    "git/.gitignore_global"
    "vim/.vimrc"
    "tmux/.tmux.conf"
    "ssh/config"
)

for file in "\${files[@]}"; do
    target="$HOME/.$(basename "$file")"
    # Special case: ssh config
    if [[ "$file" == "ssh/config" ]]; then
        target="$HOME/.ssh/config"
        mkdir -p "$HOME/.ssh"
    fi

    if [ -f "$target" ] || [ -L "$target" ]; then
        echo "Backing up $target"
        mv "$target" "$BACKUP_DIR/"
    fi

    echo "Linking $file -> $target"
    ln -s "$DOTFILES_DIR/$file" "$target"
done

echo "Done. Restart your shell or source ~/.bashrc"
echo "Backup of replaced files: $BACKUP_DIR"

Run it once on a new machine and everything lands where it should. No manual copying, no `scp` commands, no "wait which file was that again?" moments.

Developer coding workspace
If your dotfiles aren't in git, are you even a developer?

The Git Config That Saves Time

My `.gitconfig` has aliases I can't live without.

[user]
    name = Davide Andreazzini
    email = davide@davideandreazzini.co.uk

[alias]
    s = status
    co = checkout
    br = branch
    cm = commit -m
    ca = commit -am
    l = log --oneline -20
    lg = log --oneline --graph --all
    d = diff
    ds = diff --staged
    unstage = reset HEAD --

[core]
    editor = vim
    excludesfile = ~/.gitignore_global

[push]
    autoSetupRemote = true

[pull]
    rebase = true

[init]
    defaultBranch = main

Those six two-letter aliases alone save me hundreds of keystrokes a day. The `unstage` alias is the one I always forget other people don't have.

Bash Aliases Worth Stealing

Here are the aliases I've carried across every machine for years.

# Navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Git shortcuts ( redundant with .gitconfig but bash is faster )
alias gs='git status'
alias gl='git log --oneline -10'
alias gd='git diff'
alias gc='git commit -m'
alias gp='git push'

# Docker ( because docker-compose is too many characters )
alias dc='docker-compose'
alias dps='docker ps'
alias dimg='docker images'
alias dex='docker exec -it'

# Safety nets
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias grep='grep --color=auto'

# Quick edits
alias vb='vim ~/.bashrc'
alias vv='vim ~/.vimrc'
alias vt='vim ~/.tmux.conf'
alias vg='vim ~/.gitconfig'

# Reload
alias sb='source ~/.bashrc && echo "bashrc reloaded"'

The safety net aliases ( `rm -i`, `cp -i`, `mv -i` ) have saved me more times than I'll admit. The `sb` alias is the one I type most after editing my bashrc.

The .bash_prompt That Actually Helps

A good prompt shows you what you need without clutter. Mine shows git branch, dirty state, and the exit code of the last command.

# Git branch in prompt
parse_git_branch() {
    git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ [\1]/'
}

# Dirty state indicator
parse_git_dirty() {
    if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
        echo "*"
    fi
}

# Color definitions
RED='\[\033[0;31m\]'
GREEN='\[\033[0;32m\]'
YELLOW='\[\033[0;33m\]'
BLUE='\[\033[0;34m\]'
RESET='\[\033[0m\]'

# Exit code display
exit_code() {
    local ec=$?
    if [ $ec -ne 0 ]; then
        echo "${RED}[$ec]${RESET} "
    fi
}

PS1="\$(exit_code)\u@\h:\${BLUE}\w\${RESET}\${YELLOW}\$(parse_git_branch)\$(parse_git_dirty)\${RESET}\$ " 

The red exit code is the most useful part. When something fails silently, the prompt tells you immediately.

Keeping It All In Sync

The repo lives on GitHub. When I change something on my laptop, I push. When I set up a new server, I clone and run `install.sh`. That's the entire workflow.

# On a new machine
git clone git@github.com:davideandreazzini/dotfiles.git ~/dotfiles
cd ~/dotfiles
chmod +x install.sh
./install.sh

If I modify a config on any machine, I commit and push from that machine. On the others, `git pull` in `~/dotfiles` and the symlinks pick up the changes instantly. No syncing tools, no Ansible, no complicated orchestration. Just git.

One thing I learned the hard way: never put secrets in your dotfiles repo. API keys, SSH private keys, tokens — those go in a separate private repo or in environment-specific files that the install script skips.

Conclusion

Dotfiles are one of those things where the initial investment pays off forever. A weekend setting up the repo, and every new machine after that takes sixty seconds. If you don't have a dotfiles repo yet, start one today.

Mine are at github.com/davideandreazzini/dotfiles. Fork it, steal the aliases, make it yours.