Blog

Why jj? A Jujutsu cheatsheet for Git users

Keep your Git remotes. Make unfinished work easier to manage.

By hewigovens ·

Jujutsu, usually called jj, is a Git-compatible version control system with an automatically snapshotted working copy, editable changes, and an operation log for undoing local repository actions. You can use it with existing Git remotes and GitHub pull requests while your teammates keep using Git.

The appeal shows up while work is still taking shape: a fix belongs in an earlier commit, two unrelated edits need separating, or a new task interrupts a dirty working tree. jj makes those everyday revisions easier to manage.

Here is why jj is worth trying, followed by a practical Git to jj cheatsheet and a first workflow you can follow.

Why use jj instead of Git? #

Consider a small stack: a refactor, a feature built on it, and tests. While writing the tests, you notice a mistake in the refactor. In jj, you can edit that earlier change directly; its descendants are rebased automatically. Or make the correction in your current change and move it into the refactor with jj squash --into <change>.

That makes a different habit practical: write the code while you understand the problem, then shape the history while you understand the solution. Splitting a change or moving a hunk does not need a staging-area workflow or an interactive rebase script.

Experimenting with history also gets less intimidating. jj op log records repository operations, and jj undo can undo the latest one. A bad local rebase is something you can inspect and reverse. This is local recovery: undo does not retract a push or restore files jj never recorded. The operation log documentation explains the boundary.

The benefit is largest when you revise work often: small reviewable changes, stacked work, or frequent interruptions. You can keep each attempt as a change, compare it, and decide what belongs in the final result.

jj was not designed specifically for AI or coding agents. Its design grew out of everyday version control problems, but those same properties make it a good fit for agents: automatic snapshots preserve attempts, editable changes let them reorganize work, and the operation log makes mistakes easier to inspect and undo. I see a parallel with Rust: its type system and compiler diagnostics give an agent concrete feedback to work from, even though the language was designed for people. jj itself is written in Rust.

What is Jujutsu (jj)? #

  • No staging area. Most jj commands snapshot your saved edits into @, the working-copy commit. New files are tracked by default, subject to ignore rules and configuration.
  • Change IDs are stable. Editing, rewording, or rebasing a change preserves its change ID. Git identifies commits by hashes that change when rewritten; jj's commit hash changes too, but its change ID keeps the same identity. Edit an earlier mutable change and jj rebases its descendants automatically.
  • Bookmarks are for naming work. They map to Git branches on remotes. You can start without one, then move a bookmark to the change you want to publish.
  • Conflicts are recorded in changes. A rebase can finish with conflicts in the graph. You can resolve them afterward, without a rebase to continue.

For the full explanation, read the official comparison of Jujutsu and Git.

Your first jj workflow #

These examples target jj 0.45.1 with default behavior. Replace angle-bracket placeholders with your own values; the remote is named origin and its main branch is main. Follow the jj installation guide first.

Start with a fresh clone:

jj git clone <repo-url> project
cd project
jj new main@origin -m "Add search"
# Edit and save files.
jj diff
jj new

The final jj new puts you in an empty child at @. “Add search” is now at @-, its parent. When that change is ready to share:

jj bookmark set add-search -r @-
jj git push --remote origin --bookmark add-search

Your forge sees an ordinary Git branch named add-search. Open a pull request as usual. If you keep working and want to publish a later change, move the bookmark forward before pushing again. The empty working copy above the bookmark stays local.

To try jj in an existing Git checkout, run jj git init --colocate there. Git and jj share the Git repository. Read the compatibility guide before mixing their commands, especially if your workflow relies on the Git index.

Git to jj cheatsheet #

Start with these eight commands. The official Git to Jujutsu command table covers the full reference, including rebasing, cherry-picking, tags, and recovery.

Intent Git jj What to know
Inspect current work git status jj st Shows working-copy files and parents.
Inspect the current diff git diff HEAD jj diff The whole change relative to its parent(s).
Start work from main git switch -c feature origin/main jj new main@origin Creates a change; a bookmark can come later.
Finish and start the next change git commit -a -m "message" jj commit -m "message" jj also auto-tracks eligible new files by default.
Choose a partial commit git add -p; git commit jj split Selects part into one change and leaves the rest in another.
Put fixes into the parent git commit --amend -a jj squash Moves the current diff into its parent; use -i to select hunks.
Put work aside git stash jj new @- For a single-parent change: starts a sibling on the old parent.
Push one named line of work git push origin feature jj git push --remote origin --bookmark feature Pushes through that bookmark, including required ancestors.

jj describe -m "message" sets the current change’s description and keeps you editing it; jj new starts a child. Use jj log to find changes and jj help <command> for options in your installed version.

Switch tasks without a stash #

You are halfway through a feature when a small unrelated fix arrives. Describe the feature so you can find it again, then start fresh from main:

jj describe -m "WIP: search filters"
jj new main@origin -m "Fix a typo"
# Edit and save the fix.
jj log
jj edit <feature-change-id>

The feature is still in the graph. Returning to it restores its files. Use jj new @- when you want to start from the current change’s parent instead. Plain jj new starts on top of your existing work, so its files still include that work.

For simultaneous work, including separate coding agents, give each task its own workspace and change. Separate directories keep their file edits apart; jj history does not prevent two processes from overwriting the same working files.

Limits and tradeoffs #

Published does not automatically mean immutable. jj protects a configured set of revisions, usually including trunk and tags and their ancestors. A pushed feature change can still be mutable. Coordinate rewrites of shared work; see the immutable commits configuration.

A recorded conflict still needs a resolution. jj lets you organize work before resolving everything, but the result must still build and pass tests. Ordinary pushes reject conflicted changes by default.

Git compatibility has limits. jj 0.45.1 does not provide a complete submodule workflow. Repositories that depend heavily on submodules need Git for those operations. Git hooks and tools that expect a staged index also need checking. Start with a small repository and read the supported Git features.

If you rarely rewrite history and your Git workflow already feels simple, the benefit may be modest. jj becomes compelling when managing unfinished work is a significant part of the job.

JayJay, briefly #

JayJay is a native GUI for Jujutsu on macOS and Linux (beta), with a change graph, diffs, and tools for splitting changes and resolving conflicts. Its command palette runs non-interactive jj commands in the open repository using either the jj or ! prefix: jj log -r @ and !log -r @ run the same command. Try JayJay or read the workflow guide.

Further reading #

↑ Back to top