Jujutsu (JJ) is a Git-compatible version control system that simplifies version control by eliminating the staging area, using stable change IDs instead of content-based commit IDs, and providing features like deferred conflict resolution, change bubbling, and an operations log with undo/redo capabilities, while maintaining full compatibility with Git repositories and remote collaboration through bookmarks.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Jujutsu: Git-Compatible Version Control SystemAdded:
Hi, I'm David Y, a developer on duty, and today we're going to have a look at Jujutsu or JJ, which is a Git-compatible version control system.
So, JJ is compatible to Git, and you can use Git as a back end. You can also collaborate with your co-workers who use Git. It's not a problem. They will not even know that you're using Jujutsu. So, to clone a Git repository, you would write jj git clone and then the URL.
And now you have it. Very similar to Git, but what is different?
So, just by looking at this log output, you see a few differences. For example, you have these things called bookmarks or refsets, which can have some kind of functions.
And then you have this working copy, which has two identifiers if instead of just one, the commit identifier as you're used to in Git. So, all of these will make sense in a minute once we delve in. So, let's do it.
Let's navigate into the repository. So, the most important command in JJ is jj log or just jj for short, and it will just tell you the log of it. And there are a few things which are important. First of all, every change has two identifiers.
One is the Git commit hash, and the other one is the change ID.
The change ID has the advantage that it's somewhat stable, so you can change the change, and the change ID will remain, and you are still able to refer to it, which is great. Whereas, the commit ID is content-based. So, whenever you do something with it, the commit ID changes.
The other important thing to note here is that you have this empty change. I mean, I just cloned this repository. I haven't done anything, and yet there's an empty change. Why is that? The reason is in JJ, there is no staging area. That means everything you do, every file you modify, every change you make belongs to a change. Even if you don't do anything, there is still an empty change. It will be garbage collected if you don't use it, so don't worry about having too many empty changes in your list. But that's how it is, and it brings a few advantages. For example, you can much easier switch to different changes without having to worry about Git stash or anything like that. It just works without you having to worry too much.
So, let's see what happens if I make some changes. So, I can say echo some changes, and I pipe it into some file.
And now if I run JJ again, you will see that the change ID did not change. But I have another commit ID here.
And that is exactly what I mentioned before. So, those changes are not immutable. The ID will remain even though you change the thing.
Also, keep a note that I haven't changed the description, so I can do that by using JJ describe or just desk for short, and I can provide the message, this is a cool description.
Now if I run JJ again, you can see that I have now here this description.
And what you just saw, you know, that JJ does not have a staging area, etc. That is actually a common theme in JJ, I would say. So, there are a lot fewer things you have to keep in your mind.
So, you don't have to worry about something like Git stash or the staging area, etc. So, it's all about changes and navigating the changes, and also later you will see bookmarking such changes. So, the concept are a lot leaner, but you still have the power. You still have You don't You don't lose the the powerful features of Git. They are still at your hand. It's just much simpler to use them.
So, let's see how merge conflicts are handled.
I can create a new change based on this change ID U here by writing jj new U minus M and I can say I have potential conflicts in there in the description.
So, if I run jj again, you can see now two changes branched out from this original U change. One is this empty one and the other one is where I created this file. So, um if I edit Y again and I write jj diff, I can see that I have here this some file with some changes. So, let's go and edit this new change and uh sorry, this other one jj edit K.
And now, if I run jj, I'm now at this K change and I want to do a conflicting content. So, I open some file and I say this will create conflicts.
Now, if I run jj again, it's still all fine because they are independent changes. They don't merge.
But now, I want to merge them.
And to merge them in jj, you just create a new change with multiple parents. So, run jj new and then I want to have the K and the Y change. So, so those two here and I give a a description. This will result in conflicts because I know there will be conflicts.
And you can see indeed that jj rightfully detected this. So, if I run jj again, you can see here there's a conflict.
Now, in Git, you would need to resolve that conflict or you abandon that merge.
These are the two options.
And uh in many cases you're not in the mood to resolve the conflict or it's a bigger thing and you want to take your time uh and in the meantime maybe do other changes. In JJ, this is absolutely no problem. So, for example, let's say we want to do more changes to this uh K change here. We can just say JJ edit K and now if I run JJ again, I can just do whatever modifications I want and that conflict here remains. I don't have to deal with it right away. I can deal with it later.
So, that is very nice.
So, let's deal with that change here.
So, I write JJ edit S and you can see the moment I check it out, the moment I edit it, you can see here this uh warning that uh some file has a two-sided conflict. So, I can uh open this file and you can see those conflict markers. Very similar to how they are in Git. Um maybe a bit different but uh yeah.
So, let's just keep this one and get rid of everything else and write it and if I run JJ again, that conflict is resolved.
So, I don't have to deal with it anymore.
In JJ, you can also easily view some diffs. So, you can just write JJ diff and you can say from let's say U to S. So, it goes from this change to this change and of course uh the only effective change is that we have this new file with this added new line.
All right. Now, let's say we want to merge it back into this remote Git repository because that's what we want to do in the end. We have colleagues who work with Git. We have the central Git remote repository and we have to collaborate. There's no way we can convince everybody to quickly switch to JJ. Not going to happen. So, how do we map this JJ world back to the Git world, you know, the world of branches, etc. So, there are no branches in JJ, but there are bookmarks. So, you can mark certain changes with a bookmark, and you can map that bookmark to a Git branch in a remote repository. So, how does it work?
So, you can run JJ bookmark create or just BC for short.
And if you run that, you also need to provide a name. So, you can say um my change.
And now you have this new bookmark called my change. If you run JJ again, the change ID stays the same, but you have here this bookmark. Very similar to this bookmark, uh which you had when you cloned this repository. You can easily say that this master bookmark mapped to the master branch in the remote Git repository.
So, now how do you push a bookmark resulting in a Git branch in the remote repository? You just write JJ Git push bookmark my underscore change.
And now you can create a pull request in your remote GitHub repository.
So, I want to show another concept, and that is bubbling up changes. So, I reverted now this repository, so I have here my uh master change and then a new empty one.
And uh in the first change, I can just uh edit some file one.
This is file one.
And now I can create a new change on top. And there's another way to do that, and that is commit. So, commit is the same as setting a description and then creating JJ new. It's just a way of doing things, so I can just say commit a new change.
If I run JJ again, you can see that the previous change now has this description and the new one was created. So, that's a bit similar to Git, I I would say.
But, you can also use describe. So, JJ describe another change.
You can also modify descriptions all the time, so it's not a problem.
Now, in this other change, I want to create some file two.
This is file two, and now I want to show you something.
So, if I now edit this change here, this T change, I can have another change in there. So, I can say JJ edit T.
I go to some file one, and I can uh with modifications, I can modify it.
Now, if I run JJ, everything's fine.
I edit now this other one, and you can see if I open some file one, it also includes these modifications.
So, the changes I did in this change here, they get automatically bubbled up to all the descendants of this change, which is a nice feature.
There are also some other commands which are quite helpful in JJ, for example, squashing. So, since there's no staging area, you usually have this new change where you which acts as your working copy, if you will, and you can do some changes to some files.
A cool change.
And if you want to squash it down to your golden change, if you will, the one you eventually want to create a bookmark and push to Git, you can just say JJ squash. JJ squash.
And now that change will be part of this change here down the the stream or up the stream in in case of hierarchies.
So, if you added it now and you look at the file, you can see that a cool change is part of it.
And the other one was now empty because you squashed the changes uh to this change here and empty descriptionless changes are garbage collected, so they don't uh clutter your um changes list.
So far, I've only used this JJ CLI tool, which is in itself already great, but uh in the Git world, um you might know lazy Git, which is a great graphical user interface to do all kinds of Git modifications. It's a time saver and many Git operations are much easier in lazy Git than doing it with the command line. So, there's also lazy JJ, but the user interface for JJ I settled on is called JJUI, and it looks very much like the JJ output uh bare bones, but you can navigate the tree, for example, and if you want to change the description, you hit enter, and uh you change it and then you press uh option enter to modify it. And yeah, it's also much easier to navigate and to create new changes. So, if I want to create a new change based on this one, I press N, and I have it. And I can now provide a description, a very cool description.
And uh yeah, it's much easier to navigate, and you don't have to repeat all those beginnings of identifiers all the time. It's just more graphical.
And if you press escape, you can see all kinds of different commands. And uh yeah, look at it Looking at them, you can uh also see a few features which I haven't talked about yet. Uh one of the very nice features is the operations log or op log, which you can access by pressing O. So, let's try that. If I press O, you can see all the previous operations. So, each operation in JJ is stored in this operations log, and you can undo and redo operations or jump to a specific one. So, if I want to go to this one where I described a certain commit, I press R to restore, and now now I'm in there. And you can see that um the previous state has been restored.
So, in Git, where sometimes you do something and something happens and you didn't actually want that, and then it's super hard to jump back, well, in JJ, it's a no-brainer. You can just uh write undo. I can also do it with a command line interface. So, I write JJ undo, and it just undos operations and brings you to the previous state. Okay?
So, now um yeah, you have the very cool description again. So, everything is undoable and redoable, which is great.
And as always, this is not everything from JJ. There are tons of features, but please keep in mind that there are very simple mechanisms in place, and you do not need to know all of them. You can already achieve quite a lot with the things I told you because they nicely compose, and all the footguns of Git are just not existent in JJ.
And you are quite powerful with those simple primitives you now learned.
Nevertheless, please check out the tutorial of JJ, and also look in the more advanced features, also more into the collaboration with Git.
And please, in the comments, uh tell me your favorite feature of JJ, what made you move to JJ.
As always, thanks for watching and stay tuned.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











