Using Jujutsu

After playing around with Jujutsu (aka jj) for a while I have to say I really like it!

If I had to sum up the difference between git and jj I would say that it’s like the difference imperative and declarative programming. In git I have to spell out every operation it has to perform to get the commit graph I want - over and over again as I make changes. While in jj, I tell it once what I want the commit graph to look like and I can make changes throughout history and it will maintain that graph for me automatically in the background.

Command Reference

For now I want to use git and jj side by side:

$ jj git init --colocate

Branches occur when you have multiple changes that share a single parent:

$ jj new parent -m "Commit one"
$ jj new parent -m "Commit two"

To merge branches, create a new change with multiple parents:

$ jj new <rev1> <rev2> [... <revN>]

You only really need name a branch (aka bookmark) when you want to push your changes somewhere:

$ jj bookmark set -r <rev> <name>
$ jj git push -b <name> --remote <remote> --allow-new

To push again after you’ve made additional changes:

$ jj bookmark set -r <rev> <name>  # Unlike git branches, bookmarks don't move each time you commit
$ jj git push -b <name> --remote <remote>

Filesets

When working with denote-style filenames (especially those involving signatures), jj often tries to interpret parts of the filename specially, resulting in errors:

$ jj squash "content/20251213T204155==5=8--ibuffer.rst"
Error: Failed to parse fileset: Syntax error
Caused by:  --> 1:24
  |
1 | content/20251213T204155==5=8--ibuffer.rst
  |                        ^---
  |
  = expected <EOI>, `|`, `&`, or `~`
Hint: See https://docs.jj-vcs.dev/latest/filesets/ or use `jj help -k filesets` for filesets syntax and how to match file paths.

The fix is to wrap the filename in two layers of quotes, the first is for the shell, the second layer is for jj:

$ jj squash "'content/20251213T204155==5=8--ibuffer.rst'"
Rebased 1 descendant commits
Working copy  (@) now at: znpmlpnx 46e93dff (no description set)
Parent commit (@-)      : rympsmzo db0dc66d Add keybinding for ibuffer

User Config

Here lies my user config for jj

As with git, you need to set a name and email for your commits

jj/config.toml
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"

user.name = "Alex Carney"
user.email = "alcarneyme@gmail.com"

Use emacs as my editor

jj/config.toml
ui.editor = "emacsclient"

By default, running jj with no arguments shows you the log, I prefer this to be the status command

jj/config.toml
ui.default-command = "status"

For compatibilty with other tools, it’s best to render diffs and conflicts as git would

jj/config.toml
ui.conflict-marker-style = "git"
ui.diff-formatter = ":git"

Aliases

jj/config.toml
[aliases]
push-down = ["new", "-B", "@", "--no-edit"]

Repo Config

Tip

Edit the configuration for the current repo by running:

jj config edit --repo

Considering how easy it is to rewrite history in jj it’s easy to imagine accidentally creating a mess for yourself. So, by default jj will mark changes belonging to the revset trunk() as immutable.

[revset-aliases]
"trunk()" = "develop@upstream"