What using Jujutsu taught me about using Git
Introduction
Git is the de facto tool of choice for all of software engineering these days. I’ve grown up in an era knowing nothing else. Its arcane commands, and mythical existence have been intertwined with the art of programming ever since picked up the craft back in 20231. It’s gotten to the point where jobs ask you specifically if you are “Familiar with Git and version control.” Which to me is a bit like trying to hire a mechanic and asking if they are familiar with wrenches.
However, all it took was one timely YouTube video and I found myself diving down a rabbit hole of all the reasons why Git may not be the perfect tool that I thought it was.
The signs were always there. An arcane command line interface, commands that exist but you never here about, and the fact that it turns into a screaming toddler anytime there is a merge conflict. Turns out, public perception (at least amongst programming nerds) tends to sway away from Git a lot of times.
All this led me to try the new DVCS on the block. Jujutsu.
Jujutsu is a modern VCS that is fully git compatible and provides a more refined set of options for controlling your code versions. It’s main selling point is the way that it lets you freely manipulate any changes that you may have made in the past, as well as the way that it treats conflicts as ‘first class citizens’ i.e. not exploding any time you encounter one, rather, giving you sophisticated tools to deal with them.
Using it started of as a learning curve. Once you are used to the way that Git works, it’s hard to imagine a different way of doing things in the world. But I will say that once you stick with it for a solid month, it’s a really solid alternative to the way that things are done.
The main differnce that i’ve found with using Jujutsu has been the mental model that it makes you adopt. Up until now, I would always work the following way.
- Write code
- Decide the scope of the changes that I’ve introduced
- Commit the changes.
But using Jujutsu has made me instead the adopt the following:
- Decide the scope of the changes that I will introduce
- Write code
- Commit the changes.
A pretty small distinction but something that has been a real game changer in terms of the way that I plan out my work, and by extension my thoughts. Jujutsu makes you consider each commit carefully. More so than Git, you are explicitly invited to go and describe the changes that you are going to make. It makes you care about the history of your changes.
Writing ‘reviewable’ code
My main takeaway from using JJ, is to write code that can actually be reviewed. Code where there is a discernible progress of logic. Each change should be a part of a story.
Your commit history should show how changes evolved, and which changes depend on the last. They should also help you pin-point where the last time a functionality/service was changed so that you can actually go back and look at it when something breaks.
I found myself taking a lot more time either before I pushed my changes, or as I was writing them to plan out what the patch would look like. How it should be read by the reviewers, and how I can ‘tell a story’ with my commit messages regarding the reasoning for each change.
My key takeaway distilled down has been:
Don’t be so eager to push changes to a remote. Spend some time on your local machine organising your thoughts. Lay out the changes in a manner that makes sense for a reader.
I now spend a lot more time considering my changes. Thinking about my approach and ensuring that when I push, I won’t have to make egregious structural/algorithmic changes to the way that I do things.
I don’t need to show reviewers the messy way in which my mind works, they don’t need to see a convoluted list of “Fix x”’s and “Fix y”’x. They care about seeing a series of changes that make sense to them. Patches that are well described and considered. They would much rather believe that I got history correct ‘first’ try.
Jujutsu trivialises this process, letting you easily play around with your history, and clean up that slew of changes that you don’t need to commit for everything.
Build features on each other
One other thing that I learned to do better, was to open stacked PR’s. Essentially, changes that merge into other changes. Laid out in a logical manner, such that you can give the reviewer smaller delta’s to look at, with more context on how and why you are choosing to make a given change.
With the Jujutsu system of bookmarks it was very easy to see which commits can be opened up as a stack of changes. It also ensures that I’m very rarely asking for reviews on changes that are more than 1000 lines, which is an arbitrary number that I think any human can keep in their head. Any delta over 1000 lines gets unwieldy and annoying to read.
Another neat emergent feature from this way of doing things is that I’m never blocked by code review on changes that I may have done in the past. I can just branch of a feature branch with a new feature, and when the feature below gets any change requests, I just rebase the feature on top to match the new changes.
This is possible with git rebase --interactive, however the mechanism for doing so is quite clunky in my opinion. Especially so when you run into any sort of merge conflict. JJ willingness to let conflicts just exist in your work tree is such a game changer.
Actually editing diff markers
These days, every single git tool comes with the 3 options. Use HEAD, use branchname and use both. A very nifty feature built into most git GUI/TUI clients. However, something that obfuscates the way that git actually handles conflict markers. I had seen diff markers in the past, however, you always feel hesitant about modifying them directly. This is something that I learned to not fear when using Jujutsu.
Admittedly, the reason that this came to be is because I couldn’t exactly figure out how to properly merge in changes, and JJ’s default behaviour with resolving Window’s file endings is a bit opaque. But I was left with gigantic diffs coming from 3 different directions. The built in jj resolve can’t handle them. And my editor’s normal git diff highlighting was being of no help.
In this dire moment, JJ holds your hand and tells you. Don’t worry, jj undo has got you. No matter where you might end up, you can always go back.
Editing diff markers personally makes you more cognisant of the changes a patch introduces. In doing so, you better evaluate the changes that you have made, and when doing complex merges, it lets you pick and choose the pieces you want more precisely than just selecting hunks.
Whilst I still prefer the git client abstractions for selecting hunks most times. I now know how to deal with things when the situation is dire and I’m juggling some 3 sided monster of a diff.
Back to Git after all this?
I’ve had a blast using JJ. I enjoy the tool, the way it frees up my ability to switch between work. And the way it lets me scratch my ‘clean main branch’ itch. However, for work I’ve gone back to using Git the old fashioned way.
The reason for this is because whilst JJ by itself is a very powerful, the wider world of software tools isn’t built to support it.
The main gripe that my colleagues would throw at me was the fact that anytime I would open up a PR, since I would just action their changes in the commit where it made sense, I would end up with a bunch of jayant_godse force pushed to... in my pull requests that GitHub doesn’t do the best job rendering.
The Git history being immutable is something that a lot of code review tools are designed around. (I know that things like Gerrit and Sapling exist, but I haven’t had the pleasure of trying them yet.) This means you are expected to add commits on top of your changes instead of going back and pretending you knew what you were doing all along.
I personally see no benefit from littering branches with tons of meaningless commits that explain very little. I suppose that since Git was designed for a time where programmers weren’t pushing to the cloud so often, they would spend a lot more time on their patches making the commit history look pretty before they submitted things to Linus.
So whilst I’ll be sticking to Git for my professional future, I still have my eye on JJ, still use it in hobby projects, and will be awaiting the day when I can convince some other group of unwitting engineers to use another groundbreaking tool written in Rust.
I will admit part of the reason I found Jujutsu appealing is because the command line tool is invoked with
jj.
And it’s written in Rust (…btw).