Skip to main content

Jujutsu cheatsheet

14 mins

I’m learning Jujutsu, a VCS backed by git, and I thought the fastest way to incorporate it into my workflow at work is writing down the jj (short for jujutsu) commands that are equivalent to the git commands I normally use.

I’ve included explanations and alternative versions of each command as well, so if my workflow is not quite what you’re using still you may find a way to adapt it to your needs.

If you want to check a step-by-step tutorial of my workflow, please head to the tutorial.

Checking the working copy status #

Run jj st to check your working copy changes.

$ jj st
# jj status # <-- Verbose version of the command above
Working copy changes:
A test.txt
Working copy  (@) : szuwqrzx 1680a9c4 (no description set)
Parent commit (@-): xxvpknxr 0d91f18e main | description
👾 Expand to see command details
ItemDescription
stShorthand for status subcommand. Prints out changes in the working copy and details of the current and previous revision
Every time you run a jj command after making a change, jj will snapshot your working copy, creating a new commit ID with the changes made, so all changes will be automatically tracked.

Committing changes #

Option 1: Describe and new #

Give a description to your changes. The working copy message will be updated to the provided message.

$ jj desc -m <message>
# jj describe --message <message> # <-- Verbose version of the command above
# jj describe # <-- Opens up the configured editor to provide multi-line descriptions
Working copy  (@) now at: szuwqrzx e5c7bdf0 Add new test.txt file in branch # <-- Revision message is updated
Parent commit (@-)      : xxvpknxr d4546dff main | Initial commit
👾 Expand to see command details
ItemDescription
descShorthand for describe. Provides a description for a revision
-mShorthand for --message. Specifies the message used to describe the revision
Description of the revision
jj describe -m <message> command adds or amends the message for the current revision; notice that the Commit ID changed from 1680a9c4 to e5c7bdf0, but a commit already existed. Jujutsu creates empty revisions first (no changes, no description), and as you make changes the revision is updated with new commits with your changes.

Then create a new revision to put new changes in.

$ jj new
# $ jj new -r <revision> # <-- Creates a new revision after the working copy
# $ jj new --revision <revision> # <-- Verbose version of the command above
# $ jj new --insert-after <revision> # <-- Creates a new revision after <revision>
# $ jj new --insert-before <revision> # <-- Creates a new revision before <revision>
Working copy  (@) now at: lxqqxokv 081d50d7 (empty) (no description set)
Parent commit (@-)      : szuwqrzx e5c7bdf0 Add new test.txt file in branch
👾 Expand to see command details
ItemDescription
newCommand to create new revisions
-rShorthand for --revision. The new revision will be created after the specified revision, even if the specified revision is immutable.
–insert-beforeThe new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass.
–insert-afterThe new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass.
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names
Immutable revisions or shared revisions are immutable (duh). They’re represented in the log with a diamond (â—†) while mutable revisions are represented with an empty circle (â—‹).

Option 2: Commit #

Give a description to your changes and create a new revision right away.

$ jj commit -m <message>
# jj commit # <-- Opens up the configured editor to provide multi-line descriptions
Working copy  (@) now at: snolmknt d25c8673 (empty) (no description set)
Parent commit (@-)      : szuwqrzx e5c7bdf0 Add new test.txt file in branch
👾 Expand to see command details
ItemDescription
commitProvides a message for the current revision and creates a new empty revision on top of it
-mShorthand for --message. Specifies the message used to describe the revision
Description of the revision
jj commit -m <message> performs the combined actions from running jj describe -m <message> && jj new

Create a new bookmark (branch) #

Create a new bookmark called feature pointing to the previous revision.

Option 1: create bookmark #

$ jj bookmark c <name> -r <revision>
# jj bookmark create <name> --revision <revision> # <-- Verbose version of the command above
Created 1 bookmarks pointing to szuwqrzx e5c7bdf0 Add new test.txt file in branch
👾 Expand to see command details
ItemDescription
bookmarkCommand for bookmark-related operations
cShorthand for create sub-command. Used to create new bookmarks
Name of the bookmark
-rShorthand for --revision. Specifies a revision the bookmark will point to.
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

Option 2: set bookmark #

