Stacked pull requests
A stack is a chain of pull requests where each one builds on the one below it. gitfor needs no extra
setup or bookkeeping: a pull request is stacked when its base branch is another open pull request's
head branch in the same repository. Any tool that sets the base works, including plain git, Graphite's
gt, jj and git-town.
git switch -c feat-a main && git commit -m "Add the API" && git push -u origin feat-a
git switch -c feat-b && git commit -m "Use the API in the UI" && git push -u origin feat-b
# open feat-a -> main, then feat-b -> feat-a
The default branch never forms a stack, even when some pull request uses it as its head.
What gitfor does
- Shows the stack on every pull request in it, newest on top and the trunk at the bottom. Merged parents stay visible.
- Retargets on merge. When a parent merges, each child's base becomes the parent's base, and the timeline records why.
- Restacks on squash and rebase merges. Those merges rewrite the parent's commits, so gitfor replays only the child's own commits onto the merge result, then does the same for everything above it. A merge commit keeps the parent's commits, so the child is only retargeted.
- Restacks on demand. After a parent is amended or force-pushed, the child shows a "Restack onto #N" button. gitfor remembers the parent tip the child was built on, so only the child's commits are replayed.
- Merges the stack. "Merge stack" turns on auto-merge for every open pull request from the bottom up to the current one. Each merges into the trunk once its checks pass, the next is retargeted and restacked, and so on. Stacked pull requests never auto-merge into their parent's branch, and can't join the merge queue directly.
Restacks run in the Worker with the same fail-closed three-way merge as rebase merges. Conflicts, merge commits in the child's history, or histories that are too large are left for you to restack locally and force-push, with the reason in the timeline. Forks can be stacked on, but gitfor can't push to them, so their branches are retargeted but not restacked.
For agents
The MCP server has get_pull_stack and restack_pull. Open a stacked pull request with open_pull,
using the parent's head branch as base.
Data
pull_requests.stacked_on_issue_id records the merged parent after a retarget.
pull_requests.restack_upstream_sha records the parent tip a child was last built on while the parent
moves. Both live in migrations/0038_stacked_pulls.sql; the logic is in src/server/stacks.ts.