I recently switched to Jujutsu (jj) and it made me realize that “what comes after Git” might already exist.
It turns out the snapshot model is a perfect fit for AI-assisted development. I can iterate freely without thinking about commits or worrying about saving known-good versions.
You can just mess around and make it presentable later, which Git never really let you do nicely.
Plus there’s essentially zero learning curve, since all the models know how to use JJ really well.
Yes, it’s fantastic. I have a post-tool-use hook for Claude Code to snapshot the repository for every edit. It’s like the built in file history feature but native in my VCS and works for my edits too. Don’t want to froth too much but JJ is my favourite piece of software in a while, and the fact that it’s not VC-funded is a major plus point.
Jujutsu is not "VC funded". But some of the developers, including me, work at East River Source Control (I worked on Jujutsu before that, too). The majority of the code in the project doesn't come from us -- or Google, for that matter. We don't allow people to approve patches when the author is from the same company, anyway.
If I remember correctly, jj is one guy who works at Google. Which presents a separate worry, which is that one day, when jj gets popular enough, Google will consume it, make it shit, change the name of it every six months and then shut it down.
That hasn't really been the case for a while imo: Martin works at Google and is paid to work on jj (there are also other Google employees who contribute, not sure whether they're paid to). jj is in use (wide use? No idea) alongside Google's internal tool (piper) with which it can interact (and with which it has some features in common) because jj has a pluggable backend architecture.
While I hate to engage in speculation, tell spooky stories, or screech at people about the evil CLA you have to sign in order to contribute, my personal opinion is that if Google were ever to start throwing their weight around, the project would be forked in short order and development would continue as normal – it has momentum, plenty of non-Google contributors, and a community. It's also not a product per se, though as we're about to find out, you can certainly build products on top of it – that probably makes it less likely for its current home to suddenly become proprietorial about it.
Good points. I had a horrible vision of a git -> GitHub -> Microsoft -> GitHub-on-Azure style pipeline but yeah, I think there's enough good people involved around jj that your vision is probably more likely. Also, hi Steph!
jj is not "one guy who works at Google" and the vast majority of submitted code comes from non-Google developers. Even if Google were to stop developing jj (they won't) the project would be healthy and strong.
There's some legal annoyances around e.g. CLA which was a result of being a side project of Google originally. Hopefully we'll move through that in due time. But realistically it's a much larger project at this point and has grown up a lot, it's not Martin's side project anymore.
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if command -v jj >/dev/null && jj root >/dev/null 2>&1; then if ! jj status >/dev/null 2>&1; then echo 'WARNING: failed to snapshot jj repository, tell user to fix'; fi; fi"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if command -v jj >/dev/null && jj root >/dev/null 2>&1; then if ! jj status >/dev/null 2>&1; then echo 'WARNING: failed to snapshot jj repository, tell user to fix'; fi; fi"
}
]
},
In newer jj there’s a dedicated snapshot command but I’ve not updated yet. Pop this in your Claude Code settings.json. It will snapshot the repository, thus recording any changes. Explore with jj evolog.
I know there was a debug command that would do it, but that was considered an unstable interface. That sounds like a better command, I’ll have to check it out, thanks!
> You can just mess around and make it presentable later, which Git never really let you do nicely.
I'm surprised to read that, because that's how I've always used Git (and GitHub).
That's what I've understood to be good practice with Git, and it was liberating compared with what came before. One of the nicest things about Git is you can throw things in locally without worrying about how it looks, and make it presentable later.
I also did that with git, but it's no comparison in ergonomics. For instance, "move this hunk two commits up" is a task that makes many git users sweat. With jj it's barely something that registers as a task.
You sweat because you are working with the CLI. Git is intrinsically "graphical". Use a good GUI client or higher level interface (maybe jj) to manipulate git graphs --- stop worrying about "how" (i.e. wrangling with CLI to achieve what you want) and focus more on "what".
> For instance, "move this hunk two commits up" is a task that makes many git users sweat.
Citation needed. You split the commit anyway you like, e.g. with the mouse or using cursor movements or by duplicating and deleting lines. Then you move it with the mouse or cursor or whatever and squash it into the other commit. Maybe some people never intend to do it, but then these probably also don't want to learn JJ. I guess this is more of a selection bias, that these that care about history editing are also more likely to learn another VCS on their own.
I'm confirming the sentiment is accurate. Background: using Git (involuntarily) since 2010, did my fair share reading it's source, put honest effort into reading it's man pages, so. Jujutsu _is_ a revelation and I'm moving to it every time I'm able to: the git repository stays the same, it's the jj runs it now.
If you ever tried to have multiple WIP features merged in a Git working copy, I have a great news — with jujutsu complexity of the workflow increases linearly over the number of branches, if ever: it's almost trivial. Otherwise I very much encourage you to try — in and of itself the workflow is extremely effective, it's just Git makes it complex af.
1. Use "git rebase -i commitid^" (or branch point, tag etc), ideally with editor set to Magit, set that commit to "edit" (single key 'e' in Magit) and let the rebase continue, do "git reset -p HEAD^" and select the hunks you want to remove from the first commit, "git commit --amend", then "git commit -a" (add -c if useful, e.g. to copy author and date from the previous one). or to keep the author date), then "git rebase --continue" to finish.
2. Same, but use "git reset HEAD^" (add -N if useful), then "git add -p" to select the hunks you do want to include in the first commit.
Afterwards you can do the "git rebase -i" command again if you want to reorder those commits, move them relative to other commits, or move the split-out hunks into another existing commit (use the 'f' fixup or 's' squash rebase options).
After doing this a few times and learning what the commands actually do, it starts to feel comfortable. And of course, you don't have to run those exact commands or type them out, it's just a raw, git-level view. "git rebase -i" and "git add -p" / "git reset -p" are really useful for reorganising commit hunks.
Yeah, I mostly do it like that. I don't use Magit (yet? Haven't got the motivation to learn or find a good tutorial for Emacs.), but instead use the cursor to select the lines to stage or unstage with the cursor/mouse in my Git GUI. Also depending on what I want the commits to look like, I duplicate the pick commit line first (and potentially move it).
On an unrelated note, I use @~ instead of @^, because I think of moving up down the ancestry, not sideways, e.g. I'm more likely to want to change it to an older/newer commit, than I am to want to change the second parent instead. I don't get why most tutorials show it with @^, because you do focus on the commit being an ancestor, not precisely being the direct first parent, although of course for the first-level first parent, it amounts to the same.
It's already well explained in a sibling comment, but on a more conceptual basis, while commits are interpreted as diffs on the fly, a commit is a single (immutable) snapshot. So in these terms, "splitting a commit" amounts to introducing an intermediate snapshot. Having that in mind, it should become clear, that using Git you create the snapshot by working from the previous or next commit (what ever suits you more), bringing it to the state, you like it to be and commit. (In theory you could create that intermediate snapshot from any commit, but likely you want to do it from on of the direct neighbors.)
Every success story and happy conversion to jj is evidence that hg should have won the DVCS war, but more importantly, that VCSes and their inherent merits and tradeoffs were always secondary to the social networks underpinning them. GitHub as a defacto monopoly really killed innovation in the VCS space, but also shifted the focus and attention elsewhere. That's why I don't think there'll be so much of a "post-git" without as much as a "post-github".
Does VCS-agnosticity actually work? It feels like a huge burden to migrate it everytime you want to have the tools from the innovation in your daily work. Also projects want to integrate project versions into each other and reference versions and identifiers are likely VCS specific. That's why I feel VCS monopolies actually has a lot of benefits.
LOL. In 2007, I did a side-by-side comparison of SVN, Mercurial and git with my codebase at work (~700k LOC). I know hg got faster eventually, but I still can't believe people say "hg should have won the DVCS war" with a straight face. It was orders of magnitude slower in my tests. Like, 20 minutes to commit large xml files (yeah, I know, but that's where we were). Not just a few ms slower, unbearably slow for several things.
I liked its features better, but chose git, and that was the correct decision.
This isn't an apology or a deflection, but different VCSes strike different trade-offs in terms of speed per workload. The mercurial storage model is comparable to video encoding in that it has a somewhat equivalent of "key frames" and alternates differential and full revisions¹. This makes workloads like bisecting or jumping to long-distance commits more efficient, at the expense of the time to create new commits.
For your use-case, mercurial has had a `largefiles`² extension for a very long time (long before git-lfs).
> I liked its features better, but chose git, and that was the correct decision.
Anyhow, my personal story with git is that I bought into the hype and social effects without really challenging my assumptions ("it must be the best, everyone says so"), until I got very fed-up with its obnoxious UI, and someone on IRC told me "ehh, give hg a shot". Nowadays, I mostly interact with git repos through hg-git and jj.
What's the difference between "snapshots" and git commits? In my mind a git commit is already a snapshot of the repo and the changes one staged.
In what way can you move around more freely than what one can do with magit, deciding for files, hunks, or even single lines of code, whether or not they get staged and committed?
jj is very non-modal, that is, it doesn't tend to have a lot of state that commands rely on. As an example of what I mean, because jj does not have a staging area, everything is already committed, which makes it very easy to say, move to a different commit: you don't need to stash your working copy, as jj has already stashed it for you. Similarly, due to the auto-rebase behavior, you can be working in one part of the tree, realize something somewhere else should be moved, and go rebase that without even moving to it at all!
As a small example: say I'm working on something, and I find a typo. I want to send that typo in as a PR, but I don't want to do it as part of my work. I can do that with:
1. make the change in my current working copy (@)
2. jj split -o trunk (selecting the typo contents to split off the typo fix into a new change on top of (hence -o) trunk)
3. jj log (go check out what the change id of that change is
4. jj git push -c <change id I found in 3>
No need to even move my own HEAD (in git terms), just knock it out inline in a few steps while I'm working.
Now, as for magit, I don't use it, and I know that those that do love it and it does make some of this stuff easier. But not everyone can use magit. And there are "magit, but jj" projects as well, but I can't speak to them or which is best at the moment.
Technically, nothing. But psychologically git commits represent a unit of completed work, whereas with AI agents what's needed is a kind of agent-wise undo history such that you can revert back to the state of the repo 1 minute ago before Claude did an oopsie all over your repo.
You can definitely use git as a backend for building such a system, but some extra tooling is necessary.
Most of the time when I'm using Claude my working tree is already dirty because I'm mid-task. I usually try to do a throwaway commit before every interaction with Claude, but it's easy to forget, or to leave the "accept edits" mode on accidentally and my working tree gets corrupted. Also having to commit takes you out of flow because you suddenly have to deal with any new gitignores, which requires at least a glance at untracked files to make sure you're not committing anything you shouldn't be. I want to be able to undo the state of my working tree to the moment before a particular interaction with Claude, just like how I can undo a file.
jj offers "jj undo" which will undo changes to your repo, and the "oplog", which is sort of like the reflog, but on steroids. It's one of the nicest things about it.
> You can definitely use git as a backend for building such a system, but some extra tooling is necessary
Is it? There’s the stash for storing patches, the index for storing good hunks, branching for trying out different experiments. You can even use worktree if you want separate working directory especially when there will be changes in the untracked files.
Git has a lot of tooling for dealing with changes, directly or at the meta layer.
I was doing something with jj snapshots with AI now that you have mentioned.
I will admit, I didn't know jj but I wanted snapshots so I used it, so then when AI made some changes and kept on going and I wanted to go back to a particular change and I used ai to do that. It was actually really frustrating. To the point that I think I accidentally lost one of the good files within the project and I had to settle on good-enough which I had to try to get for hours to that particular point.
My point feels like I should either learn jj properly to use it or to at this point, just ask AI agents to git commit. Another point but I was using ghostty and I had accidentally clicked on the title bar and somehow moved the folder to desktop, I wasn't thinking the most accurately and I just decided to delete it thinking that it must have copied it rather than moved it. (Also dear ghostty why do you make it so easy to move folders, it isn't the best of features and can lead to some honest errors)
My face when I realized that I have deleted the project:
Anyhow decided to restore it with ~/Trash but afterwards realized that the .git/.jj history is removed because it deletes hidden folders (from my understanding) so I definitely lost that good snapshot. I do have the binary of the app which worked good but not the source code of it which is a bit frustrating
These were all just an idea of prototyping/checking how far I can move things with AI. Yeah so my experience for that project has been that I could've even learnt a new language (Odin) and the raylib project to fix that one specific bug in lower time than AI which simply is unable to fix the bug without blowing the whole project in foot.
I think the takeaway is to have good backups man. I mean I was being reckless in this project because I had nothing to lose and was just experimenting but there have been cases where people have lost databases in prod. So even backups should be essential if you find any source code which is good to be honest.
I am sure you guys must have lost some source code accidentally which you have worked upon, would love to hear some horror stories to hopefully know that I haven't been the only one who has done some mistake and to also learn something new from these stories. (I am atleast happy in the sense that I learnt the lesson from just an tinkering thing and not something truly prod)
you'r doing great. we all learn these things. AI assisted development makes it really easy to redevelop, especially since it sounds like you were vibe coding it out.
It seems to have been build on great idea. Git's "plumbing" is just a set of snapshots of the tree, and everything above is built on that so replacing the porcelain with something better fitting the problem is far more useful than trying to reinvent the wheel and making yet another distributed tree snapshot based VCS just to reinvent user facing tooling like the other VCS tried
Definitely not true about models knowing jj. I've had latest opus and gpt fail at revsets and fileset syntax, even hallucinating subcommands like jj move (maybe it existed before, interface is not stable). Luckily it's easy enough to not need them most of the time.
Yes, I use Claude with jj often, and it occasionally tries to use older commands like move. Most of the time it gets it right for me though, often plugins and such say to use git explicitly, and that's where it tends to stray.
Jujutsu has changed how I work with git. Switching tasks is just "jj edit <change>" or "JJ new <change>". The only thing it can't do properly is git worktrees (it doesn't replicate the .git dir to the worktrees, breaking tooling that relies on git) but there is a (old) issue relating to it. Not sure on the priority, though.
I do use git branches, but they solve isolation, which isn't my pain point with git.
When I'm using agents to code, I don't want to have to stop what I'm doing and commit known-good state to the repo every few minutes.
jj just snapshots everything automatically, so I know I've captured that state, and I can look back and curate it all after the fact.
It's like the shift from manually saving Word documents to autosave, but instead of forcing it with git, I can use JJ which has been intentionally designed for that workflow.
Why not just ask your agent to check in before every change? And then squish before you push or share. Works great for me and easier than convincing coworker to switch off git.
It turns out the snapshot model is a perfect fit for AI-assisted development. I can iterate freely without thinking about commits or worrying about saving known-good versions.
You can just mess around and make it presentable later, which Git never really let you do nicely.
Plus there’s essentially zero learning curve, since all the models know how to use JJ really well.