$ jj bookmark s -r <revision> <name>
Created 1 bookmarks pointing to wvzswtnl 9c498015 feature | Add new test.txt file in branch
👾 Expand to see command details
ItemDescription
bookmarkCommand for bookmark-related operations
sShorthand for set sub-command. Used to create or update bookmarks (upsert)
-rShorthand for --revision. Specifies a revision the bookmark will point to.
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names
Name of the bookmark

Update an existing bookmark (branch) #

Option 1: move bookmark #

Bookmarks don’t get auto-advanced when you make changes, so you have to update the bookmarks to point to the desired revision.

$ jj bookmark m <name> --to <revision> # <-- Moves the main bookmark to the <revision> revision
# jj bookmark move <name> --to <revision> # <-- Verbose version of the command above
# jj bookmark m <name> # <-- Moves the main bookmark to the current revision
# jj bookmark m <name> --to @ # <-- Moves the main bookmark to the current revision
Moved 1 bookmarks to okylooxl 2fb0ecac feature main* | Add 'hello world' to test.txt
👾 Expand to see command details
ItemDescription
bookmarkCommand for bookmark-related operations
mShorthand for move sub-command. Used to move bookmarks from one Change to another
Name of the bookmark
–toSpecifies a revision the bookmark will point to.
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

Option 2: set bookmark #

$ jj bookmark s <name> -r <revision> # <-- Moves the <name> bookmark to the <revision> revision
# jj bookmark set <name> --revision <revision> # <-- Verbose version of the command above
Moved 1 bookmarks to okylooxl 2fb0ecac feature main* | Add 'hello world' to test.txt
👾 Expand to see command details
ItemDescription
bookmarkCommand for bookmark-related operations
sShorthand for set sub-command. Used to upsert what revision a bookmark points to
Name of the bookmark
-rShorthand for --revision. Specifies a revision the bookmark will point to
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

See the log #

$ jj log
# jj log --limit <lines> # <-- Limits the amount of lines printed
# jj log -r .. # <-- Shows the entire history tree
👾 Expand to see command details
ItemDescription
logCommand for log-related operations
–limitNumber of lines to print
-rShorthand for --revision. The log will print the changes in the revision

Checkout an old revision #

If you create a new revision on top of an older one, __and make no changes**, you can simulate the git checkout <commit> behavior.

jj new -r <revision>
👾 Expand to see command details
ItemDescription
newCommand to create new revisions
-rShorthand for --revision. The new revision will be created after the specified revision, even if the specified revision is immutable.
–insert-beforeThe new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass.
–insert-afterThe new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass.
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

Branch-off changes (Create a branch) #

You can create new revisions at any point of the history tree with jj new -r <revision> (but you should be careful when creating new revisions before or in between shared revisions). When you create a revision in the middle of other revisions, you create an alternative branch from that point forward.

