XplormityXplormity
HomeHandbooks
Browse
Xplormity

TLDR developer handbooks for
seasoned developers.

Handbooks

RustNestJSNext.jsGitDockerTypeScriptReactNode.jsDSASQLSystem DesignTailwind CSS

Site

HomeHandbooksAboutPrivacyTerms

Connect

GitHubTwitterLinkedIn

© 2026 Xplormity. All rights reserved.

HandbooksGitCherry-Pick, Bisect & Recovery

Cherry-Pick, Bisect & Recovery

cherry-pickbisectreflogrecoveryhandbook

TL;DR

  • Cherry-pick: Copy specific commits from one branch to another.
  • Bisect: Binary search to find which commit introduced a bug.
  • Reflog: Git's "undo history" — recover lost commits, branches, resets.
  • These three commands are advanced interview favorites.

Step 1: Cherry-Pick

Cherry-pick was invented for the common scenario where you need one specific fix from another branch without merging everything else. A hotfix committed to develop needs to go to main immediately, or a feature commit needs backporting to a maintenance branch. Without cherry-pick, you'd either merge entire branches (pulling in unready code) or manually recreate the changes. It copies commits by replaying their diffs, creating new commits with the same content but different SHA hashes.

Copy specific commits without merging the entire branch:

# Copy a single commit to current branch
git cherry-pick abc1234

# Copy multiple commits
git cherry-pick abc1234 def5678

# Copy a range (exclusive start, inclusive end)
git cherry-pick abc1234..def5678

# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit abc1234

# Cherry-pick from another remote
git fetch upstream
git cherry-pick upstream/main~3

When to Use Cherry-Pick

Scenario Example
Hotfix from develop to main Bug fix committed to develop, need it in production NOW
Backport to older version Feature in v3 needed in v2 maintenance branch
Selective feature extraction Only want 2 commits from a 20-commit branch

Handling Conflicts

git cherry-pick abc1234
# CONFLICT!

# Fix conflict, then:
git add .
git cherry-pick --continue

# Or abort:
git cherry-pick --abort

Cherry-Pick with Credit

# -x adds "(cherry picked from commit abc1234)" to message
git cherry-pick -x abc1234

# Preserve original author
git cherry-pick --signoff abc1234

Step 2: Git Bisect — Finding Bug Sources

Bisect automates the tedious process of "which commit broke this?" by using binary search across your commit history. Instead of checking commits one by one (linear search through potentially hundreds of commits), bisect halves the search space each step, finding the culprit in ~7 steps even across 100 commits. It was invented because bugs often surface days after introduction, and manually testing each commit between "last known good" and "first known bad" doesn't scale. Automated bisect with a test script can find bugs while you get coffee.

Bisect uses binary search to find which commit introduced a bug. Instead of checking every commit, it halves the search space each time.

Manual Bisect

# Start bisecting
git bisect start

# Mark current commit as bad (has the bug)
git bisect bad

# Mark a known good commit (before the bug existed)
git bisect good v2.0.0   # or a commit hash

# Git checks out the middle commit. Test it, then:
git bisect good   # if this commit doesn't have the bug
# OR
git bisect bad    # if this commit has the bug

# Git narrows down and checks out next middle commit
# Repeat until Git identifies the first bad commit

# When done:
git bisect reset  # Go back to where you started

Automatic Bisect (with test script)

# Automatically run a test to determine good/bad
git bisect start HEAD v2.0.0
git bisect run npm test

# Or with a custom script
git bisect run ./test-bug.sh
# Script should exit 0 for good, 1 for bad

Example: Finding Performance Regression

# Setup
git bisect start
git bisect bad HEAD                    # Current: slow
git bisect good abc1234                # 2 weeks ago: fast

# At each step, run your benchmark:
# If response time < 200ms → git bisect good
# If response time > 500ms → git bisect bad

# After ~7 steps (for 128 commits), Git shows:
# abc1234 is the first bad commit
# Author: Someone
# Date: ...
# Message: "optimize: add caching layer"  ← ironic!

Bisect Tips

  • Log: git bisect log shows your bisect history
  • Replay: git bisect replay bisect.log re-runs a saved session
  • Skip: git bisect skip if a commit can't be tested (broken build)
  • Visualize: git bisect visualize opens gitk at current bisect state

Step 3: Reflog — Your Safety Net

Reflog is Git's hidden undo system that records every time HEAD moves — even operations that "delete" history like reset --hard, rebase, or branch -D. It was built as a safety net because Git's power means it's easy to lose work: a bad rebase, an accidental force push, or dropping a stash. Reflog entries survive for 90 days by default, meaning almost any mistake is recoverable if you know where to look. It's the first place to check when you think you've lost commits.

Reflog records every time HEAD moves. It's your undo button for almost anything:

# View reflog (recent HEAD movements)
git reflog
# Output:
# abc1234 HEAD@{0}: commit: add feature
# def5678 HEAD@{1}: checkout: moving from main to feature
# ghi9012 HEAD@{2}: reset: moving to HEAD~3
# jkl3456 HEAD@{3}: commit: important work that got "lost"

Recovery Scenarios

Recover after git reset --hard

# You accidentally ran:
git reset --hard HEAD~5  # "Lost" 5 commits!

# But they're still in reflog:
git reflog
# Shows the commit before reset

# Recover:
git reset --hard HEAD@{1}
# Or create a branch at the lost commit:
git branch recovery abc1234

