Typical Workflows
This guide covers the most common workflows for day-to-day use of Stacked PRs, from the standard flow to advanced patterns.
Standard Workflow
Section titled “Standard Workflow”The basic flow: initialize a stack, add branches for each logical unit of work, commit, push, iterate on review feedback, and merge.
# 1. Start a stack (creates and checks out the first branch)gh stack init
# 2. Work on the first layer# ... write code, make commits ...
# 3. Add the next layergh stack add api-routes# ... write code, make commits ...
# 4. Push everything and create Stacked PRsgh stack submit
# 5. Reviewer requests changes on the first PRgh stack bottom# ... make changes, commit ...
# 6. Rebase the rest of the stack on top of your fixgh stack rebase
# 7. Push the updated branchesgh stack push
# 8. Sync upstream changes as PRs get mergedgh stack syncAbbreviated Workflow
Section titled “Abbreviated Workflow”For speed, use a branch prefix with --numbered and the -Am flags to fold staging, committing, and branch creation into a single command. Branch names are auto-generated as prefix/01, prefix/02, etc.
# Alias `gh stack` as `gs` for easier usegh stack alias
# 1. Start a stack with numbered branchesgs init -p feat --numbered# → creates feat/01 and checks it out
# 2. Write code for the first layer# ... write code ...
# 3. Stage and commit on the current branchgs add -Am "Auth middleware"# → feat/01 has no commits yet, so the commit lands here
# 4. Write code for the next layer# ... write code ...
# 5. Create the next branch and commitgs add -Am "API routes"# → feat/01 already has commits, so feat/02 is created
# 6. Keep going# ... write code ...gs add -Am "Frontend components"# → creates feat/03
# 7. Push everything and create PRsgs submitEach gs add -Am "..." stages all files, commits, and (if the current branch already has commits) creates a new branch — no separate git add or git commit needed.
Making Mid-Stack Changes
Section titled “Making Mid-Stack Changes”When you’re working on a higher layer and realize you need to change something lower in the stack — don’t hack around it at the current layer. Navigate down, make the change where it belongs, and rebase.
# You're on feat/frontend but need an API change
# 1. Navigate to the API branchgh stack down# or: gh stack checkout api-routes
# 2. Make the change where it belongsgit add users_api.gogit commit -m "Add get-user endpoint"
# 3. Rebase everything above to pick up the changegh stack rebase --upstack
# 4. Navigate back to where you were workinggh stack topThis keeps each branch focused on one concern and avoids muddying the diff for reviewers.
Responding to Review Feedback
Section titled “Responding to Review Feedback”When a reviewer requests changes on a PR mid-stack:
# 1. Navigate to the branch that needs changesgh stack checkout auth-middleware# or: gh stack bottom, gh stack down, etc.
# 2. Make the fixesgit add .git commit -m "Address review feedback"
# 3. Cascade the changes through the rest of the stackgh stack rebase
# 4. Push the updated stackgh stack pushThe rebase ensures all branches above the changed one pick up the fixes. gh stack push uses --force-with-lease to safely update the rebased branches.
Syncing After Merges
Section titled “Syncing After Merges”When a PR at the bottom of the stack is merged on GitHub, use gh stack sync to update your local state:
gh stack syncThis command:
- Fetches the latest changes from the remote
- Fast-forwards the trunk branch
- Rebases all remaining stack branches onto the updated trunk
- Pushes the updated branches
- Syncs PR state from GitHub
- Prompts to prune local branches for merged PRs (use
--pruneto prune automatically)
If a conflict is detected during the rebase, all branches are restored to their original state, and you’re advised to run gh stack rebase to resolve conflicts interactively.
Rebasing Your Stack
Section titled “Rebasing Your Stack”Stacked PRs rely on rebasing rather than merge commits to keep each branch’s diff clean and reviewable. If you’re coming from a merge-commit workflow, the key difference is: instead of merging upstream changes into your branch (which creates a merge commit with multiple parents), you replay your commits on top of the latest base. The result is a linear history where each PR shows only its specific changes.
How rebasing works with stacks
Section titled “How rebasing works with stacks”When you run gh stack rebase, it performs a cascading rebase: each branch in the stack is rebased onto the tip of the branch below it, starting from the trunk. This ensures every branch has the latest changes from all lower layers.
# Rebase the entire stack (all branches, trunk to top)gh stack rebase
# Only rebase from trunk up to the current branchgh stack rebase --downstack
# Only rebase from the current branch up to the topgh stack rebase --upstackAfter rebasing, push the updated branches:
gh stack pushgh stack push uses --force-with-lease to safely update the rebased branches. This is a safe form of force push — it ensures you don’t overwrite changes that someone else pushed since your last fetch. If the remote has unexpected changes, the push is rejected and you can investigate.
Rebase from the CLI vs. the web UI
Section titled “Rebase from the CLI vs. the web UI”You can rebase stack branches from either the CLI or the GitHub web UI, but they behave differently:
CLI (gh stack rebase) | Web UI (“Rebase Stack” button) | |
|---|---|---|
| Runs where | Locally, using your Git installation | On GitHub’s servers |
| Commit signing | Commits are signed with your local Git committer config (GPG/SSH signing, if configured) | Commits retain the original author but the committer is set to whoever clicked the button — commits are not signed |
| Conflict resolution | Interactive — you resolve conflicts in your editor, then gh stack rebase --continue | Not available if there are conflicts — you must rebase locally |
Resolving conflicts
Section titled “Resolving conflicts”When a rebase encounters a conflict, gh stack rebase stops and tells you which files are conflicted:
gh stack rebase# ✗ Conflict detected rebasing feat/api onto feat/auth# C api/routes.go (lines 12–18)## Resolve conflicts on feat/api, then run: gh stack rebase --continue# Or abort this operation with: gh stack rebase --abortTo resolve:
# 1. Open the conflicted files and resolve the markers# (<<<<<<< / ======= / >>>>>>>)# Use your editor of choice
# 2. Stage the resolved filesgit add api/routes.go
# 3. Continue the rebase — remaining branches are rebased automaticallygh stack rebase --continueIf the conflict is too complex or you want to start over:
# Abort and restore all branches to their pre-rebase stategh stack rebase --abortThe rebase + force-push cycle
Section titled “The rebase + force-push cycle”The typical cycle when updating a stack after making changes looks like this:
# 1. Make changes on a mid-stack branchgh stack checkout feat/authgit add .git commit -m "Fix token validation"
# 2. Rebase everything above to incorporate the changegh stack rebase --upstack
# 3. Push all updated branches (safe force push)gh stack pushThis is equivalent but distinct from updating your branch using a merge commit. The key difference is that after changing a lower branch, rebase maintains a linear commit history so the unique set of commits on each branch have clean diffs.
gh stack push then handles the force push safely via --force-with-lease --atomic, ensuring either all branches update or none do.
For a simpler all-in-one flow, gh stack sync combines fetch, rebase, and push into a single command — useful when you just need to pull in the latest upstream changes:
gh stack syncExisting Branches into a Stack
Section titled “Existing Branches into a Stack”If you already have a set of branches that form a logical chain, you can organize them into a stack by passing them to gh stack init. Existing branches are adopted automatically — no special flags needed.
# Adopt three existing branches into a stack (bottom to top)gh stack init feat/auth feat/api feat/uiThe order matters: branches are listed from bottom (closest to trunk) to top (furthest from trunk). Any PRs already open for these branches are detected and linked to the stack.
You can also mix existing and new branches in one command:
# feat/auth exists, feat/api-v2 will be createdgh stack init feat/auth feat/api-v2After organizing branches into a stack, run gh stack submit to create a Stack on GitHub and link the PRs together.
# View the new stackgh stack view
# Create/update PRs and link them as a Stack on GitHubgh stack submitStructuring Your Stack
Section titled “Structuring Your Stack”Think of a stack from the reviewer’s perspective: the PRs should tell a cohesive story. A reviewer reading the PRs in sequence should understand the progression of changes.
Dependency order
Section titled “Dependency order”Plan your layers before writing code. Foundational changes go in lower branches, dependent changes go higher:
┌── tests ← integration tests for the full stack ┌── frontend-ui ← UI components that call the APIs ┌── api-endpoints ← API routes that use the models ┌── data-models ← shared types, database schemamain (trunk)When to create a new branch
Section titled “When to create a new branch”Create a new branch (gh stack add) when you’re starting a different concern:
- Switching from backend to frontend work
- Moving from core logic to tests or documentation
- The next changes have a different reviewer audience
- The current branch is already large enough to review
One stack, one effort
Section titled “One stack, one effort”All branches in a stack should be part of the same feature or project. If you need to work on something unrelated, start a separate stack with gh stack init or switch to an existing one with gh stack checkout.
Restructuring a Stack
Section titled “Restructuring a Stack”When you need to change the composition of a stack — remove a branch, combine branches, insert a new branch, change the order, or rename a branch — use gh stack modify:
# Open the modify TUIgh stack modify
# In the TUI:# x → drop a branch# d → fold down (into branch below)# u → fold up (into branch above)# i → insert below# I → insert above# Shift+↑/↓ → reorder# r → rename# z → undo# Ctrl+S → apply changes# q → cancel
# After modifying, push changes to remote and recreate the stack on GitHubgh stack submitCommon restructuring scenarios
Section titled “Common restructuring scenarios”Remove a branch and its unique commits from the stack:
gh stack modify- Navigate to the branch, press
xto mark it for drop - Press
Ctrl+Sto apply gh stack submit
Combine two branches into one:
gh stack modify- Navigate to the branch you want to fold
- Press
dto fold its commits into the branch below, oruto fold into the branch above - Press
Ctrl+Sto apply gh stack submit
Reorder branches:
gh stack modify- Navigate to the branch to move
- Press
Shift+↑to move up orShift+↓to move down - Press
Ctrl+Sto apply gh stack submit
For a comprehensive guide on all modify operations, see the Restructuring Stacks guide.
Using AI Agents with Stacks
Section titled “Using AI Agents with Stacks”AI coding agents (like GitHub Copilot) can create and manage Stacked PRs on your behalf. Install the gh-stack skill to give them the context they need:
gh skill install github/gh-stackOr if you prefer to use npx skills:
npx skills add github/gh-stackWith the skill installed, your agent can:
- Plan stack structure based on the work being done
- Create branches and commit changes in the right layers
- Navigate between branches to make mid-stack changes
- Push branches and create Stacked PRs
- Rebase after making changes to lower layers