Stacked pull requests
A stack is an ordered chain of open pull requests in one repository. Each pull
request targets the branch of the one below it, and the bottom one targets the
landing base, such as main. Use a stack to split a large change into
small reviewable steps that depend on each other.
You need read access to view a stack and write access to change one. A pull request belongs to at most one stack, and a stack holds 1 to 50 pull requests.
View stacks
Open the repository's stacks list at /<owner>/<repo>/stacks. Each row shows
the title of the lowest open pull request, who created the stack, every member
with its check state, and the first thing in the stack's way, such as failing
checks or requested changes.
The stack page, /<owner>/<repo>/stacks/<number>, is where you act on a stack.
It lists every member bottom up with its author, branches, checks, reviews and
readiness, and offers Land, Restack, Unstack and Close stack
with the live progress of the latest landing and restack.
A member is Ready when GitCafe would merge it now: it is open, not a draft, targets the right branch, and its checks, reviews and the landing base's rulesets allow it. A member above one that is not ready is Blocked downstack. Waiting on checks means only required checks are still running. A blocked member lists what blocks it. Readiness is a preview: landing checks every pull request again.
A pull request in a stack shows the stack in its sidebar and links to the
previous and next layer under its title, on the same tab, so you can review a
stack layer by layer. The pull request list marks stacked pull requests with
their stack and layer, such as Stack #7 · 2/3.
| 1 | cafe stack list |
| 2 | cafe stack status 14 |
Publish a stack from local commits
cafe stack submit pushes each commit after the base as its own branch and
opens or reuses one pull request per commit. Run it from your checkout; it
works with Git, jj and Sapling.
| 1 | cafe stack submit --name my-change --base main --dry-run |
| 2 | cafe stack submit --name my-change --base main |
--dry-run shows the plan without pushing. To add commits to the stack later,
run the same command with the same --name and --base: the existing pull
requests must be the start of the new plan, and new ones are appended. Each
commit keeps the branch it was published on, so submitting again after
cafe stack sync dropped landed commits, or after you inserted one, updates
the same pull requests.
If submission stops part-way, the branches and pull requests it already created remain. Fix the problem and run the same command again to continue.
To choose the version control tool once instead of passing --vcs each time:
| 1 | cafe config set stack.vcs jj --global |
Build and edit a stack locally
The cafe stack local commands give you a day-to-day loop for the commits
before and between submissions. Each layer is one commit (in jj, one change)
and becomes one pull request when you submit.
| 1 | cafe stack create -m "Add the parser" --all --branch parser |
| 2 | cafe stack create -m "Use the parser" --all |
| 3 | cafe stack log |
| 4 | cafe stack down |
| 5 | cafe stack modify --all |
| 6 | cafe stack submit --name parser |
| Command | What it does |
|---|---|
create -m <message> | Commit a new layer on the current one. Below other layers, add --insert to restack them above it |
modify | Amend the current layer (-m rewords it) and restack the layers above |
up, down | Move one layer, or as many as you name (down 2) |
top, bottom | Move to the top or bottom layer |
checkout <layer> | Move to a layer by position (1 is the bottom), commit or change ID, or branch |
log | Show the stack with each layer's pull request, and whether it needs submitting |
sync | Fetch the landing base, drop layers whose pull requests merged, and rebase the rest onto it |
Only log and sync talk to GitCafe, and only to read pull requests. Your
version control tool's model is kept:
- Git: the stack is the commits between the remote's default branch and
your branch. Start one on
mainwith--branch <name>. Lower layers are checked out without a branch.createandmodifyuse what you staged;--allstages everything. If a rebase conflicts, the command stops: resolve the files, rungit rebase --continue, thencafe stack log. - jj: the working-copy change is the current layer.
createstarts a new change, so make your edits after it; jj rebases the layers above for you. - Sapling: the stack is your draft commits.
modifyrunssl amend; if a conflict stops it, resolve it and runsl continue.
Stacks are linear: there is one layer above each layer.
Link existing pull requests
If the pull requests already exist and form a chain, link them bottom to top:
| 1 | cafe stack link 12 13 14 |
Pull 13 must target pull 12's branch, and pull 14 must target pull 13's branch. Linking does not push anything.
Land a stack
Landing merges the stack from the bottom up through the pull request you choose. Each landed pull request leaves the stack and the next one retargets to the landing base. The stack is complete when its last pull request lands.
| 1 | cafe stack merge 14 --dry-run |
| 2 | cafe stack merge 14 --strategy merge --yes |
Replace 14 with the highest pull request you want to land, or pass
--stack <number> to land the whole stack. On the stack page, Land lands
every ready pull request from the bottom, and Land through #N on a member
lands up to that one; the dialog lets you pick the merge strategy. The preview
shows readiness; permissions and
rulesets are checked again for each
pull request as it lands, and landing stops if a branch changes after you
approved it.
Every strategy can land several pull requests at once:
- merge adds a merge commit for each pull request.
- squash adds one commit per pull request, in stack order.
- rebase replays each pull request's commits onto the landing base.
With squash or rebase, GitCafe rebases the next pull request's own commits
onto what just landed and pushes its branch before landing it, so its checks
and rulesets are evaluated again on the new commits. If a ruleset requires
checks that have not run on them yet, landing stops there; merge again once
they pass. The land dialog says so when you pick squash or rebase for more than
one pull request. If the rebase conflicts, landing stops with
STACK_LAND_CONFLICT: the pull requests below are merged and the conflicting
one is left as it was. Rebase it onto the landing base, push it, and run
cafe stack merge again for the rest of the stack.
A pull request in a stack can only merge through its stack: cafe pr merge
points you to cafe stack merge. Open pull requests outside the stack that
target a landed branch move to the landing base too.
If landing does not finish
Landing continues in the background after the command returns. To check on it:
| 1 | cafe stack merge --stack 7 --operation latest |
If it is waiting for you, resume the same operation rather than starting a new one:
| 1 | cafe stack merge --stack 7 --operation <operation-id> --resume --yes |
In the browser, the stack page and each member pull request show the unfinished landing and offer Resume landing when it can continue.
Stacks synchronized with GitHub
In a repository synchronized with GitHub through the GitHub App, with pull request writes enabled, GitHub stacks are imported and stack changes made in GitCafe are sent to GitHub. GitHub performs the merge and supports merge, squash and rebase. A one-pull-request stack stays a plain pull request on GitHub until you append a second one.
Restack after the base changes
Restacking rebases each pull request in the stack onto the current version of the one below it, so the chain stays up to date after a lower pull request changes or the landing base moves. Use Restack on the stack page to restack the whole stack, Restack in a member's sidebar to start at that member, or:
| 1 | cafe stack restack --stack 7 --dry-run |
| 2 | cafe stack restack --stack 7 |
Each upper pull request replays only its own commits. If a pull request no
longer contains the previous tip of the one below it, restacking stops there
with a conflict: rebase the remaining pull requests locally and push them.
A failed restack may already have updated the lower branches, so check the
stack before trying again. cafe stack restack --stack 7 --operation latest
shows the latest result.
Unstack or close a stack
Unstacking dissolves the stack and leaves its pull requests open, each
keeping its branch and base. Use Unstack on the stack page, or the
REST API (the stack unstack operation). Pull requests
cannot be reordered or removed from a stack individually.
Closing dissolves the stack and closes all its open pull requests without merging them, for example when the change landed some other way. Use Close stack on the stack page, or:
| 1 | cafe stack close --stack 7 --dry-run |
| 2 | cafe stack close --stack 7 --yes |
Closing needs write access and permission to close each open pull request. Pull requests that already landed stay merged. Either everything closes or nothing changes. A stack that is landing or restacking cannot be closed until that finishes. Closed pull requests can be reopened one by one, but the stack cannot be restored.