Everyone knows add, commit, push. That's the entry fee, not the game.
The developers who move fast — the ones who never lose work, never ship an ugly history, and never panic when a rebase goes sideways — all quietly share the same toolbox. It's not senior "Git magic." It's about 15 commands you can learn in an afternoon and lean on for the rest of your career.
I teach backend developers over at AS Backend Institute, and if I had to name one skill that predicts seniority without touching a single line of application code, it's Git fluency. So here are the 15 I actually reach for every week — with the gotchas nobody warns you about.
1. git switch and git restore — stop using checkout for everything
For years checkout did two unrelated jobs: switching branches and throwing away file changes. That overloading caused real accidents. Modern Git split it:
git switch main # change branch
git switch -c feature/pay # create + switch
git restore src/app.js # discard changes in a file
git restore --staged x.js # unstage, keep the edits
Gotcha: git restore <file> permanently discards uncommitted changes. There's no undo. Reach for it deliberately.
2. git add -p — commit like a surgeon
Changed five unrelated things in one file? Don't dump them into one commit. Stage hunks:
git add -p
Git walks you through each change: y (stage), n (skip), s (split further), e (edit the hunk by hand). This one habit is the difference between a readable history and a "misc fixes" graveyard.
3. git commit --amend — fix the last commit, not your dignity
Forgot a file? Typo in the message? Don't add a fix typo commit:
git add forgotten.js
git commit --amend --no-edit # fold it in, keep the message
git commit --amend # ...or edit the message too
Gotcha: amend rewrites the commit (new hash). Fine locally. If it's already pushed, you'll need a force push — see #13.
4. git commit --fixup + --autosquash — amend an old commit
--amend only fixes the last commit. To fix one buried three commits back:
git commit --fixup=<hash> # a marked "fixup!" commit
git rebase -i --autosquash main # Git auto-orders it next to its target
This is the professional move most people never learn. Your fix lands exactly where it belongs, automatically.
5. git rebase -i — the history editor
Interactive rebase lets you reshape recent commits: reword, squash, fixup, drop, edit, and reorder — just by editing a list.
git rebase -i HEAD~5
Turn add validation → fix validation → actually fix it into one clean Add form validation. Rule: only rewrite commits nobody else has pulled.
6. git stash — park work without committing junk
Production's on fire and your feature half-builds? Stash it:
git stash push -u -m "wip: checkout flow" # -u also stashes untracked files
git stash -p # stash only selected hunks
git stash list
git stash show -p stash@{0}
git stash pop # apply + remove
git stash apply stash@{0} # apply, keep in list
Gotcha: without -u, brand-new untracked files stay behind. Always name your stashes — "future you" won't remember what stash@{3} was.
7. git cherry-pick — grab one commit, not the whole branch
You need one commit from another branch — a config fix, a hotfix — nothing else:
git cherry-pick <hash>
git cherry-pick -x <hash> # records "cherry picked from…" in the message
git cherry-pick -n <hash> # apply but don't commit (batch several, then commit)
Perfect for porting a single fix onto a release branch.
8. git bisect — binary-search the commit that broke it
Bug appeared "sometime in the last 200 commits"? Don't guess. Let Git find it in ~8 steps:
git bisect start
git bisect bad # current commit is broken
git bisect good v1.4.0 # this old one worked
# Git checks out the midpoint — you test and mark:
git bisect good # or: git bisect bad
# ...repeat until Git names the exact culprit
git bisect reset
Even better, automate it:
git bisect run npm test
Git runs your test on each midpoint and finds the breaking commit on its own. The first time you see this work, it feels illegal.
9. git worktree — two branches, two folders, one repo
The best-kept secret for context switching. Instead of stashing your feature to fix a prod bug, check the other branch out into a separate folder:
git worktree add ../hotfix main
# fix, commit, push from ../hotfix — your feature work stays untouched
git worktree remove ../hotfix
No stashing, no rebuild churn, no lost train of thought.
10. git log -S and git log -L — detective mode
"When did this line appear? Who deleted this function?"
git log -S "calculateTax" --oneline # commits that added/removed that string
git log -p -L :calculateTax:tax.js # full history of one function
The "pickaxe" (-S) has ended more "who wrote this and why" arguments than any meeting ever will.
11. git blame -w -C — line-by-line history, done right
git blame -w -C src/auth.js
-w ignores whitespace-only changes; -C follows code that was moved or copied — so you blame the real author, not whoever ran the formatter. Then read the commit for the why, not to assign blame. 🙂
12. git reflog — the undo button for "undo"
Nuked a branch with a bad reset? Git logs almost every move of HEAD:
git reflog
# 821cd77 HEAD@{1}: commit: Add authentication ← there it is
git branch rescue 821cd77 # recover it safely into a new branch
Limit: reflog only resurrects things Git already stored (commits, stashes). Work you never committed is gone. Translation: commit early, commit often.
13. git reset --soft / --mixed / --hard (+ safe force push)
One command, three levels of "go back," each answering "what happens to my changes?"
git reset --soft HEAD~1 # undo commit, keep changes STAGED
git reset --mixed HEAD~1 # undo commit, keep changes UNSTAGED (default)
git reset --hard HEAD~1 # undo commit, DISCARD changes (danger)
After any history rewrite, push safely:
git push --force-with-lease # refuses to clobber if someone else pushed
--force-with-lease over --force, always. It's the difference between "oops" and "I just deleted my teammate's afternoon."
14. git revert — undo on shared branches without rewriting history
A bad commit is already on main/develop. Do not reset + force-push a shared branch. Create an inverse commit instead:
git revert <hash>
History stays honest: the change happened, it broke things, it was reverted — everyone can see the trail. Rule of thumb: shared branch → revert; private branch → reset.
15. git rerere — Git remembers how you fixed a conflict
Reuse Recorded Resolution. Enable it once:
git config --global rerere.enabled true
Now when you resolve a conflict, Git records it. Hit the same conflict again (super common during long rebases) and it replays your resolution automatically. Obscure, underused, borderline magical.
The pattern underneath all of these
Notice the theme: most of these exist so you can keep a clean, honest history and never lose work. That's really what Git seniority is — not memorizing flags, but having the reflexes to move fast without breaking things.
If you're leveling up your backend skills and want more hands-on workflow stuff like this, we publish free guides over at AS Backend Institute.
Which command surprised you? And what's the one intermediate command I left out that you'd fight to keep? Drop it in the comments — I always end up learning something. 👇
Top comments (0)