Top
Best
New

Posted by Bogdanp 8/31/2025

Jujutsu for everyone(jj-for-everyone.github.io)
441 points | 411 commentspage 3
quectophoton 8/31/2025|
Would it be accurate to describe Jujutsu as "a Mercurial-inspired frontend for Git"?

Also, another question I have for people who have used Jujutsu: Is it focused on interactive use, or is it also convenient to use for automatic/non-interactive use?

For example, situations like:

* A CI/CD pipeline that periodically adds stuff to the repo, or a pipeline that modifies files when triggered by specific events.

* Server setup scripts that clone a repo with common config and then make a new commit after applying patches for host-specific changes.

Rebelgecko 9/1/2025||
IMO its inspired by the best parts of git, hg, and darcs. jj split alone is worth the price of admission IMO

I haven't done a TON of scripted use of jj, but when I have the built in templating has come in clutch. If you want a command to return in a certain format (or include/exclude certain metadata) it's usually easier- and probably more future proof- to tweak the jj command's template than to parse the string in your script.

paradox460 8/31/2025|||
Not really, but it's a strong enough description that people who know what both are would understand

JJ is a whole new VCS, that just so happens to be backwards compatible with git, and currently uses git as it's storage engine

It builds atop the knowledge we gained since mercurial and git were both new, not making mistakes that the others made, as well as knowledge from newer VCS systems like fig and sapling

sfink 9/1/2025||
> Would it be accurate to describe Jujutsu as "a Mercurial-inspired frontend for Git"?

In my opinion: yes. (Maybe with a slight caveat that I think some of the Mercurial inspiration came through Sapling.) And it's not limited to Mercurial/Sapling ideas, it has some new stuff too, but I think that still counts as "Mercurial-inspired".

> Also, another question I have for people who have used Jujutsu: Is it focused on interactive use, or is it also convenient to use for automatic/non-interactive use?

I use it in automation. I'm hampered because the primary VCS for the automation is git, so I'm doing more of a compatible layer and can't go too crazy with the jj-specific stuff.

But jj has some features that are excellent for automation.

Example: you can manually wrap things in a discardable transaction: grab the current operation ID, do all kinds of weird mutating operations that add and remove patches, maybe even merge and rebase, and then at the end (maybe in a `finally` block) do `jj operation restore <id>` and everything will go back to the way it was.

Another example is that if you want to tweak something, say a config file, in N different ways, you don't have to mess around with naming branches and switching between them or whatever. Just create N independent child commits and make the appropriate changes in each.

Or conversely: make M children for the M values of configuration value 1 and N children for the N values of configuration value 2, then create a new multiparented child (a merge commit) for each combination. `jj new <a> <b>` creates a merge commit with parents <a> and <b>, so:

    jj new $base -m 'var1=a'
    jj new $base -m 'var1=b'
    jj new $base -m 'var2=x'
    jj new $base -m 'var2=y'
    jj new 'description("var1=a")' 'description("var2=x")' -m 'var1=a, var2=x'
    # run scenario 1 with those settings
    jj new 'description("var1=a")' 'description("var2=y")' -m 'var1=a, var2=y'
    # run scenario 2 with those settings
Sorry, I'm using the `description(...message...)` revset expression to retrieve the appropriate commits when in practice you'd probably record the IDs and do `jj new $a $x` with no descriptions anywhere. But hey, the above would work as written!
zamalek 8/31/2025||
I'm a through-and-through JJ convert. One papercut I have experienced a few times (though I am getting much better at avoiding) is what to do when I forget to `jj new`. Assuming that I have pushed the current bookmark to the remote, is there any way to recover a new change that is the diff against the remote? I have tried rebasing, but that leads to bookmark ambiguity instead of the solution I'm looking for.
aseipp 8/31/2025||
If I understand you correctly, then you can use `jj interdiff --from name-of-branch@remote --to name-of-branch@git` in order to recover the diff between the remote branch commit and your local version of it; you could then turn that invocation into a Git .patch file and reapply it with patch(1). This only works for one commit.

The more general solution you're looking for I guess is some kind of "split based on interdiff" or something like that. I don't believe there's any way to accomplish this in a single stroke, though.

benoitg 8/31/2025|||
I fixed this by changing the default immutable heads to include all remote bookmarks. This way, you cannot edit them and the new commit is automatically created when you push to the remote branch.

  [revset-aliases]
  "immutable_heads()" = "trunk() | tags() | remote_bookmarks()"
