Stacked Git

The Complete History of StGit: 21 Years of Patch Stack Excellence

From a Python script born in 2005 to a Rust powerhouse in 2022, this is the comprehensive story of one of Git's most enduring workflow tools. Every decision traced, every release documented.

21
Years Active
3,598
Commits
65+
Releases
600+
Issues & PRs

Prologue: The Problem of Patch-Based Development

In the early 2000s, the Linux kernel development community faced a unique challenge. Thousands of developers contributed patches via email to mailing lists, where maintainers would review, request changes, and eventually merge them. Unlike modern pull request workflows, patches were iterative—often going through 10, 20, or even 30 revisions before acceptance.

Developers needed a way to maintain stacks of patches: organized series of changes that could be reordered, modified, and rebased while preserving their logical separation. Git, which Linus Torvalds released in April 2005, provided the foundation, but lacked native tooling for this workflow.

The Patch Stack Philosophy

A patch stack treats each logical change as an independent unit. Instead of one monolithic branch with mixed concerns, developers maintain discrete patches that can be individually modified, reordered, or dropped. This creates cleaner commit histories and easier code review.

Catalin Marinas, an ARM Linux kernel engineer, felt this pain acutely. Managing dozens of patches for ARM architecture support meant constant rebasing, careful tracking of what had been upstreamed, and painful merge conflicts. He needed tooling that understood patch stacks natively.

"With a patch stack workflow, multiple patches can be developed concurrently and efficiently, with each patch focused on a single concern, resulting in both a clean Git commit history and improved productivity." — StGit README

On July 10, 2005—just three months after Git's initial release—Catalin committed the first version of Stacked Git. The tool that would serve kernel developers for over two decades was born.

2005-2020 Era 1: The Python Foundation

The original StGit was a Python application of surprising sophistication. The initial commit (Release 0.4) shipped with 20 files totaling 2,871 lines of code, implementing the core operations that would define the tool: new, push, pop, refresh, and series.

The Initial Architecture (v0.4)

The founding release established patterns that would persist for 17 years:

stgit/
├── main.py      # Command dispatch, 819 lines of interleaved commands
├── stack.py     # Patch stack data model, 535 lines
├── git.py       # Git operations wrapper, 413 lines
├── config.py    # Configuration handling
└── utils.py     # Shared utilities

Command Structure Architecture

How should StGit organize its growing set of subcommands?

