2026-07-17 ยท Joel Holsteen
Introducing GitStack
[!info] how to read this post
I bold a sentence or two per section for skimming, not for emphasis. If you are in a hurry, read only the bold text and you should come away with the shape of the whole post.
For as long as I have used Git, one pull request meant one feature branch. A person picked up a feature, wrote it, and opened a PR when it was done. The PR came out about the size of a feature, because a feature was about as much as one person could write before it was ready to ship. The mapping held because human coding speed set the size.
That mapping is breaking. Code moves much faster now, a lot of it written by coding agents, and the amount of work that lands between "start" and "ready" has grown well past what fits in one reviewable PR. The unit of work outgrew the unit of review.
Stacking is the known fix: split one branch into a sequence of small reviews that build on each other and land bottom-up. The idea is old and good. The tooling around it has stayed complicated enough that most people bounce off it, and the existing options have not kept pace with how fast code is being written now. GitStack is my attempt to make stacking something you would actually reach for.
What GitStack is
GitStack is a source forge built around stacked reviews, and the gs CLI is
meant to feel like Git with a more modern UX. If you know
git add, git commit, and git switch, you already know most of gs. It is
close to a drop-in replacement, with better UX around review and integration
built for the coding agents that write a growing share of the code.
Why big PRs get rubber-stamped
A forty-file, 2,000 line pull request gets rubber-stamped. I have done the rubber-stamping. You open the diff, see that it is too much to hold in your head, skim for anything that looks dangerous, and approve. The size defeats the review, and the review is the whole point.
Stacking keeps each slice small enough to read without giving up the single branch you work on. You commit in one linear history, and the tool tracks which commits belong to which review.
The workflow
You commit as you go in small commits, then group them into reviews. An agent with the gs skill installed can run this whole loop for you (more on that below), but it helps to know what the tool is doing. Start a branch and commit as you work:
gs switch -c add-billing
gs add internal/billing/model.go
gs commit -m "billing: subscription model"
gs add internal/billing/store.go
gs commit -m "billing: persistence layer"
Group both commits into one review, keep working, then open a second review for the next commit:
gs review create -t "billing: subscription model and storage"
gs add internal/api/billing.go
gs commit -m "billing: POST /api/billing endpoint"
gs review create -t "billing: POST /api/billing endpoint"
A bare gs review create sweeps every commit that is not yet in a review, from
the last review up to the branch tip. The first create covers both opening
commits; the second, run after one more commit, covers just that one. The create
that reaches the branch tip pushes the branch and the stack manifest for you. The
branch now reads top to bottom as two reviews, and a reviewer opens each one on
its own instead of scrolling a single enormous diff.
Editing in the middle of the stack
A comment lands on a lower review. Without stacking tooling you would fix it, then hand-rebase every branch stacked above it. With gs you check out the review, amend the commit, and push:
gs checkout r1
gs add internal/billing/model.go
gs amend --no-edit --resolves 01H8
gs push
gs amend rewrites that review's commit and rebases every review above it onto
the new version. You do not run git rebase, and you do not resolve the same
conflict twice.
Approvals survive this when they should. A review that only got rebased, with no change to its own content, keeps its approval. A review whose content actually changed loses it, because the reviewer has not seen the new version yet. The server compares patchsets per review and works out which approvals still hold, so nobody re-reads a slice that only moved.
Built for agents, too
GitStack is meant to stay usable by coding agents as much as by people. The
model helps: small review slices, clear stack state, and reviews that land
independently on one branch. gs status and gs stack print where the work
stands, and when the repo needs an action they print a next: hint, so an agent
or a person who stepped away for an hour can read the current state instead of
guessing at it.
Asking an agent to do it
Run gs skill install and gs writes out a description of its workflow that a
coding agent can pick up. You ask the agent in plain language, and it drives
the same gs verbs you would. Tell it to "split this branch into reviews",
"respond to the comments on r2", or "land the stack", and it leans on gs status
and the printed next: hints to stay oriented as it works.
GitHub stays a mirror
If your code already lives on GitHub, you link the repo and GitStack becomes the system of record while the GitHub copy stays in sync as a mirror. You read and approve stacks in a web console, or from the CLI. Issues, comments, and CI checks flow both ways, so the rest of the team or the community can keep working in GitHub while you work in stacks.
A stack can also close the issue it fixes. Declare --fixes 121 and that issue
closes when the stack lands.
What v0.1 means
GitStack is built on GitStack, so the daily loop gets exercised before it reaches anyone else. Every change that goes into the forge lands as a stack through the forge. The core loop is the one I use every day: commit, push, review, amend, land. Reviews land bottom-up and squash by default.
It is v0.1, and there is more to come. The core loop works today, and I am building out from there.
Try it
Install the CLI:
curl -fsSL https://gitstack.sh/install.sh | sh
Or from Homebrew:
brew install gitstack/tap/gs
The getting started guide walks from install to your first landed stack. Try it on a real change and tell me where the workflow could help you more. Once you are signed in, there is a feedback board in the app for bug reports and feature requests, and that feedback is worth more to me right now than almost anything else.
I will keep writing here about what I learn building and running the forge. Follow along by RSS, or find me on GitHub, LinkedIn, and Bluesky.