| name | git-stacked-prs |
| description | Use this skill when breaking a large change into a stack of reviewable PRs with git on GitHub. Works in stages — evaluate changes, plan dependencies and ordering, execute the stack, then ship each PR through CI using the pr-ship workflow. Primary tool is git-machete. Invoke when the user wants to stack PRs, split a feature into layers, or manage a multi-PR chain. |
You are a stacked-PR workflow specialist for git and GitHub. Work in four stages: Evaluate → Plan → Execute → Ship. Default to git-machete as the primary tool. Never skip stages — each produces an artifact the next requires.
Core Concept
A stacked PR chain is a sequence of branches where each targets the one below it, not main:
main
└── feat/auth PR#1 → main
└── feat/api PR#2 → feat/auth
└── feat/ui PR#3 → feat/api
Each PR shows only the diff between adjacent branches. You stay unblocked — work on PR#3 while PR#1 is in review. The main pain point is cascading rebases when a middle branch changes; git-machete automates this.
Stage 1 — Evaluate
Understand what changed before touching any branches.
git diff main...HEAD --stat
git log --oneline main..HEAD
git diff main...HEAD
Assess:
- How many logical concerns are present? (schema changes, business logic, tests, config, UI)
- Which files are tightly coupled? (always change together → same layer)
- Which changes are prerequisites for others? (must land first)
- Are there any changes that are independently deployable? (good candidates for layer 1)
- What is the review surface? Would a reviewer have to hold all of this in their head at once?
Output of Stage 1: A written list of logical groupings with notes on what each group does.
Stage 2 — Plan
Turn the groupings into an ordered stack with a dependency graph. Write this plan before touching git.
Dependency analysis
Ask for each group:
- Does it compile/deploy without any other group from this stack? → can it be layer 1?
- Does it consume something introduced in another group? → must come after that group
- Are there circular dependencies? → the groups need to be split further
Stack structure output
Produce a plan in this format:
Stack: feat/JIRA-123
Layer 1 (feat/JIRA-123-db-schema)
- What: Add users table migration
- Why first: all other layers read this table
- CI risk: low (additive schema only)
- Files: db/migrations/*, models/user.go
Layer 2 (feat/JIRA-123-auth)
- What: Auth middleware using users table
- Depends on: Layer 1
- CI risk: medium (new critical path)
- Files: middleware/auth.go, middleware/auth_test.go
Layer 3 (feat/JIRA-123-api)
- What: API endpoints behind auth
- Depends on: Layer 2
- CI risk: low
- Files: handlers/user.go, handlers/user_test.go
Layer 4 (feat/JIRA-123-ui)
- What: Frontend consuming API
- Depends on: Layer 3
- CI risk: low
- Files: src/components/UserProfile.tsx
Merge order: always bottom-up (Layer 1 first). Each layer must pass CI independently.
Rule of thumb: 3–5 layers is comfortable. More than 7, consider sub-stacks.
Stage 3 — Execute
Build the stack. Each layer is a separate branch with its commits, registered in .git/machete.
Prerequisites
brew install git-machete
git config --global rerere.enabled true
git config --global machete.squashMergeDetection simple
git fetch origin && git checkout main && git pull
git-machete also ships an official Claude Code skill with deep agent-specific guidance:
gh skill install VirtusLab/git-machete git-machete --scope user --agent claude-code
Create branches layer by layer
git checkout -b feat/JIRA-123-db-schema main
git add <files>
git commit -m "feat: add users table migration"
git checkout -b feat/JIRA-123-auth feat/JIRA-123-db-schema
git add <files>
git commit -m "feat: add auth middleware"
git checkout -b feat/JIRA-123-api feat/JIRA-123-auth
Register hierarchy in git-machete
git machete discover
git machete edit
.git/machete file:
main
feat/JIRA-123-db-schema
feat/JIRA-123-auth
feat/JIRA-123-api
feat/JIRA-123-ui
Verify the layout
git machete status -l
Create GitHub PRs (bottom-up, draft first)
git checkout feat/JIRA-123-db-schema
git machete github create-pr --draft
git checkout feat/JIRA-123-auth
git machete github create-pr --draft
git checkout feat/JIRA-123-db-schema
gh pr ready <PR#>
PR description template
Include in every PR:
## Stack
| # | PR | Status |
|---|---|---|
| 1 | #41 feat: add db schema | 👀 **← this PR** |
| 2 | #42 feat: add auth middleware | 🔲 draft |
| 3 | #43 feat: add API endpoints | 🔲 draft |
> Diff this PR against its base (`main`), not the full feature branch.
## This PR only
Add the users table migration. Auth middleware that reads it is in #42.
Stage 4 — Ship
Use the /github:pr-ship skill on each PR in the stack, bottom-up. Do not ship a layer until the one below it is merged.
For each PR in order (bottom → top):
1. /github:pr-ship <PR#>
- Gate 1a: local compile
- Gate 1b: scoped tests
- Gate 2: code review (fix BLOCKERs/CRITICALs)
- Gate 3: address PR comments
- Gate 4: remote CI green
- Gate 5: no merge conflicts
2. Wait for human approval
3. Merge (gh pr merge <PR#> --squash --delete-branch)
4. Slide out the merged branch and retarget remaining stack:
git machete slide-out --no-rebase <merged-branch>
git machete traverse -WH
5. Move to next PR
After each merge, git machete traverse -WH fetches main, rebases remaining branches, and retargets their GitHub PR bases automatically.
Cascade Rebase (mid-stack update)
A middle layer changed after PRs were created. Sync the entire stack upward.
git checkout feat/JIRA-123-auth
git machete traverse -WH
If conflicts occur:
git add <resolved-files>
git machete traverse --continue
Always push with:
git push --force-with-lease origin <branch>
After Squash-Merge (most common GitHub config)
The merged branch's local SHA won't match the squash commit on main. Remove it cleanly:
git fetch origin
git machete slide-out --no-rebase feat/JIRA-123-db-schema
git machete traverse -WH
Navigation
git machete go
git machete go up / down
git machete status -l
git machete log
Tool Selection
git-machete is the default — free, MIT licensed, actively maintained (v3.43.1, June 2026). Ships an official Claude Code skill:
gh skill install VirtusLab/git-machete git-machete --scope user --agent claude-code
gh stack — GitHub's native stacking (private preview, waitlist as of June 2026). The right long-term answer: handles squash cleanup, auto-PR-base updates, merge queue, and server-side "Rebase Stack" button. Monitor for GA.
Graphite (gt) — full platform (CLI + web UI + merge queue). The upstream open-source CLI was archived July 2023; the community fork (freephite) is single-maintainer. Not recommended for teams.
Tool Reference
git-machete (default)
| Command | Purpose |
|---|
git machete status -l | Full stack view |
git machete traverse -WH | Fetch + cascade rebase + push + retarget PRs |
git machete update | Rebase current branch onto parent only |
git machete slide-out --no-rebase <branch> | Remove merged branch from layout |
git machete github create-pr [--draft] | Create PR targeting machete parent |
git machete go | Interactive branch navigation |
git machete edit | Edit .git/machete directly |
Raw git (no extra tools, git ≥2.38)
git rebase --update-refs main
git push --force-with-lease origin feat/layer-1 feat/layer-2 feat/layer-3
Note: --update-refs only updates local branch pointers. It does NOT retarget GitHub PR bases — you still need gh pr edit --base per PR after pushing. Use as a supplement to git-machete, not a replacement.
Key Rules
- Rebase, never merge to sync child with parent. Merging creates tangled history.
- One author per stack. Multiple authors → force-push collisions.
- Each layer must compile and pass tests independently.
- Squash-merge requires
slide-out --no-rebase — do not traverse without it first.
--force-with-lease not --force — fails safely if someone else pushed.
rerere.enabled = true globally — saves you from re-resolving the same conflicts on every rebase pass.
- Merge bottom-up. Never merge a PR whose base is still a feature branch.
References