Git cherry pick: move one commit to the branch that needs it

A fix is finished. It sits on a branch that also carries three other changes nobody has reviewed, and the release branch needs only the fix. Merging the whole branch would bring the other three along. That gap is what git cherry-pick exists for. The command itself is one line. The decisions around it are where the work is, because a pick duplicates a change rather than relocating it, and that duplication has consequences several days later when the two branches meet again.

What cherry-pick actually does to history

The manual page states the behaviour in one sentence: given one or more existing commits, apply the change each one introduces, recording a new commit for each. Two words in that sentence matter. A pick records a new commit, and the original stays exactly where it was.

The new commit carries the same patch and the same authorship, but it has a different parent, a different committer timestamp, and therefore a different hash. Nothing has moved. The repository now contains two commits that make the same change, on two branches, with two identities.

The command also requires a clean working tree, meaning no modifications relative to the HEAD commit. Uncommitted work has to be committed or stashed first, unless the -n flag is used, which is covered further down.

The duplication is the part worth thinking about before running anything. When the release branch and the development branch are later merged, Git has to reconcile two commits whose patches are identical. Most of the time this resolves without a conflict, because the content already matches on both sides. It stops being quiet when either copy was edited afterwards: the follow-up change on one side no longer applies cleanly against the other. This is the usual origin of a conflict that seems to come out of nowhere weeks after a backport.

So the honest description of what people mean by moving a commit is this: copy it to where it is needed, verify it there, and then decide separately whether the original should be dropped. The dropping is a different operation, and often the right answer is to leave the original alone.

The five steps the job needs

The sequence is short enough to memorise, and every step exists to catch a specific mistake.

git log --oneline -20 feature/checkout-fix
git switch release/2.7
git status
git cherry-pick 9f2c1ab
git show --stat HEAD

Reading the log first is not ceremony. Picking by hash requires the right hash, and a branch tip is rarely the commit that holds the fix. Switching before picking matters because the pick applies to whatever branch is currently checked out, and a pick landed on the wrong branch is a second cleanup job. git status confirms the tree is clean, which the pick demands anyway.

Several flags change the result in ways worth knowing:

-x appends a line reading "cherry picked from commit ..." to the message. The manual is specific about when this helps: use it between two publicly visible branches, such as backporting a fix to a maintenance branch, and avoid it when picking from a private branch, because the reference is useless to anyone who cannot resolve it. Note also that the trailer is added only for picks that complete without conflicts.

-e opens the message for editing before the commit is recorded. -s adds a Signed-off-by trailer. -S signs the commit.

--ff changes the shape of the result in one specific case. If the current HEAD is already the parent of the commit being picked, --ff fast forwards to that commit instead of creating a copy. The history then stays linear and no duplicate hash is created at all.

Picking ranges, merges, and commits that turn out empty

More than one commit can be named in a single invocation. git cherry-pick master~4 master~2 applies the fifth and third last commits reachable from master and creates two new commits.

Ranges work too, with a trap in the notation. No traversal is done by default, as though --no-walk were given, but a range feeds all of its arguments into a single revision walk. The manual illustrates the consequence with git cherry-pick maint master..next: that does not mean maint plus everything between master and next. Specifically, maint will not be used if it is already contained in master. Reading the set before applying it avoids the surprise:

git log --oneline --no-merges master..next
git cherry --abbrev -v release/2.7 next

The second command answers a related question. git cherry compares by patch rather than by hash, after removing whitespace and line numbers, and prints a minus sign for commits that already have an equivalent upstream and a plus sign for those that do not. It is the fastest way to see whether a backport has already happened. git log --cherry-mark --left-right A...B shows the same information in a log listing, marking equivalent commits with =.

Merge commits cannot normally be picked, because Git has no way to know which side of the merge is the mainline. -m 1 names the parent number, counting from one, and the change is then replayed relative to that parent.

Empty commits have their own set of rules. Picking a commit that was empty to begin with fails, and the manual points at an explicit git commit --allow-empty as the intended response. A commit that becomes empty because the change is already present in the target branch stops the pick so the situation can be examined, which is the default --empty=stop. --empty=drop discards such commits and --empty=keep records them. --keep-redundant-commits still works as a deprecated synonym for --empty=keep.

-n is the flag for combining. It applies each named commit to the working tree and the index without creating any commit, and it lifts the requirement that the index match HEAD. Three related fixes can be picked this way and committed once.

When the pick stops in the middle

A stopped pick leaves a well defined state, and knowing what that state is removes most of the anxiety around it.

The current branch and the HEAD pointer stay at the last commit that was made successfully. A ref called CHERRY_PICK_HEAD is set to point at the commit that could not be applied. Paths that applied cleanly are updated in both the index and the working tree. Conflicting paths record up to three versions in the index, and the files themselves contain the conflict bracketed by the usual <<<<<<< and >>>>>>> markers. Nothing else is touched.

