Pull requests

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.

sh
1cafe stack list
2cafe 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.

sh
1cafe stack submit --name my-change --base main --dry-run
2cafe 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:

sh
1cafe 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.

sh
1cafe stack create -m "Add the parser" --all --branch parser
2cafe stack create -m "Use the parser" --all
3cafe stack log
4cafe stack down
5cafe stack modify --all
6cafe stack submit --name parser
CommandWhat it does
create -m <message>Commit a new layer on the current one. Below other layers, add --insert to restack them above it
modifyAmend the current layer (-m rewords it) and restack the layers above
up, downMove one layer, or as many as you name (down 2)
top, bottomMove to the top or bottom layer
checkout <layer>Move to a layer by position (1 is the bottom), commit or change ID, or branch
logShow the stack with each layer's pull request, and whether it needs submitting
syncFetch 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 main with --branch <name>. Lower layers are checked out without a branch. create and modify use what you staged; --all stages everything. If a rebase conflicts, the command stops: resolve the files, run git rebase --continue, then cafe stack log.
  • jj: the working-copy change is the current layer. create starts a new change, so make your edits after it; jj rebases the layers above for you.
  • Sapling: the stack is your draft commits. modify runs sl amend; if a conflict stops it, resolve it and run sl continue.

Stacks are linear: there is one layer above each layer.

If the pull requests already exist and form a chain, link them bottom to top:

sh
1cafe 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.

sh
1cafe stack merge 14 --dry-run
2cafe 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:

sh
1cafe stack merge --stack 7 --operation latest

If it is waiting for you, resume the same operation rather than starting a new one:

sh
1cafe 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:

sh
1cafe stack restack --stack 7 --dry-run
2cafe 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:

sh
1cafe stack close --stack 7 --dry-run
2cafe 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.

Read as MarkdownGitCafe documentation
Documentation