Recover a deleted branch

# You deleted a branch:
git branch -D feature/important  # Oops!

# Find it in reflog:
git reflog | grep "feature/important"
# Or find the last commit on that branch:
git reflog --all | grep "feature/important"

# Recreate the branch:
git branch feature/important abc1234

Undo a bad rebase

# Rebase went wrong:
git rebase -i main  # Made a mess

# Find pre-rebase state in reflog:
git reflog
# Look for "rebase (start)" entry — the one BEFORE it is your safe state

# Reset to pre-rebase state:
git reset --hard HEAD@{5}  # wherever it was before rebase

Recover after git commit --amend

# Amended a commit but want the original back:
git reflog
# HEAD@{1} is the original commit before amend

git reset --soft HEAD@{1}  # Get original commit back

Step 4: Stash — Temporary Storage

Stash was created for the constant interruption scenario: you're mid-feature when a critical bug needs immediate attention on another branch. You can't switch branches with uncommitted changes (they'd carry over or conflict), but you don't want to commit half-finished work. Stash saves your working directory and index state to a stack, gives you a clean tree to switch branches, and lets you reapply changes later. It's also useful for temporarily shelving experimental changes while you try a different approach.

# Save current changes without committing
git stash

# Save with a description
git stash push -m "WIP: login form validation"

# Stash specific files
git stash push -m "partial work" src/auth.ts src/login.tsx

# List all stashes
git stash list
# stash@{0}: On feature: WIP: login form validation
# stash@{1}: On main: debugging session

# Apply most recent stash (keep in stash list)
git stash apply

# Apply and remove from stash list
git stash pop

# Apply specific stash
git stash apply stash@{2}

# Drop a specific stash
git stash drop stash@{1}

# Clear all stashes
git stash clear

# Create branch from stash (if stash conflicts with current state)
git stash branch new-branch stash@{0}

Step 5: Worktrees — Parallel Development

Worktrees solve the same problem as stash but better: working on multiple branches simultaneously without constantly stashing and switching. Each worktree is a separate checkout of the same repository at a different path, sharing the same .git directory. You can have main open in one terminal for hotfixes while your feature branch stays untouched in another. They're faster than cloning the repo twice and share the same fetch/push remotes and reflog.

Work on multiple branches simultaneously without stashing:

# Add a worktree (checks out branch in a new directory)
git worktree add ../hotfix-branch hotfix/critical-bug

# Now you have:
# /project/         ← main branch (original)
# /hotfix-branch/   ← hotfix branch (separate working tree)

# Work in the hotfix directory:
cd ../hotfix-branch
# Make fixes, commit, push — without touching your main work

# List worktrees
git worktree list

# Remove when done
git worktree remove ../hotfix-branch

Use Cases

  • Fix urgent bug while in the middle of a feature
  • Run tests on one branch while developing on another
  • Compare behavior across branches side-by-side

Step 6: Advanced Recovery Toolkit

These commands handle the edge cases where reflog isn't enough: orphaned commits from squashed PRs, corrupted pack files, or objects that have expired from reflog. git fsck --lost-found finds dangling commits not referenced by any branch or reflog entry. git log --all --full-history -- <path> finds when a file was deleted. These are last-resort tools that save you from the rare but catastrophic scenarios where work seems permanently lost.

Find Lost Commits (no reflog reference)

# Find all dangling/unreachable commits
git fsck --lost-found

# The commits are saved in .git/lost-found/
# Inspect them:
git show abc1234

Undo the Last Commit (keep changes)

git reset --soft HEAD~1
# Changes are staged, ready to re-commit differently

Fix the Wrong Branch

# Committed to main instead of feature:
git branch feature        # Create feature branch at current commit
git reset --hard HEAD~1   # Move main back one commit
git checkout feature      # Continue on feature

Interview Questions

  1. When would you use cherry-pick vs merge?

    • Cherry-pick for copying specific commits (hotfixes, backports). Merge for integrating entire branches. Cherry-pick doesn't record the relationship between branches.
  2. How does git bisect work?

    • Binary search across commits. You mark one commit as "good" and one as "bad". Git checks out the midpoint, you test it, and it halves the search space. O(log n) instead of O(n).
  3. What's the reflog and when is it useful?

    • A log of all HEAD movements. Useful for recovering from mistakes: accidental resets, deleted branches, bad rebases. It's your safety net.
  4. What's the difference between git stash and git worktree?

    • Stash saves changes and reverts your working directory (single directory). Worktree creates a separate directory for another branch — lets you work on both simultaneously.
Interactive Rebase & Clean HistoryBranching Strategies & Workflows

On this page

  • TL;DR
  • Step 1: Cherry-Pick
  • When to Use Cherry-Pick
  • Handling Conflicts
  • Cherry-Pick with Credit
  • Step 2: Git Bisect — Finding Bug Sources
  • Manual Bisect
  • Automatic Bisect (with test script)
  • Example: Finding Performance Regression
  • Bisect Tips
  • Step 3: Reflog — Your Safety Net
  • Recovery Scenarios
  • Step 4: Stash — Temporary Storage
  • Step 5: Worktrees — Parallel Development
  • Use Cases
  • Step 6: Advanced Recovery Toolkit
  • Find Lost Commits (no reflog reference)
  • Undo the Last Commit (keep changes)
  • Fix the Wrong Branch
  • Interview Questions