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
| Goal | Command |
|---|---|
Copy a commit onto HEAD | git cherry-pick <sha> |
| Copy the tip of a branch | git cherry-pick main |
| Record original SHA in the message | git cherry-pick -x <sha> |
| See both branches | git log --oneline --decorate --graph --all -n 10 |
| Find a SHA | git log --oneline main -n 5 |
| Finish after resolving conflicts | git add <file> then git cherry-pick --continue |
| Cancel an in-progress pick | git cherry-pick --abort |
| Switch branches | git switch feature/login |
Practice drills
Inside git-cherry-lab/app (recreate the bare remote and clone if needed):
- From
feature/login, cherry-pick the first timeout hotfix frommain. Confirmgit logshows two commits with the same message and different SHAs. - Add a new commit on
main, cherry-pick it onto the feature with-x, and confirmgit log -1includes(cherry picked from commit …). - On
feature/login, make a docs-only commit. Switch tomainand cherry-pick that commit somaingets it without the rest of the feature. - Make
config.txtconflict (edit it on the feature, a different value onmain), cherry-pick, resolve markers,git add, and--continue. - Recreate a conflicting pick (do not reuse a SHA you already continued), then
--abortand confirmgit statusis 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.