Cherry-pick one change
Documentation/Native client workflows
Cherry-pick one change6 min read

Cherry-pick one change

On this page

Cherry-pick applies one ordinary accepted change from another branch in the same repository. Use it to backport a crash fix to a release branch without taking an unfinished feature, or bring a shared engine correction into a customized engine branch. A mount pin can then point the parent project at the resulting engine revision. First finish and submit the pick inside the child workspace. Then publish the parent pin, either with scribe project update followed by submit, or with scribe project publish from the parent. Project publication requires finished child resolution workflows; it cannot submit an active cherry-pick for you.

Example: backport a fix

Suppose development has change 40 (an unfinished renderer) and change 42 (a crash fix). The release branch should receive only the delta introduced by 42:

sh
scribe switch release
scribe cherry-pick 42 --from development
scribe status
# Review the resulting files in your editor before submitting.
scribe submit -m "Backport crash fix from development change 42"

--from must name the branch that accepted the change. A branch's history also reaches back through its origin into the branch it forked from, but selecting one of those inherited revisions is refused with change_not_found naming the branch to pick it from: recorded provenance identifies where the change was accepted, not which branch was asked for it. Select its accepting branch instead; the ordinary source-type, target, and permission checks still apply.

The source delta is the selected commit compared with its immediate first parent. It does not include every earlier source change. A fix that depends on earlier work may need that work selected separately; conflict-free application does not prove the program builds or behaves correctly. Run the project's checks before submitting.

Full and partial working copies are supported; partial-view integration explains excluded changes and conflicts. Submit or revert opened files and complete pending sync before starting. Unopened modifications to affected files prevent application; unrelated local edits are preserved. Explicit moves use the native rename-aware resolver. Repository lock rules apply across branches as usual: planning refuses a pick over a path another workspace holds and acquires the locks the policy requires for the paths it rewrites, which cherry-pick --abort then releases. See locks in merging.

Conflicts, interruption, and abort

Non-overlapping UTF-8 text edits inside the view are merged automatically by the CLI. Overlapping text, binary content, deletion conflicts and ambiguous renames need a decision:

sh
scribe resolve
scribe resolve --theirs Assets/material.bin
scribe resolve --ours Source/local-settings.cpp
# Edit a conflicted text file, then mark that complete result:
scribe resolve --working Source/crash.cpp
scribe cherry-pick --continue
scribe status
scribe submit -m "Backport crash fix with release-specific resolution"

--ours keeps the target result; --theirs takes the selected source result, including deletion. Keeping a different target result is submitted as an explicit keep decision, so the pick still accounts for the source change. Included paths appear in scribe status opened for keep until submission; excluded decisions remain in the resolution journal without opening local files. Use the resolver's group choices for structural/rename conflicts. scribe cherry-pick --continue resumes the saved source and target revisions; it does not silently pick a newer source head. scribe cherry-pick --abort abandons an unsubmitted pick, restores its target baseline and saves displaced edits in .scribe/_state/recovery. It cannot undo an accepted commit.

If the target advances before submission, acceptance rejects the stale target. For a full-view, ungrouped plan, use scribe resolve --update and resolve against the new target. Partial-view plans and rename/collision groups require aborting and restarting; preserve manual resolutions you want to reuse. If a submission outcome is unknown, repeat scribe submit -m "the original message" to resolve the saved operation before changing or abandoning it. Never generate a new operation merely because the connection failed.

History and repeated picks

A successful pick is one new commit on the target. scribe log and JSON history show the accepting source branch ID and change number as cherry_pick. This is provenance of the selected intent, not a second parent or proof that every source byte won a conflict. Choosing the target result for every file still creates a provenance-only commit. The original accepted source remains unchanged.

A source already in the target's ancestry, or already recorded as picked by a true ancestor, is reported as already applied. This includes picks inherited by new branches and through full merges. A competing duplicate publication is rejected with a durable outcome. Reverting file contents does not erase the recorded pick.

A later scribe merge development still considers the unpicked work. Picks never add merge ancestry; a subsequent full merge can need conflict resolution even when some source changes were selected earlier. Merge commits and cherry-pick commits are not supported as sources. Choose the original ordinary change. Changes cannot be selected across repositories, and a pick does not create a fork. Ancestry checks are bounded to 100,000 visited commits.

Web interface

Open repository History, choose Cherry-pick on an ordinary change, select another target branch, then Preview changes. The preview pins the current target head and shows every affected path with before-source, target and source links. Compare file contents loads text previews. Unchanged-side cases receive a default choice; when both sides changed, choose Use source, Keep target, or enter the complete resolved UTF-8 text. The web editor requires explicit resolution in those cases; the CLI also provides automatic non-overlapping text merging.

The web/SDK preview supports up to 128 affected paths and directs renamed-file cases, including affected base files absent from the target, to the CLI. Binary files can be selected without downloading their bytes. Edited text is limited to 512 KiB per file and 2 MiB total. Source/target selection can refer to larger binary objects because their accepted manifests are reused. Storage still enforces path collisions, live permissions, lock policy and the pinned target head. Mount declarations are ordinary .scribe/config.toml edits and retain all normal configuration checks and mounted-repository permission boundaries.

Only writers see the history action. Read permission suffices for the authenticated preview API; publishing needs write permission, and changing lock policy needs the existing elevated permission. The exact operation and resolutions are saved before transmission. If a response is lost, use the same dialog retry or Saved submissions. Saved submissions are scoped to the account and namespace. A rejected operation must be dismissed and reviewed again with a new preview before making a new request.

TypeScript SDK and HTTP API

ts
const plan = await client.cherryPickPlan("orbit-engine", {
  branch: "release", revision: "38", source: "development", change: "42",
});
const intent = await client.prepareCherryPick("orbit-engine", {
  branch: plan.target.id, revision: plan.target.head,
  source: plan.source.id, change: plan.source.head,
  message: "Backport crash fix",
  resolutions: [{ path: "Assets/material.bin", choice: "target" }],
}, operationId); // native operation ID: 32 lowercase hex characters, not all zero, generated once
// Durably persist the complete intent before this call.
const outcome = await client.submitCommit(intent);
// After uncertainty, resolve/retry this exact intent; never replace its operation ID.
const known = await client.operationOutcome(intent);

resolutions can omit paths with automatic choices but must resolve every conflict. Unknown or duplicate paths are refused. Do not submit when plan.alreadyApplied is true. prepareCherryPick rechecks the pinned target before freezing object bytes.

Authenticated HTTP routes under /api/v1:

  • POST /repositories/:repo/cherry-picks/preview: {branch, revision, source, change}.
  • POST /repositories/:repo/cherry-picks: the selection plus action: "cherry-pick", a UUID operationId, message, and resolutions. The UUID identifies the HTTP request; the API generates and persists its own native operation ID for it.
  • GET /operations/:id: resolve the same saved operation, shared with file commits.

Browser writes retain the existing origin/CSRF checks. Automation tokens need contents:read for preview and contents:write for submission and outcome lookup. Responses use the existing accepted/rejected/pending commit result contract. The API persists its native operation ID and exact prepared objects before any native writes; clients must persist the HTTP request before sending it as well.

Source docs/cherry-pick.mdSnapshot 2b8dcccb