From: Martin von Zweigbergk <martinvonz@gmail.com>
To: Elijah Newren <newren@gmail.com>
Cc: Johannes Schindelin <Johannes.Schindelin@gmx.de>,
git <git@vger.kernel.org>
Subject: Re: Replaying merges
Date: Sat, 18 May 2024 10:50:02 -0700 [thread overview]
Message-ID: <CANiSa6hU6r-7K_GAyNtO4-_VUHBDfByd6ws3VdZEj4KKrSmryg@mail.gmail.com> (raw)
In-Reply-To: <CANiSa6gyNpJ3cUNLD1hFnBYeDFm6aFYv8k41MGvX+C90G8oaaw@mail.gmail.com>
Now HTML-free
On Sat, May 18, 2024 at 9:33 AM Martin von Zweigbergk
<martinvonz@gmail.com> wrote:
>
>
>
> On Fri, May 17, 2024, 18:45 Elijah Newren <newren@gmail.com> wrote:
>>
>> Hi Johannes!
>>
>> On Fri, May 17, 2024 at 5:35 PM Johannes Schindelin
>> <Johannes.Schindelin@gmx.de> wrote:
>> >
>> > Hi Elijah,
>> >
>> > I took the suggestion to heart that you explained a couple of times to me:
>> > To replay merge commits (including their merge conflict resolutions) by
>> > using the _remerged_ commit as merge base, the original merge commit as
>> > merge head, and the newly-created merge (with conflicts and all) as HEAD.
>> >
>> > I noodled on this idea a bit until I got it into a usable shape that I
>> > applied to great effect when working on the recent embargoed releases.
>> >
>> > Here it is, the script [*1*] that I used (basically replacing all the
>> > `merge -C` instances in the rebase script with `replay-merge.sh`):
>> >
>> <snip>
>> > For the most part, this worked beautifully.
>>
>> Cool to see someone try it out.
>>
>> > However. The devil lies in the detail.
>>
>> Yup, but details rather than detail. ;-)
>>
>> <snip>
>> > The biggest complication being the scenario... when a merge
>> > conflict had been addressed in the original merge commit, but in the
>> > replayed merge there is no conflict. In such a scenario, this script _will
>> > create not one, but two merge conflicts, nested ones_!
>>
>> Only if merge.conflictStyle="diff3"; if merge.conflictStyle="merge",
>> then there will be no nested conflict (since the nested conflict comes
>> from the fact that the base version had a conflict itself).
>>
>> This is one of the issues I noted in my write up a couple years ago:
>> https://github.com/newren/git/blob/replay/replay-design-notes.txt#L315-L316
>>
>> Further, it can get worse, since in the current code the inner
>> conflict from the base merge could be an already arbitrarily nested
>> merge conflict with N levels (due to recursive merging allowing
>> arbitrary nested of merge conflicts), giving us an overall nesting of
>> N+1 merge conflicts rather than just the 2 you assumed. That's ugly
>> enough, but we also need to worry about ensuring the conflict markers
>> from different merges get different conflict marker lengths, which
>> presents an extra challenge since the outer merge here is not part of
>> the original recursive merge.
>>
>> In addition to these challenges, there's some other ones:
>> * What about when the remerged commit and the newly-created merge
>> have the "same" conflict. Does it actually look the "same" to the
>> diff machinery so that it can resolve the conflict away to how the
>> original merge resolved? (Answer: not with a naive merge of these
>> three commits; we need to do some extra tweaking. I'm actually
>> suprised you said this basic idea worked given this particular
>> problem.)
>> * What about conflicts with binary files? Or non-textual conflicts
>> of other types like modify/delete or rename/rename?
>>
>> > I still do think that your idea has merit, but I fear that it won't ever
>> > be as easy as performing multiple three-way merges in succession.
>>
>> I totally agree we need to do more than the simple merge of those
>> three "commits"; I have ideas for this that address some of the
>> challenges over at
>> https://github.com/newren/git/blob/replay/replay-design-notes.txt#L264-L341
>
>
> Another approach is to not eagerly evaluate the auto-merged parent tree and instead do some algebra on the trees. For example, if the parents of the merge commit is calculated by merging tree B and tree C with tree A as base, then you can consider the result as tree B+C-A. If the merge commit itself has tree D and you're rebasing it onto tree E, then the result is E+(D-(B+C-A)). You can then evaluate that by recursively merging the trees as usual. This is effectively what jj does and it works very well.
>
> I don't think Git has support for merging more than 3 trees (or tree entries, etc.) at once yet, but that's not very hard. Here's how jj does it: https://github.com/martinvonz/jj/blob/main/lib%2Fsrc%2Fmerge.rs. I think the tests at the end there are quite easy to read and they explain well how it works.
>
>
>
>
prev parent reply other threads:[~2024-05-18 17:50 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-18 0:35 Replaying merges Johannes Schindelin
2024-05-18 1:45 ` Elijah Newren
[not found] ` <CANiSa6gyNpJ3cUNLD1hFnBYeDFm6aFYv8k41MGvX+C90G8oaaw@mail.gmail.com>
2024-05-18 17:50 ` Martin von Zweigbergk [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CANiSa6hU6r-7K_GAyNtO4-_VUHBDfByd6ws3VdZEj4KKrSmryg@mail.gmail.com \
--to=martinvonz@gmail.com \
--cc=Johannes.Schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=newren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).