Tutorials

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

Developer in a time machine control room in front of a giant map of branching timelines

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.

Bisect turns two hundred suspect commits into about eight tests — fully automatable with a test script.

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 list full of WIP on main entries 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).
The five capabilities that separate driving Git from surviving it.

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.