$ jj log -r ..
@  wtsupkoz dev@local.com 2026-01-01 00:00:00
│
â—†  nsskxqrz dev@camilosep.com 2026-01-01 00:00:00 main git_head() 5382dc8c
│  Feature1 (#2)
â—†  ztkpvvpx dev@camilosep.com 2026-01-01 00:00:00 10150ef3
~
$ jj new -r ztkpvvpx
$ jj log -r ..
@  szmursqw dev@camilosep.com 2026-01-01 00:00:00 0ebd0726 # <-- Branched-off after commit `ztkpvvpx`. One branch is on @ and the other on `main`
│     ◆  nsskxqrz dev@camilosep.com 2026-01-01 00:00:00 main 5382dc8c
│     │
├─────╯  Feature1 (#2)
â—†  ztkpvvpx dev@camilosep.com 2026-01-01 00:00:00 git_head() 10150ef3
â—†  xxvpknxr dev@camilosep.com 2026-01-01 00:00:00 d4546dff
│  Initial commit
~

Rebase bookmarks #

Rebase one (or more) revisions onto others.

Rebasing can be performed in many different ways, depending on your particular needs. I will only list the specific commands that fit my regular workflow, but the official documentation covers a lot more and includes detailed examples; you can access the rebase docs running jj rebase --help.

Rebase specific revision and its descendants on top of another revision #

I usually use this to update my feature revisions on top of the latest changes in main.

The after -s, and its descendants, are the ones I want to put on top of main, so I need to make sure is the very first revision after branching-off my changes from main so that the whole branch’s commits are put on top of main.

If you’re using a bookmark as reference for -s <revision>, the bookmark should not be at the latest revision of the branch, but at the very beginning, so that all the descendents are put on top of main; otherwise you will only put the latest commit on top of main and the other commits will remain diverted. In other words:

  1. put the bookmark at the very beginning of your branch changes when you start working in a new feature
  2. when you’re done adding changes, pull main and rebase your bookmark changes
  3. only then advance the bookmark to the top of your changes
  4. push the bookmark to the remote
$ jj rebase -s <revision> -o <revision>
# jj rebase --source <revision> --onto <revision> # <-- Verbose version of the command above
# Example: Rebase latest changes on top of main
# jj log -r ..
# @  szmursqw dev@camilosep.com 2026-01-01 00:00:00 0ebd0726 # <-- We want to rebase the working copy onto main
# │     Feature2
# │     ◆  nsskxqrz dev@camilosep.com 2026-01-01 00:00:00 main 5382dc8c
# │     │
# ├─────╯  Feature1 (#2)
# â—†  ztkpvvpx dev@camilosep.com 2026-01-01 00:00:00 git_head() 10150ef3
# â—†  xxvpknxr dev@camilosep.com 2026-01-01 00:00:00 d4546dff
# │  Initial commit
# ~
# jj rebase --source szmursqw --onto main # <-- Put the `szmursqw` revision and its descendants on top of `main`
# jj log -r ..
# @  szmursqw dev@camilosep.com 2026-01-01 00:00:00 0ebd0726 # <-- Working copy is rebased
# │     Feature2
# â—†  nsskxqrz dev@camilosep.com 2026-01-01 00:00:00 main 5382dc8c
# │     Feature1 (#2)
# â—†  ztkpvvpx dev@camilosep.com 2026-01-01 00:00:00 git_head() 10150ef3
# â—†  xxvpknxr dev@camilosep.com 2026-01-01 00:00:00 d4546dff
# │  Initial commit
# ~
👾 Expand to see command details
ItemDescription
rebaseCommand to rebase revisions
–sourceThe specified revision and its descendants will be put on top of the specified by –onto
–ontoThe specified revision will be the base to put new revision on top of it
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

Rebase specific revision without its descendants on top of another revision #

This effectively moves one revision to another place, changing the history on both places.

$ jj rebase -r <revisions> --o <revision>
# jj rebase --revisions <revisions> --onto <revision> # <-- Verbose version of the command above
# Example: Put Feature change 3 on top of Feature change 2 
# jj log -r ..
# @  nyqksrko dev@camilosep.com 2026-01-07 10:23:01 0e0c4578
# │  (empty) Bug change 2
# â—‹  mmsrponm dev@camilosep.com 2026-01-07 10:21:24 git_head() 206054be # <-- We have a feature change in the middle of 2 bug changes.
# │  (empty) Feature change 3
# â—‹  kunyllzw dev@camilosep.com 2026-01-07 10:18:46 e8b28556
# │  (empty) Bug change 1
# │ ○  yqyzpqrm dev@camilosep.com 2026-01-07 10:18:13 3ec7f6a1
# │ │  (empty) Feature change 2
# │ ○  qvzrsyxu dev@camilosep.com 2026-01-07 10:17:29 13be60fb
# ├─╯  (empty) Feature change 1
# â—‹  rtxxnyrv dev@camilosep.com 2026-01-07 10:16:57 88ace0dd
# │  (empty) main is here
# â—‹  wzzvukst dev@camilosep.com 2026-01-07 10:16:29 7147e3d5
# │  (empty) Change 1
# â—†  zzzzzzzz root() 00000000
# jj rebase -r mmsrponm --onto yqyzpqrm # <-- Put the `mmsrponm` revision, without its descendants (Bug change 2),  on top of `yqyzpqrm` (Feature change 2)
# jj log -r ..
# @  nyqksrko dev@camilosep.com 2026-01-07 10:24:31 b7fa4eef # <-- The bug changes are isolated in a different branch
# │  (empty) Bug change 2 
# â—‹  kunyllzw dev@camilosep.com 2026-01-07 10:18:46 git_head() e8b28556
# │  (empty) Bug change 1
# │ ○  mmsrponm dev@camilosep.com 2026-01-07 10:24:31 34c10f2f # <-- The feature change is moved next to the other feature changes
# │ │  (empty) Feature change 3
# │ ○  yqyzpqrm dev@camilosep.com 2026-01-07 10:18:13 3ec7f6a1
# │ │  (empty) Feature change 2
# │ ○  qvzrsyxu dev@camilosep.com 2026-01-07 10:17:29 13be60fb
# ├─╯  (empty) Feature change 1
# â—‹  rtxxnyrv dev@camilosep.com 2026-01-07 10:16:57 88ace0dd
# │  (empty) main is here
# â—‹  wzzvukst dev@camilosep.com 2026-01-07 10:16:29 7147e3d5
# │  (empty) Change 1
# â—†  zzzzzzzz root() 00000000
👾 Expand to see command details
ItemDescription
rebaseCommand to rebase revisions
–revisionsThe specified revision(s) (without their descendants) will be put on top of the specified by –onto
–ontoThe specified revision will be the base to put new revision on top of it
Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names

Conflict resolution #

$ jj st
Working copy  (@) now at: txwlpmzs 2b8e0f34 (conflict) New Revision 5 # <-- Marks the conflicted revision
Parent commit (@-)      : tnmsrotu fdb344a6 main* | New Revision 4
Added 0 files, modified 1 files, removed 0 files
Warning: There are unresolved conflicts at these paths:
test.txt    2-sided conflict
$ jj resolve # <-- a TUI will be opened where you can navigate through files and select the resolution for each conflict
Hint: Using default editor ':builtin'; run `jj config set --user ui.merge-editor :builtin` to disable this message.
Working copy  (@) now at: txwlpmzs 36c3e143 (empty) New Revision 5
Parent commit (@-)      : tnmsrotu fdb344a6 main* | New Revision 4
Added 0 files, modified 1 files, removed 0 files
Existing conflicts were resolved or abandoned from 1 commits.

Squash revisions #

You can combine changes from different revisions together

$ jj squash # <-- Squashes @ into @-
# jj squash -r <revision> # <-- Squashes <revision> into its parent
# jj squash --from <revision> --into <revision1> # <-- Squashes <revision> into <revision1>
# jj squash -r <revision> -m <message> # <-- Squashes <revision> into its parent and changes the description to <message>
# jj squash -r <revision> --use-destination-message # <-- Squashes <revision> into its parent and keeps the message from the parent
# jj squash --from <revision> --into <revision1> --use-destination-message # <-- Squashes <revision> into <revision1> and keeps the message from <revision1>
👾 Expand to see command details
ItemDescription
squashCommand to squash revisions
-rShorthand for --revision. The revision holding the changes to squash into the other
–intoThe revision that will take the changes from the other one
<revision | revision1>Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names
-mShorthand for --message. Specifies a new message used to describe the squashed revision
Description of the squashed revision

Track branches #

Jujutsu doesn’t automatically track remote branches. You need to track them before you can start making new changes in them.

$ jj bookmark track <branch>@<remote> # <-- Tracks a branch named <branch> in the remote named <remote>
# jj bookmark track main@origin # <-- Tracks the remote main branch
👾 Expand to see command details
ItemDescription
bookmarkCommand for bookmark-related operations
trackSubcommand to track remote bookmarks
The name of the branch in the remote
The name of the remote

Push bookmarks #

Bookmarks are pushed like git would do it passing the –force-with-lease flag (`git push –force-with-lease).

$ jj git push -b <bookmark> --allow-new # <-- Push only the bookmark named <bookmark> and automatically track the remote bookmark if it's new
# jj git push --bookmark <bookmark> --allow-new # <-- Verbose version of the command above
👾 Expand to see command details
ItemDescription
gitCommand for git-related operations
pushSubcommand to push bookmarks to a remote
–bookmarkOnly the specified bookmark is pushed
The name of the bookmark to push
–allow-newTrack the remote bookmark if it’s a new bookmark

Pull remote changes #

All tracked bookmarks will pull the remote changes and highlight if there are conflicts.

jj git fetch