Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXIII · Cherry-PickRanges

Cherry-picking multiple commits — ranges and batches

Advanced⏱ ~20 mingit

What you'll learn

  • Use `git cherry-pick A..B` to pick every commit reachable from `B` but not from `A`
  • Predict the inclusive/exclusive boundary of the range notation
  • Combine multiple cherry-picks into a single commit with `-n` to keep the batch atomic
  • Recognise that the range replays commits in chronological order, not the order they appear on the branch
  • Stop a multi-commit cherry-pick cleanly when an intermediate commit conflicts irrecoverably

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

Not yet marked complete on this device.

Cherry-pick is most often shown as a one-commit operation, but it scales: a range of commits can be replayed in one command, and a batch can be combined into a single commit. The range notation, the order of replay, and the no-commit flag are the three things that distinguish a multi-commit cherry-pick from a one-commit one.

The A..B range notation

The two-dot range A..B means “every commit reachable from B that is not reachable from A”. The upper bound B is included; the lower bound A is excluded:

OLDEST=abc1234
NEWEST=def5678
git cherry-pick $OLDEST..$NEWEST
# picks every commit strictly between abc1234 (exclusive) and
# def5678 (inclusive), in chronological order

This is the same range notation git log A..B uses, and the semantics are identical. If you want a range that includes both endpoints, use A^..B (which means “reachable from B but not from the parent of A”).

gitGraph
    commit id: "A (excluded)"
    commit id: "c1"
    commit id: "c2"
    commit id: "B (included)"

A common production case: backport every fix tagged between two release tags. Resolve the tags to commit hashes, then run the range cherry-pick.

Combining a batch with —no-commit (-n)

A range cherry-pick by default creates one commit per picked commit. For a backport of ten small fixes, that produces ten new commits on the maintenance branch — readable, but noisy. The -n flag applies all the changes to the working tree and index without committing, and the next git commit creates one combined commit:

OLDEST=abc1234
NEWEST=def5678
git cherry-pick -n $OLDEST..$NEWEST
git status
# both modified:   several files...
git commit -m "backport: combined fixes from abc1234..def5678"

The combined commit is a single OID, single message, single audit entry. The trade-off is that the per-commit history of the source branch is collapsed into one on the destination branch — useful when the source commits are trivial, lossy when they are not.

Chronological order and stop-on-conflict

The range is replayed in chronological order (parent before child), the same order git log --reverse A..B would print. If an intermediate commit conflicts and --no-commit is not used, the cherry-pick stops at that commit and leaves the operation in an in-progress state:

OLDEST=abc1234
NEWEST=def5678
git cherry-pick $OLDEST..$NEWEST
# CONFLICT in commit abc1235 (the second commit in the range)
git status
# both modified:   ...
git cherry-pick --continue  # after resolving
# picks abc1236, abc1237, ... up to def5678

The commits before the conflict are already committed; the commits after are not yet applied. --continue resumes with the commit after the one you resolved, and the range proceeds.

Picking merge commits

A merge commit has two parents. Cherry-picking a merge commit without guidance produces a non-obvious result because Git does not know which parent to treat as the merge base. The -m flag selects the mainline parent:

MERGE_COMMIT=abc1234
git cherry-pick -m 1 $MERGE_COMMIT
# -m 1 means "treat parent 1 as the mainline"; the diff is computed
# against parent 1

-m 1 is almost always what you want: parent 1 is the branch the merge brought into, and the cherry-pick should replay “what the merge added on top of parent 1” rather than “the entire history from parent 2”. Picking a merge commit without -m is a common production mistake; the resulting diff is usually much larger than expected because Git picks the wrong mainline.

UnderTheHood: how the range is enumerated

git cherry-pick A..B runs git rev-list A..B internally to enumerate the commits, then calls the same single-commit replay machinery once per commit. The -n flag is a per-commit flag that suppresses the final git commit step. The chronological order is enforced by git rev-list, which emits commits in parent-first order by default. This is why a conflict in the middle of the range stops the whole operation: the replay loop is sequential.

Production discipline

The production discipline for range cherry-picks has four rules:

  1. Always preview the range with git log A..B --oneline before picking. The range is a textual contract; verify what you are about to replay before you replay it.
  2. Use -n for batches of trivial fixes; use the default per-commit behaviour for batches where each commit is meaningful. The choice is about readability on the target branch, not about correctness.
  3. Treat the batch as a unit: review the combined diff with git diff HEAD~1 (after committing) or git diff --cached (before committing when using -n). A range cherry-pick is easy to run by accident and hard to spot in review without the combined diff.
  4. Always use -x on every picked commit. Without -x, the range cherry-pick produces commits with no source-hash trail, and the duplicated history is untraceable.

Cross-course references

  • Git, CI/CD & GitOps — Part XI (Rebasing)git rebase --onto is the explicit way to pick a range with a new base; it is the alternative when the source branch is not the natural starting point for the replay.
  • Git, CI/CD & GitOps — Part IV (History)git rev-list and range enumeration are the same machinery git log A..B uses.
  • Ansible for Production Sysadmins — Part XXXVII (RepoArch) — multi-commit backports of playbook refactors follow the same range-then-commit discipline.

Quiz

Knowledge check · 4 questions

  1. Q1. An engineer runs `git cherry-pick abc1234..def5678`. Which commit is included in the range?

  2. Q2. Without `-n`, a range cherry-pick that conflicts on the third commit of ten leaves the target branch with the first two commits applied and no record of the remaining seven.

  3. Q3. Which flag combines a batch of cherry-picks into a single commit by accumulating changes in the index, and what command must be run afterwards to create the commit?

  4. Q4. Plan a multi-commit backport of ten trivial fixes from `main` to `release-1.x`, choosing between per-commit and combined-batch behaviour.

    Between `v1.10.0` and `v1.11.0`, ten small bug-fix commits landed on `main`. None of them change the public API. The team wants all ten on `release-1.x` for the next patch release. Each fix touches a different file, so no commit conflicts with the next. The maintenance branch is two months behind `main`.

Passing score: 75%. Answers are checked in this browser.