dylanhu 8/31/2025|||
I also run into this often, I wonder if there is a simple built-in feature that would allow you to diff against the remote.
paradox460 9/1/2025||
Interdiff
abound 8/31/2025|||
There's a few different ways to fix this, but here's what the official docs suggest: https://jj-vcs.github.io/jj/latest/FAQ/#i-accidentally-chang...

TL;DR - `jj evolog` and then restore changes from the relevant secret commit ID

sgjennings 8/31/2025|||
I recently updated that FAQ so it preserves the original commit ID. It’s in the prerelease docs now and will move to “latest” with the release this week: https://jj-vcs.github.io/jj/prerelease/FAQ/#i-accidentally-c...
zamalek 8/31/2025|||
But that would undo what I've done, right? I Wang to keep the changes but only diff them against what I've pushed.
dzaima 8/31/2025|||
You can do a `jj new; jj restore --restore-descendants -f commit_id -t @-` with a commit id (not change id!) from the evolog to "inject" a snapshot from the evolog below your current @, leaving the file state at @ (and everywhere else in the log) intact.

Or if you have a commit ID (or otherwise revset; so a "main@origin" or whatever would work) of a specific commit that you want already, can use that instead of course too.

_bent 8/31/2025|||
i guess you can run `jj duplicate` before?
AndrewHampton 8/31/2025||
Is `jj split` a good option?
zamalek 8/31/2025||
I've tried it, but then I have to remember what has been pushed.
njaremko 8/31/2025||
Has anyone who's enjoying Jujutsu tried Meta's Sapling? I've been using it lately with the VS Code plugin, and it's been great. My understanding is that Jujutsu is pretty heavily inspired by Sapling and Google's patch-based git workflow?

https://sapling-scm.com/

sunshowers 8/31/2025||
I used to work on Sapling and Mercurial while at Facebook. I've been using Jujutsu full time for the last two years.

