Is Git Finally Getting a Successor? Jujutsu (jj), Sapling, and Pijul for Stacked Diffs and Monorepos in 2025
Git is still the default language of software collaboration in 2025. But the gravitational pull of our day-to-day pain points has not disappeared: stacked diffs are clumsy, rebases are brittle and slow in large repos, and a monorepo can make even simple operations feel like walking through molasses.
If you have felt this friction, you have probably heard murmurs about three contenders: Jujutsu (jj), Sapling, and Pijul. Each promises a better developer experience and a more forgiving model for history manipulation. Critically, two of them (jj and Sapling) can slot into your existing Git-centric infrastructure today. Pijul brings a genuinely different theory of version control that is elegant and academically appealing, though its surrounding ecosystem is still catching up.
This is a hands-on, opinionated tour for practitioners: where each fits, where they shine, how they behave in stacked-diff and monorepo workflows, and how to pilot them safely alongside GitHub and GitLab without disrupting teammates.
TL;DR
- If you need better stacked diffs with minimal team disruption: pilot Jujutsu (jj) or Sapling locally and push to GitHub/GitLab like normal. Both are Git-compatible.
- If you wrangle a massive monorepo: Sapling’s sparse features and interactive tooling are strong; Git’s partial clone and sparse-checkout are improving, but Sapling’s UX is purpose-built.
- If you care about a principled patch-based model and conflict commutation: study and trial Pijul. It is promising, but the interop story for mainstream Git hosting is still early.
- None of these replace Git server-side in most organizations this year. But you can materially improve local developer velocity and stacked-diff hygiene by adopting jj or Sapling as a client.
Why alternatives now?
Git’s strengths are durability, ubiquity, and a robust ecosystem: code hosting, CI, security workflows, developer muscle memory. But Git’s UX was designed around a workflow where commits are cheap and you are the only person rewriting your history. That model breaks down when you:
- Maintain long-lived stacks of reviewable changes.
- Rebase often to keep stacks on top of rapidly moving main branches.
- Operate in huge monorepos where operations like
git status
,git rebase
, orgit log
become progressively slower.
Alternatives try to make these workflows smoother through better data models, better defaults for rewriting history, and first-class operations on stacks.
The contenders at a glance
-
Jujutsu (jj): A Git-compatible DVCS with a modern, stack-oriented UX and stable change identifiers. It uses Git as a storage and transport layer while giving you a friendlier mental model for rewriting history. Project home: https://github.com/martinvonz/jj
-
Sapling (sl): A Git-compatible client from Meta’s source control team, optimized for huge repos, stacked diffs, and interactive workflows. It grew out of years of running Mercurial at Facebook scale and now targets Git compatibility and performance. Project home: https://sapling-scm.com/
-
Pijul: A patch-based VCS grounded in formal patch theory. It treats changes as first-class objects that can commute and avoids many rebasing pain points by design. Project home: https://pijul.org
Supporting references and background:
- Git partial clone and sparse-checkout: https://git-scm.com/docs/partial-clone, https://git-scm.com/docs/git-sparse-checkout
- Meta’s prior art on scaling source control (Mercurial/Mononoke and successors) and Sapling posts: search for Sapling SCM blog and Meta engineering articles.
- Patch theory and Pijul: Pijul Book at https://pijul.org/documentation and academic background on patch theory from Pierre-Étienne Meunier and earlier work in Darcs.
Stacked diffs: the crux of developer experience
Stacked diffs are the practice of splitting a large change into a sequence of small, reviewable commits that build on each other. It is a better way to develop complex features, yet tools historically made it awkward:
- You can create stacked commits in Git, but keeping them in shape requires careful rebasing and fixup commits.
- Review tooling is hit-or-miss for dependent pull requests or merge requests. People resort to shell scripts, bespoke bots, or vendor tools.
jj and Sapling embrace stacked diffs as a first-class workflow. Pijul’s patch model essentially makes stacked changes a natural consequence of how it represents history.
Jujutsu (jj): Git-compatible, change IDs, and friendly rebases
jj’s core idea is to separate the developer-facing notion of a change from the low-level commit object. It introduces a stable change-id that survives history rewriting and is separate from the volatile commit hash that Git cares about. You can rebase, split, and reorder changes without losing track of which logical change is which.
Key traits:
- Git compatibility: jj can clone a Git repo and push to a Git remote. Your teammates can keep using Git. You still use GitHub/GitLab for code review and CI.
- Stable change IDs: rewrite-friendly and easier to reason about than Git commit hashes that change with every rebase.
- Revsets and stack operations: powerful revision queries and baked-in stack manipulation.
- Low friction adoption: you can layer jj on top of your existing repo locally and keep all remote workflows unchanged.
Example: starting from a Git repo
bash# Clone an existing Git repo using jj (creates a Git-backed jj repo) jj git clone https://github.com/org/repo.git repo cd repo # Configure your identity jj config set user.name 'Your Name' jj config set user.email 'you@example.com' # Create a feature branch backed by Git jj branch create feature/jj-pilot # Start a stack: first change jj new # ... edit files ... jj describe -m 'Part 1: refactor module X to enable Y' # Split off a second change on top of the first jj new # ... more edits ... jj describe -m 'Part 2: add API endpoint Z' # Visualize the stack jj log --revisions 'ancestors(@, 5)' # Rebase entire stack onto latest main jj git fetch jj rebase -s 'descendants(@)' -d main # Push to Git remote; your changes appear as normal Git commits jj git push --remote origin --branch feature/jj-pilot
Notes:
- In jj,
jj new
creates a new change on top of the currently checked out change and associates your working copy with it. jj describe
updates the metadata of the current change.jj commit
can also be used to finalize and create another change from your working copy.- Revset syntax like
descendants(@)
lets you operate on your entire stack with one command.
Review on GitHub/GitLab
- Your pushed branch contains a stack of commits. You can create a single PR and review commit-by-commit, or create dependent PRs by targeting each PR to the branch of the previous commit in the stack.
- Tools like Graphite (https://graphite.dev) or ghstack (https://github.com/ezyang/ghstack) automate stacked PRs. jj works fine with these because it produces normal Git commits.
Why jj is compelling for stacked diffs
- Change IDs survive rebases and reorders, which makes long-running stacks tractable.
- The command set reinforces good hygiene: splitting, moving, squashing, and rebasing entire stacks are daily operations, not once-in-a-while footguns.
Caveats
- You still live in Git’s object store, so raw clone size and blob transport are unchanged versus Git. That is good for interop but does not magically shrink your monorepo.
- Your teammates do not need jj, but they will see rewritten commit hashes when you rebase before push. That is normal when you keep feature branches private until merge.
Sapling (sl): Built for scale, optimized for stack ergonomics
Sapling’s pedigree is running one of the world’s largest source trees. It is a Git-compatible client focused on a tight feedback loop for large repos, with first-class concepts for stacked diffs, sparse checkouts, and interactive operations. It ships a smart log view, branchless workflows, and commands like restack and absorb that feel like superpowers when you have dozens of in-flight changes.
Key traits:
- Git compatibility: clone from and push to Git remotes. Your teammates can keep using Git.
- Huge-repo performance: practical sparse workflows, watchman integration for fast status, and UX designed for big codebases.
- Stacks by default: a branchless UX makes commit stacks the primary unit of work rather than a tangle of named branches.
Example: a stacked workflow over a Git repo
bash# Clone a Git repo into a Sapling working copy sl clone https://github.com/org/repo.git cd repo # See the smartlog visualization of your history and stack sl smartlog # Create a first change in your stack # ... edit files ... sl commit -m 'Part 1: refactor module X to enable Y' # Create a second change on top # ... edit files ... sl commit -m 'Part 2: add API endpoint Z' # Tidy up the stack: automatically absorb fixups into the right commit # (detects which commit a change likely belongs to and amends it) sl absorb # Rebase the entire stack on top of the latest main sl pull --rebase sl restack # Push the branch to Git remote for review sl push origin HEAD:feature/sl-pilot
Sparse workflows for monorepos
bash# Enable sparse mode and focus on a sub-tree sl sparse enable sl sparse set //services/auth/... //shared/proto/... # Later, add another directory to your sparse view sl sparse add //frontend/webapp/... # Show which paths are currently included sl sparse list
These sparse commands feel integrated, whereas in vanilla Git you are often juggling partial clone filters, cone-mode sparse patterns, and index performance edge cases. Sapling’s tight integration with file watching (e.g., Watchman) makes status and diff snappy even with large trees.
Why Sapling is compelling
- The smartlog and branchless-by-default workflow make stacked diffs routine rather than exceptional.
- Large repo ergonomics are not an afterthought. Sapling makes sparse checkouts and restacking feel natural.
- Like jj, it is a drop-in local improvement. You can push to Git remotes and review on GitHub/GitLab.
Caveats
- While Sapling aims for Git compatibility, it is another client to install and learn. In some organizations that can be a barrier without clear sponsorship.
- Some advanced features shine brightest with specific server-side support (e.g., Meta’s internal stack-aware tooling). In the open ecosystem, you will rely on Git hosting conventions and third-party stacked-PR tools.
Pijul: Patch theory in practice
Pijul’s differentiator is theoretical: it models changes as patches that can commute. Instead of treating the commit graph as the thing you reshape, Pijul propagates and reorders patches in a principled way. Conflict resolution becomes an operation that records how to integrate two patches, and that knowledge travels with the patch. This eliminates much of the brittleness of rebasing and merge conflict churn.
Key traits:
- Patch-first model: patches can be applied in different orders when they are independent, providing a natural fit for stacked work.
- Conflict recording: once you resolve a conflict between patches, that resolution can be reused rather than rediscovered repeatedly in different branches.
- Fresh ecosystem: a clean design with an active community, but far less tooling around GitHub/GitLab interop.
Example: Pijul workflow
bash# Initialize a new repository pijul init myproj cd myproj # Record your first patch # ... edit files ... pijul add . pijul record -m 'Part 1: refactor module X to enable Y' # Record a second patch on top # ... more edits ... pijul record -m 'Part 2: add API endpoint Z' # Visualize patches and their dependencies pijul log # Push to a remote Pijul repository pijul remote add origin ssh://example.com/repo pijul push origin
Interop considerations
- There is no first-class GitHub/GitLab compatibility story comparable to jj or Sapling. You can convert repos using export/import pipelines (e.g.,
git fast-export
into a format Pijul can import, or community tools in the opposite direction), but these are usually one-time migrations or periodic mirrors rather than a seamless daily bridge. - If your organization is married to GitHub/GitLab workflows, Pijul is best tried in a sandbox or for greenfield projects rather than as a drop-in replacement for a team that lives in Git PRs.
Why Pijul is compelling
- The model is simply nicer. Stacked work, commutation of independent changes, and reusable conflict resolutions are what we all want rebases to feel like.
Caveats
- Ecosystem and interop: without ubiquitous hosting and review tooling, the path to team-wide adoption in a Git shop is steep.
- Familiarity: developers must learn a new mental model. The payoff is real, but it is not incremental the way jj and Sapling are.
Monorepo performance and ergonomics
Let’s be blunt: the way a VCS behaves in a modest repo is almost irrelevant to how it feels in a monorepo. In large trees, the winning features are:
- Sparse checkouts that are both fast and easy to change.
- Fast status and diff without scanning millions of files.
- Rebases and stack operations that do not stall for minutes.
How the contenders fare:
- Git in 2025: partial clone, promisor remotes, and cone-mode sparse-checkout have matured. You can absolutely tame large repos with careful configuration and modern Git versions. But the ergonomics still push complexity onto users.
- Sapling: strong first-party sparse UX, integrated with fast status. The developer loop (edit, status, diff, absorb, restack) tends to feel sub-second even in huge trees when set up with a file-watching service.
- jj: uses Git for storage and transport, so clone and blob fetching behave like Git. jj’s advantage shows up in local operations on stacks and history queries, which are faster and more intentional than chaining Git plumbing commands. For sparse, you will rely on Git’s mechanisms under the hood.
- Pijul: performance is improving and the patch model avoids pathological rebases by design. Real-world performance in monorepos is promising but lacks the breadth of battle-tested data you have for Git/Sapling at Meta scale.
If you manage a large monorepo today, Sapling is the most pragmatic way to unlock a meaningfully better developer loop without abandoning Git hosting.
GitHub/GitLab interop: what changes, what stays the same
The good news: both jj and Sapling interoperate with Git remotes. That means:
- You can keep GitHub/GitLab as your source of truth, branch protection, CI, and code review platform.
- A pilot can start with a single developer using jj or Sapling locally without any change to servers or teammates.
For code review of stacked diffs, you have options:
- Single PR, review commit-by-commit: treat your branch as a sequence of logically separate commits and review them individually. GitHub supports per-commit diffs and review comments.
- Dependent PRs: create PR A from branch A; then create PR B from branch B with base branch set to A; and so on. Tools like Graphite or ghstack automate this choreography so that when A merges, B automatically retargets and the stack marches forward.
- GitLab offers similar patterns via branch targeting and MR dependencies. If you do not have a first-class dependency feature tier, you can emulate it with clearly named branches and pipeline gates.
Operational guidelines to avoid disruption:
- Keep feature stacks on unprotected branches. Rebase and restack locally as needed; push force-updates to your feature branch. Do not rebase shared or protected branches.
- Encourage squash merges or rebase merges into main depending on your policy. Stacked diffs are an authoring and review convenience; your mainline policy remains intact.
- Use CI decorators that trigger per-commit and per-PR checks to preserve signal in stacked review.
Hands-on pilot plans: adopting jj or Sapling alongside Git
You do not need a migration plan; you need a 90-day pilot. Here are three practical blueprints.
Pilot A: individual-only, low friction (jj or Sapling)
- One or two engineers adopt jj or Sapling locally.
- They push branches to origin as usual and open PRs on GitHub/GitLab.
- They demonstrate stacked PRs using Graphite or ghstack in a small feature area.
- Measure: time-to-reviewable, rebase time on main moving by X commits/day, number of conflicts rediscovered, developer sentiment.
Pilot B: team-level stacked diffs (jj or Sapling)
- A team agrees to author changes as stacks for a quarter.
- Adoption kit: 1-page guidelines, pre-commit hooks, and short demos on restacking and absorb/fixup.
- CI config: ensure branch protection and checks do not break on force-push to feature branches.
- Measure: PR size distribution, review turnaround, merge conflict churn on busy weeks.
Pilot C: patch-theory exploration (Pijul)
- A small greenfield project uses Pijul end-to-end with its native hosting (or self-hosted) to assess the patch model in practice.
- Keep a Git mirror if you require conventional CI or security scanning.
- Measure: conflict frequency and resolution reuse versus Git baselines, subjective workflow clarity.
Exit criteria
- If jj or Sapling yields a tangible improvement in stacked diff velocity without ecosystem friction, graduate from pilot to optional tooling in your org’s developer portal.
- If Pijul’s model proves superior for a particular team and interop is manageable, expand its use in that slice of your organization with clear boundaries.
Workflows: the concrete feel of each tool
jj stack hygiene
bash# Split the current change into two jj split # Move some files to the parent change jj move --to @- path/to/file.rs # Squash two adjacent changes jj squash -r @-..@ # Rebase your stack onto main jj git fetch jj rebase -s 'descendants(@)' -d main
Sapling stack hygiene
bash# Automatically absorb fixup edits into the right commits sl absorb # Restack entire stack after pulling the latest main sl pull --rebase sl restack # Edit a middle commit interactively sl goto <commit-hash-or-bookmark> # Amend it and then restack again sl commit --amend -m 'Tweaked Part 2' sl restack
Pijul patch hygiene
bash# Amend an existing patch pijul amend <patch-hash> # Unrecord a patch to back it out cleanly pijul unrecord <patch-hash> # Conflicts resolved once can be reused when patches commute again # (built into Pijul's conflict recording)
Risks, caveats, and mitigations
- Team fragmentation: two developers using jj or Sapling, others on Git. Mitigation: keep interop simple. Only push conventional Git branches; do not require others to install new tools.
- Force-push anxiety: stacked workflows often rewrite feature branches. Mitigation: enforce branch protection on main and release branches; document that feature branches may be force-pushed by their authors.
- CI churn on stacked PRs: additional branches and retargeting can spawn more pipelines. Mitigation: configure CI to deduplicate or cancel superseded pipelines; use path filters to scope work.
- Tool learning curve: new commands and mental models. Mitigation: 30-minute brown-bag training with live demos; cheat sheets; shell aliases.
How to choose in 2025
- You want immediate improvement in stacked diffs without changing servers: pick jj or Sapling. If your repo is huge or you need sparse UX, Sapling has the edge. If you are intrigued by stable change IDs and a modern DVCS feel, jj is excellent.
- You want to explore a principled alternative to rebasing headaches: study Pijul. Use it on a team that can control its hosting or for a new service; keep a Git mirror if needed.
- You want the safest organizational rollout: start with local-only pilots and graduate to team-level adoption with clear, written conventions.
Frequently asked questions
Does GitHub support stacked PRs?
- Not natively as a first-class feature (at the time of writing), but it is perfectly workable. Create PR B targeting the branch of PR A. Tools like Graphite and ghstack automate creation, retargeting, and land-by-rebase flows.
Will jj or Sapling break our CI or security scanners?
- No, not if you keep pushing normal Git branches containing normal Git commits. Your scanners and CI do not know or care that you authored locally using jj or Sapling.
Can we partially adopt in a monorepo?
- Yes. Individual services or directories can use stacked diffs and sparse workflows even if the rest of the repo stays unchanged. That is the best way to demonstrate value without organizational friction.
What about Perforce or Mercurial?
- Perforce remains a strong fit for very large, centralized monorepos with specific binary asset needs. Migrating to it is a strategic decision, not a lightweight pilot.
- Mercurial’s ideas inspired a lot of Sapling’s UX, but Git interop and developer familiarity tipped the scales toward Git-compatible clients in most orgs.
Opinion: is Git finally getting a successor?
Short answer: not yet, but it is getting better clients.
Git remains the substrate of collaboration for reasons beyond its technical design: ecosystem gravity, hosting, enterprise workflows, and decades of tooling. Replacing that is not a 12-month project.
However, your day-to-day developer experience can improve dramatically this year by adopting a better local client. jj and Sapling give you modern, stack-aware workflows without asking your organization to rewrite its infrastructure. They transform stacked diffs from a frustrating hack into an everyday pattern.
Pijul points toward a future where patch-based models reduce structural friction further. Whether that future becomes mainstream depends on hosting, interop, and the willingness of teams to shift their mental models. It is the one to watch if you value conceptual cleanliness and long-term maintainability of history.
If your goal is pragmatic progress in 2025, the most leveraged move is to pilot jj or Sapling locally, make stacked diffs the norm, and let your Git servers keep doing what they do best.
A 30-day starter plan
- Day 1: Pick one team and one feature area. Install jj or Sapling on two developer machines. Configure a GitHub app for stacked PR automation (Graphite or ghstack) if you want dependent PRs.
- Day 2–7: Build a feature as a stack of 3–7 small changes. Practice restack/rebase daily. Land via your normal merge policy.
- Day 8–14: Measure PR size, review latency, conflict churn. Collect qualitative feedback. Iterate on the workflow (e.g., absorb fixups earlier; split more aggressively).
- Day 15–30: Expand to 4–6 developers. Publish a one-page guide: how to create, restack, review, and land stacks with your CI. Prepare an internal tech talk.
Success criterion: Would developers fight to keep the tool if you tried to take it away? If yes, standardize it as an optional client in your engineering handbook.
Closing thoughts
- Git is not going away. But its future is likely client-driven, not server-replaced.
- jj and Sapling are mature enough to deliver real benefits today in stacked diffs and everyday rebases. They let you keep the Git-native world while fixing the parts that hurt.
- Pijul’s patch theory is a breath of fresh air. Even if it is not your production tool next quarter, it is worth learning because it reframes what a version control system can be.
If you try only one thing after reading this, try authoring your next feature as a stack of small diffs using jj or Sapling. Then rebase it three times in a busy week. If that experience feels boring rather than fraught, you are on the right path.