A fix landed on main. Your feature branch still needs that one commit — not the rest of main, and not a full merge. The inverse happens too: one commit on a feature must go to main now, without the unfinished work beside it. That is cherry-pick: copy a commit’s patch onto wherever HEAD is.

Branch create/switch/merge lives in Stay Off Main. Conflict markers live in When Push Fails. Replaying a whole branch is a different job — Git Rebase. This page is only “one commit, other branch.”

Warm-up: cherry-pick lab

Reuse the bare-remote pattern so you can push without GitHub. From a scratch folder:

mkdir -p git-cherry-lab
cd git-cherry-lab
git init --bare remote.git
git clone remote.git app
cd app

echo 'App v1' > README.md
echo 'timeout=30' > config.txt
git add README.md config.txt
git commit -m "Initial app files"
git branch -M main
git push -u origin main

Create a feature branch with work that is not ready for main:

git switch -c feature/login
echo 'Login form placeholder' > login.txt
git add login.txt
git commit -m "Add login placeholder"
git push -u origin feature/login

Land a hotfix on main while the feature is still in flight:

git switch main
echo 'timeout=10' > config.txt
git add config.txt
git commit -m "Hotfix: lower timeout"
git push origin main

Everything below runs inside app/ unless noted. Delete git-cherry-lab when you finish.

Note: Prefer git switch for changing branches. Older docs use git checkout; the switch forms below are clearer for day-to-day branch moves.

Mental model: copy the patch, new SHA

Cherry-pick copies a commit’s patch onto HEAD. Git reads the chosen commit, applies that diff to your current tree, and makes a new commit. The original stays on its branch.

That new commit has a new SHA. Same message and (usually) the same file changes — not the same object. Cherry-pick is a copy, not a move. main still has the hotfix after you pick it onto feature/login.

Find the hotfix SHA from main (the tip, after the lab above):

git log --oneline main -n 3

Typical output (your hashes will differ):

a1b2c3d (main, origin/main) Hotfix: lower timeout
e4f5g6h Initial app files

Note: You can pass a branch name (git cherry-pick main) when the commit you want is that branch’s tip. A SHA is clearer once main has moved on.

Hero use case: hotfix on main, needed on the feature

feature/login still has timeout=30. main already shipped timeout=10. You want that one fix on the feature without merging all of main.

See the split

Switch to the feature and look at both tips:

git switch feature/login
git log --oneline --decorate --graph --all -n 10

You should see the login commit on the feature and the hotfix only on main. They share the initial commit, then diverge.

Cherry-pick the hotfix

Copy the hotfix SHA from git log, then pick it onto the feature:

git cherry-pick a1b2c3d

Git applies the timeout change and commits it on feature/login. Confirm the graph:

git log --oneline --decorate --graph --all -n 10

Sample shape (hashes will differ):

* c9d8e7f (HEAD -> feature/login) Hotfix: lower timeout
* b6c5d4e (origin/feature/login) Add login placeholder
| * a1b2c3d (main, origin/main) Hotfix: lower timeout
|/
* e4f5g6h Initial app files

Same message on two commits; two SHAs. config.txt on the feature now matches the hotfix. login.txt is still only on the feature — you did not merge main.

Note: The original hotfix commit is still on main. Cherry-pick did not move it. Later, when you merge the feature, Git may already have equivalent changes on both sides — that is expected.

Record the original SHA with -x

A copied commit does not remember where it came from unless you ask. -x appends a trailer with the source SHA so later git log still points at the original.

Add a second hotfix on main, then pick it with -x:

git switch main
echo 'App v1 (patched)' > README.md
git add README.md
git commit -m "Hotfix: mark README as patched"
git push origin main
git log --oneline -n 1

Switch back and pick with the trail recorded:

git switch feature/login
git cherry-pick -x <readme-hotfix-sha>
git log -1

The new commit message includes the source:

    Hotfix: mark README as patched

    (cherry picked from commit d4e5f6a...)

Note: Use -x when the copy will live on a shared branch and someone may need to find the original. Skip it for throwaway local experiments.

Conflicts: continue or abort

Cherry-pick applies a patch. If your branch already changed the same lines, Git pauses with the same conflict markers as a merge.

Put a conflicting timeout on the feature, then add another hotfix on main:

