Steering a cherry-pick with a comment
The bot creates cherry-picks for a merged change's Pick-to: footer, one branch level at a
time, approves and stages the conflict-free ones, and stops to ask a human when it cannot continue.
You steer it by leaving a review comment on the bot's own cherry-pick change
whose first line is a directive.
The bot always answers in the same thread, and always with one of four things:
- Acknowledged
- it will do it; the executor finishes asynchronously and narrates the result.
- Completed
- done, right now.
- Rejected
- understood but refused, and the reason is in the reply.
- Help
- the verb was not one it knows; it lists the ones it does.
cpbot: ?what the bot thinkscpbot: retrytry the failed step againcpbot: retry with parent …re-pick onto a base you choosecpbot: reparent [sha]rebase the pick changecpbot: stagestage nowcpbot: skipleave it, stop naggingcpbot: abandonabandon this pickcpbot: replanmatch the current footercpbot: pick-to <branch>pick this change somewhere newcpbot: resolveask the AI conflict resolvercpbot: resolve-by-handtake the conflict back
How to write a command
- Post it as an ordinary Gerrit review comment on the cherry-pick change: the one the bot linked from your original with "Successfully created cherry-pick to …".
- The directive must be the first substantive line. Gerrit's own
Patch Set N:framing is skipped. Everything after the first line is ignored, so an explanation below it is welcome. - A
cpbot:line deeper in a comment, or a quoted earlier reply (> cpbot: retry), does not fire. That is deliberate: talking about a command is not giving one. - The verb is case-sensitive and lower-case. Spacing after the colon does not matter.
Who may give one
cpbot: ? is open to everyone. Every other verb needs one of:
- being the owner of the source change, the original that was picked from;
- being a reviewer on the cherry-pick or on the source change;
- having push access to the target branch.
cpbot: pick-to is posted on the change to pick from rather than on a cherry-pick, so it
is checked against that change: its owner, a reviewer on it, or push access to every branch named.
cpbot: retry on that change is checked the same way, per target.
Approving the AI conflict resolver's skip suggestion (cpbot: skip on a pick where it
suggested one) is narrower: it is a judgement about whether the change belongs on the branch, so only the
source change's owner or an Approver may give it.
An unauthorized command is answered with that rule, and logged. Anonymous comments are refused.
Verbs
cpbot: ? status
Read-only. Replies with the bot's view of this pick: its state and detail, the target project~branch, the pick change, the relation-chain parent pick, the expected parents, and any ancestors it skipped. Start here when something looks odd.
cpbot: retry re-drive a stranded pick
Puts the pick back into the pipeline at the step that failed and lets the bot try again.
| Stranded because | Retry re-enters at |
|---|---|
| merge conflict in the pick | picking: re-evaluates the change as it now stands |
| the cherry-pick could not be created | picking: creates it again |
| staging was refused | staging |
| CI failed | staging: re-stages so CI runs again |
| the target branch was closed | validating: the branch may have reopened |
| a change with this Change-Id was already open on the target | validating |
Typical use: the pick had conflicts, you uploaded a resolved patch set, and cpbot: retry
makes the bot re-check it. Also valid on a pick you unstaged by hand: it goes back to staging. Rejected on
abandoned picks.
On the change that was picked from
Posted on a merged change that is not a cherry-pick (or on a cherry-pick that has merged), cpbot: retry
picks that change to the targets its own Pick-to: footer names. It creates the picks that do not exist yet,
and retries the stranded ones however far down the waterfall they sit. The reply lists every target and what
happened to it. This is the place to answer a "target branch is closed" note once the branch reopens.
It only moves forward. A pick someone abandoned stays abandoned (restore it in Gerrit to bring it back), and a pick someone unstaged stays unstaged. The change's owner and its reviewers reach every target; anyone else reaches the targets they can push to.
The bot approves only patch sets it uploaded itself. Its +2 means "this is the
reviewed source, cherry-picked cleanly" and nothing else. A patch set a human pushed is parked as
awaiting human review with the change's stakeholders in attention: a reviewer gives Code-Review +2,
then cpbot: stage stages it. To get a fresh bot-authored patch set instead, use
retry with parent.
cpbot: retry with parent <ref> re-pick onto a parent you choose
For the case where the bot picked onto the wrong base, typically the branch tip while the change's real
parent was still open on that branch. The bot re-runs the cherry-pick onto the base you
name. Gerrit records the result as a new patch set on the same change, and if it is
conflict-free the bot approves and stages as usual. No local checkout needed. This is the one way to get a fresh
bot-approved patch set on a stranded pick: the revision is the bot's own cherry-pick of the reviewed source.
If the parent you named is still open, the bot approves and then waits (hashtag
cpbot-waiting-parent, plus one comment naming the change it waits on) until that parent is
staged or merged, and stages right after. Gerrit may show the waiting change as Merge Conflict
against the branch tip in the meantime; that badge is about the parent not being in the branch yet, not
about the pick.
<ref> may be any of:
- a commit SHA (7 to 40 hex characters). It must be visible in the target project; an open change's revision is fine.
- a change URL (
…/c/qt/qtbase/+/771343) or a bare change number. - a Change-Id (
I…).
A change reference is resolved by Change-Id on the pick's own target branch. You may point at the change on any branch, your dev original or its 6.12 pick, and the bot finds its twin on the branch it is picking to.
| The named change, on the target branch, is | The bot does |
|---|---|
| open (NEW, STAGED, INTEGRATING) | re-picks onto its current revision |
| MERGED | it is already in the branch: the bot re-picks onto the current branch tip, still as a new patch set |
| absent | Rejected: wait until it has been picked to this branch |
| ABANDONED | Rejected |
Rejected if the pick has no Gerrit change yet (use plain retry), or if the arguments are anything other than with parent <ref>.
cpbot: reparent [<sha>] rebase the existing pick change
Rebases the pick's change without re-picking. Without a SHA the bot rebases onto the relation-chain parent's pick on this branch, re-establishing the chain. With a SHA it rebases onto exactly that commit; the SHA is taken as given. A rebase is only possible while the pick's change is still NEW.
Which one? Prefer retry with parent when the pick is conflicted
or was created from the wrong base. Prefer reparent when the content is right and only the chain is wrong.
cpbot: stage stage now
Moves an already-created, approved pick into staging, or re-stages one whose staging or CI failed. Rejected if the pick has no Gerrit change yet, or is merged or abandoned.
Branches with restricted staging. Where Stage is an exclusive grant in
Gerrit, usually to Qt Release Managers on ESM branches such as tqtc/esm-*, the bot has no staging
rights by policy. Release management stages changes there by hand and normally takes only security fixes.
The bot says so when it is refused, and cpbot: stage will be refused again. The pick is not broken;
it waits for a release manager, or for you to abandon it if it does not belong there. The dashboard offers
Stage only to people who could stage the change themselves.
cpbot: skip leave it, stop nagging
Adds the cpbot-skipped hashtag and stops the bot from nagging about this pick. The pick stays
in its stranded state and is not abandoned; use abandon for that. Only valid on a stranded pick.
cpbot: abandon abandon this cherry-pick
Abandons the pick's Gerrit change and marks the pick terminal. Safe to repeat. The bot will not recreate it unless the source is replanned and the target is still in the footer.
cpbot: replan match the source's current footer
Re-reads the source change's current Pick-to: footer and makes the set of picks match it.
| Bucket | Meaning |
|---|---|
| created | a newly listed target gets a fresh pick |
| restored | an abandoned pick for a still-listed target is restored and re-driven |
| retried | a stranded pick for a still-listed target is re-driven |
| abandoned | a pick whose target left the footer is unstaged if needed, then abandoned |
| deferred | a dropped pick that CI is integrating right now waits until CI finishes |
| unchanged | everything else |
The reply lists exactly what changed. Picks that were rerouted onto an LTS or ESM shadow branch count as satisfying their public target.
cpbot: pick-to <branch> [<branch> …] pick this change somewhere new
Post it on a merged change — the original, or one of its merged cherry-picks — to pick that
change to the branches named, exactly as if its Pick-to: footer had named them. The same
planner runs, so the reply may list intermediate branches the pick-to policy adds on the way.
This is how a branch that has no pick change to talk to is reached: when a pick was never created, or its branch closed before it could continue, pick the change to the branch that still needs it from a change that can be its source. A target already picked from this same change is not picked again; the reply says so.
cpbot: resolve ask the AI conflict resolver
Hands a pick with merge conflicts to the AI conflict resolver (see AI conflict resolution). On most branches it already has every conflict; this is how to ask on an ESM branch, where it works only on request, and how to ask again after it gave a conflict back or after you turned down its resolution. Posted on a skip suggestion, it asks for a resolution instead of the skip.
cpbot: resolve-by-hand take the conflict back
The resolver stands down and the conflict is yours: resolve it, upload, and reply cpbot: retry. A
resolution it is still working on is refused when it arrives. If its patch set is already on the change, amend
or replace it, and remove its Conflicts-Resolved-By trailer if you rework it substantially.
AI conflict resolution
When a pick has merge conflicts, the bot hands it to an AI conflict resolver, which works from a checkout of
the repository and the source change. Nobody is told while it works. On ESM branches it works only when someone
replies cpbot: resolve, so a person is in the loop from the start. It ends in one of three ways:
- A resolution to review. It uploads a patch set whose commit message carries
Conflicts-Resolved-By: <model>, and the bot comments with the resolver's triviality rating (trivial, minor, moderate or substantial), its confidence, what it changed and the checks it ran. The bot never approves an AI resolution: a person compares it with the source change, gives Code-Review +2 and repliescpbot: stage. The trailer stays in history as the record; if you rework the resolution substantially, remove it. - A suggestion to skip. When the change fixes code that does not exist on the branch, it says
so with its evidence and suggests skipping instead. The source change's owner or an Approver approves with
cpbot: skip. The pick then stays open, taggedcpbot-skipped, and the branches after it in itsPick-tofooter are not picked.cpbot: resolveasks for a resolution instead. - The conflict back. When it cannot resolve the conflict, or does not answer in time, the conflict strands as it would have without it, with its reason at the top of the comment.
The dashboard shows these states as Resolving conflict, Resolution needs review and Skip suggested, with the ratings on the cell, and its Awaiting resolution review filter lists what is waiting on a person.
When the bot stops
A pick moves planned → validating → picking → picked → approving → staging → staged → integrating → merged,
with a reparent step when a chain needs it. When it cannot continue it says so on the pick, and once on your
source change. These are the stops and the usual way past them.
| Comment on the pick | Meaning | Usual next step |
|---|---|---|
| "This cherry-pick has merge conflicts and needs manual resolution." | created with conflict markers | resolve, upload, get a reviewer's +2, stage; or retry with parent if the base was wrong |
| "The current patch set was uploaded by …, not by the cherry-pick bot" | a human pushed a revision; the bot will not vouch for it | a reviewer gives Code-Review +2, then stage |
| "The merge conflicts in this cherry-pick were resolved with AI assistance" | the AI conflict resolver uploaded a declared resolution | review it against the source, Code-Review +2, stage; or resolve-by-hand |
| "The conflict resolver … suggests this change is not needed on …" | the resolver found nothing on the branch for the change to fix | the owner or an Approver approves with skip; or resolve, resolve-by-hand |
| "Reparenting produced conflicts" | the rebase onto the chain parent conflicted | fix by hand, retry |
| "The cherry-pick could not be created." | Gerrit refused the create | read the reason on the source change, retry |
| "Staging was rejected: …" | Gerrit refused the stage call; its reason is quoted | fix the cause, stage; on tqtc/esm-* this is the maintainer's gate, not an error |
| "CI integration failed for this cherry-pick." | integration failed | fix or wait, stage |
| "Target branch is closed and no shadow successor is configured." | closed target, no LTS or ESM shadow | usually nothing; the branches after it were re-planned past it |
| "A change with this Change-Id is already open on the target branch." | someone picked it by hand | finish the hand pick, or abandon this one |
The trail on your source change is one line per pick the bot created, with its link, and one line per problem it could not solve. When a branch seems to have disappeared from the cascade, that trail is where to look.
What it does unasked
- Waterfall.
Pick-to: 6.12 6.11 6.8 6.5produces one pick at a time. The 6.12 pick carriesPick-to: 6.11 6.8 6.5, and each level is planned when the previous one merges. The footer on each pick is the record of what is still downstream of it. - Gap fill. Open feature branches between your targets that you omitted are added; the source comment says which.
- LTS and ESM rerouting. A target whose public branch is closed for pushing is rerouted to
lts-x.y, thentqtc/lts-x.y, thentqtc/esm-x.yin the private twin project. The rerouted pick keeps the rest of the footer and its relation-chain parent. - Relation chains. Chained changes are picked onto each other's picks so they stage in order. If the parent's pick does not exist yet, the child is created on the tip and rebased when it appears.
- Continuing past a dead branch. If a target is closed with no shadow, the branches listed after it are picked directly from the last merged level.
Operator API
For bot administrators. The same verbs exist over the Bearer-authenticated HTTP API:
POST /picks/:id/retry, POST /picks/:id/reparent, POST /admin/replan/:change,
and the branch-lifecycle routes /admin/close-branch, /admin/open-branch, /admin/branched.