Thuta Learning
AdvancedDevOps & Toolsintermediate

Cherry-pick and Bisect

What you'll walk away with

  • Explain the core ideas behind Cherry-pick and Bisect
  • Read the diagram and trace how state or data flows through the Git/GitHub workflow
  • Decide how this applies to your own project or team

Build the mental model

Cherry-pick and bisect are both surgical tools: instead of operating on a whole branch, they let you target one specific commit at a time, for two very different purposes.

Cherry-pick takes one commit from anywhere in the repository and reapplies just that commit's changes onto your current branch, creating a new commit with a new hash and the same content. The classic use case is a hotfix: a critical bug got fixed on one branch, and you need that exact fix on another branch right now, without merging in everything else that branch has accumulated.

  • Used sparingly for isolated, well-understood fixes, cherry-pick is genuinely useful.
  • Used as a habitual substitute for merging or rebasing whole branches, it quietly creates duplicate commits with different hashes for the same change, confusing history.

Bisect solves a different problem: given a bug that exists now but didn't exist at some point in the past, which commit introduced it? git bisect performs a binary search — you tell it one known-good commit and one known-bad commit, typically HEAD, and Git checks out a commit roughly halfway between for you to test. You report good or bad, and it narrows the range, converging on the exact commit in roughly log-base-2 of the candidate commits.

Cherry-pick
Applying the changes from one specific commit onto a different branch by creating a new commit with the same content but a new hash.
Bisect
A binary-search workflow (git bisect) that finds the exact commit that introduced a bug by repeatedly testing commits between a known-good and known-bad point.
text
CHERRY-PICK (COPY ONE COMMIT) AND BISECT (BINARY SEARCH)
--------------------------------------------------------
CHERRY-PICK

hotfix:  base---FIX
                  |
                  | git cherry-pick FIX
                  v
main:    base---update---FIX'   (new hash, same change)

BISECT

good                                   bad (HEAD)
 |---------|---------|---------|---------|
 c1        c2        c3        c4        c5
           git bisect checks c3 first (halfway)
           mark good/bad, range shrinks each step
           until the first bad commit is found

Connect it to a real scenario

The cherry-pick example builds two branches from one shared commit. On a hotfix branch we commit a fix, Fix null pointer crash. Meanwhile main moves forward with an unrelated commit, Update readme, so the branches genuinely diverge. Running git cherry-pick with the hotfix commit's hash while on main reapplies just that change; git log --oneline afterward shows the fix present with a brand new hash, different from the original, because it now has a different parent.

Mark the boundaries

git bisect start begins the search, git bisect bad marks the current HEAD as broken, and git bisect good marks the known-clean first commit.

Git checks out a midpoint

Git checks out a candidate roughly halfway between good and bad for you to test; you answer git bisect good or git bisect bad based on what you find.

Range narrows

After two rounds in this example, Git reports the exact first bad commit by hash and message.

Reset

git bisect reset returns you to your original branch afterward.

Try the working example

bash
export GIT_AUTHOR_NAME="Thuta Learner"
export GIT_AUTHOR_EMAIL="learner@example.com"
export GIT_COMMITTER_NAME="Thuta Learner"
export GIT_COMMITTER_EMAIL="learner@example.com"

# --- cherry-pick ---
mkdir cherry-pick-demo && cd cherry-pick-demo
git init -q -b main
export GIT_AUTHOR_DATE="2026-01-01T09:00:00"
export GIT_COMMITTER_DATE="2026-01-01T09:00:00"
echo "v1" > app.js
git add app.js
git commit -m "Initial commit"

git checkout -b hotfix
export GIT_AUTHOR_DATE="2026-01-01T09:30:00"
export GIT_COMMITTER_DATE="2026-01-01T09:30:00"
echo "v1-fixed" > app.js
git add app.js
git commit -m "Fix null pointer crash"

HASH=$(git rev-parse HEAD)
git checkout main
export GIT_AUTHOR_DATE="2026-01-01T10:00:00"
export GIT_COMMITTER_DATE="2026-01-01T10:00:00"
echo "unrelated change" > readme.txt
git add readme.txt
git commit -m "Update readme"

export GIT_AUTHOR_DATE="2026-01-01T10:30:00"
export GIT_COMMITTER_DATE="2026-01-01T10:30:00"
git cherry-pick $HASH
git log --oneline
cd ..

# --- bisect (separate repo, 5 commits, bug introduced in commit 3) ---
mkdir bisect-demo && cd bisect-demo
git init -q -b main

