Git Configuration¶
Git is the one tool every engineer on this stack uses dozens of times
per day. Its configuration surface is enormous, mostly sensible, and
almost entirely mis-defaulted. This page establishes an XDG-compliant
~/.config/git/config with path-based identity profiles, commit
signing via SSH keys, and a set of defaults that catch common failure
modes before they happen.
The organizing idea is path-based profiles: one global config that
sets universal defaults, plus profile-specific overrides that activate
automatically based on where a repository lives on disk. No more
forgetting to set your work email before the first commit, no more
leaking a personal SSH signing key into a work commit, no more
per-repo git config user.email dance.
Why path-based, not per-repo?
Per-repo .git/config overrides work but fail at scale: every new
clone requires remembering to set them, and the state lives inside
the git directory where it is easy to miss during audits.
Path-based profiles via includeIf move the policy up one level —
as long as you clone work repos under ~/work/ and personal ones
under ~/personal/, identity is correct without any per-repo
action. The filesystem layout becomes the contract.
File layout¶
~/.config/git/
├── config # Main config — global defaults + includeIf rules
├── ignore # Global gitignore (backstop)
├── attributes # Global gitattributes
├── work.config # Loaded when gitdir matches ~/work/
├── personal.config # Loaded when gitdir matches ~/personal/
├── opensource.config # Loaded when gitdir matches ~/opensource/
└── allowed_signers # SSH signing key registry for git verify-commit
Git reads ~/.config/git/config when XDG_CONFIG_HOME is set (which
the framework guarantees via ~/.zshenv and ~/.profile). The legacy
~/.gitconfig is still honored if present, but maintaining both is a
recipe for drift — pick one location and stick with it. Delete
~/.gitconfig after migrating.
Main config¶
The main config sets universal defaults and declares which profile to load for each path prefix. It contains no email, no signing key, and no repository-specific configuration — those live in the profile files.
Key settings worth calling out:
| Section | Setting | Why |
|---|---|---|
[pull] |
rebase = true |
Rebase on pull rather than merge — cleaner history |
[push] |
autoSetupRemote = true |
Auto-create remote branch on first push |
[fetch] |
prune = true |
Remove local refs for branches deleted on remote |
[merge] |
conflictStyle = zdiff3 |
Show original content alongside both sides (git 2.35+) |
[rebase] |
updateRefs = true |
Update dependent branches when rebasing their base |
[rebase] |
autoSquash = true |
Auto-squash fixup! and squash! commits |
[rebase] |
autoStash = true |
Stash local changes before rebase, restore after |
[rerere] |
enabled = true |
Reuse recorded conflict resolutions |
[diff] |
algorithm = histogram |
Better rename detection |
[diff] |
colorMoved = zebra |
Highlight moved lines in a distinct color |
[commit] |
gpgsign = true |
Default all commits to signed |
[commit] |
verbose = true |
Show diff in commit message editor |
[branch] |
sort = -committerdate |
List branches by committer date, newest first |
[help] |
autocorrect = prompt |
Never auto-execute a suggested command |
The full config is at git/config in the dotfiles repository.
Profile files¶
Each profile file contains exactly the identity-specific settings — email, signing key, and anything else that varies between contexts. Keep them short.
# ~/.config/git/work.config
[user]
email = dev@springbig.com
signingkey = ~/.ssh/id_ed25519_work.pub
[gpg]
format = ssh
[gpg "ssh"]
allowedSignersFile = ~/.config/git/allowed_signers
# Work repos use git@github.com-work to route through a work-specific
# SSH identity (see SSH page for host alias setup).
[url "git@github.com-work:springbig/"]
insteadOf = https://github.com/springbig/
insteadOf = git@github.com:springbig/
# ~/.config/git/personal.config
[user]
email = you@billwoika.com
signingkey = ~/.ssh/id_ed25519_personal.pub
[gpg]
format = ssh
[gpg "ssh"]
allowedSignersFile = ~/.config/git/allowed_signers
[url "git@github.com-personal:"]
insteadOf = https://github.com/
insteadOf = git@github.com:
To verify which profile is active:
The --show-origin flag prints the exact file each value was loaded
from, which makes debugging trivial when a profile doesn't activate as
expected.
includeIf pattern gotchas
The pattern gitdir:~/work/ matches when the repository's .git
directory resolves to a path starting with ~/work/. The trailing
slash is required — without it, only a literal file at ~/work
would match. Use gitdir/i: for case-insensitive matching
(relevant on macOS with case-insensitive filesystems). Symlinks
are resolved, so cloning into a symlinked directory still matches
the real path.
Commit signing with SSH keys¶
The framework uses SSH keys (not GPG) for commit signing. SSH keys are already managed for authentication; reusing them for signing eliminates the GPG key management overhead entirely.
The allowed_signers file¶
The allowed_signers file maps email addresses to public keys. Git
uses it to verify commit signatures:
dev@springbig.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...
you@billwoika.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...
Registering keys with GitHub¶
Add your SSH key to GitHub twice: once as an "Authentication Key"
(for push/pull) and once as a "Signing Key" (for commit signature
verification). Both entries use the same .pub file contents. After
registering, commits signed with that key show as "Verified" in the
GitHub UI.
Diff and merge tools¶
The framework uses delta as the git pager. Delta provides
syntax-highlighted diffs, line numbers, and commit decorations. It is
configured in the main git config under the [delta] section.
Useful aliases¶
The framework ships these in the git config itself (separate from the shell aliases):
| Alias | Command | Purpose |
|---|---|---|
s |
status -sb |
Short status with branch |
lg |
log --oneline --graph --decorate --all |
Visual log |
last |
log -1 HEAD --stat |
Last commit with stats |
branches |
branch -vv --sort=-committerdate |
Branches by recency |
pushf |
push --force-with-lease --force-if-includes |
Safe force-push |
uncommit |
reset --soft HEAD^ |
Undo last commit, keep changes |
amend |
commit --amend --no-edit |
Amend without editing message |
cleanup |
merged branches | xargs branch -d |
whoami |
config --list --show-origin |
Show all active config |
profile |
config --show-origin user.email |
Show active identity |
Repository maintenance¶
Periodic maintenance keeps large repositories fast: