{
  "path": "/jujutsu-megamerges-for-fun-and-profit",
  "site": "at://did:plc:zviscnpwyvj6y32agi5davn5/site.standard.publication/3me4txfib342s",
  "tags": [
    "jujutsu",
    "megamerge",
    "git",
    "workflows"
  ],
  "$type": "site.standard.document",
  "title": "Jujutsu megamerges for fun and profit",
  "bskyPostRef": {
    "uri": "at://did:plc:zviscnpwyvj6y32agi5davn5/app.bsky.feed.post/3mjxfw4kvuc2f"
  },
  "description": "A practical guide to creating and maintaining powerful megamerge workflows in\nJujutsu for faster, conflict-free development.\n",
  "publishedAt": "2026-04-20T00:00:00.000Z",
  "textContent": "_This article is written both for intermediate Jujutsu users and for Git users\nwho are curious about Jujutsu._\n\nI'm a big [Jujutsu] user, and I've found myself relying more and more on\nwhat we in the JJ community colloquially call the \"megamerge\" workflow for my\ndaily development. It's surprisingly under-discussed outside of a handful of\npower users, so I wanted to share what that looks like and why it's so handy,\nespecially if you're in a complex dev environment or tend to ship lots of small\nPRs.\n\n[jujutsu]: https://jj-vcs.github.io/jj/latest\n\n_In a hurry? Skip to the end for some quick tips._\n\nMerge commits aren't what you think they are\n\nIf you're an average Git user (or even a Jujutsu user who hasn't dug too deep\ninto more advanced workflows), you may be surprised to learn that there is\nabsolutely nothing special about a merge commit. It's not some special case\nthat has its own rules. It's just a normal commit that has multiple parents. It\ndoesn't even have to be empty![^1]\n\n<figure>\n\n  \n\n  <figcaption>Gotta put it all together!</figcaption>\n</figure>\n\n[^1]:\n\tIn Git, merge commits that contain new changes outside of conflict\n\tresolution are called an \"evil merge\". Evil merges [aren't really \"evil\" in\n\tJujutsu] since it has a more consistent model than Git.[^2]\n\n\t<figure>\n\n    \n    \n\t  <figcaption>Bubble, bubble, toil and trouble.</figcaption>\n\t</figure>\n\n\tDefinitely tangential, but I felt it necessary to mention.\n\n[aren't really \"evil\" in Jujutsu]: https://jj-vcs.github.io/jj/latest/conflicts/#advantages\n\n[^2]:\n\tThanks to [Andrew Hoog] for helping me figure out footnotes in Astro. Did\n\tyou know that you can reference footnotes from other footnotes?\n\n[Andrew Hoog]: https://www.andrewhoog.com/posts/how-to-annotate-blog-posts-with-footnotes-in-astro\n\nYou may be even more surprised to learn that merge commits are not limited\nto having two parents. We unofficially call merge commits with three or more\nparents \"octopus merges\", and while you may be thinking to yourself \"in what\nworld would I want to merge more than two branches?\", this is actually a really\npowerful idea. Octopus merges power the entire megamerge workflow!\n\nSo what the hell is a megamerge?\n\nBasically, in the megamerge workflow you are rarely working directly off the\ntips of your branches. Instead, you create an octopus merge commit (hereafter\nreferred to as \"the megamerge\") as the child of every working branch you care\nabout. This means bugfixes, feature branches, branches you're waiting on PRs\nfor, other peoples' branches you need your code to work with, local environment\nsetup branches, even private commits that may not be or belong in any branch.\n_Everything_ you care about goes in the megamerge. It's important to remember\nthat you don't push the megamerge, only the branches it composes.\n\n<figure>\n\n  \n  \n  <figcaption>Scary! Too much merge!</figcaption>\n</figure>\n\nIt's okay if this sounds like a lot. After all, you know how much effort you\nput into switching contexts if you have to revisit an old PR to get it reviewed,\namong other things. However, this enables a few really valuable things for you:\n\n1. You are always working on the combined sum of all of your work. This\n   means that if your working copy compiles and runs without issue, you know\n   that your work will all interact without issue.\n2. You rarely have to worry about merge conflicts. You already don't need to\n   worry about merge conflicts a ton since conflicts are a first-class concept\n   in Jujutsu, but since you're literally always merging your changes together\n   you'll never be struck with surprise merge conflicts on the forge side.\n   There might be the occasional issue with contributors' changes, but in my\n   experience this hasn't been a major problem.\n3. There's way less friction when switching between tasks. Since you're\n   always working on top of the megamerge, you never need to go to your VCS to\n   switch tasks. You can just go edit what you need to. This also means it's way\n   easier to make small PRs for drive-by refactors and bugfixes.\n4. It's easier to keep your branches up to date. With a little magic, you\n   can keep your entire megamerge up to date with your trunk branch with a\n   single rebase command. I'll show you how to do that later on.\n\nHow do I make one?\n\nStarting a megamerge is super simple: just make a new commit with each branch\nyou want in the megamerge as a parent. I like to give that commit a name and\nleave it empty, like so:\n\n<figure>\n  <script src=\"https://asciinema.org/a/791783.js\" id=\"asciicast-791783\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Making megamerges. It's not so hard after all!\n  </figcaption>\n</figure>\n\nYou're then left with an empty commit on top of this whole thing. This is where\nyou do your work! Anything above the megamerge commit is considered WIP. You're\nfree to split things out as you need to, make multiple branches based on that\nmegamerge commit, whatever you want to do. Everything you write will be based on\nthe sum of everything within the megamerge, just like we wanted!\n\nOf course, at some point you'll be happy with what you have, and you'll be left\nwondering:\n\nHow do I actually submit my changes?\n\nHow you get your WIP changes into your megamerge depends on where they need to\nland. If you're making changes that should land in existing changes, you can\nuse the squash command with the --to flag to shuffle them into the right\ndownstream commits. If your commit contains multiple commits' worth of changes,\nyou can either split it out into multiple commits before squashing them or\n(what I prefer) interactively squash with squash --interactive to just pick\nout the specific pieces to move.\n\n<figure>\n  <script src=\"https://asciinema.org/a/791782.js\" id=\"asciicast-791782\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Hunk, I choose you!\n  </figcaption>\n</figure>\n\nOf course, Jujutsu is a beautiful piece of software and has some automation for\nthis! The absorb command will do a lot of this for you by identifying which\ndownstream mutable commit each line or hunk of your current commit belong in and\nautomatically squashing them down for you. This feels like magic every time\nI use it (and not the evil black box black magic kind of magic where nothing can\nbe understood), and it's one of the core pieces of Jujutsu's functionality that\nmake the megamerge workflow so seamless.\n\n<figure>\n  <script src=\"https://asciinema.org/a/xYC1SQupOHOH2Y7i.js\" id=\"asciicast-xYC1SQupOHOH2Y7i\" async=\"true\"></script>    \n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Ope, that was fast.\n  </figcaption>\n</figure>\n\nAbsorbing won't always catch everything in your commit, but it'll usually get at\nleast 90% of your changes. The rest are either easily squashable downstream or\nunrelated to any previous commit.\n\nConveniently, things aren't much more complicated if I have changes that belong\nin a new commit. If the commit belongs in one of the branches I'm working on, I\ncan just rebase it myself and move the bookmark accordingly.\n\nLet's break that rebase down to better understand how it works:\n\n<figure>\n  <script src=\"https://asciinema.org/a/954708.js\" id=\"asciicast-954708\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    A little bit of rocket surgery, as a treat.\n  </figcaption>\n</figure>\n\nIf I've started work on an entirely new feature or found an unrelated bug to\nfix, then it's even simpler! Using a few aliases, I can super easily include new\nchanges in my megamerge:[^3]\n\n[^3]:\n\tAliases are a super powerful part of Jujutsu. There are two types you should\n  look into: [revset aliases], which allow you to create custom functions which\n  return one or more commits with the [revset language], and [command aliases],\n  which let you extend Jujutsu's default functionality and add your own.\n\n  There are also template aliases which let you change how Jujutsu logs to\n  the terminal using the [templating language], and fileset aliases, which act\n  similarly to revset aliases but act on files instead of revisions using the\n  [fileset language].\n\n[revset aliases]: https://jj-vcs.github.io/jj/latest/revsets/#aliases\n[revset language]: https://jj-vcs.github.io/jj/latest/revsets\n[command aliases]: https://jj-vcs.github.io/jj/latest/config/#aliases\n[templating language]: https://docs.jj-vcs.dev/latest/templates\n[fileset language]: https://docs.jj-vcs.dev/latest/filesets\n\nHere's a quick explanation of what closest_merge(to) is actually doing:\n\nUsing that revset alias, stack lets us target any revset we want and insert it\nbetween trunk() (your main development branch) and our megamerge commit:\n\n<figure>\n  <script src=\"https://asciinema.org/a/954709.js\" id=\"asciicast-954709\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Whoa, that was neat!\n  </figcaption>\n</figure>\n\nThis is more useful if I have _multiple_ stacks of changes I want to include in\nparallel; if it's just one, I have another alias that just gets the entire stack\nof changes after the megamerge:\n\nThis one doesn't require any input! Just have your commits ready and stage 'em:\n\n<figure>\n  <script src=\"https://asciinema.org/a/954710.js\" id=\"asciicast-954710\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Wait, what? You can do that?\n  </figcaption>\n</figure>\n\nThe last missing piece of this megamerge puzzle is (unfortunately) dealing with\nthe reality that is _other people_:\n\nHow do I keep all this crap up to date?\n\nThat's a great question, and one I spent a couple months trying to answer in\na general sense. Jujutsu has a really easy way of rebasing your entire working\ntree onto your main branch:\n\n<figure>\n  <script src=\"https://asciinema.org/a/954717.js\" id=\"asciicast-954717\" async=\"true\"></script>\n\n  <figcaption class=\"-mt-12 pt-3 w-full bg-bg-1 relative\">\n    Nice.\n  </figcaption>\n</figure>\n\nHowever, this o",
  "canonicalUrl": "https://isaaccorbrey.com/notes/jujutsu-megamerges-for-fun-and-profit"
}