Most developers learn git add, git commit, git push, and call it done. But git is a remarkably deep tool, and the commands most people never learn are exactly the ones that save the most time. Here are the git techniques I actually use to recover from mistakes, understand history, and move faster.
git reflog: Your Time Machine
git reflog is the most underused command in git. It records every movement of HEAD — including commits, resets, and checkouts — even if you have overwritten history.
git reflog
Output looks like this:
a3b1c2d HEAD@{0}: reset: moving to HEAD~2
f7e9d1a HEAD@{1}: commit: add user profile endpoint
c4a8b3e HEAD@{2}: commit: fix validation bug
Accidentally ran git reset --hard and lost commits? Find the commit hash in reflog and recover:
git checkout f7e9d1a
# or to restore your branch
git reset --hard f7e9d1a
reflog has saved me from “I just deleted an hour of work” panic more times than I can count.
git bisect: Binary Search for Bugs
You know a bug was introduced somewhere between last week and today, but you have 60 commits in between and no idea which one is the culprit. git bisect automates a binary search through your history.
git bisect start
git bisect bad # current commit is broken
git bisect good v1.2.0 # this version worked fine
Git checks out a commit in the middle. You test whether the bug is present, then tell git:
git bisect bad # bug is still here
# or
git bisect good # bug is not here
Git narrows the range by half each time. In about 6 rounds, you will pinpoint the exact commit that introduced the bug — out of 60 candidates.
When done:
git bisect reset # return to original HEAD
git stash: More Than Just Saving Work
Most developers know git stash saves uncommitted changes. Fewer know about the useful flags:
# Stash with a description (essential for multiple stashes)
git stash push -m "wip: user auth refactor"
# Stash only staged changes
git stash push --staged
# List stashes
git stash list
# Apply a specific stash without removing it
git stash apply stash@{2}
# Apply and drop in one command
git stash pop
# Stash untracked files too
git stash push --include-untracked
The -m flag is critical. A stash named “WIP on main: 3f8d9a fix button” tells you nothing in two weeks. Name your stashes.
git rebase -i: Rewriting History Cleanly
Interactive rebase lets you clean up commits before merging into main. Useful for combining “WIP”, “fix typo”, and “actually fix it” commits into one coherent commit.
# Rebase the last 4 commits interactively
git rebase -i HEAD~4
An editor opens with your commits listed. Change pick to:
squash(ors) — combine with previous commitreword(orr) — edit the commit messagedrop(ord) — delete the commit entirelyedit(ore) — stop to amend the commit
pick f7e9d1a add user profile endpoint
squash c4a8b3e fix validation bug
squash a3b1c2d fix again
reword b2c4d5e update tests
The result: a clean, readable history instead of a trail of “oops” commits.
git worktree: Multiple Branches at Once
git worktree lets you check out multiple branches simultaneously in different directories — without stashing or committing half-finished work.
# Create a worktree for a hotfix branch
git worktree add ../project-hotfix hotfix/login-bug
# Work in the new directory without touching your main checkout
cd ../project-hotfix
# ... fix the bug, commit, push ...
# Clean up when done
git worktree remove ../project-hotfix
This is invaluable when you are deep in a feature branch and a production bug comes in that needs an immediate fix. No stashing, no context switching drama.
git log: Finding Things Faster
The default git log output is noisy. These flags make it actually useful:
# Compact one-line view with graph
git log --oneline --graph --decorate
# Search commit messages
git log --grep="authentication"
# Find commits that changed a specific function
git log -L :functionName:path/to/file.ts
# See what changed in a specific file over time
git log --follow -p src/components/Button.tsx
# Find who wrote a specific line (blame with context)
git blame -L 42,55 src/utils/auth.ts
The -L :functionName:file flag is particularly useful — it shows you the entire history of changes to a specific function, not just the file.
git cherry-pick: Grab One Commit From Anywhere
If you committed something to the wrong branch and need to bring it to another branch without merging everything:
# Get the commit hash from git log or reflog
git cherry-pick a3b1c2d
# Cherry-pick without committing (apply as staged changes)
git cherry-pick --no-commit a3b1c2d
# Cherry-pick a range of commits
git cherry-pick a3b1c2d..f7e9d1a
Useful Aliases to Set Up Once
Add these to your ~/.gitconfig and save yourself keystrokes every day:
[alias]
st = status
co = checkout
br = branch
lg = log --oneline --graph --decorate --all
undo = reset HEAD~1 --mixed
amend = commit --amend --no-edit
aliases = config --get-regexp alias
git undo is the one I use most — it un-commits the last commit while keeping your changes staged, so you can fix something and recommit cleanly.
Conclusion
Git rewards investment. Spending a few hours with these commands will save you time every week for the rest of your career. The most important ones to internalize first: reflog for recovering from mistakes, bisect for tracking down bugs, and rebase -i for keeping your history readable.
The rest will come when you need them. Now you know they exist.