Jujutsu VCS: My Personal Cheat Sheet

Cover Image for Jujutsu VCS: My Personal Cheat Sheet
Rahul M. Juliato
Rahul M. Juliato
#jujutsu#vcs# git# cheat-sheet

A practical quick-reference for the JJ (Jujutsu) version control system: not a tutorial, but a ready-to-use guide with the most essential commands and workflows.

🧠 Heads-Up: This Is Not a Tutorial!

If you're expecting a walkthrough on how to use Jujutsu (JJ) from scratch, this post probably isn't what you're looking for.

There are already some great tutorials out there, including the excelent official one.

While this post serves as a cheat sheet and not a beginner tutorial, if you're looking for a video walkthrough to complement it, check out DevOps Toolbox awesome video:

Ā» Also available here


🐦 Why JJ?

Jujutsu is a powerful version control system for software projects. You use it to get a copy of your code, track changes to the code, and finally publish those changes for others to see and use. It is designed from the ground up to be easy to use, whether you're new or experienced, working on brand new projects alone, or large scale software projects with large histories and teams.

What sets JJ apart is its focus on automatic working commits, which track your changes continuously without the usual manual commit overhead. This design makes JJ extremely fast, highly scriptable, and encourages maintaining a cleaner, more intuitive project history.

That said, JJ introduces new workflows and concepts that may feel unfamiliar at first. Common operations like merge or rebase behave differently, so keeping a quick reference or cheat sheet handy can make your transition smoother and more productive.


šŸ“Ž The cheat sheet!

Basic Commands

jj git init Inits jj with git backend
jj st prints status
jj status same as above
jj prints log
jj log same as above
jj diff diffs current change
jj diff –git diffs current change in git style
jj desc adds a description to the current change
jj describe same as above
jj describe -m "..." same as above, but inline
jj new ends a change and inits a new one
jj new -m ends a change and inits a new one
  setting a description
jj file annotate filename annotates filename, similar to
  git blame or hg annotate
jj undo undoes last jj command
jj squash combines changes and descriptions
jj squash file instantly squashes file down to
  last change
jj squash -i opens an ui to select
  what to squash
jj abandon drops everything on current change
  and starts a new one in place.
jj split splits current change creating a new change with the selected content on @-
jj commit -m "..." adds a description for current change and starts a new one (equivalent to jj desc -m "..." && jj new)

Time Traveling

jj new -B @ -m "msg" Creates a new change Before current (@)
  setting a description
jj edit change-id Moves to whathever change-id
jj next –edit Jumps to next change
jj edit @+  
jj edit @- Jumps to prev change

Branchless Workflow

jj new change-id Creates a new change before a
  given change-id

More on log

jj log -r revsets Applies a revset to log, similar
  to hg(1) (Mercurial)
jj --limit number Limits log lines
jj log -r 'heads(all())' Shows all 'heads' or 'forked'
  changes top change

Merging

Note: there's no jj checkout nor jj merge, those used to exist but are now deprecated. We use jj new ... for everything.

jj new x yz -m "message" Creates a new change by merging
  x and yz change ids defining
  a message "merge". User can
  merge as many change-ids as
  they wish.

Rebasing

Notice: rebase always succeeds even with conflicts pending.

jj rebase -s o -d x Rebases change with id o (source)
  to change with id x (destination)

Merge Conflicts

If a conflict is present, jj st will tell you on which files you need to look for conflicts and solve.

Just save your file after solving and nothing else, no need to continue anything.

jj resolve Opens an ui to choose how to solve conflicts
  (plus: mouse is supported)

Log - Template Language

jj log -T 'TEMPLATE' Applies a template to jj log
jj help -k templates Print the help doc with all template
  language options

A bunch of options are provided to go with templates, some examples:

Format log to have commit-id, new line, description and ---- before next log entry.

jj log -T 'commit-id ++ "\n" ++ description ++ "\n------\n"'

Get short commit IDs of the working-copy parents:

jj log --no-graph -r @ -T 'parents.map(|c| c.commit_id().short()).join(",")'

Show machine-readable list of full commit and change IDs:

jj log --no-graph -T 'commit_id ++ " " ++ change_id ++ "\n"'

Log - Revset Language

