Stacks & ordered merging
Stack Claude Code sessions on each other's branches, keep every branch synced as the stack moves, see the whole stack as a map, and merge a stack — or a list of pull requests — in order, including through GitHub's and Bitbucket's merge queues.
A stack is a chain of sessions where each one is built on the one below it, and Plexus keeps the whole chain in sync while the agents work, then merges it in order.
Split a large change into pieces that can each be reviewed on their own: an API session, a UI session on top of it, an end-to-end test session on top of that. Each piece gets its own worktree, its own agent and its own pull request, and each pull request shows only its own changes because it targets the branch of the session below it.
Stack sessions
There are two ways to put a session in a stack.
Start a new one on top. On the session you want to build on, open the ⋯ menu and choose New stacked session…. The New Session dialog opens with Stack on set to that session and the base branch locked to its branch: the new session starts from that branch and syncs whenever the session under it moves. Name it and click Create. You can also pick a session in the Stack on field of any New Session dialog.
Stack an existing session. Open the ⋯ menu of a session that already exists — one you cut from main before you knew it belonged in a stack, say — and choose Stack on…. Pick the session it should sit on. The picker says what will happen before you choose: the session will sync onto the one you pick, and its pull request will target that session's branch. The session's current parent is listed but can't be picked again, and the picker leaves out anything Plexus can't stack it on: itself, a session already stacked on it, archived sessions, and sessions on the project's main checkout.
Either way the session appears nested under its parent in the sidebar, joined to it by a connector line, and its Stack panel opens. A stack can be up to eight sessions deep, and the session underneath has to be started — a stacked session is cut from its branch.
Drag a stack by its bottom session to reorder it in the sidebar; everything stacked on it moves with it. A stack is drawn inside one list at a time: the project's session list, or a split view. If part of a stack is in a split view and part is not, each part is drawn as a stack of its own, starting from its lowest session, and the sessions stay stacked on one another all the same. To take a session out of its stack, choose Unstack in its ⋯ menu: it stays based on the same branch and simply stops being synced.
The Stack panel
A stacked session's context bar carries the Stack pill: Stack 2 of 3 says where the session sits. Click it to show or hide the Stack panel beside the session. The panel opens by itself for a session in a stack or in Merge in order; for any other session with an open pull request the pill shows its number (#129) and opens the same panel for that one pull request.
The panel draws the stack as a map, top to bottom, with your base branch at the bottom:
- Each layer is numbered from the bottom — layer 1 is the session your stack starts from.
- Each layer shows its pull request's number and title (or No pull request yet), the session's name — click it to open that session — and its state.
- Under that, three facts: the pull request's checks, its review (Approved, Review or Changes requested), and whether the branch is Synced. A merged layer reads Merged into main instead.
- The layer you have open is highlighted.
- The header names what you're looking at — Stack and how many sessions, Merge in order, or Pull request — and shows GitHub stack when the pull requests form one stack on GitHub.
At the foot of the panel is its merge button, with a line that says what it will merge or what it's waiting for. If the layer you have open needs you, a banner above it says why, with the one thing to do about it.
Move between layers with ⌥↑ and ⌥↓ (Alt+↑ and Alt+↓ on Windows and Linux): each opens the session one layer up or down the stack, with its panel. They work even while the agent terminal has focus, and you can rebind them under Settings → Shortcuts — see Keyboard shortcuts.
In a narrow window the panel opens over the session rather than beside it; press Esc or click outside it to close it. In a split view each pane has its own panel, and the pill shows for the pane you have focused.
Sync
When the session underneath gets new commits — its agent finishes a turn, you commit in it, or someone pushes to it — every session above it is Behind. Plexus syncs them: it replays each one's own commits on top of the new parent, from the bottom of the stack up. The row reads Syncing while it runs and goes quiet when it's done; no notification, because the quiet row is the confirmation.
- A sync only runs while that session's agent is not working. If it is, the sync waits for the turn to end, and the row stays Behind until then.
- Uncommitted changes don't stop it. Plexus syncs around them and leaves your uncommitted files exactly as they are. Only when those edits touch a file the sync must change — or an untracked file sits where the sync would write one — does it change nothing and treat it like a conflict: see When a sync conflicts.
- A session in the middle of a rebase or merge is left alone, and reads Needs you: finish the git operation.
- A branch you checked out from an existing branch is never synced by itself — it may be shared outside Plexus. Its row reads Needs you: sync it yourself, with Sync now.
- If the branch was already pushed, Plexus pushes it again after syncing, with a lease: if someone else pushed to it in the meantime, the push is refused rather than overwriting their work. The whole branch goes up, so commits on it you hadn't pushed yet are published with it. Turn off Push after syncing to keep pushing in your own hands.
With Sync stacked sessions set to Only mark them, nothing moves by itself: the rows read Behind, and Sync now in the session's ⋯ menu syncs it (and everything above it). Off stops checking altogether.
When a sync conflicts
Plexus never leaves a worktree half-rebased. A sync that conflicts is undone first, so the branch is exactly as it was. What happens next depends on When a sync conflicts:
- Hand it to the session's agent (the default): Plexus gives that session's agent the exact command to run and the files that conflicted, in its own terminal, as soon as the agent is idle. The row reads Resolving while the agent works. The agent is asked not to push; once it has finished, Plexus records the result and pushes it.
- Ask me first: the row reads Needs you: conflict, with Resolve with agent — the same hand-off, when you say so.
- Stop: the row reads Needs you: conflict with the files, and Open terminal to resolve them yourself.
Overlapping uncommitted edits go the same way: the agent is asked to commit or stash its work in progress so the sync can run — or, when nobody is there to ask, the row reads Needs you: overlapping edits with Ask agent to commit.
Each conflict is handed to the agent once. If the agent stops without resolving it, the row says Needs you: conflict rather than asking it again and again. A new commit on either branch counts as a new conflict. The same setting decides what happens when a pull request being merged conflicts with what merged before it.
States
Every session row, the context bar and the Stack panel show a session's one state as a small chip, and every dashboard row shows it in the Session column. A session with nothing to report shows nothing. Amber always means Needs you, and the chip names the one thing to do; in the context bar the same action is a button.
| Chip | In full | Color | What it means | What to do |
|---|---|---|---|---|
| (none) | Synced: up to date with the session under it, and nothing else to report | — | ||
| Behind | Behind | Gray | The session under it moved; a sync runs by itself (or, with Only mark them, when you choose Sync now) | — |
| Syncing | Syncing | Blue | Plexus is replaying the branch onto the session under it | — |
| Waiting | Waiting for agent | Blue | The sync, or a hand-off, waits for the session's agent to finish its turn | — |
| Resolving | Agent resolving | Green | The session's agent is resolving a conflict, or committing its work so the sync can run | — |
| Conflict | Needs you: conflict | Amber | A sync or a merge conflicted, and nobody is on it | Resolve with agent, or Open terminal |
| Overlap | Needs you: overlapping edits | Amber | Uncommitted edits touch files the sync must change; nothing was changed | Ask agent to commit |
| Checks | Checks running | Blue | The pull request's checks are running | — |
| Checks failed | Needs you: checks failed | Amber | A check failed; merging stops here until it passes | Open checks |
| Review | Needs review | Blue | Waiting for a review, or changes were requested | — |
| Ready | Ready to merge | Green | Checks passed (or there are none), approved (or no review is required), not a draft | Merge it from the Stack panel |
| Merging | Merging | Blue | Plexus or GitHub is merging it | — |
| In queue | In merge queue | Blue | In GitHub's or Bitbucket's merge queue, waiting for the host to merge it | — |
| Add to queue | Needs you: add to merge queue | Amber | Bitbucket requires its merge queue and doesn't let Plexus add to it | Open on Bitbucket |
| Merged | Merged | Purple | Merged into its base branch | — |
A few other stops read Needs you too, each with its reason and one action: a pull request that was closed (Open pull request, or Take it out of Merge in order), no pull request yet in a merge (Create pull request), a draft (Open pull request), a pull request that targets the wrong branch (Sync now), a branch you must sync yourself (Sync now), a git operation still in progress (Open Git tab), a sync that failed (Sync now), and a stack whose session underneath is gone (Unstack). See When something needs you.
Pull requests in a stack
Open a pull request for a stacked session the usual way: its target branch is already the parent's branch. If the parent has no pull request yet, Create PR offers Open pull requests for the whole stack, which pushes every branch and opens one pull request per session, from the bottom up.
On GitHub, a stack becomes a GitHub stack. With Create stacks on GitHub on (the default), Plexus groups a stack's open pull requests — two or more, one on top of the other — into one of GitHub's own stacked pull requests, so GitHub shows the stack and can merge it in one go. The Stack panel's header then reads GitHub stack. GitHub's stacks are a straight line, so a stack of yours that branches usually isn't grouped; the list in the descriptions below still covers it. If you stacked pull requests on GitHub yourself and each one belongs to a session in the project, Plexus nests those sessions to match.
Each pull request in a stack also gets a short list of the stack at the end of its description, between markers, with the current pull request highlighted — except where GitHub already shows its own stack. Plexus rewrites only that block and never touches the rest of the description. Turn it off with List the stack in pull request descriptions. Bitbucket has no stacks of its own, so there the list is how a reviewer sees the stack.
When the bottom pull request merges
When a session's pull request merges — on GitHub, on Bitbucket or from Plexus — the sessions stacked on it move down a level:
- Each one is re-attached to the merged session's own base (usually your main branch), in the sidebar too.
- Its pull request is retargeted to that branch before anything deletes the merged branch, so no pull request is left pointing at a branch that is gone. (Inside a GitHub stack, GitHub does this itself.)
- It is synced onto the updated base, replaying only its own commits — which is what makes this work after a squash merge, where the parent's commits reach main under new ids. If GitHub already rebased it, Plexus takes that rather than pushing again.
If a pull request ends up targeting the wrong branch anyway, its row reads Needs you: pull request targets main, with Sync now to fix it.
Merging
Merge from the Stack panel. Its button merges the layer you have open and everything below it, so it is labelled for where you are: Merge on the bottom layer, Merge 2 on the second, Merge stack on the top. Merge up to here in a stacked session's ⋯ menu does the same. For a session that isn't stacked, the button merges its one pull request.
The button is enabled once every layer it would merge is Ready. Until then it is disabled, and the line under it names what it waits for — Waiting for checks on #128, Changes requested on #126, Waiting for #127 to sync.
Every merge asks once. Clicking the button opens a short confirmation on it: which pull requests merge into which branch, and how —
- GitHub merges them together, as one stack when they form a GitHub stack; otherwise Plexus merges them one at a time, bottom first;
- whether the branch requires a merge queue;
- what moves next: Then #128 moves onto main and syncs;
- and the merge method — your repository's default unless you set Merge method.
Choose Merge to start, or Cancel. While it runs, the rows read Merging, the panel shows the progress with a Pause button, and a progress strip in the context bar — GitHub is merging 2, or Merging 1 of 3 — keeps it in view from any session of the project; click it to open the session it's working on. Merged layers stay in the map, in purple, until the merge is done. Then the sessions above them move onto your main branch and sync, and a notification says what merged: Merged #126 and #127 — guest-checkout-e2e moved onto main.
Plexus merges with Merge method — by default your repository's own default — and deletes each merged branch on your provider once nothing targets it (turn that off with Delete branches after merging). Nothing already merged is ever undone.
Pause stops after the current step; the merge button carries on from where it stopped (Paused. Carries on from #127.). If you start a second merge while one is running, it starts after the first one finishes. A merge left behind when Plexus quits comes back paused, and its rows keep their states.
Merge in order
Pull requests that don't depend on each other can still trip over each other: two that touch the same header merge fine one at a time, but the second conflicts once the first is in. Merge in order merges a list of them one after another. When the next one conflicts with your main branch, or the branch requires pull requests to be up to date, Plexus syncs it onto main first.
- On each session, open the ⋯ menu and choose Add to Merge in order. It needs an open pull request, and a session that is part of a stack merges with its stack instead.
- Open any of them: the panel shows the Merge in order list, numbered, and the pill reads In order 1 of 3. Drag a row by its handle to reorder it, or focus the handle and use
↑and↓. - Click Merge 3 and confirm.
Plexus merges the first, syncs the next onto main, waits for its checks, merges it, and so on. Merging stops on anything that needs you and carries on by itself once it's fixed: if a pull request's checks fail, its row turns amber with Open checks, the panel says Carries on by itself once #130 is fixed, and the moment the checks pass, Plexus goes on — there is no Retry to find. A notification lists what merged when the list is done.
Remove from Merge in order in the ⋯ menu takes a session back out. Pull requests in the list only ever merge when you click its own Merge button; merging a stack never sweeps them along. On the dashboards, each of them is marked with its place in the list — 1 of 3.
Merge queues
When a branch requires a merge queue, Plexus goes through it and never merges past it. The confirmation says so before you start.
- GitHub: Plexus adds the pull request to GitHub's merge queue for you. The row reads In queue while GitHub runs its checks, then Merged.
- Bitbucket: Bitbucket doesn't let other apps add to its merge queue, so this one click is yours. The row reads Needs you: add to merge queue and names the queue (and says if the queue is paused), with Open on Bitbucket to take you to the pull request. If you aren't looking at that session, a notification says the same. Add it there; Plexus keeps watching, and once Bitbucket reports it queued the row reads In queue, then Merged. For a pull request other sessions are stacked on, Plexus first asks Bitbucket to keep its branch after the merge, so the pull requests above it can be moved onto main before the branch goes.
When something needs you
Everything that can stop a sync or a merge reads Needs you: … in amber, with its reason and one action — the table under States lists them. There is no Retry button anywhere: fixing the cause is enough. When the checks pass, the conflict is resolved, the pull request is reopened or the agent has finished, Plexus notices and carries on by itself. A merge stopped on a pull request that was closed waits until it is reopened or taken out.
The sidebar's N need you count includes these, counting each session once. You get a notification only when you aren't already looking at that session, or Plexus is in the background — otherwise the amber chip in front of you is the notice.
Settings
All of these live under Settings → Git ▸ Stacks, and can be set globally, per workspace or per project (Global → Project).
| Setting | Default | What it does |
|---|---|---|
| Sync stacked sessions | Automatically | Automatically syncs as soon as the session underneath moves; Only mark them shows Behind and waits for Sync now in the ⋯ menu; Off stops checking altogether. |
| Push after syncing | On | Pushes a synced branch again (with a lease) when it was already on your remote — the whole branch, including commits you hadn't pushed yet. |
| When a sync conflicts | Hand it to the session's agent | Or Ask me first (Needs you: conflict with Resolve with agent), or Stop (the files, and it's up to you). Covers conflicts while syncing and while merging. See When a sync conflicts. |
| Create stacks on GitHub | On | Makes a stack's pull requests a native GitHub stack, so GitHub shows the stack and merges it in one go. |
| List the stack in pull request descriptions | On | The stack list at the end of each pull request's description, skipped where GitHub shows its own stack. |
| Merge method | Use the repository's default | Or Squash and merge, Create a merge commit or Rebase and merge. |
| Delete branches after merging | On | Deletes a merged pull request's branch on your remote once nothing targets it. |
The two layer shortcuts, Move to the layer above in the stack and Move to the layer below in the stack, live under Settings → Shortcuts with every other binding.
What a plan gates
Syncing and merging need a plan — an individual plan, or a seat on an organization's. Everything runs between your machine and your own GitHub or Bitbucket account; none of it passes through us.
Free on every plan, and without one:
- the state chips, the Stack pill, and the Stack panel's map with every layer's facts;
- Unstack and Remove from Merge in order;
- the actions that only open something: Open checks, Open on Bitbucket, Open pull request, Open terminal, Open Git tab and Take it out;
- basing a session on another session's branch by hand, opening a pull request against any branch, and the protection that keeps a branch alive while another session is based on it.
With a plan: Stack on…, the Stack on field in New Session, Open pull requests for the whole stack, every merge button, Merge in order, Sync now, Resolve with agent and Ask agent to commit — and the automation behind them: syncing, retargeting, GitHub stacks and merging.
Related
- Sessions & worktrees — the sessions a stack is made of
- Split view — sessions side by side, and how a stack is drawn across one
- Pull requests — opening and tracking a single pull request
- Git — Rebase onto… and the rebase badge for a single session
- Dashboard — the state of every session, stacked or not
- Keyboard shortcuts —
⌥↑and⌥↓ - Plans, seats and billing — what a plan unlocks