This is a reference for me, to carry the config to new machines. Three values are machine-local, everything else is portable as-is.
The file
[user]
email = <your-email>
name = Andreas Fertsch-Röver
signingkey = <gpg-key-id> # used by commit.gpgsign below
[core]
preloadindex = true # parallel stat() on the index, faster status on big repos
pager = less
sshCommand = /usr/bin/ssh # pin the ssh that holds my keys
[fetch]
prune = true # drop local refs for branches deleted upstream
[push]
default = upstream # push to the tracked branch, not to a same-named one
[merge]
summary = true # list the merged commits in the merge message
verbosity = 1 # report conflicts, stay quiet otherwise
ff = false # always create a merge commit, so branch history stays visible
[commit]
gpgsign = true # sign every commit
[init]
defaultBranch = main # no hint banner on git init
[alias]
# staging and committing
all = add . --all
co = commit
coa = commit --amend
coall = !git all && git co # stage everything, then commit
wip = !git all && git commit -m 'WIP' # park the current state to switch context
undo = reset --soft HEAD~ # drop the last commit, keep its changes staged
pushf = push --force-with-lease
cp = cherry-pick
# moving around
ch = checkout
sw = switch
fsw = !git fetch && git switch # fetch first, so a fresh remote branch is found
main = switch main
omain = !git fetch && git switch --detach origin/main # throwaway checkout of upstream main
master = checkout master
omaster = !git fetch && git switch --detach origin/master # throwaway checkout of upstream master
uprebase = pull --rebase --autostash
upmerge = pull --no-rebase
up = uprebase
rom = !git fetch && git rebase origin/main --autostash # rebase onto current upstream main
fp = !git fetch --prune --verbose origin '+refs/tags/*:refs/tags/*' # fetch and refresh all tags
fa = !git fetch --all --verbose
# looking
st = status
l = log
last = log -1 HEAD
lme = "!git log --author=\"$(git config user.name)\"" # only my commits
ls = log --stat --no-abbrev-commit
lsm = !git fetch && git log --stat --no-abbrev-commit origin/main...HEAD # my commits since main
dch = diff --stat origin/main...HEAD # the size of the PR I am about to open
g = grep --break --heading --line-number
dif = diff --color --color-words --abbrev # word-level diff, unstaged
difc = diff --color --color-words --abbrev --cached # word-level diff, staged
diffc = diff --color --abbrev --cached # line-level diff, staged
glog = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' # compact one-line graph
graph = log --graph --all --decorate --stat --date=iso # full graph across all branches
# cleanup
smb = !git branch --merged | grep -i -v -E \"hotfix|release|main|master\" # merged branches, minus protected ones
delm = !git smb | xargs git branch -d # delete exactly those
ignore = !git update-index --assume-unchanged # stop tracking local edits to a tracked file
unignore = !git update-index --no-assume-unchanged
ignored = !git ls-files -v | grep ^[a-z] # what is currently assume-unchanged
# stashing
pop = stash pop
slist = stash list
stash-unstaged = "!f() { git stash push --staged -m staged; git stash push -u -m rest; git stash pop stash@{1}; }; f" # stash only the unstaged part
# worktrees
wt = worktree
wtl = worktree list
wtr = worktree remove
wta = "!f() { git fetch && git worktree add \"../$1\" \"$1\"; }; f" # add worktree ../<branch> for <branch>
wtd = "!f() { git worktree remove \"../$1\" && git branch -d \"$1\"; }; f" # remove that worktree and the branch
[format]
# date, full hash, refs; subject and author on the next line
pretty = format:%C(blue)%ad%Creset %C(yellow)%H%C(green)%d%n%Creset %C(blue)%s %C(magenta) [%an]%Creset%n
[color "branch"]
current = yellow reverse
local = yellow
remote = green
[color "diff"]
meta = yellow bold
frag = magenta bold
old = red
new = green
[mergetool]
prompt = false # do not ask before launching the merge tool
keepBackup = false # no .orig files left behind
Machine-local, fill on arrival
Three things change per machine. Set them once and never touch the rest:
git config --global user.email you@example.com
git config --global user.signingkey <gpg-key-id>
git config --global core.sshCommand /usr/bin/ssh
core.sshCommand matters when a package manager drops a second ssh binary earlier in PATH than the one holding your keys. Pinning the absolute path removes the guessing. A credential helper is deliberately not in the file: the right value differs on every OS.
Two settings are per-environment rather than per-machine, which is why they stay out of the global file. If a remote serves a self-signed certificate, point git at its CA bundle instead of switching verification off everywhere:
git config --local http.sslCAInfo /path/to/ca-bundle.crt
git config --local http.sslVerify false # last resort, and only in that one repo
http.sslVerify = false in the global file disables certificate verification for every HTTPS remote on the machine, including the ones you did not think about. Scoped to a single repository it is a known trade-off; scoped globally it is a hole you forget about.
Defaults worth changing
Four settings do more for me than all the aliases combined.
fetch.prune = true-
Deletes local remote-tracking refs for branches that are gone upstream. Without it,
git branch -raccumulates months of merged feature branches and tab completion turns into archaeology. push.default = upstream-
Pushes to the branch you are actually tracking, not to a same-named branch on the remote. On a detached or renamed local branch this is the difference between a push and an incident.
merge.ff = false-
Forces a merge commit even when a fast-forward is possible. The topology then still shows which commits arrived together as one unit, which is what you want when reading history six months later.
init.defaultBranch = main-
Stops the hint banner and matches every remote I work with.
Aliases worth a note
Most of them are typing shortcuts and need no explanation. These carry a reason.
wip,undo-
A pair.
wipcommits whatever state you are in so you can switch context;undoisreset --soft HEAD~, which removes that commit again and leaves its changes staged, as if you had never left. pushf-
--force-with-lease, not--force. After a rebase the push aborts if the remote moved in the meantime, instead of overwriting a colleague’s commits. up,rom-
Both carry
--autostash, so a dirty working tree no longer blocks a rebase: git stashes it, rebases, and puts it back. omain-
Detaches at
origin/maininstead of switching to the local branch. For a quick build, bisect, or "does it also break on main", with no local branch to clean up afterwards. dch-
Triple-dot, so it diffs against the merge base. It reports what my PR adds and not what main moved on to.
stash-unstaged-
Splits the working tree: it parks the staged part and the rest in two separate stashes, then restores the staged part. For handing over a clean, reviewable change while the half-finished rest waits in
stash@{0}. smb,delm-
smblists merged branches minusmain,master,release*andhotfix*;delmdeletes exactly that list. Add your own protected names to the grep before running it anywhere that matters.
|
Warning
|
|
Worktrees
wta and wtd create and tear down a worktree in a sibling directory named after the branch. They carry the whole parallel-branch workflow I described in My LLM Git Workflow, so I will not repeat it here.
None of this changes what git does. It changes how fast I can read what git says, and that is why the file travels with me.