git switch feature/login
echo 'timeout=60' > config.txt
git add config.txt
git commit -m "Raise timeout for local login tests"

git switch main
echo 'timeout=5' > config.txt
git add config.txt
git commit -m "Hotfix: emergency timeout"
git push origin main

Pick the emergency fix onto the feature:

git switch feature/login
git cherry-pick <emergency-sha>

config.txt has markers: HEAD is the feature (timeout=60); the other side is the commit you picked (timeout=5). Edit to the result you want, delete the marker lines (<<<<<<<, =======, >>>>>>>), and stage — same anatomy as When Push Fails.

git add config.txt
git cherry-pick --continue

Git offers the original hotfix message. Keep it or edit, then save. If you picked with -x, that trailer is already in the message.

To cancel instead of finishing, abort from the conflicted state — the branch returns to how it was before the pick:

git cherry-pick --abort

Do not leave a cherry-pick half-finished. Either --continue after git add, or --abort. git status tells you when a pick is in progress.

Note: --abort drops only the in-progress pick. Commits already on the feature stay. A finished cherry-pick is a normal commit; this command does not delete it.

When merge or rebase is the better tool

Cherry-pick is for a named commit (or a few) when you do not want the rest of that branch yet.

Bring all of main into the feature with a merge, as in Stay Off Main:

git switch feature/login
git fetch origin
git merge origin/main

Replay the whole feature on top of updated main (linear history, no merge commit) with Git Rebase — a different job, not a second way to copy one hotfix.

The inverse of the hero: one commit on the feature must ship on main now, without the rest of the feature.

git switch main
git cherry-pick <feature-only-sha>
git push origin main

Note: Repeated cherry-picks of the same work make duplicate commits that later merges have to sort out. If you find yourself picking a long list, you probably wanted merge or rebase instead.

Quick reference card

GoalCommand
Copy a commit onto HEADgit cherry-pick <sha>
Copy the tip of a branchgit cherry-pick main
Record original SHA in the messagegit cherry-pick -x <sha>
See both branchesgit log --oneline --decorate --graph --all -n 10
Find a SHAgit log --oneline main -n 5
Finish after resolving conflictsgit add <file> then git cherry-pick --continue
Cancel an in-progress pickgit cherry-pick --abort
Switch branchesgit switch feature/login

Practice drills

Inside git-cherry-lab/app (recreate the bare remote and clone if needed):

  1. From feature/login, cherry-pick the first timeout hotfix from main. Confirm git log shows two commits with the same message and different SHAs.
  2. Add a new commit on main, cherry-pick it onto the feature with -x, and confirm git log -1 includes (cherry picked from commit …).
  3. On feature/login, make a docs-only commit. Switch to main and cherry-pick that commit so main gets it without the rest of the feature.
  4. Make config.txt conflict (edit it on the feature, a different value on main), cherry-pick, resolve markers, git add, and --continue.
  5. Recreate a conflicting pick (do not reuse a SHA you already continued), then --abort and confirm git status is clean of cherry-pick state.

Solid answers — clear and portable:

git switch feature/login
git log --oneline main -n 3
git cherry-pick <timeout-hotfix-sha>
git log --oneline --decorate --graph --all -n 10

git switch main
echo 'note' >> README.md
git add README.md && git commit -m "Hotfix: README note" && git push
git switch feature/login
git cherry-pick -x <readme-note-sha>
git log -1

git switch feature/login
echo 'API docs' > docs.txt
git add docs.txt && git commit -m "Add API docs"
git switch main
git cherry-pick <docs-sha>
git log --oneline -n 3

# 4: after both sides changed config.txt
git switch feature/login
git cherry-pick <conflicting-sha>
# edit config.txt: remove markers, keep the result you want
git add config.txt
git cherry-pick --continue

# 5: new conflict on config.txt, then abort instead of continue
git cherry-pick <new-conflicting-sha>
git cherry-pick --abort
git status

If those five feel routine, you already have the habit: copy one commit when you need that patch, leave the rest of the branch alone, and use -x when the trail matters. Snapshot basics remain in Git Without the Panic. Whole-branch replay is Git Rebase. When you know it used to work and need to find the breaking commit, continue with git bisect.

Next optional step Binary-search history when you know it used to work. Find the Break: git bisect When Something Stopped Working