DevLogTechnical writing
Blogs

Jujutsu (jj): The Git-Compatible VCS That Finally Has an Undo Button

November 04, 2026

Git works. We all use it. But a big part of Git's daily friction — staging, stashing, rebasing, and the dread of a wrong git reset — comes from the model, not from missing features. Jujutsu, the version control system behind the jj command, keeps Git's data but replaces the model, and a lot of those problems quietly disappear.

Jujutsu shipped version 0.47.1 on November 4, 2026, still pre-1.0 — which is exactly why it's worth a look now. The project was started in late 2019 by Martin von Zweigbergk as a hobby, grew into his full-time work at Google (where several other Googlers contribute; it's not a supported Google product), and sits at roughly 32,400 stars on GitHub, written in Rust under the Apache-2.0 license. The practical detail: it stores commits in a real Git repo. You can jj git clone a GitHub repository and push back to it while your teammates keep using git, none the wiser.

Three ideas carry it.

The working copy is a commit. Almost every jj command snapshots your uncommitted changes into a commit before doing anything, and amends that commit as you keep editing. So there is no staging area, no git stash, and no "let me commit this now because I'm scared of losing it." New files are tracked implicitly. Since the working copy is just another commit, commands work on it the same way they work anywhere else: jj describe edits the message of any commit, including the one you haven't finished.

Everything is recorded, so undo is real. Jujutsu logs every operation — commits, pulls, pushes, rewrites — and each entry carries a snapshot of the whole repo. jj undo reverses the last operation, and because operations are first-class, you can revert one that isn't even the most recent. jj --at-op <id> loads the repository exactly as it stood at any operation. The next time someone asks "how did we end up here?", it's a one-liner to find out.

Commits are changes, and rewriting is normal. Each commit has two identities: a content-based commit ID, like Git's hash, and a stable change ID that follows the work through rewrites. Edit a description, rebase a branch — the commit ID changes, the change ID doesn't. History rewrite stops being a scary operation. Descendants are rebased automatically: touch a commit and everything on top of it is rebuilt onto the new version. The project describes this as "a completely transparent version of git rebase --update-refs combined with git rerere." Conflicts are first-class objects, and they can be checked out: jj writes conflict markers into the working copy, tracks the conflicting parts, and parses your resolution back in. The workflow is always the same — check out the conflicted commit, resolve, amend — instead of juggling rebase --continue, merge --continue, and cherry-pick --continue.

The project is honest about the tradeoffs. The README calls Jujutsu experimental: on-disk formats may change before 1.0.0, submodule support is unfinished, and there's no native email-based workflow. It's also a heavily self-hosted project — von Zweigbergk has used jj to develop jj since early January 2021 and says he has never had to re-clone from source. That's the endorsement that counts.

You don't have to leave Git to try it. Clone a scratch repo with jj git clone, spend an afternoon with jj new, jj describe, and jj undo, and see whether the undo button changes your habits. For a lot of people, it does. import re

with open('/tmp/hermes/writer/jujutsu-post/jj-devlog-post.md') as f: text = f.read()

parts = text.split('---') body = parts[2].strip() # after second --- words = body.split() print('body words:', len(words))

excerpt from frontmatter

fm = parts[1] m = re.search(r'excerpt:\s"([^"])"', fm) excerpt = m.group(1) print('excerpt chars:', len(excerpt)) print('excerpt:', excerpt)

sanity: excerpt < 160

assert len(excerpt) < 160 print('OK: excerpt under 160 chars')

← More Blogs