Initial
Single main.py with all commands inline—simple but unscalable as commands multiplied
Chosen
Separate commands in stgit/commands/* module files—adopted in v0.5 (fcee87cf)

Release 0.5: The Mailing List Revolution (August 2005)

Within a month, StGit gained the features that made it indispensable for kernel development. The mail command (b4bddc06) allowed developers to send patch series directly to mailing lists—a critical feature when email was the primary collaboration mechanism.

stg mail

Send patches via SMTP with proper threading and cover letters. Integrated maintainer aliases.

stg import

Import patches from files, mbox format, or URLs. Parse email headers to extract authorship.

stg pull

Integrate upstream changes while preserving the patch stack. Automatic rebasing.

stg clean

Remove empty patches after conflicts are resolved. Stack hygiene.

How to integrate with upstream mailing lists?

Kernel patches flow through email. How should StGit participate?

Chosen
Implement native stg mail with SMTP support, threading, and cover letters
Alternative
Use git format-patch + external MUA—more flexible but less integrated

Release 0.6-0.7: Clone, Fold, Pick (September 2005)

The pace was relentless. September brought clone for initializing stacks in new repositories, fold for merging patches, id for showing patch hashes, and crucially, pick for cherry-picking patches between stacks.

Release 0.7 also restored the tutorial to the repository (d1c8fcd7)—a sign that adoption was growing and documentation mattered.

Release 0.8-0.9: Branch Management and UX (October 2005)

These releases focused on ergonomics. Branch cloning (cc3db2b1) allowed creating new stacks from existing ones. The show command displayed patch contents. Pager support (52f3900c) made reading diffs comfortable. And refresh --undo provided a safety net.

Release 0.10: The Testing Foundation (November 2005)

A pivotal release. Catalin copied Git's testsuite framework (aa9b1b9d), establishing automated testing that would protect the codebase through decades of changes.

How to ensure StGit quality and prevent regressions?

Chosen
Adopt git-core's proven test framework—shell-based tests that run actual stg commands

The goto command also appeared in 0.10 (994fdba7)—allowing direct navigation to any patch in the stack. Help output was classified by command category (5dc51fea), improving discoverability.

The Long Tail: v0.11 - v0.22 (2006-2020)

Over the next 14 years, StGit evolved steadily. Each release addressed real-world pain points from kernel developers and other users:

v0.11 (2006)
Debian Packages & Series Rearranging
Official Debian packaging made StGit installable via apt. Series file editing allowed reordering patches with a text editor.
v0.12
Branch Parent Configuration
Configurable parent branches for stacks, enabling more complex workflows.
v0.13
mdiff for Range Comparison
Compare ranges of patches against their previous versions—crucial for reviewing revision series.
v0.14
Binary File Handling
Proper handling of binary files in patches, expanding beyond text-only diffs. Addressed #112: stg export errors on empty patches
v0.15
Pick --revert
Create revert patches from existing commits. Essential for bug fix workflows.
v0.16
Deprecate stg status
Replaced with git status -s alias (aa99027f). Philosophy: don't duplicate Git where unnecessary.
v0.17
Python 2 Explicit
Required python2 executable explicitly (97987532), preparing for the Python 3 transition.
v0.18
GitHub Migration
Migrated from gna.org to GitHub (f248e80e). The project entered the modern era of issue tracking and pull requests. Early issues like #24: branch names with slashes and #61: unhandled exceptions began to be tracked.
v0.19
Python 3 Support
First dual Python 2/3 support. Multi-version testing with tox (baf040bf). Addressed #95: Windows support requests.
v0.20
Shell Completions
Completion packages added to setup.py. Better command-line experience.
v0.21
Python 3.8 Promoted
Python 3.8 prioritized in CI (8fc2c5cd). stg log improvements.
v0.22
Code Quality: isort
Applied isort linting (a7f96f69). Completion improvements. The Python codebase was maturing.

Notable Issues from Era 1

GitHub Issues provided visibility into real-world usage patterns and pain points:

#24 Branch names containing slashes
Users working with git-flow style branches (feature/foo) encountered issues. Fixed by properly handling slash characters.
#78 Windows: WindowsError in repository.py
Windows support was challenging in Python era. Multiple file locking and path issues needed addressing.
#145 Performance: git diff-index expensive on stg new
Performance issues in the Python implementation. This would be one motivation for the eventual Rust rewrite.

Key Decisions Throughout Era 1

How to bring external patches into a stack?

Chosen
Implement stg import with multiple format support: raw diff, mbox, URL

How to integrate upstream changes while preserving the patch stack?

Chosen
Implement stg pull that automatically rebases the entire stack

Should StGit duplicate Git functionality or wrap it?

Chosen
Delegate to Git where appropriate. Example: stg status became alias to git status -s

How should StGit convert regular commits to patches?

Chosen
Add stg uncommit command—peel commits off HEAD into patches (73e2d98e)

How should StGit integrate with editors?

Chosen
Create stgit.el Emacs mode (56d81fe5)—maintained continuously to present day

Era 1 Summary

In 15 years, Era 1 established StGit as the definitive patch stack tool. Starting from 2,871 lines of Python, it grew to a comprehensive system with 22 releases, supporting the Linux kernel's patch-email workflow. The architecture was stable, the test suite comprehensive, and the user base loyal.

But Python was showing its age. Startup time was noticeable. The codebase had accumulated technical debt. And a new maintainer was about to ask: what if we rewrote it in Rust?

2020-2022 Era 2: Python Maturation

Between 2020 and 2022, StGit underwent significant modernization while still in Python. This era saw the project professionalize its processes, reach v1.0 after 15 years, and prepare the foundation for what would become the Rust rewrite.

Release v0.23: Project Modernization (October 2020)

CONTRIBUTING.md was added (91cf6737), signaling a new focus on community growth. GitHub CI Actions replaced Travis CI (c2dfe597). Black formatting was applied (cb053a02). The Python codebase was being prepared for serious maintainership.

How should StGit prepare for Python 3 exclusively?

Chosen
Modernize Python idioms systematically: new-style classes, modern exception syntax, 0o octal prefix, flake8 linting

Release v1.0: First Major Version (February 2021)

After 15+ years of 0.x releases, StGit finally reached v1.0. This wasn't a dramatic feature release—it was a statement of stability and maturity.

v1.0 (February 2021)
First Major Version
Release checklist added (58f43d2c). Import improvements prevented invalid/duplicate patch names (0e569fd4). The version number reflected confidence in the API.
v1.1 (March 2021)
Zsh Completions
Zsh completions joined bash. -C option added to import/fold (51ffa0b4).
v1.2 (May 2021)
Stack Metadata v5
New stack metadata version (76417963). stgit.new.verbose config option added.
v1.3 (June 2021)
Autosign Fix
Regression fix for stgit.autosign configuration.
v1.4 (October 2021)
Empty Patch Control
Disallow committing empty patches by default (8c3e7b21). Addressed #158: stg commit allows empty patches. Safer workflows.
v1.5 (January 2022)
Python 3.10 Support
Python 3.10 added to CI (cde32036). Pick improvements. This would be the last pure Python release.

Era 2 Summary

Era 2 transformed StGit from a mature tool into a professionally maintained project. CI/CD, code formatting, contribution guidelines, and a proper version scheme brought StGit into the modern development era. But even as v1.5 shipped, the Rust rewrite was already underway.

2022 Era 3: The Rust Rewrite

In 2021, Peter Grayson began what would become StGit's most ambitious transformation: a complete rewrite from Python to Rust. The goal wasn't just language migration—it was to make StGit faster, safer, and more maintainable for the next 20 years.

Why Rust?

Python's startup overhead was noticeable when running many stg commands in scripts (see #145). Type safety was enforced only by discipline. Error handling was inconsistent. And the git2 library for Rust provided robust Git operations without shelling out to git commands.

How to rewrite StGit from Python to Rust?

Chosen
Incremental hybrid approach using pyo3. Rust commands coexist with Python during transition.
Not Chosen
Full Rust rewrite from scratch—too risky, would lose feature parity for extended period
Not Chosen
Continue with Python—would not address fundamental performance and safety concerns

The Rewrite Timeline

v2.0.0-alpha.1 (January 2022)
First Rust Alpha
The journey begins. Core commands reimplemented in Rust. Python still handles some operations. aeea854d: Begin Rust reimplementation of StGit. Community interest in #283: publishing to crates.io.
v2.0.0-alpha.2 (July 2022)
stg email format/send
Wrappers for git format-patch and git send-email with StGit patch name support (97ba3cad). The mail workflow preserved.
v2.0.0-beta.1 (July 2022)
Python Implementation REMOVED
The critical milestone. Python code deleted (13678392). StGit is now 100% Rust. No turning back. Early bug reports like #192: stg push fails helped stabilize.
v2.0.0-beta.2 (August 2022)
Stabilization
Bug fixes and polish. Clap 3.2 migration for future-proof CLI (9de4de48).
v2.0.0-beta.3 (August 2022)
Git Compatibility
Ensured compatibility with various Git versions. Edge cases resolved. #202: Emacs stgit mode issues fixed.
v2.0.0-rc.1 (September 2022)
Zero unsafe, clap 4.0
Updated to clap 4.0 (eb8a86d4). Zero unsafe blocks in the codebase. Safety achieved.
v2.0.0-rc.2 (October 2022)
Final Polish
Last release candidate. Address pedantic clippy lints (c99039f3). Ready for release.
v2.0.0 (November 2022)
Complete Rust Rewrite Shipped!
1 year from start to finish. 471 commits. Zero unsafe code. StGit reborn in Rust.

Notable Issues During the Rewrite

#195 Libgit2 and Stacked Git do not support sparse checkouts
Upstream limitation in libgit2 (used via git2 crate). This would later motivate the switch to gitoxide.
#240 Short command forms like "stg ser" no longer work in 2.x
Clap's strict command matching broke muscle-memory abbreviations. Fixed with explicit alias support.
#235 How to update old branches with metadata version 4
Migration path needed for users with old stack metadata. Documented upgrade procedures.

Key Technical Decisions in the Rewrite

clap for CLI

Command-line parsing with clap. Type-safe argument handling. Automatic help generation.

git2 crate

Rust bindings to libgit2. Direct Git operations without shelling out. Faster and safer.

Zero unsafe

No unsafe blocks in the StGit codebase. Memory safety guaranteed by the compiler.

Typed Arguments

Patch names, ranges, and specifiers are now strongly typed. Compile-time correctness.

Era 3 Summary

The Rust rewrite was a triumph of incremental migration. By maintaining feature parity throughout and using a hybrid pyo3 approach, Peter Grayson delivered a complete language migration in under a year. The result: a faster, safer, more maintainable StGit ready for the next two decades.

2022-Present Era 4: Rust Maintenance & Evolution

With the Rust foundation in place, Era 4 has focused on refinement, new features, and keeping StGit current with the Rust ecosystem. In just 3 years, there have been 886 commits and 35 releases—more than all of Era 1.

Post-Rewrite Stabilization (v2.0.1 - v2.0.4)

The first months after v2.0.0 focused on catching edge cases and ensuring the Rust implementation matched Python's behavior exactly. Issues like #256: author set to gitoxide and #273: Windows errors drove rapid iteration.

v2.1.0: Configurable Conflict Policy (December 2022)

The first feature release post-rewrite. Configurable conflict resolution policies gave users control over how StGit handles merge conflicts during push operations. This addressed #212: stg rebase conflict handling.

Should StGit switch from git2 to gitoxide?

gitoxide is a pure-Rust Git implementation under active development.

Chosen in v2.2.0
Adopt gitoxide (054edb2c). Pure Rust dependencies, no C library overhead. Addressed #195 limitations.

v2.2.0: gitoxide Adoption (February 2023)

A significant architectural change. StGit migrated from git2 (Rust bindings to libgit2, a C library) to gitoxide (pure Rust Git implementation). This removed the last non-Rust dependency and improved build portability.

v2.3.0: Official Packages (Mid 2023)

Official deb, rpm, and msi packages published. Addressed #324: more .deb versions needed. StGit became installable on Debian, RedHat/Fedora derivatives, and Windows without building from source.

v2.4.0: After a 1-Year Gap (Late 2024)

After a longer development cycle, v2.4.0 brought accumulated improvements and dependency updates. Fixed #471: stg import --reject broken.

v2.5.0: New Year 2025 Release (January 2025)

New name subcommand for patch renaming (PR #513). stg rebase can now be called without arguments (PR #507). Commands organized by group in help output (PR #514).

v2.5.5: Current Latest (November 2025)

Squash improvements preserving consensus authors (#490). Continuous stgit.el Emacs mode updates (PR #536, PR #552, PR #573). gix (gitoxide) updated to 0.74.

Continuous stgit.el Maintenance

Throughout Era 4, the Emacs integration (stgit.el) has received continuous attention, driven by issues like #521: stgit.el update and #560: Emacs backwards compatibility:

Recent and Upcoming Work

Active development continues with PRs for new features:

PR #608 Add --exec option for running commands during rebase
Run commands after each patch during rebase, similar to git rebase --exec.
PR #601 Add stg branch --reset command
Soft reset the stack to a clean state. In development.
#591 stg new --fixup <patch>
Feature request: Create fixup patches more easily. Active discussion.

Era 4 Velocity

Metric Era 1 (15 years) Era 4 (3 years)
Releases 22 35
Commits ~1,200 ~886
Releases/Year 1.5 11.7

The Rust codebase enables dramatically faster iteration. Monthly releases are now common.

The People Behind StGit

StGit's 21-year history is remarkable not just for its technical evolution, but for the continuity of its maintainership. Just two primary maintainers have guided the project across two decades.

The Maintainers

CM
Catalin Marinas

Founder, 554 commits

PG
Peter Grayson

Current maintainer, 1,862 commits

Catalin Marinas created StGit in 2005 while working on ARM Linux kernel development. He maintained the project through Era 1, establishing the core concepts and API that persist today.

Peter Grayson took over maintainership and led the Rust rewrite. His 1,862 commits represent over half of all StGit development, including the complete Era 3 and Era 4 work.

Key Contributors

KW
Karl Wiberg

343 commits

GH
Gustav Hållberg

188 commits

YD
Yann Dirson

140 commits

DK
David Kågedal

70 commits

CL
Chuck Lever

62 commits

TB
Topher Brown

29 commits

The Two-Maintainer Model

What's remarkable about StGit's history is the continuity. Unlike many open-source projects that cycle through maintainers every few years, StGit has had essentially two maintainers in 21 years. This has resulted in:

Conclusion: 21 Years and Counting

Stacked Git is a rare example of open-source longevity. Born from the practical needs of kernel development, it has evolved while staying true to its original purpose: making patch-based workflows manageable.

The decision graph you can explore captures every major choice, every release, and every architectural shift. From Python to Rust, from gna.org to GitHub, from v0.4 to v2.5.5—the history is preserved.

StGit continues to serve developers who need the precision of patch stacks. Whether you're maintaining Linux kernel drivers, developing for Chromium, or managing any complex series of changes, the tool Catalin Marinas created in 2005 remains relevant today.

Explore the Full Decision Graph

This story is derived from a comprehensive decision graph with 416 nodes and 495 edges, tracing every major decision, action, and outcome in StGit's history. Click below to explore the interactive visualization.

View Interactive Decision Graph

Links & Resources