Everyone's .gitconfig accumulates aliases the way a coat pocket accumulates receipts. Most of mine were added in a fit of enthusiasm, used twice, and forgotten. So I went through the lot recently and deleted everything I couldn't remember using in the last month. What survived is a short list, and every one of them earns its place by saving me either keystrokes I type constantly or a mistake I make repeatedly. Those are the only two reasons worth having an alias.
Here's the section, then I'll explain the ones that matter.
[alias]
st = status -sb
co = checkout
br = branch
lg = log --graph --abbrev-commit --decorate \
--format=format:'%C(bold blue)%h%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)'
last = log -1 HEAD --stat
unstage = reset HEAD --
amend = commit --amend --no-edit
wip = commit -am "wip"
nuke = clean -fd
please = push --force-with-lease
aliases = config --get-regexp ^alias\\.
the ones i type without thinking
st is status -sb, the short format with branch info. Plain git status is a wall of helpful text I stopped reading years ago. The short format fits on a screen and tells me exactly what I need: what's staged, what isn't, and where I am relative to upstream. I type g st so often it's basically muscle memory now.
lg is the pretty graph log. It's a horror of a format string and I copied the bones of it from someone else's dotfiles long ago, but the output is the single most useful view of a repo I have. One coloured line per commit, the branch structure drawn down the left, author on the right. When someone asks "what happened to this branch", I run git lg and the answer is usually obvious in three seconds.
unstage is reset HEAD --. I added this purely because I could never remember the incantation to take something back out of the index. Git's own hint text tells you the command every time you git status, which is precisely why I didn't learn it, I just read the hint. Giving it a name I'd actually remember was the fix.
the ones that stop me hurting myself
please is push --force-with-lease, and the name is deliberate. I never want to type push --force because the plain force is how you quietly overwrite a colleague's commits that you didn't know were there. --force-with-lease refuses to push if the remote has moved under you since you last fetched, which is the safety check --force lacks. Aliasing it to please means the polite version is the easy version and the dangerous one takes effort. I've made the safe path the lazy path, which is the only reliable way to get me to take it.
amend is commit --amend --no-edit, for when I've committed and immediately spotted a typo or a forgotten file. The --no-edit keeps the existing message so I'm not dropped into an editor to confirm something I already wrote. Quick and quiet. The one rule I hold to: I only ever amend commits I haven't pushed. Rewriting shared history is a separate conversation and not one an alias should make easy.
wip commits everything with the message "wip", and nuke is clean -fd to throw away untracked junk. The first is for "I need to switch branches right now and don't want to lose this mess". The second is for "the working tree is full of build artefacts and generated files, get rid of them". nuke is genuinely destructive, it deletes files git isn't tracking and they don't go to a stash or the bin, so I gave it a name that makes me feel the weight of it before I hit enter. An alias that destroys things should never have a gentle name.
the rule, and the meta-alias
The rule I apply before adding anything: an alias has to replace something I type often enough to feel it, or guard a mistake I actually make. "Might be handy someday" is how you end up with forty aliases and no memory of what half of them do. If I haven't reached for it in a month, it goes.
Which is why the last one is aliases, a meta-alias that lists all the others. It sounds silly until the day you're on a fresh machine, or someone's pairing with you and asks "what's that g lg thing you keep doing". Then git aliases prints the lot and you don't have to go spelunking in your dotfiles. Small, but it closes the loop.
One thing I deliberately don't do is alias things that change behaviour in surprising ways. I've seen setups where git commit was quietly aliased to also run a linter, or where co did a checkout plus a submodule update plus a fetch. They're convenient right up until you're on someone else's machine, type the muscle-memory command, and get something you didn't expect. An alias should be a shorthand for a command, not a different command wearing the same name. If I want a multi-step thing, it gets a name that admits it's a multi-step thing, usually a shell function or a little script in ~/bin, not an alias that masquerades as plain git.
Everything in that file is there because I'd genuinely miss it, and that's the whole test. Type often, or guard a mistake, or get cut. The reward for that discipline is a config I can read top to bottom and a set of habits that travel with me to any machine, because they're built on the real commands underneath. The aliases are just the fast path to the things git could already do.