Tips
To fork and push for a PR:
jj git remote add fork https://github.com/veqqq/sqlite3
jj bookmark track master --remote=fork
jj git fetch --remote fork # idk why it always wants this...
jj git push --remote fork
To modify a commit a few ago (e.g. made some typo) without modifying later commits:
jj log # find change id
jj edit id
# make your change
jj edit id # of current head to go back
j git push --named <branch-name>=<rev>creates a new bookmark pointing to rev, to avoid default<change-id>names.
Ceate your own cache/staging area out of an empty change. A classic approach is to create two empty changes in a row, start editing files in the child change, and then when you’re finished with part of a change, you can squash it into the parent change, treating that as the staging area. When you’re happy with the contents of the staging area, you can give it a description, and then create a new change after that to be your next staging area. … using
jj squashinstead ofgit add
basically every time I use
jj editto make “just a few changes,” I completely lose track of what I changed and need to track down the original version. it’s definitely almost always worth it to do a separate change and then squash
My personal “killer” use-case is as follows: I maintain a tree of branches that depend on each other, and will become PRs. (Maybe there are two independent branches A and B on top of the current trunk, and then C that has both A and B as parents, and then D on top of C.) I want to change a commit in one of the lower branches, say A. How do I do this?
With
git rebase -iit has become recently possible using--update-refs, but this is advanced and tricky and not quite natural (you have to pretend to rebase D on top of trunk, when you really want to rebase A and “just update the rest”). In jj this is very natural, just start working on whatever commit in A you want to update.
How to Learn
- https://jj-for-everyone.github.io/
- https://maddie.wtf/posts/2025-07-21-jujutsu-for-busy-devs
- https://steveklabnik.github.io/jujutsu-tutorial/
Why
Since 2012 I have been the git expert for my team at jobs: able to untangle its bad UI, complicated merges, conflicts, and errors. My month in jujutsu has me more confident than my 18 years in git. It’s the most impressive developer tooling I’ve picked up in ages, and I’m mostly impressed because it feels friendly, safe, and reliable. … I guess I’m also saying that I have 35 years with the sharp knives school of tool design and I’m more than ready to stop getting hurt by my tools and blamed for it. I don’t know this history well, but I have gotten the impression that the field of safety engineering was driven strongly by aviation safety, which had the crucial insight that if an accident is blamed on user error the planes will keep crashing; if you approach an accident with the idea that you don’t need to find a single root cause but take every reasonable intervention available to prevent problems you can achieve incredible things. To bring it back to code, why is it trivial in almost all web frameworks to output invalid HTML? - pushcx
knowledge of how to work one jj command transfers to other ones really well.
I mean, look at the
git logdocs.. How the hell is anyone supposed to make sense of this? And these behaviors are different for every command. Note in particular the explosion of individual flags governing display options, as well as the combination of commit-range syntax (sorry, “special notation”) and even more individual flags to further filter on the commit set. Little of this transfers to other git commands.Compare that to the
jj logdocs. You could print it on a single page and still have blank space.git log’s junk drawer of complexity is almost totally replaced by three DSLs: one for specifying sets of revisions, one for specifying sets of files, and one for templating output. And those three DSLs get used pervasively across jj. You learn them, then you can use them everywhere. Way simpler, way more compositional, way more intuitive. - oneirine
Weird branch names!
colleague pushes branches like dudename-hashasletters
with PR/MR-based workflows, the branch name is largely a less-informative version of the PR’s subject line … less useful than commit msgs in the log, and now they’re one less thing to pay attention to.
But here’s a PR commit script to maintain meaningful branch names.