Jujutsu workshop

A Git-compatible version control system
2025-12-27 / blinry / CC BY-SA 4.0 / workshop, tech

At 39C3, I gave a workshop about Jujutsu, a new-ish version control system that I fell in love with last year! :)

Here’s the script that I used in the workshop:

     ___       _       _
    |_  |     (_)     | |
      | |_   _ _ _   _| |_ ___ _   _
      | | | | | | | | | __/ __| | | |
  /\__/ / |_| | | |_| | |_\__ \ |_| |
  \____/ \__,_| |\__,_|\__|___/\__,_|
             _/ |
            |__/  A Git-compatible VCS

                 blinry

# Getting to know you!

Who is using Git in any way?
Who is familiar with the command line?
Who has tried Jujutsu?

# Workshop in 3 parts

Please interrupt me and ask questions!

# Motivation

Git is kinda hard
  https://git-man-page-generator.lokaltog.net
  https://spderosso.github.io/onward13.pdf
Multiple friends told me about a new VCS
  https://github.com/jj-vcs/jj
Tried it last year
Has entirely replaced Git for me! 😮

# What I like about Jujutsu

Both simple and powerful
    Some workflows that are hard with Git are easy with jj!
    Trivial to modify commit history
Simpler mental model
    No index, working directory *is* the commit
Deal with conflicts when you're ready
Possible to undo mistakes (finally!)
GIT-COMPATIBLE! ✨
    Anyone remember git-svn?

# History

Started in 2019
Hobby project of Martin von Zweigbergk
Initially called "Jujube" :D
    https://commons.wikimedia.org/wiki/Category:Ziziphus_jujuba_(fruit)
Renamed to "Jujutsu" in 2021

# 6 basic commands

jj git init
    Until v0.32, use --colocate.
jj log
jj show
jj describe
jj new
jj commit

Things to note
    Working directory is "snapshotted" every time you run a jj command!
    Git: Edit files, add files, commit
    jj: Edit files, commit
        Uncommited changes are recorded in the current commit!

Questions so far?
Self-help: jj <subcommand> -h / --help

~~~ Install jj & try the 6 commands for 10 minutes! ~~~

# Manipulating history

jj edit
    Automatic rebasing!
    No stashing required!
jj squash (-r <revision>)
jj split (-r <revision>)
    Alternative to Git's index and interactive adding
jj abandon
Creating and resolving conflicts

# More things `jj new` can do

jj new <revision>
jj new <revision 1> <revision 2>
jj new -A/-B <revision>

# Rebasing

jj rebase --branch <revision> --onto <revision>

~~~ Try manipulating history for 15 minutes! ~~~

# Git interop

`jj log` also shows commit ids
Changes vs commits
jj evolog

# Working with Git remotes

Branches don't *need* names!
jj git remote add origin <url>
jj bookmark
    create
    move
    delete
    track origin@main
jj git push
(jj git push --named <bookmark name>=<revision>)
jj git fetch
Common alias: jj tug
    tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "heads(::@ & (~empty()))"]

# Common starting problems

I don't see the full log!
    jj log -r ..
My files are added by default!
    jj config set --user snapshot.auto-track "none()"
        jj file track <file>
I made a mistake!
    jj undo
    Working with the operations log
        jj op log
        jj op revert <operation id>
        jj op restore <operation id>
I wanna go back to Git!
    Always possible to switch between jj & git!
    rm -rf .jj
    Probably in a detached HEAD state, so:
        git switch <branch>

# More juicy things!

Revset language
    inspired by Mercurial!
    https://docs.jj-vcs.dev/latest/revsets/#operators
    author(blinry)
    comitter_date(after:"10 minutes ago")
    heads(::)
Templates
    https://docs.jj-vcs.dev/latest/templates/
    "format_short_id(id)" = "id.shortest(4)"
jj absorb

# Now what?

You can use jj *right now*!
It might make your life easier?

# Cheat sheets

https://justinpombrio.net/src/jj-cheat-sheet.pdf
https://jj-vcs.github.io/jj/latest/git-command-table/

# Suggestion

1. Make a local copy of one of your projects
2. Delete all remotes (to make the commits mutable)
3. `jj git init`
4. Play around, try some of the jj commands again
    (edit, describe, new, commit, split, ...)
5. Find three places where you can improve the history!

# My graph-drawing command:

bash -c 'while true; do OUT="$(jj log --ignore-working-copy --color=always -r ::)"; clear; echo "$OUT"; sleep 0.1; done'

# Find me on the Internet!

Mastodon
    https://chaos.social/@blinry
Projects & contact
    https://blinry.org
Slides
    https://blinry.org/jj-workshop/

~~~ Try jj on your own project (until we get kicked out) ~~~

Several people have asked for my config file, here it is! It also includes some experiments I’ve commented out:

[ui]
default-command = "log"
pager = "less -FX"
movement.edit = true
#diff-editor = ["nvim", "-c", "DiffEditor $left $right $output"]
diff-formatter = ["difft", "--color=always", "$left", "$right"]

[snapshot]
max-new-file-size = 100000000
#auto-track = "none()"

[aliases]
tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "heads(::@ & (~empty()))"]

[revset-aliases]
'2weeks' = 'committer_date(after:"2 weeks ago")'

[template-aliases]
#"format_timestamp(timestamp)" = "timestamp.ago()"
#"format_timestamp(timestamp)" = 'timestamp.format("%Y-%m-%d")'
#"format_timestamp(timestamp)" = ''
"format_short_id(id)" = "id.shortest(4)"
#"format_short_commit_id(id)" = ""
#"format_short_signature(signature)" = "signature.email().local()"
"format_short_signature(signature)" = ''

#"format_short_signature_oneline(signature)" = ''
"format_short_signature_oneline(signature)" = 'signature.name()'

[templates]
log = 'builtin_log_oneline'
draft_commit_description ='''
    concat(
      coalesce(description, default_commit_description, "\n"),
      surround(
        "\nJJ: This commit contains the following changes:\n", "",
        indent("JJ:   ", diff.stat(72)),
      ),
      "\nJJ: ignore-rest\n",
      diff.git(),
    )
'''

[git]
colocate=true

Comments?

Send a message to @blinry@chaos.social or drop me a mail at mail@blinry.org. Also, you can support me on Patreon or subscribe to my newsletter!