Git Beyond the Basics: Rebase, Cherry-Pick, Bisect and Friends
You know add, commit, push. Here's the next tier of Git — interactive rebase, cherry-pick, bisect, reflog and worktrees — explained with the scenarios where each one saves you.
Marcus Webb
Senior Writer
Hugo Nina
Fact Checked
Most developers learn exactly enough Git to survive: add, commit, push, pull, make a branch, open a PR. That’s fine — until the day you need to untangle three unrelated changes accidentally piled into one branch, or find which of two hundred commits broke the build, or recover the branch you just obliterated.
This is the second tier of Git: five capabilities that turn the tool from a save button into an actual instrument. Each comes with the real scenario where you’ll reach for it.
Interactive rebase: edit history before publishing it
Scenario: your feature branch works, but its history reads wip, fix typo, actually works now, oops. Nobody reviewing your PR benefits from that archaeology.
git rebase -i lets you rewrite a range of commits — squash several into one, reword messages, reorder, or drop them:
git rebase -i main
Git opens a plan file listing your branch’s commits, oldest first:
pick a1f9c3d Add password reset endpoint
pick 8823bd1 wip
pick 41c07ee fix typo
pick 9d2fa10 Add reset email template
Change verbs, save, close:
pick a1f9c3d Add password reset endpoint
squash 8823bd1 wip
squash 41c07ee fix typo
pick 9d2fa10 Add reset email template
Result: two clean, reviewable commits. The shortcut worth wiring into your hands: commit follow-up fixes with git commit --fixup a1f9c3d, then git rebase -i --autosquash main files them into place automatically.
The golden rule: rebase rewrites commits into new commits (new hashes). Do it freely on branches only you have; never on branches others have pulled — that’s how you generate the fabled “force-push destroyed my morning” incident. For shared branches, merge.
Cherry-pick: transplant one commit
Scenario: a critical fix landed on main, and you need exactly that fix on the release-2.4 branch — not the forty other commits around it.
git switch release-2.4
git cherry-pick f3a2b1c
Cherry-pick replays a single commit’s changes as a new commit on your current branch. It shines for backporting fixes and for rescuing specific commits from a branch that otherwise went sideways (“I’ll cherry-pick the two good commits onto a fresh branch and delete this mess” is a professional move, not an admission of failure).
Caveat: the transplanted commit is a copy with a different hash, so if the branches later merge, Git usually reconciles the duplication — but heavy cherry-pick workflows can produce confusing histories. It’s a scalpel, not a strategy.
Bisect: binary search for the breaking commit
Scenario: the export feature worked two weeks ago; it’s broken today; two hundred commits happened in between; the git log gives you no clue.
You could check out commits one by one like an animal — or let Git binary-search:
git bisect start
git bisect bad # current commit is broken
git bisect good v2.3.0 # this tag was fine
Git checks out the midpoint commit. Test it, then declare git bisect good or git bisect bad; Git halves the range and repeats. Two hundred commits take about eight tests — and if you can express “broken” as a command’s exit code, Git does the whole thing unattended:
git bisect run npm test -- --grep "export"
Minutes later: f81d2aa is the first bad commit, with author, date and message. Combined with small, focused commits (see the rebase section — the tools reinforce each other), bisect turns “no idea when this broke” from a day of spelunking into a coffee break. When it fingers the culprit, git bisect reset returns you to the present.
Reflog: the undo history of your undo history
Scenario: you just ran the wrong git reset --hard and watched a day of work evaporate. Breathe.
Git’s reflog records every position your HEAD and branches have occupied for the last ~90 days — including states that no branch points to anymore:
git reflog
# 3f9e2a1 HEAD@{0}: reset: moving to origin/main
# 7c41b88 HEAD@{1}: commit: Implement CSV streaming ← there it is
# ...
git branch rescue 7c41b88
Your “lost” commit is now safely on a branch named rescue. The same trick recovers deleted branches, botched rebases and amends you regret: find the pre-disaster entry in the reflog, branch or reset to it, done.
Internalizing the reflog changes your relationship with Git. Once you know that committed work is almost impossible to truly lose, the scary commands stop being scary, and you start using the powerful workflows confidently. Corollary: commit early and often — the reflog can only save what was committed.
Worktrees: two branches, zero stashing
Scenario: you’re deep in a half-finished feature — files everywhere, tests red — and an urgent production fix lands in your lap. The traditional dance (stash, switch, fix, switch back, unstash, pray) is where mistakes love to happen.
Worktrees check out a second branch in a separate directory, sharing the same repository:
git worktree add ../myapp-hotfix hotfix/payment-timeout
Now ../myapp-hotfix contains the hotfix branch with its own working files, while your feature chaos stays untouched in the original folder. Open it in a second editor window, fix, test, push, then:
git worktree remove ../myapp-hotfix
No stashing, no mental context lost, no “why is this test failing — oh, it’s half my stashed changes.” Worktrees also pair beautifully with long-running builds and with running two versions side by side for comparison. Users of AI coding agents will recognize this as the isolation mechanism those tools use to work without trampling your checkout.
Supporting cast: three small greats
git stash push -m "spike: caching idea"— the humble stash improves enormously the moment you name your stashes.git stash listfull ofWIP on mainentries is a junk drawer; named stashes are a shelf.git log --oneline --graph --all— the X-ray view of what’s actually going on. Alias it. Every “Git is confusing” moment starts with not looking at the graph.git commit --amend— fold a forgotten file or a message typo into the last commit (unshared commits only — the golden rule again).
A workflow that ties it together
The tools above assume a habit worth stating explicitly: make small commits with intentional messages. Small commits make bisect precise, cherry-picks clean, rebases painless and reviews humane. Write messages that explain why — six months from now, Fix race condition when token refreshes during retry will earn your gratitude in a way fix bug never will. Our clean code guide makes the deeper argument: code (and history) is read far more than it’s written.
This is also the level of Git fluency that shows in hiring — interviewers and reviewers notice tidy branches and confident recovery. If you’re building a portfolio, our junior developer portfolio guide explains how commit history quietly becomes evidence of professionalism. And to keep all of this fast at the keyboard, the aliases-and-prompt section of our terminal setup guide will save you a few hundred keystrokes a day.
Final Thoughts
The gap between surviving Git and driving it is five commands wide: rebase to shape history before sharing, cherry-pick to move exactly what you mean, bisect to find breakage by search instead of memory, reflog to make disasters reversible, and worktrees to hold two contexts without juggling. None of them is conceptually hard; each becomes permanent the first time it rescues you.
Practice them on a toy repository this week — break things on purpose, recover them, bisect a planted bug. An hour of deliberate play, and the tool most developers fear quietly becomes one you trust.