A GitHub CLI extension for managing stacked branches and pull requests.
Stacked PRs break large changes into a chain of small, reviewable pull requests that build on each other. gh stack automates the tedious parts — creating branches, keeping them rebased, setting correct PR base branches, and navigating between layers.
gh extension install github/gh-stack
Requires the GitHub CLI (gh) v2.0+ and Git 2.36+.
gh extension upgrade stack
Official, unpinned stable release installations check for a newer
latest release in the
background, caching successful checks for 24 hours. When an update is available,
commands can append an upgrade notice to stderr after success or an operational failure,
also at most once every 24 hours. On failure, the original error appears first
and the exit code is unchanged. This includes non-interactive use; stdout and
JSON output are unchanged.
The check and reminder timestamps are shared across repositories for your user.
They are stored as YAML in gh-stack/state.yml beneath GitHub CLI's state
directory (~/.local/state/gh by default on macOS/Linux).
Commands never wait for a release check. Short commands may finish before a notice is ready. Failed or interrupted attempts can retry after a 15-minute cooldown instead of waiting a full day. Completed checks are cached so a later command can show the notice. No upgrades happen automatically, and development, locally linked, prerelease, and pinned installations are excluded. Help, version, and completion commands do not run the notifier. Usage errors and explicit user cancellations do not show upgrade notices.
Set GH_STACK_NO_UPDATE_NOTIFIER=1 to disable these checks and notices, including
in CI or scripts. Any non-empty value disables the notifier. Optional check
failures do not affect command exit codes; set GH_DEBUG=1 for diagnostics.
Install a release containing this feature to receive notices for future releases.
Install the gh-stack skill so your AI coding agent knows how to work with stacked PRs and the gh stack CLI:
gh skill install github/gh-stack
# Start a new stack (creates and checks out the first branch)
gh stack init
# ... make commits on the first branch ...
# Add another branch on top
gh stack add api-endpoints
# ... make commits ...
# Push all branches
gh stack push
# View the stack
gh stack view
# Open a stack of PRs
gh stack submit
A stack is an ordered list of branches where each branch builds on the one below it. The bottom of the stack is based on a trunk branch (typically main).
frontend → PR #3 (base: api-endpoints) ← top
api-endpoints → PR #2 (base: auth-layer)
auth-layer → PR #1 (base: main) ← bottom
─────────────
main (trunk)
The bottom of the stack is the branch closest to the trunk, and the top is the branch furthest from it. Each branch inherits from the one below it. Navigation commands (up, down, top, bottom) follow this model: up moves away from trunk, down moves toward it.
When you submit, gh stack creates one PR per branch and links them together as a Stack on GitHub. Each PR's base is set to the branch below it in the stack, so reviewers see only the diff for that layer.
Stack metadata is stored in <common-dir>/gh-stack (a JSON file, not committed to the repo), where <common-dir> is Git's common directory. In a normal clone this is .git/gh-stack. All linked worktrees share this catalog, including stack membership, ordering, and PR metadata.
The gh-stack recovery journals, gh-stack-rebase-state and gh-stack-modify-state, also live in the common directory and record the worktrees involved. Git's own HEAD, index, rebase, and cherry-pick markers remain per worktree.
On upgrade, nonconflicting legacy worktree catalogs are consolidated automatically and originals are preserved as backups. Conflicting definitions stop migration rather than choosing one; the error identifies the files to reconcile. Finish or abort legacy in-progress operations in their original worktree first. Do not mix old and new gh-stack versions within one clone.
Complete paused operations before switching preview stages or gh-stack versions. If recovery reports an incompatible execution lifecycle, finish or abort it using the matching build in its recorded original worktree; do not edit or remove the journal.
You can keep independent stacks in linked worktrees or distribute a stack's branches across them. rebase and sync automatically update the clean worktree that owns each affected branch. Dirty, busy, missing, or changed owners stop the operation; unrelated worktrees are left alone. A trunk that cannot safely fast-forward can use the existing fetched-remote fallback.
Mutations are serialized across the clone, while read-only views remain available. Paused operations must be continued or aborted before another mutation. Recovery runs in the recorded worktree even when --continue or --abort is invoked elsewhere. gh-stack never automatically stashes changes, creates/removes worktrees, or steals another checkout.
Mutation locks coordinate gh-stack processes only, not arbitrary Git commands, editors, or other tools. Keep affected worktrees idle while history is being rewritten. During a pause, make only the requested conflict-resolution edits and staging in the reported worktree.
Navigation to a branch checked out elsewhere reports its path and fails without switching. Add --print-path to up, down, top, bottom, trunk, or an explicit-target checkout to get the owning path instead. Unoccupied targets are checked out here before printing this worktree's path; successful stdout contains only the absolute path and a newline. See the worktree workflow for a shell wrapper that checks errors before changing directories.
modify supports stacks distributed across worktrees. It preflights the owners needed by the staged actions and cascade, renames in each branch's owner, and cherry-picks or rebases in the receiving owner's worktree. Unoccupied branches use the initiating worktree; other worktrees are never switched to different branches. Dropped/folded branches and their worktrees remain intact. If the origin's nearest surviving branch is owned elsewhere, modify keeps the preserved original branch checked out and reports the survivor's path. Trunk is only read.
Repositories created with git init --separate-git-dir support main-worktree invocation and main-owner discovery through an existing absolute or relative core.worktree backlink, including one stored in the main config.worktree. The discovery limitation is only linked-worktree invocation without a main-worktree backlink: an operation requiring that main owner fails with actionable guidance, while unaffected worktrees continue. An administration directory is never treated as a checkout destination.
gh stack initInitialize a new stack in the current repository.
gh stack init [flags] [branches...]
Initializes a new stack locally. In interactive mode (no arguments), prompts for a branch name and offers to use the current branch as the first layer.
When explicit branch names are given, existing branches are adopted automatically and any missing branches are created. The trunk defaults to the repository's default branch unless overridden with --base.
An existing branch checked out in another worktree can be adopted without checking it out here. If the final branch is occupied elsewhere, init reports its owner and leaves your current checkout unchanged.
Enables git rerere automatically so that conflict resolutions are remembered across rebases.
| Flag | Description |
|---|---|
-b, --base <branch> |
Trunk branch for the stack (defaults to the repository's default branch) |
Examples:
# Interactive — prompts for branch names
gh stack init
# Non-interactive — specify branches upfront
gh stack init feature-auth feature-api feature-ui
# Use a different trunk branch
gh stack init --base develop feature-auth
# Adopt existing branches into a stack
gh stack init feature-auth feature-api
gh stack addAdd a new branch on top of the current stack.
gh stack add [flags] [branch]
For an existing stack, creates a new branch at the current HEAD, adds it to the top of the stack, and checks it out. Must be run while on the topmost branch of a stack. If no branch name is given, prompts for one.
An existing branch checked out in another worktree can also be adopted without switching either checkout. Commit/stage shortcuts cannot be used to adopt a foreign-owned branch and fail before staging or changing stack membership.
When run interactively from a branch that is not part of a stack, add offers to initialize a new stack instead. The supplied or auto-generated branch name becomes the first layer; without one, the standard init prompts are used.
You can optionally stage changes and create a commit as part of the add flow. When -m is provided without an explicit branch name, the branch name is auto-generated in date+slug format (e.g., 03-24-add_login).
| Flag | Description |
|---|---|
-A, --all |
Stage all changes (including untracked files); requires -m |
-u, --update |
Stage changes to tracked files only; requires -m |
-m, --message <string> |
Create a commit with this message before creating the branch |
Note:
-Aand-uare mutually exclusive.
Examples:
# Create a branch by name
gh stack add api-routes
# Prompt for a branch name interactively
gh stack add
# Stage all changes, commit, and auto-generate the branch name
gh stack add -Am "Add login endpoint"
# Stage only tracked files, commit, and auto-generate the branch name
gh stack add -um "Fix auth bug"
# Commit already-staged changes and auto-generate the branch name
gh stack add -m "Add user model"
# Stage all changes, commit, and use an explicit branch name
gh stack add -Am "Add tests" test-layer
# Stage only tracked files, commit, and use an explicit branch name
gh stack add -um "Update docs" docs-layer
# Commit already-staged changes and use an explicit branch name
gh stack add -m "Refactor utils" cleanup-layer
gh stack checkoutCheck out a stack by its stack number, a pull request number, a PR URL, or a branch name.
gh stack checkout [<stack-number> | <pr-number> | <pr-url> | <branch>]
A bare number is interpreted first as a stack or PR number (repo
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|