Jujutsu megamerges for fun and profit

73 points by knl


edwintorok

Megamerges are one of the reasons why I use Jujutsu. But the main reason is the ability to defer solving conflicts.

With 'git rebase' you either have to solve a conflict immediately or abandon the rebase (and I'm always worried I solved them wrong and there won't be a history). With 'jj' if I find myself I side a complicated conflict resolution I can look at the history and choose to edit some ancestors to maybe eliminate the conflict in the first place (especially for add/add conflicts if I just add my variables or functions in different places that can sometimes avoid the conflict; refactoring code on both sides can also help). And there is always the 'jj' operation log that can track how a commit evolved over rebases/merges/splits (unfortunately only local, and can't be pushed to a remote for backup).

This improved conflict handling then makes it very easy to use the mega merge workflow. It can also be used as a "test merge" of all your pending PRs, to try to keep them conflict free as much as possible, especially as they evolve during a review.

altano

Megamerges being easy to manage is awesome.

The section on absorbing changes is technically correct but the jj absorb isn't good enough in practice to work from a megamerge and then absorb your changes. If you try to work like this you'll be disappointed. I can't explain exactly what it does wrong but it at least (a) chooses target commits poorly and (b) it even puts you in a conflict state sometimes. I've used mercurial/sapling absorb thousands of times with no surprise behavior ever but jj just has something wrong with the algorithm. Or my mental model needs adjusting but I don't understand how.

jade_

I read the trunk() mention and I wanted to say you can alias that away: set a revset alias of T = trunk(). Then you can jj rebase -d T. The uppercase avoids conflicting with change ids.

valpackett

It’s important to remember that you don’t push the megamerge

You absolutely can though! It's not the most common use case, but for something like an operating system fork where you have a whole bunch of patchsets you want to have "active" at the same time it's totally appropriate.


Very neat stack/stage aliases, I've been mostly doing this kind of thing visually in jjui but I'll take them for a spin next time! For restack I've had a rebaseall = ["rebase", "--source", "all:roots(trunk()..@)", "--onto", "trunk()"]… hm, I really don't remember what's with the all: haha.

I've also had this one addparent = ["rebase", "--source", "@", "--onto", "all:@-", "--onto"] for only manually adding a parent to the octomerge, but I haven't really been using it since discovering jjui.

restrictedchoice

Can someone help me understand how a megamerge workflow is better than working on stacked commits in a linear graph? I suppose if the streams of work are not dependent, a megamerge allows you to work on them simultaneously with less risk of merge conflicts. Is that basically it?