fastapi-restack · git:20260922.549c04f · 2026-09-22 · sha256 2e132e86ed1897a5
fastapi-restack git:20260922.549c04fA
Immutable. This exact content is served forever at /api/v1/blob/2e132e86ed1897a5.
---
name: fastapi-restack
description: Use when the user has a stack of dependent branches or PRs that needs rebasing — the base branch moved, the bottom branch merged, or a mid-stack branch was amended. Derives the parent/child chain from git ancestry, rebases each branch onto its new parent, and force-pushes with a lease. Works with plain git; uses a forge CLI only to retarget PR/MR bases when one is available. Also known as `klaussy-restack`.
---
Rebase a stack of dependent branches so each one sits on top of its parent again, then push the stack. `klaussy restack` does the git mechanics: it maps the chain from ancestry and reflogs, rebases bottom-up with `--onto`, verifies, and pushes with a lease. Your job is the two decisions it can't make: confirming the chain, and resolving conflicts.
If `klaussy` isn't on PATH (the command isn't found), follow `.agents/skills/fastapi-restack/manual.md` instead; it's the same procedure by hand.
**First, check for a stack tool.** If the repo uses Graphite, git-town, spr or ghstack, or the host carries a native stack (see the forge commands below), use that tool's own restack command so its metadata stays consistent, and say which you found.
## 1. Map and confirm
Run `klaussy restack plan --base master`. It fetches, then prints the chain bottom-up, which branches already landed in the base, who authored the commits, and any line that needs you:
- **BLOCKED** (dirty tree): let the user commit or stash. Never restack over uncommitted work.
- **ASK** (two branches on one commit): ask which is the parent.
- **NOTE** (a fork): each child rebases separately; say so.
Show the user the chain and confirm it before anything is rewritten; a wrong parent drops or duplicates commits. If the stack holds commits by someone else, get explicit confirmation before force-pushing over their work. Optionally cross-check the chain against the forge's request bases; if they disagree, surface it rather than picking one.
## 2. Rebase
Run `klaussy restack run --chain <confirmed chain>` with the chain exactly as confirmed. It records every branch's tip first, so `klaussy restack undo` puts everything back until you push.
Exit code 2 means a conflict. For each file it names:
1. Show the user both sides of the hunk.
2. Resolve it by understanding the change. Never reach for `--ours` / `--theirs` to make it go away.
3. `git add` the file, then `git rebase --continue`, then `klaussy restack run --continue`.
If a conflict is genuinely ambiguous, run `klaussy restack undo` and hand it back with the specifics. A half-rebased stack is worse than stopping.
## 3. Verify
Run `klaussy restack verify`. Every branch should keep its own commit count. Lines marked `!` are commits whose content changed in the move, which is expected where you resolved a conflict and a red flag anywhere else. Read those before pushing.
## 4. Retarget, then push
If a request's parent changed or landed, retarget its base with the forge commands below, before the push where the forge allows, so it never briefly shows its parent's commits as its own.
### Forge commands (GitHub)
`origin` points at GitHub, so the `gh` CLI is the adapter. Confirm a flag with `gh <command> --help` before running one you haven't used in this repo; CLI interfaces drift between versions.
| Need | Command |
| :--- | :--- |
| Read a ticket | `gh issue view <n> --comments` |
| Open a request | `gh pr create --base <branch> --title <title> --body-file <file>` |
| Request status | `gh pr view <n> --json state,mergeable,reviewDecision,baseRefName` |
| CI status | `gh pr checks <n>`, then `gh run view <run-id> --log-failed` on a failure |
| Retarget a request | `gh pr edit <n> --base <branch>` |
`{owner}/{repo}` are placeholders `gh` fills from the current repo, leave them literal.
**GitHub has native stacks**, driven by the `gh-stack` extension. `gh extension list` says whether it's installed. If it isn't, **offer to install it** — `gh extension install github/gh-stack`, one command, no repo changes — and say what it buys before asking: a stack map and layer navigation on every request page, plus cascading rebase when the base moves. Ask rather than installing unprompted, since it touches the user's `gh` setup and not this repo, but do ask; silently settling for bare chained bases hands back a worse result than the one command would have. Declining is a fine answer and the fallback below still works.
The extension is in public preview, so check `gh stack <command> --help` before relying on a flag.
| Need | Command |
| :--- | :--- |
| Link requests that already exist into a stack | `gh stack link --base <branch> <branch-or-pr> <branch-or-pr> ...` |
| Track a carved chain locally | `gh stack init --base <branch> <branch> ...` |
| Push the tracked chain and open or update its requests | `gh stack submit` |
| See the stack | `gh stack view` |
| Cascading rebase after the base moved | `gh stack rebase` |
| Fetch, rebase, push, and sync in one pass | `gh stack sync` |
Arguments run bottom-up, nearest the base first. Two constraints decide whether a stack is available at all: **every branch must live in this repo** (cross-fork stacks aren't supported), and the extension has to be installed.
`link` and `init` are two different entry points and the difference shows up later. `link` stacks requests that already exist and leaves nothing behind locally, so a later `gh stack rebase` needs `gh stack checkout <stack-number>` first to pick the stack back up. `init` registers the branches locally up front and `submit` then opens the requests itself, which means the bodies are its own — write them with `gh pr edit <n> --body-file` afterwards if they have to say something specific.
Without it, chained `--base` targets still give reviewers a per-layer diff, and GitHub often offers to convert an eligible chain into a stack — a banner on the request, or "Add to stack" behind the stack icon. Say which route you took.
A missing, unauthenticated or nonexistent CLI is not a failure here. Print what's left instead: each branch, its new parent, and the one field to change. Never ask the user to install a hosting CLI to finish a rebase.
Then run `klaussy restack push`. It force-pushes bottom-up with `--force-with-lease --force-if-includes`, never pushes `master`, and stops at the first refusal. A refused lease means someone else pushed: fetch and look. Never fall back to a bare `--force`.
## 5. Report
Report the stack's new shape, the pre-restack tips from the plan output (the recovery path now that the push is done), and anything left to retarget by hand. Say plainly that only what CI re-runs is verified; a clean rebase is not a passing test.
## Rules
- Git is the source of truth for topology. A forge CLI may enrich or confirm the chain; it never gates the rebase.
- Do not squash, reword or reorder while restacking. A restack moves commits; changing them at the same time makes the diff impossible to review.
- Never delete branches as part of a restack, even ones that already landed.
## When NOT to use
- A single branch based on `master` is behind — that's a plain `git rebase origin/master`, no stack machinery needed.
- The user wants to land the stack (merge each request in order) — different task, different risks.
- The branches live in a fork or you lack push rights — the rebase will succeed locally and the push will fail; check first.
- The stack is shared and teammates have unpushed work on it — coordinate before rewriting, force-with-lease can't protect what it hasn't seen.