← leojofer.com

Git to Jujutsu from first principles

You probably already know about Git, but bear 🐻 with me. This will all be relevant in a second.

This is a Git commit graph. It has nodes (commits) which contain changes and are linked hierarchically. Commits are identified by a content-based ID and they can split off into different branches.

Your uncomitted work exists “on top” of some commit (here: the box). So far, so good. You can store unfinished work in stashes (illustrated here as the grey bubble).

Git commit graph with uncommitted work on top and a stash

In theory, very beautiful, very sleek.

In practice? Well .. relevant xkcd

Let’s suppose I edit an earlier commit in this beautiful commit graph and rebase:

Git commit history

Because all commit IDs are bound to their contents, this means that every ID in this graph changes.

Git commit history, with IDs highlighted

Our tooling loves this (/s). None of the commits above “Implement sporgler” have actually changed in their meaning. They are just linked to a different commit. And they have a different identity now after the rebase. So how can we avoid this?

The required change appears obvious in retrospect: make Change IDs stable across content changes. This way, we are referring to the intended unit of change rather than the mechanical one, so to speak.

But if we can track a unit of change as it - well - changes, then we can simplify so much further. Why be “on top” of a commit? We can just be inside of a commit all the time.

And if we are, then a merge conflict is also just a commit. A commit that we can jump in and out of — and fix later if now the timing isn’t right. This means you can’t really get stuck anymore. Hard Git things become easy.

And now you may be thinking: well, what if I accidentally squash two commits or delete something? Jujutsu has local VCS-level undo, so if you screw up it’s just jj undo and everything is back to how it used to be.

Oh and it works on top of Git :)