Stacked changes
Split big work into a chain of small branches, each on the one below. zev stack restacks them together after an edit, pushes them as one pull request each, and cleans up as they merge.
Search for a command to run…
Split big work into a chain of small branches, each on the one below. zev stack restacks them together after an edit, pushes them as one pull request each, and cleans up as they merge.
Big changes are hard to review. Split them into small steps that build on each other, for example the API, then the page that uses it, then the docs, and reviewers can take one at a time. That's a stack: a chain of branches where each one starts from the one below.
zev stack add api # a new branch on top of where you are, and you're on it# …commit…zev stack add ui # the next one, on top of api# …commit…zev stack # the whole stack: each branch, its commits, where you areStack api on main, newest on top ▶ ui 1 commit: UI page (you're here) api 1 commit: API endpoint mainzev stack up, down, top and bottom move along it.
A reviewer asks for a fix in api. Go down, commit the fix, and run zev stack restack. zev rebases each branch above onto the new version of the one below it, replaying only that branch's own commits. That's the step that goes wrong by hand in Git, where rebasing docs after ui changed replays ui's old commits again unless you remember git rebase --onto.
zev stack bottom# …fix and commit…zev stack restack✓ ui is back on top of api✓ docs is back on top of uiThe whole restack is one operation, so one zev undo takes all of it back. If a branch conflicts, restack stops there and says which branch and what to do. Everything below it stays restacked.
zev stack push✓ #12 api → main opened✓ #13 ui → api opened✓ #14 docs → ui openedEach branch is pushed and gets its own pull request, aimed at the branch below it, so each one shows only its own change. Every description lists the whole stack and marks which one you're looking at. Push again after changes and the same pull requests are updated: zev replaces its stack list and keeps whatever you wrote above it.
Merge the bottom pull request first. Then:
zev stack sync✓ api is merged into main: taken out of the stackzev stack push✓ #13 ui → main retargetedsync pulls the base, takes merged branches out of the stack (and deletes them locally), and restacks the rest onto the base. The next push points the next pull request at main. Repeat until the stack is empty.
zev stack the stack you're onzev stack add <name> a new branch on top of this onezev stack up | down | top | bottomzev stack restack every branch back onto the one below itzev stack sync pull the base, drop merged branches, restackzev stack push push all, one pull request eachzev stack remove [<name>] take a branch out of its stack (it stays as a branch)zev stack list every stack hereAll of them take --json and --brief. An agent can split its own work into a reviewable stack and read the state of the stack in one line:
$ zev stack --briefstack api on main: api(2) > ui*(1,restack) > docs(1)Git has the pieces (branch, rebase --onto, a separate tool to open pull requests) but nothing that knows the branches belong together. You track the order yourself, rebase each branch by hand, retarget pull requests by hand after a merge, and one wrong rebase duplicates commits. In zev the stack is part of the repository, restack does every branch at once and correctly, and push and sync keep the pull requests in step.
See also: Branches and merging, Undo anything.