Skip to content
Joey Wang
Menu

Search

Full Stack Development

Stacked Git: a Patch-Stack Workflow on Top of Git

Stacked Git (StGit) separates the messy work of thinking through code from the clean history Git commits are for, using a patch-stack workflow.

· 4 min read

engineering #git#stgit#productivity#workflow

Audio summary

Most Git workflows are optimized for sharing code, not for thinking while coding. That mismatch is the root cause of messy commits, endless rebases, and painful code reviews. Stacked Git (StGit) is a patch-stack workflow on top of Git that fixes this without giving up Git compatibility.

The core problem with a plain Git workflow

Git commits are immutable history units. Development work is not: it’s non-linear, exploratory, frequently reordered, and revised many times before review.

Typical pain points: “I should have refactored first,” “this commit mixes three unrelated changes,” “the reviewer wants a small change in commit #3,” “I need to temporarily remove this change to debug.”

Git’s answer to all of these is interactive rebase, stashing, resetting, commit squashing, and a fair amount of mental gymnastics. Git is doing its job. We’re just using it for the wrong phase of the work.

The key insight: commits are not ideas

During development you think in ideas; Git stores commits. Those are not the same unit, and forcing one into the other is where the friction comes from.

Enter Stacked Git (StGit)

StGit adds a lightweight abstraction on top of Git: patches, arranged as a stack. Each patch represents one logical idea. You work in patches, reorder them, edit them, and temporarily disable them, and only convert them into Git commits once you’re ready to share.

Mental model: Git vs StGit

Traditional Git

A --- B --- C --- D   (commits are fixed)

Reordering or editing history requires rewriting everything after the change.

Stacked Git

Base commit

   ├─ Patch: refactor
   ├─ Patch: feature
   ├─ Patch: tests

Patches are movable, editable, and applied independently. Git commits get generated later, once the shape of the change is settled.

Under the hood: what StGit actually does

StGit stores patches as metadata, applies them on top of a Git branch, and keeps Git history clean and linear underneath. You are not replacing Git, you’re adding a layer for development ergonomics.

Initial setup

stg init

This enables StGit on the current branch.

Check status:

stg series

Basic daily workflow

1. Start a new logical change

stg new refactor-api

Make changes, then record them:

stg refresh

Think of stg refresh as “update this patch.”

2. Stack another idea on top

stg new add-endpoint
stg refresh

Add tests:

stg new tests
stg refresh

View the stack:

stg series
+ refactor-api
+ add-endpoint
+ tests

Each line is one clean, reviewable idea.

Reordering work without a rebase

Real life happens: “that refactor should have happened first.” With plain Git, that’s an interactive rebase. With StGit:

stg float refactor-api

That’s it.

Conceptual diagram

Before:
  refactor
  feature
  tests

After float:
  refactor
  feature
  tests

The stack adjusts safely and automatically.

Temporarily removing a change to debug

Suspect a patch is causing a bug?

stg pop add-endpoint

If the bug is gone, that confirms it. Restore it with:

stg push add-endpoint

No stash, no branch, no reset.

Fixing reviewer comments without touching unrelated code

Say the reviewer wants a change to validation logic that lives in add-endpoint:

stg goto add-endpoint
# edit code
stg refresh

Everything above it re-applies automatically. This is where StGit earns its keep.

Switching tasks without a branch explosion

Mid-feature, an urgent bug appears.

stg pop feature-x
stg new hotfix-null-check
stg refresh

Export just the fix:

stg export --commit hotfix-null-check

Resume work:

stg pop hotfix-null-check
stg push feature-x

From patches to Git commits

When ready to share, export patches as commits:

stg export --commit

Each patch becomes one Git commit. To squash selectively before opening a PR:

stg squash refactor-api add-endpoint

Development vs sharing

Development phase:
  [patch][patch][patch]

Sharing phase:
  [commit][commit]

StGit optimizes development. Git optimizes distribution. Use each for what it’s good at.

git config --global alias.ss "!stg series"
git config --global alias.sn "!stg new"
git config --global alias.sr "!stg refresh"
git config --global alias.sp "!stg pop"
git config --global alias.spu "!stg push"

Now StGit feels native:

git sn feature-x
git sr
git ss

When StGit is especially valuable

Long-lived feature branches, heavy refactors, frequent review iteration, and frequent context switching are where it pays off most. It’s also just a better fit for anyone who cares about a clean, linear history.

Git commits are for history. StGit patches are for thinking. Once those two concerns are separated, code review stops being a negotiation over commit boundaries and goes back to being a review of the code.