JJ

Updated: 23 December 2025

jj is a Git-compatible version control system. Another useful reference is Steve’s JJ tutorial or JJ for Everyone

Installation and Setup

jj supports Nushell, so naturally the configuration and installation is in (the jj installation docs)[https://jj-vcs.github.io/jj/v0.23.0/install-and-setup/#nushell] - this will give some really nice autocomplete for Nushell which is great

Initializing

To init jj in a repo that’s currently using git, use:

Terminal window
1
jj git init --colocate

Status

All changes in jj are located in a working copy, these changes can be seen with:

1
> jj status
2
3
Working copy changes:
4
A jj.txt
5
Working copy (@) : kwvkkoxy 53889ed8 (no description set)
6
Parent commit (@-): nlmkywks 2c0668f0 main | Add content for special notes

Additionally, the change history can also be seen with jj log and looks something like this:

1
> jj log
2
3
@ kwvkkoxy nabeel@email.com 2025-10-27 13:04:27 53889ed8
4
│ (no description set)
5
○ nlmkywks nabeel@email.com 2025-10-27 13:02:08 main git_head() 2c0668f0
6
│ Add content for special notes
7
○ mvvunzxz nabeel@email.com 2025-10-27 13:02:08 03f621ee
8
│ add initial notes
9
○ mwmtqrmv nabeel@email.com 2025-10-27 13:02:08 754fba48
10
│ (empty) initial commit
11
│ ○ yotxkrrt nabeel@email.com 2025-10-27 13:03:26 example-feature-2 8cf8df5c
12
│ │ file 2
13
│ ○ xwylmzso nabeel@email.com 2025-10-27 13:03:26 b626e378
14
│ │ Add content for special notes
15
│ ○ qzwwoopw nabeel@email.com 2025-10-27 13:03:26 30bc460f
16
│ │ add initial notes
17
│ ○ lvzvxwuo nabeel@email.com 2025-10-27 13:03:26 e265e2f2
18
├─╯ (empty) initial commit
19
│ ○ nzyvlzlt nabeel@email.com 2025-10-27 13:03:16 example-feature d6e7e087
20
│ │ some content
21
│ ○ ruvknzzw nabeel@email.com 2025-10-27 13:03:16 b959bbdb
22
│ │ Add content for special notes
23
│ ○ lylxkkts nabeel@email.com 2025-10-27 13:03:16 da0f7c98
24
│ │ add initial notes
25
│ ○ nxlklokt nabeel@email.com 2025-10-27 13:03:16 01643ac6
26
├─╯ (empty) initial commit
27
◆ zzzzzzzz root() 00000000

Diff

You can also view the diff for changes relative to your current HEAD by using:

Terminal window
1
> jj diff

Diffs can also be done relative to other refs, for example relative to main like so:

Terminal window
1
> jj diff --from main@origin

There are, of course, other flags that are supported to enable additional functionality

Descriptions

We can add a decription to our working copy with jj describe

Terminal window
1
> jj describe -m "Some notes on JJ"
2
> jj status
3
4
Working copy changes:
5
A jj.txt
6
Working copy (@) : kwvkkoxy 6dcc5582 Some notes on JJ
7
Parent commit (@-): nlmkywks 2c0668f0 main | Add content for special notes

The Working Copy

jj changes are done in a working copy. We can also have multiple simultaneous working copies in JJ. JJ allow us to create a working copy with a description like so:

Terminal window
1
> jj new -m "Working copy description"

A working copy can also be created after or before a specific commit using -a or -b respectively

The working copy can be moved around using jj edit and referencing a commit we’d like to move this on top of:

Terminal window
1
> jj edit

Rebasing and Moving the Working Copy

Moving the Working Copy

Rebasing can be done using jj rebase which allows you to move commits around. The simplest usecase for this is to move your working copy to a different bookmark, for example - moving the working copy to main can be done like so:

Terminal window
1
> jj rebase -r @ -d main

This moves @ (the working copy) to -d (destination) the main bookmark, effectively letting you “checkout” the main branch

Generally using jj rebase, jj new, or jj edit are how you would “checkout” a bookmark, depending on the current state of your working copy and your intended change

Moving a Branch

You can also rebase a branch using:

Terminal window
1
jj rebase -b my/feature/branch -d main

A “branch” in the context of jj refers to a set of revisions that are not also part of the destination

Working from Another Revision

jj lets us create a working copy on top of another revision/bookmark, this can be done using jj new. Often we’d like to start working relative to some main bookmark, this looks like so:

Terminal window
1
> jj new main

This now moves our working copy to be on top of the main bookmark. Creating new bookmarks can then be done from this point

Commits

With jj you probably want to use bookmarks instead of commits

Commits can be created based on the current working copy and can be done with:

Terminal window
1
> jj commit

This will simply create a commit with all the files in the current working copy. If you’d like to partially include files you can use jj commit -i which allows for an interactive commit

Bookmarks

Creating Bookmarks

jj uses the idea of bookmarks instead of branches. A bookmark is like a commit on a branch.

A bookmark can be created with:

Terminal window
1
> jj bookmark create <bookmark/branch name>

Tracking Bookmarks

jj can track remote branches by using:

Terminal window
1
> jj bookmark track <bookmark-name>@<remote-name>

Updating Bookmarks

Take a look at Steve’s tutorial on this for more details

Updating a bookmark is jj’s way of adding a commit to a branch. This is kinda weird but basically updates a commit to be the next branch

When working relative to a bookmark, you can make changes. After comitting those changes, you can update the current commit to be the new bookmark for that change like so:

Terminal window
1
> jj bookmark set <bookmark-name>

We can set the bookmark to a specific revision using the -r flag. We can set the current working copy with @ or the previous commit with @-

Terminal window
1
> jj bookmark set <bookmark-name> -r @-

Pushing Changes

jj allows for multiple different backends. Pushing to a git backend is done using:

Terminal window
1
> jj git push

What this does under the hood is moves the bookmarks to around to maintain and push to git branches appropraiately

This command also allows you to specify which bookmarks or commits you’d like to push using -b or -c respectively

Restore a file

Restoring a file will reset the changes made to that file and can be done with:

Terminal window
1
> jj restore path/to/file.txt

Conflict Resolution

jj has conflict resolution tooling builtin which can be accessed using jj resolve in the case of conflicts. Changes can be resolved using the resolution tool like so:

Terminal window
1
> jj resolve path/to/file.txt

Which will open a similar tool to the once used for interactive commits

Basic Workflow

The bookmark concept can be a little tricky when working with git, basically here’s a worflow that I find seems to work:

  1. Just start doing some work
  2. Branching is done with either:
    1. Use jj bookmark create <bookmark> to label your working area as a new branch (this is from your current location in the tree)
    2. Use jj edit <bookmark/commit> to select a commit to start working from, this is like branching from somewhere else
  3. Use jj describe to set a description for your working area - this will become the commit message
  4. Use jj commit to make a commit
  5. You can then move the bookmark to it’s new location with jj bookmark set <bookmark> -r @- (the @- refers to the parent commit/revision)
  6. Use jj git push -b <bookmark> to push the bookmark
  7. Go back to 1.

Getting Commit Details

The jj show command can be used to get details about a commit or revision. It’s also possible to refer to the working area using @, which means that information about the current working area can be fetched with:

Terminal window
1
> jj show @

Additionally, the -T param can be used with some commands, including the jj show command, to structure the output