5 Git Commands Beyond Commit and Push
Five Git commands beyond commit and push: stash, reflog, cherry-pick, rebase -i, and bisect for recovery, cleanup, and bug hunting.
Most developers live in add, commit, push, and pull, but Git’s real leverage is in the recovery, history-editing, and debugging commands that the daily loop never touches.
You tend to meet the rest of them the hard way. A reset --hard swallows an afternoon of work, or a bug turns up somewhere in 200 commits and nobody can say which one broke it. The five below solve problems the basics can’t: shelving half-done work, recovering commits you thought were gone, moving a single fix between branches, cleaning up messy history before a PR, and pinpointing the exact commit that introduced a bug. Each comes with a mental trigger, the exact syntax, and the one gotcha that trips people up.
Key Takeaways
git stashshelves your half-done work and returns a clean working tree, letting you switch context without a throwaway commit.git reflogrecords every place HEAD has pointed, so after a bad reset or rebase you can find the lost commit’s hash and restore it withgit reset --hard <ref>.git cherry-pick <hash>copies a single commit onto your current branch, which is ideal for moving one hotfix to a release branch without merging everything else.- History-rewriting commands like
git rebase -iandgit reset --hardare safe on local, unpushed work and dangerous on shared branches;git reflogis your recovery path when one goes wrong. git bisectbinary-searches your history to find the exact commit that introduced a bug in log₂(n) steps.
Discover how at OpenReplay.com.
git stash: shelve work to switch context fast
Use this when a bug report lands while your working tree is a half-finished mess and you need to jump branches now. git stash parks your uncommitted changes somewhere safe and hands the working tree back to you clean, matching your last commit. No throwaway “WIP” commit needed.
git stash # shelve tracked changes, clean the tree
git stash --include-untracked # also shelve new, untracked files
git stash list # see all stashes: stash@{0}, stash@{1}, ...
git stash pop # reapply the latest stash and delete it
git stash apply stash@{1} # reapply a specific stash, keep it in the list
The gotcha: pop and apply are not interchangeable. git stash pop applies your shelved changes and deletes the stash; git stash apply applies them but keeps the stash, which is safer if the reapply might conflict. If a pop hits a merge conflict, the stash is not dropped. Resolve the conflict first, then check before assuming it’s gone.
git reflog: recover work you thought you lost
Use this when a bad reset --hard, a botched rebase, or a deleted branch makes commits vanish and panic sets in. git reflog records every position HEAD has held in your local repo, so the “lost” commit almost always still exists. You just need its hash.
git reflog
# ab12cd3 HEAD@{0}: reset: moving to HEAD~1
# ef45gh6 HEAD@{1}: commit: the work you thought vanished
git reset --hard ef45gh6 # restore HEAD to that commit
# or, to inspect it on a new branch first:
git switch -c recovery ef45gh6
Find the ref for the state you want, then git reset --hard <ref> to jump back to it, or check it out on a new branch to inspect it safely first. This is the “oh no, I lost my work” hero of the toolkit.
The gotcha: the reflog is strictly local and per-clone. It does not travel to the remote and it does not exist in a fresh clone, so re-cloning to “get your work back” won’t help. Entries are also pruned over time by garbage collection, so recover sooner rather than later.
git cherry-pick: move one commit between branches
Use this when a single hotfix commit needs to land on a release branch, but you don’t want to merge the entire feature branch it came from. git cherry-pick <hash> copies one specific commit onto your current branch, leaving everything else behind.
git switch release-2.4
git cherry-pick 9f3c1a7 # apply one commit here
git cherry-pick 9f3c1a7 3b8e2d0 # apply several, in order
The gotcha: cherry-pick creates a new commit with a new hash. The original isn’t preserved or moved. If that same commit later reaches this branch through a normal merge, you can end up with duplicate changes in the history. Use it deliberately, not as a routine substitute for merging.
git rebase -i: clean up history before a PR
Use this when your feature branch has five “fix typo” and “wip” commits and you want a tidy, reviewable history before opening a pull request. git rebase -i <base-branch> opens an editor where you can reorder, squash, edit, or drop commits.
git rebase -i main
# pick a1b2c3d Add feature scaffold
# squash e4f5g6h fix typo
# reword h7i8j9k Wire up handler
# drop k0l1m2n debug logging
Marking a commit squash merges it into the one above it, reword changes only its message, and drop leaves it out of the result entirely. If you only need to collapse a whole branch into one staged change, git merge --squash <branch> is the sibling tool. It stages the combined changes without creating a commit, so you still run git commit yourself.
The gotcha, and the golden rule: rewrite history freely while it’s still local, but never rebase commits you’ve already pushed to a shared branch. Rewriting shared history forces everyone else into painful reconciliation, a hazard Pro Git calls “The Perils of Rebasing”.
git bisect: find the commit that introduced a bug
Use this when something worked last week and is broken now, and you have no idea which of 200 commits did it. git bisect runs a binary search through history: mark one bad commit and one good commit, test the midpoints Git checks out, and it pinpoints the first bad commit in log₂(n) steps.
git bisect start
git bisect bad # current commit is broken
git bisect good v2.3.0 # this older tag worked
# Git checks out a midpoint — test it, then tell Git:
git bisect good # this one is fine
git bisect bad # this one is broken
# ...repeat until Git reports "<hash> is the first bad commit"
git bisect reset # return HEAD to where you started
You can automate the whole search: git bisect run <script> uses a script’s exit status to mark each commit good or bad with no manual input. End every session with git bisect reset to return HEAD to where you started before the search.
The gotcha: bisect assumes each checked-out commit builds and runs your test cleanly. A commit that fails to compile for an unrelated reason skews the result. Mark those with git bisect skip rather than guessing good or bad.
A safety net for the destructive ones
Two of these commands rewrite history, and the rule for both is the same: git rebase -i and git reset --hard are safe on local, unpushed work and dangerous on shared branches. When you need a safe undo on a branch others have pulled, prefer git revert, which records a new commit that reverses the change without altering existing history. And when a local rewrite goes wrong, git reflog is the escape hatch that gets your commits back.
These five earn their place because each answers a question commit and push can’t: save this, recover that, move one, tidy up, hunt the bug. Wire each one to its trigger, and reach for it the moment the situation shows up.
FAQs
What is the difference between git reset --hard and git revert?
git reset --hard rewinds HEAD and discards commits, rewriting history, which makes it dangerous on any branch others have pulled. git revert instead records a new commit that reverses the target change while leaving existing history intact. Use reset --hard only on local, unpushed work; use revert as the safe undo on shared branches. If a reset goes wrong, git reflog can recover the discarded commits by hash.
How do I recover a commit after git reset --hard deleted it?
Run git reflog, which records every position HEAD has held in your local repository. Find the entry for the state you want, note its hash, then run git reset --hard <hash> to restore it, or git switch -c recovery <hash> to inspect it on a new branch first. The reflog is local and per-clone, so re-cloning will not recover the work, and entries are pruned over time by garbage collection, so recover sooner rather than later.
Does git switch replace git checkout for changing branches?
git switch and git restore were introduced in Git 2.23 (August 2019) to split git checkout's overloaded roles: switch handles moving between branches and restore handles restoring files. Both manual pages carried an experimental warning for years, but that warning was dropped as of Git 2.55.0, so they are now simply the recommended commands for those operations. git checkout continues to work and is not deprecated, so existing workflows remain valid.
When should I use git cherry-pick instead of merging?
Use git cherry-pick when you need exactly one commit on another branch (for example, moving a single hotfix to a release branch) without pulling in the rest of the source branch's history. A merge brings the entire branch's changes, which is the wrong tool for isolating one fix. Note that cherry-pick creates a new commit with a new hash, so if the same change later arrives through a normal merge you can end up with duplicate changes in the history.