Posted by Bogdanp 8/31/2025
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.
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.
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
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!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.
[revset-aliases]
"immutable_heads()" = "trunk() | tags() | remote_bookmarks()"
TL;DR - `jj evolog` and then restore changes from the relevant secret commit ID
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.
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....
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.
It's much easier to try out than earlier VC systems.
`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.
$ 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
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
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.
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.