Stow is a symlink farm manager. You keep real files inside the repository, and Stow creates symbolic links to them where programs expect to find them.
Each top-level folder in the repository is a package. The contents of a package mirror the path they should have relative to your home directory:
~/dotfiles
├── git
│ └── .gitconfig → ~/.gitconfig
├── zsh
│ ├── .zshrc → ~/.zshrc
│ ├── .zsh_aliases → ~/.zsh_aliases
│ └── .config
│ └── starship.toml → ~/.config/starship.toml
├── ghostty
│ └── .config
│ └── ghostty
│ └── config → ~/.config/ghostty/config
└── nvim
└── .config
└── nvim
└── init.lua → ~/.config/nvim/init.lua
brew install git stow neovim. On Debian/Ubuntu: sudo apt install git stow.# Identity. Set your user name and email address
git config --global user.name "userName"
git config --global user.email "myEmail@gmail.com"
# Default branch name for new repositories
git config --global init.defaultBranch main
# Editor used for commit messages and rebases
git config --global core.editor nvim
# Quality-of-life settings
git config --global pull.rebase true # keep history linear when pulling
git config --global push.autoSetupRemote true # first `git push` just works
git config --global core.excludesFile ~/.gitignore_global # ignore junk everywhere
# Then, check your settings:
git config --list
nmaximo7@My-Mac-Studio ~ % git config --list
credential.helper=osxkeychain
init.defaultbranch=main
user.name=your-username
user.email=your@gmail.com
core.editor=nvim
cat > ~/.gitignore_global <<'EOF'
.DS_Store
.AppleDouble
.LSOverride
*.swp
*~
EOF
GitHub no longer accepts account passwords for Git over HTTPS. Pick one of these methods, in order of preference.
# 1. Generate a key (accept the default path, set a passphrase)
ssh-keygen -t ed25519 -C "you@example.com"
# 2. Start the agent and store the passphrase in the macOS keychain
eval "$(ssh-agent -s)"
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# 3. Copy the public key
pbcopy < ~/.ssh/id_ed25519.pub
# Then go to GitHub → Settings → SSH and GPG keys → New SSH key, paste the key and save.
# Add this to ~/.ssh/config so the key loads automatically:
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Since GitHub removed password authentication for Git operations over HTTPS, you must generate a Personal Access Token (PAT) to use in place of your password. Here is how to generate a classic PAT for pushing to your repositories:
cd ~/dotfiles
git status # review what will be committed
# Add any changes in the working directory to the staging area
# Save changes to the local repository
git add .
git commit -m "Initial commit: git and zsh packages"
# It only stages already tracked files, so use git add . first for new files (as above).
# Upload the local repository content to our recently created github remote repository:
git push
Username...: nmaximo7
Password...: YOUR PERSONAL TOKEN
# How to save the token so you don't have to paste it every time
# !!! DO NOT DO THIS
git config --global credential.helper store
# That saves your token in plain text in ~/.git-credentials, readable by any process running as you.
# On macOS, keep the default helper instead, which uses the encrypted Keychain:
git config --global credential.helper osxkeychain
Clone it into your home directory. Stow’s default target is the parent folder, so ~/dotfiles or ~/macOS gives you ~ automatically.
cd ~
# SSH (Option A)
git clone git@github.com:YOUR-USERNAME/macOS.git
# or HTTPS (Option B)
git clone https://github.com/YOUR-USERNAME/macOS.git
cd ~/macOS
# Open it in your editor (optional):
code ~/macOS # Visual Studio Code
Create one folder per tool. Move (do not copy) each existing config file into its package, so the original is replaced by a symlink in the next step.
cd ~/macOS/
# Git package
mkdir git
cp ~/.gitconfig git
# Zsh package
mkdir zsh