Four sequencer subcommands operate on the state stored in .git/sequencer:

git cherry-pick --continue
git cherry-pick --skip
git cherry-pick --abort
git cherry-pick --quit

--continue resumes after the conflict is resolved and staged. --skip drops the current commit and moves on to the rest of the sequence. The difference between the last two is the one people get wrong under pressure. --abort cancels the operation and returns to the state before the sequence started. --quit forgets the operation in progress and clears the sequencer state, leaving the working tree as it is.

The manual page includes a backport that fails and then succeeds, which is worth copying as a habit: pick, run git diff to see what needs reconciling, run --abort to get back to a clean tree, then pick again with -Xpatience, which spends extra time avoiding mistakes based on incorrectly matched context lines. Reaching for a different strategy option before hand-editing a conflict often turns a messy resolution into no resolution at all.

Cherry-pick against merge, rebase, and revert

Four commands move changes between branches, and the search term is only the right one for a subset of those jobs.

Command Effect on the current branch Effect on the source branch
git merge brings every commit reachable from the source unchanged
git cherry-pick copies only the commits named unchanged, originals remain
git rebase replays the current branch onto a new base the current branch is rewritten
git revert adds a new commit that undoes an earlier one unchanged

Merge is the right answer whenever the whole branch is wanted, and it is the cheaper answer because it creates no duplicate patches. Cherry-pick fits when a subset is wanted and the remainder is genuinely not ready. Rebase fits when the shape of the history matters more than preserving hashes, and it rewrites the branch it runs on, so it belongs on branches nobody else has pulled. Revert is the answer when a change has to be removed from a branch that other people already have, because it adds history rather than rewriting it.

The failure case is using cherry-pick as a habit instead of a decision. Repeatedly picking from a long-lived branch produces two histories that diverge in content while appearing to agree, and every future merge between them pays for it.

The commit habits that make picking safe

Most painful picks are caused by how the source commit was made, not by the pick.

One concern per commit is the whole discipline. A commit that contains the fix plus an unrelated rename brings the rename along, and there is no flag that separates them afterwards. git add -p stages selected hunks, which is how a mixed working tree becomes a set of pickable commits.

Pick from commits that will not be rewritten. If the source branch is rebased later, the hashes change, and a -x trailer then references a commit that no longer exists in the repository. Picking from merged or otherwise published commits keeps that reference meaningful.

Keep the direction consistent. Fixes normally travel from the development branch into the maintenance branch. Picking in both directions between the same pair of branches is how the same change ends up present in two different shapes.

Verify at the destination rather than trusting the exit status. git show --stat HEAD confirms which files the new commit touched, and comparing the branch against its remote counterpart before pushing confirms nothing else came with it:

git show --stat HEAD
git diff release/2.7 origin/release/2.7 --stat

Where the time goes on a Mac

Little of the effort in a backport is typing. It is reading a log in one window, switching branches in a second, watching a test run in a third, then opening a conflicted file in an editor that is showing a different directory than the shell is. Each of those transitions is small. A pick that stops twice can involve a dozen of them.

That is a layout problem rather than a Git problem. When a file list, a terminal, and an assistant share one working directory in a single window, the log output and the conflicted file are visible in the same place, and the switching between them stops being part of the task. What that arrangement covers is described on the Features page, and how it differs from dual pane browsers and terminal-first tools is set out on Compared with other file managers.

What to change first

Before the next backport, run git cherry against the target branch to check whether the change is already there, and read the commit with git show to confirm it contains only what the fix needs. If the window switching around those two commands is what actually costs the time, that part is solved by the layout of the tools rather than by learning more flags, and Atriens is one example of that shape.

Frequently asked questions

Does cherry-pick remove the commit from the branch it came from?

No. The original commit stays where it is, and the pick records a separate commit with the same patch on the current branch. Removing the original is a different operation, usually an interactive rebase on a private branch or a revert on a published one, and it is often unnecessary.

Why does the picked commit have a different hash?

A commit hash covers its parent, its tree, and its metadata including the committer timestamp. The copy has a different parent and a different commit time, so the hash differs even though the patch is identical. git cherry and git log --cherry-mark compare by patch content instead, which is why they can match the two.

What is the difference between cherry-pick --abort and --quit?

--abort cancels the whole operation and returns the repository to the state it was in before the sequence started. --quit forgets the operation in progress and clears the sequencer state without rewinding, leaving whatever is currently in the index and working tree. Use --abort to start over and --quit to keep a partial result.

Can a merge commit be cherry-picked?

Only with -m naming which parent counts as the mainline, because Git cannot infer which side of the merge the change should be measured against. git cherry-pick -m 1 <merge> replays the change relative to the first parent. Picking the individual commits from the merged branch is usually clearer than picking the merge itself.

Back to all posts