Jujutsu is in a sense the final form of that style of VCS, which I characterize as making commits first-class rather than branches, and providing powerful tools for managing long queues of stacked changes (git rebase -i is nice but has many limitations that don't exist in Jujutsu).

I go into some more detail in my testimonial, the first one at https://jj-vcs.github.io/jj/latest/testimonials/#what-the-us....

thramp 8/31/2025|||
I’d second Rain’s reply, but having gone from git to sapling to jujutsu, I feel like the jump from sapling to jujutsu was as big as the jump from git to sapling, in terms of “oh, this is a way nicer workflow”. I really like and miss Sapling’s interactive smart log, but I found jj’s conceptual simplicity to be more compelling than ISL. That said, VisualJJ and Jujutsu Kaizen (both listed on https://jj-vcs.github.io/jj/latest/community_tools/) might give you the ISL-style experience you’re looking for.
sfink 9/1/2025||
I'm mostly git -> mercurial -> sapling -> jj. For me, Mercurial -> Sapling was a mostly lateral, slightly upward move. Sapling -> jj felt really good, though oddly not because anything felt horribly wrong or missing in Sapling. Things just feel nicer and lighter in jj. Once I know a couple of things, I can combine them to do lots and lots of other things without learning any new commands. In fact, the total command set is pretty small, yet covers about the same range as other systems (a little more here, a little less there). Also, improvements come in at understandable and predictable places: at its current stage of development, it's not growing brand new commands. It is instead slightly improving existing commands to enable significant new capabilities. It has the feel of a well-designed system with mostly orthogonal components. (Give it a decade, and perhaps it'll start crumbling under its own weight too...)
rich_sasha 9/1/2025||
Having disliked Git for a long time, I concluded that looking for alternatives is actually a waste of time, unless you do it as a hobby.

VCSes are natural monopolies. There is a natural and unforced benefit from using the same thing as everyone else, unless it is so terrible that you literally can't work it. Because:

1. You use it for collaboration with others

2. You use it on new or under-configured machines where suddenly you're stuck with the lowest common denominator, ie got

3. 99%+ of online materials assume you're using git.

4. The RoI is actually not that great. If you do learn the system, you're only in the same place professionally as if you learned git in the first place.

Git is famous for its user interface, and not in a good way. But the 5-8 commands I use daily, I now remember. I still have to Google for interactive rebase or how to nuke last commit - but I do it rarely enough that it doesn't matter.

Jujutsu sounds really nice, likewise I really wanted to like mercurial. But in the end I decided it wasn't worth my time.

KingMob 9/1/2025|
This over-estimates the lock-in, because jj uses git under the hood. You can try jj, and your colleagues need never know. If you hate it after a couple weeks, you can return to git, no sweat.

It's much easier to try out than earlier VC systems.

thibran 8/31/2025||
jj clicks almost for me. I still struggle to understand when do I switch to a "branch" using 'jj new' and when do I use 'jj edit'. Also the manual setting of bookmarks after 'jj git pull' seems strange.
infamousclyde 9/1/2025||
To the author: I really enjoyed your style of writing. This had more explicit and realistic examples compared to other HN tutorials on the subject, and that made it easy to inhale in one sitting. Thanks for your work!
0-R-1-0-N 8/31/2025||
I would like to see an example of someone showing the workflow for using jj and doing feature branch. I don’t really get that yet. Most examples only show that they commit once and then push. But what if it requires multiple commits.
SAI_Peregrinus 9/5/2025||
One way (there are other ways to do some things):

`jj log` if needed to see what revision ID you need to use

`jj new <starting revision> -m <message>` to create a new change with a given message with <starting revision> as the parent. (you can also use the `--insert-before <revset>` and/or `--insert-after <revset>` to set the parent & child revison(s) for the new revision).

`jj bookmark set <feature 1 name> --revision <revset>` to make a bookmark (a branch name) on the revison you just made.

Make changes on this branch as desired. `jj new` for each new change you want to have its own revision.

If you added more revisions to the branch, `jj bookmark move <feature 1 name> --to '@'` (you can also use the change ID instead of '@', '@' just means "the current change you're editing").

`jj edit <starting revset>` to go back to your starting revset.

`jj new -m <message>` to make a new revset on top of the starting revset (the starting revset now has two children: it has branched)

`jj bookmark set <feature 2 name> --revision <revset>` to name the second branch you just created.

Make changes as desired. `jj new` for each new change you want to have its own revision.

If you added more revisions to the branch, `jj bookmark move <feature 2 name> --to '@'`.

`jj new <feature 1 name> <feature 2 name> -m "merge feature 1 & feature 2"` to make a new revision with the feature 1 & feature 2 bookmark revisions as its parents (this is a merge).

Alternatively, you could use `jj rebase` to move any number of commits to anywhere in the commit graph you want.

hellcow 8/31/2025||
Here:

  $ jj new -m 'build my feature'
  $ touch my_feature.c
  $ jj new -m 'add some other feature'
  $ touch other_feature.c
  $ jj bookmark move main -t @
  $ jj git push
sltr 9/1/2025||
I can also recommend Koppel's JJ Workshop

https://github.com/jkoppel/jj-workshop

DAddYE 9/1/2025||
How does JJ works with LazyGit? I can’t see myself living without it.
paradox460 9/2/2025||
Replaces it entirely

Check out jjui. While it's not a 1:1 of lazygit, it's better in some ways and lacking in others, but quickly getting better. I really enjoy that it shows you the command every action runs under the hood. Great to expand your knowledge of JJ early on

steveklabnik 9/2/2025||
There is a lazyjj but I don’t use lazygit so I can’t comment on how complete of an alternative it is.
hu3 9/1/2025|
Personally I don't think adding a layer on top of git is a good idea.

I know it's hard to break git's hegemony but it would be much more powerful and less constrained to build on top of a new foundation.

SAI_Peregrinus 9/5/2025||
Git is a content-addressable filesystem with a VCS interface on top of it. jj needs a content-addressable filesystem for a backend, and currently only uses git's implementation in released versions, but the devs are working on their own to serve as a "native" backend. At least in theory, just about any content-addressable filesystem could be used, e.g. IPFS, though many would not be efficient.
dcre 9/1/2025|||
jj’s design is not constrained by git. git is one possible backend, but you don’t even need to use jj with git. I will eat a shoe if you can name something jj is prevented from doing by its relation to git.
hu3 9/1/2025||
Interesting. How can I use jj without git?
tfsh 9/1/2025||
Right now there's no active public backends, there are a few prolific JJ contributors who are working on their own forge though (https://ersc.io).

At Google, JJ is natively supported for interfacing with the monorepo, this is where the proof of being backend agnostic comes from. Hopefully as the network effects catches on we'll see more and more of a desire for 3P forges.

KingMob 9/1/2025||
You might be interested in pijul then, which has a theory of patches that is interesting.
More comments...