Git, CI/CD & GitOpsXIII · Cherry-PickFoundations
What cherry-pick does — replaying a single commit onto another branch
What you'll learn
- Explain what `git cherry-pick <commit>` does to the working tree, index, and branch tip
- Identify why the cherry-picked commit receives a new OID even when its diff is unchanged
- Use `git cherry-pick -x <commit>` to record the original commit hash in the new message
- Distinguish cherry-picking from merging in terms of which commits are replayed
- Recognise that cherry-pick introduces duplicated history that must be tracked
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git cherry-pick <commit> takes the changes introduced by a single
commit and replays them on top of the current branch as a brand-new
commit. The new commit is not a copy in any deep sense: it is a
fresh commit object with a fresh OID, a fresh tree, and the current
branch tip as its parent. The relationship between the original
commit and the cherry-picked commit is a textual relationship —
“this change came from there” — not a parent/child relationship.
That distinction is the entire lesson.
The shape of the operation
A cherry-pick is a one-commit rebase. Internally, Git computes the
same three-way merge it would use for git rebase or git merge,
but only for the single commit you named:
flowchart LR
A["main tip"] --> B["commit X (the source)"]
B --> C["parent of X"]
A --> D["current branch tip"]
D -- "cherry-pick X" --> E["new commit X' (different OID)"]
E --> D
The parent of X is the merge base. The current branch tip and X are
the two heads. Git produces a new tree that combines them, and
records it as a new commit whose parent is the current branch tip.
If X introduced a three-line change to deploy.yaml, the cherry
pick produces a new commit that introduces the same three-line
change — but the commit object is different because its parent is
different.
Why the new commit has a new OID
Two Git commits are equal if and only if every byte they reference
is identical: the tree, the author, the committer, the message, and
both parents. A cherry-picked commit always has a different
parent than the original (it points at the current branch tip, not
at the original parent of X), so its OID cannot be the same.
COMMIT=abc1234
git cherry-pick $COMMIT
git log -1 --format='%H %s'
# new OID, original subject line
This is why Git cannot help you by saying “this commit was already applied” — it has no parent-pointer relationship to compare against. The decision is made on the content of the change during the three-way merge, and if the merge succeeds, a new commit is produced unconditionally.
Recording the origin with -x
The -x flag appends a (cherry picked from commit <hash>) line
to the cherry-picked commit message. This is the only built-in
mechanism Git gives you to trace a cherry-picked commit back to its
source:
COMMIT=abc1234
git cherry-pick -x $COMMIT
git log -1 --format='%B'
# original subject
#
# (cherry picked from commit abc1234)
# Author: Original Author <author@example.com>
Use -x whenever the cherry-pick crosses a release boundary (for
example, a fix moving from main into release-2.x). The recorded
hash lets an auditor and a future engineer reconstruct what was
moved and where it came from. Without -x, the only trace of the
cherry-pick is the duplicated diff in two branches, and that trace
gets weaker with every subsequent commit.
UnderTheHood: the merge algorithm
The same merge machinery that powers git merge and git rebase
powers git cherry-pick. The merge base is the parent of the named
commit, and the two heads are the current branch tip and the named
commit. Because the merge base is not shared with the current
branch in the way a normal merge base is, three-way merge can
produce textual conflicts that a regular merge would not — the
“current branch tip” may have moved on from the merge base in
unrelated directions.
Production discipline
The production discipline for cherry-picking has three rules:
- Always pass
-xwhen crossing release boundaries. The recorded hash is your only proof that a fix moved between branches. Without it, the next person to investigate the duplicated change is guessing. - Prefer one well-described cherry-pick over many silent
ones. If you need ten commits from
mainin a release branch, cherry-pick them deliberately with-xand a release-notes entry; do not let the history diverge silently. - Never cherry-pick onto a branch that already has the change through a merge. A merge and a cherry-pick of the same change produce two different OIDs, and Git will not deduplicate them.
Cross-course references
- Git, CI/CD & GitOps — Part XI —
git rebaseis a loop of cherry-picks under the hood; the conflict and OID-rewriting rules are shared. - Git, CI/CD & GitOps — Part IX — Fast-forward and three-way merges use the same merge machinery; cherry-pick is the degenerate one-commit case.
- Ansible for Production Sysadmins — Part XXXVII (RepoArch) — the same “deliberate move with audit trail” pattern applies when backporting playbooks between release branches.
Quiz
Knowledge check · 4 questions
Q1. An engineer runs `git cherry-pick abc1234` on a branch whose tip is `main`. The diff applied to the working tree is identical to the diff in `abc1234`. Why is the resulting commit not recorded as `abc1234`?
Q2. Running `git cherry-pick -x <commit>` appends a `(cherry picked from commit <hash>)` line to the new commit message.
Q3. Name the flag that records the original commit hash in a cherry-pick, and explain what it writes into the new commit message.
Q4. Diagnose what has been lost when a fix is cherry-picked without `-x` between release branches.
A security fix lands on `main` as commit `f1x0a11`. Two weeks later it is needed on `release-2.x`. An engineer cherry-picks it onto `release-2.x` without `-x`, then `release-2.x` is cut and shipped. Six months later, a CVE is announced for code that `f1x0a11` partially mitigated, and the security team needs to know whether `release-2.x` contains the fix.
Passing score: 75%. Answers are checked in this browser.