Jujutsu megamerges for fun and profit

This article explores the 'megamerge' workflow in the Jujutsu (jj) version control system, explaining how octopus merges can streamline development by combining multiple active branches into a single working state.
This article is written both for intermediate Jujutsu users and for Git users who are curious about Jujutsu.
I’m a big Jujutsu user, and I’ve found myself relying more and more on what we in the JJ community colloquially call the “megamerge” workflow for my daily development. It’s surprisingly under-discussed outside of a handful of power users, so I wanted to share what that looks like and why it’s so handy, especially if you’re in a complex dev environment or tend to ship lots of small PRs.
Merge commits aren’t what you think they are
If you’re an average Git user (or even a Jujutsu user who hasn’t dug too deep into more advanced workflows), you may be surprised to learn that there is absolutely nothing special about a merge commit. It’s not some special case that has its own rules. It’s just a normal commit that has multiple parents. It doesn’t even have to be empty!
You may be even more surprised to learn that merge commits are not limited to having two parents. We unofficially call merge commits with three or more parents “octopus merges”, and while you may be thinking to yourself “in what world would I want to merge more than two branches?”, this is actually a really powerful idea. Octopus merges power the entire megamerge workflow!
So what the hell is a megamerge?
Basically, in the megamerge workflow you are rarely working directly off the tips of your branches. Instead, you create an octopus merge commit (hereafter referred to as “the megamerge”) as the child of every working branch you care about. This means bugfixes, feature branches, branches you’re waiting on PRs for, other peoples’ branches you need your code to work with, local environment setup branches, even private commits that may not be or belong in any branch. Everything you care about goes in the megamerge. It’s important to remember that you don’t push the megamerge, only the branches it composes.
It’s okay if this sounds like a lot. However, this enables a few really valuable things for you:
- You are always working on the combined sum of all of your work. This means that if your working copy compiles and runs without issue, you know that your work will all interact without issue.
- You rarely have to worry about merge conflicts. Since you’re literally always merging your changes together you’ll never be struck with surprise merge conflicts on the forge side.
- There’s way less friction when switching between tasks. Since you’re always working on top of the megamerge, you never need to go to your VCS to switch tasks. You can just go edit what you need to.
- It’s easier to keep your branches up to date. With a little magic, you can keep your entire megamerge up to date with your trunk branch with a single rebase command.
How do I make one?
Starting a megamerge is super simple: just make a new commit with each branch you want in the megamerge as a parent. I like to give that commit a name and leave it empty, like so:
jj new x y z
jj commit --message "megamerge"
You’re then left with an empty commit on top of this whole thing. This is where you do your work!
How do I actually submit my changes?
How you get your WIP changes into your megamerge depends on where they need to land. If you’re making changes that should land in existing changes, you can use the squash command with the --to flag to shuffle them into the right downstream commits.
# Squash an entire WIP commit
jj squash --to x
Of course, Jujutsu has some automation for this! The absorb command will do a lot of this for you by identifying which downstream mutable commit each line or hunk of your current commit belong in and automatically squashing them down for you. This feels like magic every time I use it.
# Automagically autosquash your changes
jj absorb
Automation with Aliases
You can use revset-aliases to change how Jujutsu logs to the terminal and automate the insertion of new changes into your megamerge. By defining aliases like stack or stage, you can target any revset and insert it between your trunk branch and your megamerge commit seamlessly.
Source: Hacker News