# check.sh is the automated test: exit 0 = good, exit 1 = bad (bug present).
printf "#!/bin/sh
grep -q RESULT=8 calc.sh
" > check.sh
chmod +x check.sh

write_commit() {
  echo "echo RESULT=$1" > calc.sh
  echo "$3" >> history.txt
  git add calc.sh check.sh history.txt
  GIT_AUTHOR_DATE="2026-01-02T0$2:00:00" GIT_COMMITTER_DATE="2026-01-02T0$2:00:00"     git commit -m "$3"
}

write_commit 8 9 "Add calc"
write_commit 8 10 "Add subtract"
write_commit 5 11 "Add multiply (introduces bug)"
write_commit 5 12 "Add divide"
write_commit 5 13 "Add docs"

FIRST_GOOD=$(git rev-parse HEAD~4)
git bisect start
git bisect bad HEAD
git bisect good $FIRST_GOOD
git bisect run ./check.sh
git bisect reset
cd ..
You should see
Cherry-pick — git log --oneline on main after the cherry-pick:

9fe14a1 Fix null pointer crash
5a74be8 Update readme
e4fa43b Initial commit

Note 9fe14a1 differs from the original hotfix commit's hash, 8c3ee50, because it now has a different parent.

Bisect — with check.sh scripted to exit 0 on the good behavior and 1 on the
bug,  drives the whole search unattended. Git
checks out each midpoint candidate, runs check.sh, and uses its exit code
as the good/bad answer automatically:

Bisecting: 1 revision left to test after this (roughly 1 step)
[<hash>] Add multiply (introduces bug)
running './check.sh'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[<hash>] Add subtract
running './check.sh'
<hash> is the first bad commit
commit <hash>
    Add multiply (introduces bug)

 calc.sh     | 2 +-
 history.txt | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)
bisect found first bad commit

Git correctly identifies "Add multiply (introduces bug)" as the first bad commit — exactly the one that changed calc.sh's RESULT value.

5-minute try-it

Reproduce the cherry-pick example with your own hotfix branch and confirm the cherry-picked commit's hash differs from the original. Then create six or seven commits where you know exactly which one introduces a bug (e.g. a function that starts returning the wrong value), and use git bisect start / bad / good to find it — confirm Git's answer matches the commit you know is guilty.

One important caution

Overusing cherry-pick as a shortcut instead of merging can leave a commit's history scattered across branches with different hashes, making it hard to tell what's actually been applied where.

Forgetting to run git bisect reset leaves the repository checked out at a detached-HEAD commit from the middle of the search instead of your original branch.

Git - git-bisect DocumentationGit & GitHub

Easy traps

  • Overusing cherry-pick as a shortcut instead of merging can leave a commit's history scattered across branches with different hashes, making it hard to tell what's actually been applied where.
  • Forgetting to run git bisect reset leaves the repository checked out at a detached-HEAD commit from the middle of the search instead of your original branch.
  • Always run `git status` before any destructive or history-rewriting command, to know exactly what state you're in.

Exercise

Reproduce the cherry-pick example with your own hotfix branch and confirm the cherry-picked commit's hash differs from the original. Then create six or seven commits where you know exactly which one introduces a bug (e.g. a function that starts returning the wrong value), and use git bisect start / bad / good to find it — confirm Git's answer matches the commit you know is guilty.

You'll know it worked when: Cherry-pick — git log --oneline on main after the cherry-pick: 9fe14a1 Fix null pointer crash 5a74be8 Update readme e4fa43b Initial commit Note 9fe14a1 differs from the original hotfix commit's hash, 8c3ee50, because it now has a different parent. Bisect — with check.sh scripted to exit 0 on the good behavior and 1 on the bug, drives the whole search unattended. Git checks out each midpoint candidate, runs check.sh, and uses its exit code as the good/bad answer automatically: Bisecting: 1 revision left to test after this (roughly 1 step) [<hash>] Add multiply (introduces bug) running './check.sh' Bisecting: 0 revisions left to test after this (roughly 0 steps) [<hash>] Add subtract running './check.sh' <hash> is the first bad commit commit <hash> Add multiply (introduces bug) calc.sh | 2 +- history.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) bisect found first bad commit Git correctly identifies "Add multiply (introduces bug)" as the first bad commit — exactly the one that changed calc.sh's RESULT value.

Cherry-pick and Bisect | Thuta Learning