JustToThePoint English Website Version
JustToThePoint en español

Dotfiles with Git + GNU Stow.

Stow

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

Install the tools, First-time Git setup

  1. On macOS: brew install git stow neovim. On Debian/Ubuntu: sudo apt install git stow.
  2. First-Time git setup.
    # 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
    

Create the GitHub repository

  1. Go to github. Log in to your account. Click on the New repository button or the + icon, then click on New repository.
  2. Next you have to fill out a name of the repository (dotfiles), a description (it is really optional), and select its visibility or access permission (public -Anyone on the internet can see this repository. You choose who can commit- or private -You choose who can see and commit to this repository-).
  3. Do not add a README, .gitignore or license yet. An empty repository avoids merge conflicts on your first push.

Authenticate with GitHub

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

Option B: Personal Access Token (PAT) over HTTPS

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:

  1. Navigate to Developer Settings: Click your profile picture in the upper-right corner of GitHub, select Settings, then scroll to the very bottom of the left sidebar and click Developer settings.
  2. Access the Tokens Menu: In the left sidebar, click Personal access tokens, then select Tokens (classic).
  3. Generate a New Token: Click the Generate new token button near the top right and select Generate new token (classic). You may be prompted to confirm your GitHub password.
  4. Configure Token Permissions: Give your token a descriptive “Note” (like “macosToken”), set an expiration date, and check the main box next to repo. This grants the token the necessary permissions to push code to your repositories.
  5. Copy Your Token: Critical. Scroll to the bottom and click Generate token. Immediately click the copy icon next to the generated token string. You will not be able to see this token again once you navigate away from the page.

First commit and push

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 and organize the repository

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

Mirror your home directory inside packages

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

Manage and sync dotfiles with Git + GNU Stow

Dry run, then stow

  1. 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.

  2. 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)
    
  3. Verify.

    ls -l ~ | grep -- '->'          # symlinks in your home folder
    ls -l ~/.gitconfig              # should show: .gitconfig -> dotfiles/git/.gitconfig
    
  4. 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

Shell: Oh My Zsh + Starship

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

.gitignore, secrets and the password database

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

Back up your apps with a Brewfile

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

Set up a new Mac

# 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."

Daily workflow

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

How To Remove GIT History And Make Your Repo Smaller

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 
Bitcoin donation

JustToThePoint Copyright © 2011 - 2026 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.