jj log -r 'REVSET' Applies a revset to jj log
jj help -k revsets Prints the help doc with all revsets
  language options

A bunch of options are provided to go with templates, some examples:

Show the parent(s) of the working-copy commit (like git log -1 HEAD):

jj log -r @-

Show all ancestors of the working copy (like plain git log):

jj log -r ::@

Show commits not on any remote bookmark:

jj log -r 'remote_bookmarks()..'

Show commits not on `origin` (if you have other remotes like `fork`):

jj log -r 'remote_bookmarks(remote=origin)..'

Show the initial commits in the repo (the ones Git calls "root commits"):

jj log -r 'root()+'

Show some important commits (like `git –simplify-by-decoration`):

jj log -r 'tags() | bookmarks()'

Show local commits leading up to the working copy, as well as descendants of those commits:

jj log -r '(remote_bookmarks()..@)::'

Show commits authored by "martinvonz" and containing the word "reset" in the description:

jj log -r 'author(martinvonz) & description(reset)'

Diff - Fileset Language

jj help -k filesets Prints the help doc with all filesets
  language options

A bunch of options are provided to go with templates, some examples:

Show diff excluding Cargo.lock.

jj diff '~Cargo.lock'

List files in src excluding Rust sources.

jj file list 'src ~ glob:"**/*.rs"'

Split a revision in two, putting `foo` into the second commit.

jj split '~foo'

JJ & Git - Co-locate

This means jj side by side with git.

Your project will have on its root, both a .jj and a .git directory.

jj git init –colocate . Set a new version control
  with both jj and git, or if
  git is present, make arrangements
  so both can be colocated.

Simple workflow to main branch:

jj log
jj commit -m "msg"
jj bookmark set --revision @- main
jj git push -r @-
jj op log

Workflow to push to a 'new git branch'

jj
jj bookmark set -r @ 'feat/blahk'
jj git push -r @ --allow-new --remote origin

To the next pushes, simply:

jj git push -r @

Some other useful commands since jj/git colocated relies heavily on bookmarks:

jj bookmark move --from=@- --to=@
jj bokmark delete '...'
jj bookmark track main@origin
jj git push --all --deleted

A complete set and more examples with:

jj help -k bookmarks

Note, after performing jj motions, probably the git part of the thing will be HEADLESS or in a detached state.

If you wish to perform "regular" git operations, most probably you need to first "git checkout" to a branch.


Stageless Workflow

Jujutsu has no traditional staging area like Git. This means changes you make in your working copy are immediately part of your current change (also called a working commit). So what if you’ve modified 10 files but only want to ā€œcommitā€ 2 of them?

šŸ’” You can mimic partial commits with a few simple commands.

Special thanks to @Cassiano for for pointing this out to me!

šŸŖ„ The trick: jj split

Use jj split to break your current change into two:

jj split

This opens a TUI (Text User Interface) where you can select which files or even hunks to split into a new change.

What happens:

  • The selected changes go into a new change (on top of your current one)

  • The rest stay in the original change

šŸ’¬ Think of it like ā€œstagingā€ part of your work and ā€œcommittingā€ it, without ever touching an index.

🧪 Example Workflow

  1. You edit fileA.ts, fileB.ts, and fileC.ts.
  2. But you only want to commit the changes to fileA.ts.
  3. Run:
jj split
  1. Select only fileA.ts in the UI.
  2. Give the new change a description with:
jj describe -m "Refactor fileA logic"

Now your fileA.ts changes are safely committed. You're still on the remaining changes for fileB.ts and fileC.ts.

šŸ”„ You can repeat the process to incrementally split off changes until you're happy.

šŸ Wrapping Up

This cheat sheet is not a comprehensive tutorial, as said before, it’s a quick reference, a jumping-off point, and a distilled guide for navigating jj. Whether you're exploring jj out of curiosity or looking to speed up your workflow without staging overhead, I hope this post gave you enough to get started with confidence.

Remember: tools don’t define your flow, your habits and clarity do. If jj helps you think more clearly about your changes, then it’s doing its job. And if not? That’s fine too, there’s always git or even hg right around the corner.

If you have tweaks, corrections, or your own tricks, I’d love to hear them!

Thanks for reading! šŸ§ šŸ’»šŸ”„