Jujutsu cheatsheet
Table of Contents
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.
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
| Item | Description |
|---|---|
| st | Shorthand for status subcommand. Prints out changes in the working copy and details of the current and previous revision |
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
| Item | Description |
|---|---|
| desc | Shorthand for describe. Provides a description for a revision |
| -m | Shorthand 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
| Item | Description |
|---|---|
| new | Command to create new revisions |
| -r | Shorthand for --revision. The new revision will be created after the specified revision, even if the specified revision is immutable. |
| –insert-before | The new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass. |
| –insert-after | The 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 |
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
| Item | Description |
|---|---|
| commit | Provides a message for the current revision and creates a new empty revision on top of it |
| -m | Shorthand 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 newCreate 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
| Item | Description |
|---|---|
| bookmark | Command for bookmark-related operations |
| c | Shorthand for create sub-command. Used to create new bookmarks |
| Name of the bookmark | |
| -r | Shorthand 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
| Item | Description |
|---|---|
| bookmark | Command for bookmark-related operations |
| s | Shorthand for set sub-command. Used to create or update bookmarks (upsert) |
| -r | Shorthand 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
| Item | Description |
|---|---|
| bookmark | Command for bookmark-related operations |
| m | Shorthand for move sub-command. Used to move bookmarks from one Change to another |
| Name of the bookmark | |
| –to | 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 <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
| Item | Description |
|---|---|
| bookmark | Command for bookmark-related operations |
| s | Shorthand for set sub-command. Used to upsert what revision a bookmark points to |
| Name of the bookmark | |
| -r | Shorthand 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
| Item | Description |
|---|---|
| log | Command for log-related operations |
| –limit | Number of lines to print |
| -r | Shorthand 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
| Item | Description |
|---|---|
| new | Command to create new revisions |
| -r | Shorthand for --revision. The new revision will be created after the specified revision, even if the specified revision is immutable. |
| –insert-before | The new revision will be created before the specified revision, only if the specified revision is mutable; use –ignore-immutable to bypass. |
| –insert-after | The 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 -s, and its descendants, are the ones I want to put on top of main, so I need to
make sure 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:
- put the bookmark at the very beginning of your branch changes when you start working in a new feature
- when you’re done adding changes, pull
mainand rebase your bookmark changes - only then advance the bookmark to the top of your changes
- 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
| Item | Description |
|---|---|
| rebase | Command to rebase revisions |
| –source | The specified revision and its descendants will be put on top of the |
| –onto | The 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
| Item | Description |
|---|---|
| rebase | Command to rebase revisions |
| –revisions | The specified revision(s) (without their descendants) will be put on top of the |
| –onto | The 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
| Item | Description |
|---|---|
| squash | Command to squash revisions |
| -r | Shorthand for --revision. The revision holding the changes to squash into the other |
| –into | The revision that will take the changes from the other one |
| <revision | revision1> | Identifier of the revision. Valid values are @, @-, Change IDs or Bookmark names |
| -m | Shorthand 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
| Item | Description |
|---|---|
| bookmark | Command for bookmark-related operations |
| track | Subcommand 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
| Item | Description |
|---|---|
| git | Command for git-related operations |
| push | Subcommand to push bookmarks to a remote |
| –bookmark | Only the specified bookmark is pushed |
| The name of the bookmark to push | |
| –allow-new | Track 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