{
  "path": "/logues/newlogue-newlogue",
  "site": "https://logue.is/newlogue-newlogue",
  "$type": "site.standard.document",
  "title": "Jujutsu megamerges for fun and profit",
  "content": {
    "$type": "is.logue.content",
    "items": [
      {
        "$type": "is.logue.block.text",
        "plaintext": "Published on April 20, 202613 min read"
      },
      {
        "$type": "is.logue.block.text",
        "facets": [
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 108,
              "byteStart": 0
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#italic"
              }
            ]
          }
        ],
        "plaintext": "This article is written both for intermediate Jujutsu users and for Git users who are curious about Jujutsu."
      },
      {
        "$type": "is.logue.block.text",
        "facets": [
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 20,
              "byteStart": 13
            },
            "features": [
              {
                "uri": "https://jj-vcs.github.io/jj/latest",
                "$type": "is.logue.richtext.facet#link"
              }
            ]
          }
        ],
        "plaintext": "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."
      },
      {
        "$type": "is.logue.block.text",
        "facets": [
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 13,
              "byteStart": 0
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#italic"
              }
            ]
          },
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 28,
              "byteStart": 13
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#italic"
              },
              {
                "uri": "https://isaaccorbrey.com/notes/jujutsu-megamerges-for-fun-and-profit#tldr",
                "$type": "is.logue.richtext.facet#link"
              }
            ]
          },
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 50,
              "byteStart": 28
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#italic"
              }
            ]
          }
        ],
        "plaintext": "In a hurry? Skip to the end for some quick tips."
      },
      {
        "$type": "is.logue.block.heading",
        "level": 2,
        "plaintext": "Merge commits aren’t what you think they are"
      },
      {
        "$type": "is.logue.block.text",
        "facets": [
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 350,
              "byteStart": 349
            },
            "features": [
              {
                "uri": "https://isaaccorbrey.com/notes/jujutsu-megamerges-for-fun-and-profit#user-content-fn-1",
                "$type": "is.logue.richtext.facet#link"
              }
            ]
          }
        ],
        "plaintext": "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!1"
      },
      {
        "$type": "is.logue.block.code",
        "language": "ansi",
        "plaintext": "@  myzpxsys Isaac Corbrey 12 seconds ago 634e82e2\n│  (empty) (no description set)\n○    mllmtkmv Isaac Corbrey 12 seconds ago git_head() 947a52fd\n├─╮  (empty) Merge the things\n│ ○  vqsqmtlu Isaac Corbrey 12 seconds ago f41c796e\n│ │  deps: Pin quantum manifold resolver\n○ │  tqqymrkn Isaac Corbrey 19 seconds ago 0426baba\n├─╯  storage: Align transient cache manifolds\n◆  zzzzzzzz root() 00000000"
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": "Gotta put it all together!"
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": "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!"
      },
      {
        "$type": "is.logue.block.heading",
        "level": 2,
        "plaintext": "So what the hell is a megamerge?"
      },
      {
        "$type": "is.logue.block.text",
        "facets": [
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 487,
              "byteStart": 477
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#italic"
              }
            ]
          },
          {
            "$type": "is.logue.richtext.facet",
            "index": {
              "$type": "is.logue.richtext.facet#byteSlice",
              "byteEnd": 592,
              "byteStart": 562
            },
            "features": [
              {
                "$type": "is.logue.richtext.facet#bold"
              }
            ]
          }
        ],
        "plaintext": "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."
      },
      {
        "$type": "is.logue.block.code",
        "language": "ansi",
        "plaintext": "@  mnrxpywt Isaac Corbrey 25 seconds ago f1eb374e\n│  (empty) (no description set)\n○      wuxuwlox Isaac Corbrey 25 seconds ago git_head() c40c2d9c\n├─┬─╮  (empty) megamerge\n│ │ ○  ttnyuntn Isaac Corbrey 57 seconds ago 7d656676\n│ │ │  storage: Align transient cache manifolds\n│ ○ │  ptpvnsnx Isaac Corbrey 25 seconds ago 897d21c7\n│ │ │  parser: Deobfuscate fleem tokens\n│ ○ │  zwpzvxmv Isaac Corbrey 37 seconds ago 14971267\n│ │ │  infra: Refactor blob allocator\n│ ○ │  tqxoxrwq Isaac Corbrey 57 seconds ago 90bf43e4\n│ ├─╯  io: Unjam polarity valves\n○ │  moslkvzr Isaac Corbrey 50 seconds ago 753ef2e7\n│ │  deps: Pin quantum manifold resolver\n○ │  qupprxtz Isaac Corbrey 57 seconds ago 5332c1fd\n├─╯  ui: Defrobnicate layout heuristics\n○  wwtmlyss Isaac Corbrey 57 seconds ago 5804d1fd\n│  test: Add hyperfrobnication suite\n◆  zzzzzzzz root() 00000000"
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": "Scary! Too much merge!"
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": "It’s okay if this sounds like a lot. After all, you know how much effort you put into switching contexts if you have to revisit an old PR to get it reviewed, among other things. However, this enables a few really valuable things for you:"
      },
      {
        "$type": "is.logue.block.orderedList",
        "content": [
          {
            "$type": "is.logue.block.listItem",
            "content": [
              {
                "$type": "is.logue.block.text",
                "facets": [
                  {
                    "$type": "is.logue.richtext.facet",
                    "index": {
                      "$type": "is.logue.richtext.facet#byteSlice",
                      "byteEnd": 63,
                      "byteStart": 0
                    },
                    "features": [
                      {
                        "$type": "is.logue.richtext.facet#bold"
                      }
                    ]
                  }
                ],
                "plaintext": "You are always working on the combined sum of all of your work."
              },
              {
                "$type": "is.logue.block.text",
                "plaintext": " This means that if your working copy compiles and runs without issue, you know that your work will all interact without issue."
              }
            ]
          },
          {
            "$type": "is.logue.block.listItem",
            "content": [
              {
                "$type": "is.logue.block.text",
                "facets": [
                  {
                    "$type": "is.logue.richtext.facet",
                    "index": {
                      "$type": "is.logue.richtext.facet#byteSlice",
                      "byteEnd": 47,
                      "byteStart": 0
                    },
                    "features": [
                      {
                        "$type": "is.logue.richtext.facet#bold"
                      }
                    ]
                  }
                ],
                "plaintext": "You rarely have to worry about merge conflicts."
              },
              {
                "$type": "is.logue.block.text",
                "plaintext": " You already don’t need to worry about merge conflicts a ton since conflicts are a first-class concept in Jujutsu, but since you’re literally always merging your changes together you’ll never be struck with surprise merge conflicts on the forge side. There might be the occasional issue with contributors’ changes, but in my experience this hasn’t been a major problem."
              }
            ]
          },
          {
            "$type": "is.logue.block.listItem",
            "content": [
              {
                "$type": "is.logue.block.text",
                "facets": [
                  {
                    "$type": "is.logue.richtext.facet",
                    "index": {
                      "$type": "is.logue.richtext.facet#byteSlice",
                      "byteEnd": 57,
                      "byteStart": 0
                    },
                    "features": [
                      {
                        "$type": "is.logue.richtext.facet#bold"
                      }
                    ]
                  }
                ],
                "plaintext": "There’s way less friction when switching between tasks."
              },
              {
                "$type": "is.logue.block.text",
                "plaintext": " 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. This also means it’s way easier to make small PRs for drive-by refactors and bugfixes."
              }
            ]
          },
          {
            "$type": "is.logue.block.listItem",
            "content": [
              {
                "$type": "is.logue.block.text",
                "facets": [
                  {
                    "$type": "is.logue.richtext.facet",
                    "index": {
                      "$type": "is.logue.richtext.facet#byteSlice",
                      "byteEnd": 47,
                      "byteStart": 0
                    },
                    "features": [
                      {
                        "$type": "is.logue.richtext.facet#bold"
                      }
                    ]
                  }
                ],
                "plaintext": "It’s easier to keep your branches up to date."
              },
              {
                "$type": "is.logue.block.text",
                "plaintext": " With a little magic, you can keep your entire megamerge up to date with your trunk branch with a single rebase command. I’ll show you how to do that later on."
              }
            ]
          }
        ]
      },
      {
        "$type": "is.logue.block.heading",
        "level": 2,
        "plaintext": "How do I make one?"
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": "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:"
      },
      {
        "$type": "is.logue.block.code",
        "language": "sh",
        "plaintext": "jj new x y z\njj commit --message \"megamerge\""
      },
      {
        "$type": "is.logue.block.text",
        "plaintext": ""
      }
    ]
  },
  "publishedAt": "2026-04-20T23:59:34.986Z",
  "textContent": "Published on April 20, 202613 min read\n*This article is written both for intermediate Jujutsu users and for Git users who are curious about Jujutsu.*\nI’m a big [Jujutsu](https://jj-vcs.github.io/jj/latest) 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.\n*In a hurry? *[*Skip to the end*](https://isaaccorbrey.com/notes/jujutsu-megamerges-for-fun-and-profit#tldr)* for some quick tips.*\n## Merge commits aren’t what you think they are\nIf 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![1](https://isaaccorbrey.com/notes/jujutsu-megamerges-for-fun-and-profit#user-content-fn-1)\n```ansi\n@  myzpxsys Isaac Corbrey 12 seconds ago 634e82e2\n│  (empty) (no description set)\n○    mllmtkmv Isaac Corbrey 12 seconds ago git_head() 947a52fd\n├─╮  (empty) Merge the things\n│ ○  vqsqmtlu Isaac Corbrey 12 seconds ago f41c796e\n│ │  deps: Pin quantum manifold resolver\n○ │  tqqymrkn Isaac Corbrey 19 seconds ago 0426baba\n├─╯  storage: Align transient cache manifolds\n◆  zzzzzzzz root() 00000000\n```\nGotta put it all together!\nYou 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!\n## So what the hell is a megamerge?\nBasically, 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.\n```ansi\n@  mnrxpywt Isaac Corbrey 25 seconds ago f1eb374e\n│  (empty) (no description set)\n○      wuxuwlox Isaac Corbrey 25 seconds ago git_head() c40c2d9c\n├─┬─╮  (empty) megamerge\n│ │ ○  ttnyuntn Isaac Corbrey 57 seconds ago 7d656676\n│ │ │  storage: Align transient cache manifolds\n│ ○ │  ptpvnsnx Isaac Corbrey 25 seconds ago 897d21c7\n│ │ │  parser: Deobfuscate fleem tokens\n│ ○ │  zwpzvxmv Isaac Corbrey 37 seconds ago 14971267\n│ │ │  infra: Refactor blob allocator\n│ ○ │  tqxoxrwq Isaac Corbrey 57 seconds ago 90bf43e4\n│ ├─╯  io: Unjam polarity valves\n○ │  moslkvzr Isaac Corbrey 50 seconds ago 753ef2e7\n│ │  deps: Pin quantum manifold resolver\n○ │  qupprxtz Isaac Corbrey 57 seconds ago 5332c1fd\n├─╯  ui: Defrobnicate layout heuristics\n○  wwtmlyss Isaac Corbrey 57 seconds ago 5804d1fd\n│  test: Add hyperfrobnication suite\n◆  zzzzzzzz root() 00000000\n```\nScary! Too much merge!\nIt’s okay if this sounds like a lot. After all, you know how much effort you put into switching contexts if you have to revisit an old PR to get it reviewed, among other things. However, this enables a few really valuable things for you:\n1. **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.\n2. **You rarely have to worry about merge conflicts.** You already don’t need to worry about merge conflicts a ton since conflicts are a first-class concept in Jujutsu, but since you’re literally always merging your changes together you’ll never be struck with surprise merge conflicts on the forge side. There might be the occasional issue with contributors’ changes, but in my experience this hasn’t been a major problem.\n3. **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. This also means it’s way 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 can keep your entire megamerge up to date with your trunk branch with a single rebase command. I’ll show you how to do that later on.\n## How do I make one?\nStarting 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:\n```sh\njj new x y z\njj commit --message \"megamerge\"\n```\n"
}