Simulate.
The -n flag means “no changes: only show what would happen”. -v makes it verbose. \
cd ~/macOS
stow -nv git
# You should see lines like:
LINK: .gitconfig => macOS/git/.gitconfig
If you see WARNING or ERROR lines, fix them before continuing. Do not use –adopt yet.
Run Stow on named packages, not on *. The wildcard also matches files like README.md and folders like .git, which are not packages.
Create the links.
stow -v git
# Stow several packages at once:
stow -v git zsh
# If the repository is not in ~/macOS, set the target explicitly:
stow -vt ~ git zsh
# -t / --target = where the links are created (~ = your home directory)
Verify.
ls -l ~ | grep -- '->' # symlinks in your home folder
ls -l ~/.gitconfig # should show: .gitconfig -> dotfiles/git/.gitconfig
Avoid “folded” directories. When a target folder (like ~/.config) does not exist yet, Stow links the entire folder to the repository (“tree folding”). Of course, you can avoid that: stow -v --no-folding zsh
macOS uses Zsh by default. Oh My Zsh manages plugins and Starship provides a fast, customizable prompt.
# Install Oh My Zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# Install Starship using Homebrew
brew install starship
# Install a Nerd Font
brew install --cask font-meslo-lg-nerd-font
# Create the Stow package structure.
cd ~/macOS/
mkdir -p zsh/.config
# Move and generate the config files
# Instead of creating a blank .zshrc, move the one Oh My Zsh just generated into your zsh package.
mv ~/.zshrc ~/macOS/zsh/
# Then, create your aliases file
touch ~/macOS/zsh/.zsh_aliases
# Generate the Starship Tokyo Night preset directly into the Stow directory.
starship preset tokyo-night -o ~/macOS/zsh/.config/starship.toml
Connect everything in your .zshrc.
# --- Oh My Zsh ---------------------------------------------------------
export ZSH="$HOME/.oh-my-zsh"
# Starship draws the prompt, so Oh My Zsh must not set a theme
ZSH_THEME=""
plugins=(git)
source "$ZSH/oh-my-zsh.sh"
# --- Personal config ---------------------------------------------------
# Aliases (only load if the file exists)
[ -f "$HOME/.zsh_aliases" ] && source "$HOME/.zsh_aliases"
# Machine-specific or secret settings (NOT tracked by Git, see section 11)
[ -f "$HOME/.zshrc.local" ] && source "$HOME/.zshrc.local"
# Useful plugins
# brew install zsh-autosuggestions zsh-syntax-highlighting
source "$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
source "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
# --- Starship prompt (keep this at the very bottom) --------------------
eval "$(starship init zsh)"
A starter ~/dotfiles/zsh/.zsh_aliases:
# Navigation
alias ..='cd ..'
alias ...='cd ../..'
# Listing
alias ll='ls -lah'
# Editor
alias v='nvim'
alias vim='nvim'
# Git shortcuts
alias gs='git status -sb'
alias gl='git log --oneline --graph --decorate -15'
# Dotfiles
alias dots='cd ~/dotfiles'
Apply the symlinks and reload:
cd ~/macOS
stow -nv zsh # dry run first
stow -v zsh # create the links
source ~/.zshrc # reload the shell
Never commit your password database to a public repository
cd ~/macOS/
# Keep Passwords.kdbx in a synced folder (e.g. ~/Documents/Passwords/) and
mkdir KeePassXC
cp [...]/passwords.kdbx keePassXC
# Add at the end .gitignore
cat >> .gitignore <<'EOF'
# --- Secrets: never commit ---
*.kdbx
*.kdb
*.key
*.pem
.env
.env.*
.zshrc.local
.git-credentials
id_rsa*
id_ed25519*
EOF
A Brewfile lists every formula, cask, and App Store app so a new Mac can install them with one command.
cd ~/macOS
brew bundle dump --force # writes ./Brewfile
# Review it, delete what you no longer need, then commit it:
git add Brewfile
git commit -m "Add Brewfile"
git push
# Restore later with:
brew bundle --file=~/nixOS/Brewfile
# 1. Command Line Tools
xcode-select --install
# 2. Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 3. Git and Stow
brew install git stow
# 4. Clone your dotfiles (set up GitHub authentication first)
git clone git@github.com:YOUR-USERNAME/dotfiles.git ~/dotfiles
cd ~/dotfiles
# 5. Install apps and tools
brew bundle --file=~/nixOS/Brewfile
# 6. Back up any default files that would conflict, then stow
mv ~/.zshrc ~/.zshrc.bak 2>/dev/null
mv ~/.gitconfig ~/.gitconfig.bak 2>/dev/null
stow -v git zsh ghostty nvim
Automate it: Save this as ~/nixOS/install.sh
#!/usr/bin/env bash
set -euo pipefail
DOTFILES="$HOME/nixOS"
PACKAGES=(git zsh ghostty nvim)
# Homebrew
if ! command -v brew >/dev/null 2>&1; then
echo "Homebrew is not installed. Install it from https://brew.sh and re-run." >&2
exit 1
fi
# Tools and apps
brew bundle --file="$DOTFILES/Brewfile"
# Make sure ~/.config exists so Stow does not fold it into the repo
mkdir -p "$HOME/.config"
# Link every package (dry run first: abort on conflicts)
cd "$DOTFILES"
stow -nv "${PACKAGES[@]}"
stow -v "${PACKAGES[@]}"
echo "Done. Open a new terminal session."
cd ~/dotfiles
# Edit any linked file as usual, e.g. nvim ~/.zshrc (it edits the repo copy)
git status # what changed?
git diff # review the changes
git add -p # stage interactively, hunk by hunk
git commit -m "zsh: add git aliases"
git push
# On another machine:
cd ~/dotfiles
git pull
stow -R zsh # re-link if files were added or removed
git checkout --orphan abranchnane # Start a new branch from scratch without any previous commit history.
git add -A # Stage all changes in the working directory for the next commit.
git commit -am "Fresh start"
git branch -D main # The `git branch -D main` command is used to forcefully delete the specified branch named "main" in Git.
git branch -m main # Rename the current branch to "main" in Git.
git push -f origin main # It pushes the renamed branch ("main") to the remote repository and sets it as the upstream branch.
git gc --aggressive --prune all # It performs garbage collection, which cleans up unnecessary files and optimizes the repository for better performance. --aggressive runs a more aggressive garbage collection