Skip to content

LazyJJ vs Graphite

Both LazyJJ and Graphite provide stacked PR workflows, but they take fundamentally different approaches. JJ offers native stacking built into the version control system itself—not bolted onto Git.

What JJ Does Better

What You Loved in GraphiteHow JJ Does It Better
gt modify -a amends current commitEvery file edit automatically amends—no command needed
gt stack restack rebases dependenciesAutomatic—descendants rebase when you edit any commit
gt ls visualizationjj log—same concept, native to the tool, faster
Git passthrough for git commandsFull Git compatibility—jj git push, jj git fetch
Branches treated as commitsTrue commits with stable change IDs, branches optional
Undo… (missing in Graphite!)jj undo—undo ANY operation
Stack workflowNative to JJ’s architecture, not bolted on

Open Source & No Service Dependency

JJ is fully open source (Apache 2.0). LazyJJ works with any Git remote—GitHub, GitLab, Bitbucket, self-hosted. No cloud service required.

Graphite’s CLI is source-available but their core service is proprietary. Features like merge queues, dashboards, and PR stack status require their cloud.

More Powerful Foundation

JJ provides capabilities Graphite simply can’t offer:

  • First-class conflicts — Conflicts don’t block your workflow. Keep working, resolve later. See Working with Conflicts.
  • Full undo — Every operation is recorded and reversible via jj undo and jj op restore. See Operation Log.
  • Revsets — A query language for selecting commits: jj log -r "mine() & mutable()".
  • Anonymous branches — No branch names required. Work freely, create bookmarks only when pushing PRs.
  • Offline support — All operations are local. No network calls.

Feature Comparison

FeatureLazyJJ/JJGraphite
Open sourceYes (Apache 2.0)CLI source-available
Service dependencyNoneRequired for some features
Git hostingAnyGitHub only
Conflict handlingFirst-classGit’s model
Undo supportFull operation logLimited
Query languageRevsetsNone
Offline supportFullPartial
CustomizationFullLimited
Stacking modelNative to VCSWrapper on Git

Graphite Pain Points You Won’t Miss

  1. Signed Commits Break — Organization-mandated GPG signing fails when Graphite rebases internally. No workaround.

  2. Collaboration Friction — If one developer restacks, collaborators on dependent branches are stuck. Graphite works best solo.

  3. Metadata Fragility — Use any git command directly (git rebase, git merge) and Graphite’s metadata breaks.

  4. Third-Party Dependency — Your workflow depends on Graphite’s infrastructure for API auth and PR management.

  5. The Fundamental Mismatch — Graphite fights Git’s branch model. JJ embraces a change-based model from the ground up.

The Mental Model Upgrade

Graphite’s Approach

  • Treats branches as commits (but they’re still Git branches underneath)
  • Forces “one commit per branch” discipline
  • Metadata tracks relationships between branches

JJ’s Approach

  • Commits have stable change IDs that survive rewrites
  • Branches (bookmarks) are optional labels
  • Relationships live in the commit graph—no metadata needed

This isn’t just a nicer CLI—it’s a fundamentally better architecture. See the Mental Model guide for the full explanation.

Command Cheatsheet

Viewing Your Stack

GraphiteLazyJJNotes
gt logjj stack-viewView current stack
gt log short / gt lsjj stacks-allView all your stacks
gt branchjj log -r @Show current commit

Creating and Modifying

GraphiteLazyJJNotes
gt create -m "msg"jj describe -m "msg" && jj newCreate named commit
gt create -am "msg"jj describe -m "msg" && jj newStage all + create
gt modify(automatic)Amend current commit
gt modify -a(automatic)Stage all + amend
gt modify -cjj new && jj describeAdd new commit to branch

Key difference: In JJ, your working copy automatically amends the current commit. No explicit “modify” step needed—just edit files.

Syncing and Submitting

GraphiteLazyJJNotes
gt syncjj stack-syncFetch + rebase onto trunk
gt submitjj stack-submitPush current stack
gt submit --stack / gt ssjj stack-submitPush entire stack
GraphiteLazyJJNotes
gt checkout / gt cojj editSwitch to commit
gt up / gt ujj edit <change-id>Move up one
gt down / gt djj edit <change-id>Move down one
gt top / gt tjj stack-topGo to top of stack

Note: JJ doesn’t have direct “up/down one commit” commands. Use jj edit <change-id> to jump to any commit, or jj stack-top to go to the top.

Reorganizing

GraphiteLazyJJNotes
gt foldjj squashSquash into parent
gt squashjj squash --keep-emptiedSquash commits in branch
gt splitjj splitSplit commit into multiple
gt reorderjj rebaseReorder commits
gt movejj rebase -r X -d YMove commit to new parent

Recovery

GraphiteLazyJJNotes
gt undojj undoUndo last operation
(none)jj op logView operation history
(none)jj op restore <id>Restore to any point

Key Workflow Differences

1. No Staging Area

Terminal window
# Graphite (like Git)
gt add file.txt
gt modify
# JJ - just edit files
vim file.txt
# Done! Changes are automatically in your commit

2. Change IDs vs Branch Names

Terminal window
# Graphite requires naming every branch
gt create my-feature
# JJ uses change IDs - branches optional
jj new -m "Add feature"
# Works with change ID: qpvuntsm
# Branch name (bookmark) only needed for GitHub PRs

3. Automatic Rebasing

Terminal window
# Graphite - must explicitly restack
gt modify -a
gt stack restack
# JJ - automatic
jj edit mid-stack-commit
vim file.txt
# Descendants automatically rebase!

4. Conflicts Don’t Block You

Terminal window
# Graphite (like Git) - conflict stops you
gt sync
# ❌ Resolve conflicts now or you're stuck
# JJ - conflicts are just data
jj stack-sync
# ✓ Conflict marked, but you can keep working
jj new -m "Other feature"
# Work on something else, resolve conflict later

See Working with Conflicts for details.

When to Use Graphite

Graphite might still be a good choice if you:

  • Need their merge queue feature
  • Want their web dashboard and analytics
  • Prefer their specific PR workflow
  • Are invested in their ecosystem

The Transition

What Transfers Directly

  • Stack thinking — You already understand stacked PRs
  • Visualizationjj log is like gt ls, just better
  • Breaking work into small PRs — Same workflow
  • Rebasing discipline — You’re comfortable with history manipulation

What’s Different

  • No staging — Edit files, they’re in the commit. Period.
  • Automatic amend — No gt modify -a needed
  • Bookmarks — Optional, only for pushing to GitHub
  • Operation log — Trust jj undo and experiment freely

Graphite users have an advantage—you already understand stacking. You just need to unlearn Git’s limitations.

Real-World Example

Graphite Workflow

Terminal window
gt sync
gt create -am "Database schema"
gt create -am "User model"
gt create -am "API endpoints"
gt submit --stack
# Reviewer requests changes to user model
gt checkout user-model
vim src/models/user.js
gt modify -a
gt submit
# Conflicts in API endpoints
gt checkout api-endpoints
# Resolve conflicts manually
gt submit

JJ Equivalent

Terminal window
jj stack-sync
jj new -m "Database schema"
vim schema.sql
jj new -m "User model"
vim src/models/user.js
jj new -m "API endpoints"
vim src/api/users.js
jj pr-stack-create
# Reviewer requests changes to user model
jj edit <user-model-commit>
vim src/models/user.js
# Automatically amends!
# Automatically rebases API endpoints!
jj stack-submit
# Conflicts? They're just marked, not blocking
# Resolve when convenient

Notice what’s missing:

  • No gt modify -a (automatic)
  • No gt stack restack (automatic)
  • No manual conflict resolution in dependent commits (propagates automatically)

Next Steps

Project by Ernesto Jiménez Ernesto Jiménez Bluesky GitHub