{"record":{"id":"f9e20876393f51c0","repo":"can1357/oh-my-pi","slug":"cherry-pick-of-sha-is-empty","errorCode":null,"errorMessage":"cherry-pick of {sha} is empty","messagePattern":"cherry-pick of (.+?) is empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"crates/pi-vcs/src/error.rs","lineNumber":41,"sourceCode":"\t#[error(\"reference not found: {name}\")]\n\tRefNotFound {\n\t\t/// The ref name as given by the caller.\n\t\tname: String,\n\t},\n\n\t/// A revision/object lookup failed (`rev-parse` style spec, blob path,\n\t/// tree).\n\t#[error(\"object not found: {spec}\")]\n\tObjectNotFound {\n\t\t/// The revision or object spec as given by the caller.\n\t\tspec: String,\n\t},\n\n\t/// Cherry-picking `sha` produced an empty commit (already applied or\n\t/// auto-resolved to HEAD). Callers should skip and continue the range —\n\t/// replaces the historical `/the previous cherry-pick is now empty/` stderr\n\t/// regex.\n\t#[error(\"cherry-pick of {sha} is empty\")]\n\tEmptyCherryPick {\n\t\t/// The commit that collapsed to a no-op.\n\t\tsha: String,\n\t},\n\n\t/// A merge-style operation (cherry-pick, stash pop, 3-way apply) hit\n\t/// conflicting changes.\n\t#[error(\"merge conflict in {} file(s)\", paths.len())]\n\tConflict {\n\t\t/// Worktree-relative paths left in a conflicted state.\n\t\tpaths: Vec<String>,\n\t},\n\n\t/// A patch did not apply (context mismatch, missing file, malformed input).\n\t#[error(\"patch does not apply: {message}\")]\n\tPatchFailed {\n\t\t/// Human-readable reason, including the offending path when known.\n\t\tmessage: String,","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-vcs/src/error.rs#L23-L59","documentation":"Error::EmptyCherryPick is raised when cherry-picking a commit produced an empty result — the change was already applied upstream or auto-resolved to HEAD, so nothing was committed. It deliberately replaces the historical stderr-regex `/the previous cherry-pick is now empty/`, letting callers handle the skip-and-continue case structurally instead of scraping git output.","triggerScenarios":"Cherry-picking a range where a commit's changes are already contained in HEAD (duplicate pick, rebase replay, backport of an already-merged fix). The library detects the no-op and returns this error with the offending sha so callers can skip and continue.","commonSituations":"Backporting a PR that was partially merged before, cherry-picking a range that overlaps an earlier cherry-pick batch, replaying commits onto a branch that already contains them, double-clicking a pick in a tool.","solutions":["Treat this as non-fatal: skip the sha and continue cherry-picking the remaining commits in the range.","If the change is genuinely needed, check whether it landed with a different content (merge commit vs squash) and pick the right original commit.","Verify with `git log --cherry-pick --right-only HEAD...<sha>^..` whether the commit is already applied.","If you truly want an empty commit to record it, re-run with `--allow-empty` semantics if the API exposes it."],"exampleFix":"// before\nrepo.cherry_pick(\"abc123\")?; // whole batch fails on duplicate\n// after\nmatch repo.cherry_pick(sha) {\n    Err(Error::EmptyCherryPick { .. }) => continue, // skip already-applied commit\n    Err(e) => return Err(e),\n    Ok(()) => {}\n}","handlingStrategy":"try-catch","validationCode":"fn already_applied(repo: &pi_vcs::Vcs, sha: &str) -> bool {\n    // equivalent of `git log --cherry-pick --right-only HEAD...<sha>^..<sha>` being empty\n    repo.cherry_check(sha).map(|c| c.is_empty()).unwrap_or(false)\n}\nlet pending: Vec<_> = shas.into_iter().filter(|s| !already_applied(&repo, s)).collect();","typeGuard":"fn is_empty_cherry_pick(err: &pi_vcs::Error) -> bool {\n    matches!(err, pi_vcs::Error::EmptyCherryPick { .. })\n}","tryCatchPattern":"for sha in range {\n    match repo.cherry_pick(sha) {\n        Err(pi_vcs::Error::EmptyCherryPick { sha }) => {\n            log::info!(\"skipping {sha}: already applied to HEAD\");\n        }\n        other => other?,\n    }\n}","preventionTips":["Track which commits have been cherry-picked (cherry-pick state file or convention, e.g. `(cherry picked from commit ...)` trailers).","Check overlap with `git log --cherry-pick` before starting a range pick.","Pick from a stable base ref, not from a branch that moves between runs.","Design batch pickers to treat EmptyCherryPick as progress, not failure."],"tags":["vcs","git","cherry-pick","empty-commit"],"backgroundTag":"cherry-pick-empty","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}