Jujutsu Impressions

TL;DR

  • JJ does things differently from Git, but it’s compatible enough to try. The core unit is the persistent change, which evolves through snapshots.
  • Actions are logged as operations, enabling undo and restore to any point in time.
  • No Git-style branches. Changes have persistent IDs, and you use bookmarks for Git-forge workflows.
  • You can colocate jj with an existing Git repo, keeping your Git workflow intact while jj handles history.
  • Curious? Follow the tutorial or experiment on a current repo to see the difference firsthand.

In this post I’ll share some insights about using the Jujutsu version control system, which I’ll call jj for the rest of the post.

jj is a version control system, currently built on top of git, using its building blocks. However, it’s not just a new porcelain; it defines new abstractions and data structures of its own.

Concept: The Change as the Atomic Unit

The most important concept is the change. The change defines an atomic unit of, well, change. Its analog is the git commit. But in jj, a change can develop in time. Technically, it’s a git commit that keeps getting amended, the difference being that the change keeps its identity via a persistent ID and a description field. A change updates whenever jj takes a snapshot of the repo—every time you call jj. If you think about it, jj can’t lose work, as it always starts by taking a snapshot, even for informative commands like jj log.

Thus the change represents an amended commit, the “last” change. However, jj lets you inspect the internal, previous commits inside the change, by calling jj evolog. These internal commits are not synced to git forges and are not automatically garbage collected, like git amended commits are.

When we are done with a change, we can give it a description (which we can do at any time using jj description) and create an empty change on top of it, ready to receive new modifications. We do that using jj commit.

For example, when you initialize a repo, the initial current change is empty. You add files, edit them. When you are ready, you give this change a name via jj description and then start a new change via jj new, or do both at the same time via jj commit.

Graph, Branches, and Bookmarks

Moving around (git checkout) is done with jj edit. But we need to be careful, as any edit we make after the jump will get snapshots (“amended”) on top of the current change, modifying it. If you want to jump in to do some work, it’s better to use jj new.

You might see the claim there are no branches in jj. There aren’t branches in the usual git sense. In jjs graph there are “branches”, but they don’t need names. The persistent change IDs serve as feature names and jump addresses (using edit or new). Nevertheless, branch names are introduced in jj in order to be compatible with git forges; they are called bookmarks, and they need to be moved explicitly across the changes.

Logging and Time Travel

jj logs what’s happening using operations: these are the commands you enter and the current change you’re in, in addition to some metadata. The logs enable handy features like jj undo or the deeper jj op restore (restore to any point in time). For browsing the operation log, see jj op log.

There is a comparison table between git and jj commands, which could be useful but it’s important to not fixate on how git is doing things in order to be open to the new paradigm jj represents. However, some features seem to have been added in response to git users’ needs or workflows, or perhaps jj developers rediscovered the same needs.

Mixing JJ with Git

I chose one existing git project and converted it to a mixed usage of jj and git, using jj git init --colocate. It means jj initialize its presence in an existing repo and it will keep updating the .git folder with what’s happening, at a level compatible with git constructs; for example, the changes are saved as git commits, jj git fetch is fetching from git forges into .git/, etc. I haven’t used rebasing, squashing or other history-changing operations so I can’t comment on how easy they are to use, maybe next time.

If you found it interesting, give it a try. There’s the tutorial, or you can run it on an existing git repo.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • המודל שלכם הוזה? כך תבנו בעצמכם מערכת RAG | גיקטיים
  • Built an MCP server for LLMs to search email from terminal | Daniel Fleischer posted on the topic | LinkedIn
  • Summarize Hacker News Posts with Haystack & OPEA | Haystack