Subject: Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)

From: Sergey Organov <[email-masked]>
Date: Wed, 07 Mar 2018 17:34:07 +0300

Johannes Schindelin <[email-masked]> writes:

> Hi Sergey,
>
> On Wed, 7 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <[email-masked]> writes:
>> 
>> > On Tue, 6 Mar 2018, Phillip Wood wrote:
>> >
>> >> On 03/03/18 00:29, Igor Djordjevic wrote:
>> >> > 
>> >> > On 02/03/2018 12:31, Phillip Wood wrote:
>> >> >>
>> >> >>> Thinking about it overnight, I now suspect that original proposal
>> >> >>> had a mistake in the final merge step. I think that what you did is
>> >> >>> a way to fix it, and I want to try to figure what exactly was wrong
>> >> >>> in the original proposal and to find simpler way of doing it right.
>> >> >>>
>> >> >>> The likely solution is to use original UM as a merge-base for final
>> >> >>> 3-way merge of U1' and U2', but I'm not sure yet. Sounds pretty
>> >> >>> natural though, as that's exactly UM from which both U1' and U2'
>> >> >>> have diverged due to rebasing and other history editing.
>> >> >>
>> >> >> Hi Sergey, I've been following this discussion from the sidelines,
>> >> >> though I haven't had time to study all the posts in this thread in
>> >> >> detail. I wonder if it would be helpful to think of rebasing a merge
>> >> >> as merging the changes in the parents due to the rebase back into the
>> >> >> original merge. So for a merge M with parents A B C that are rebased
>> >> >> to A' B' C' the rebased merge M' would be constructed by (ignoring
>> >> >> shell quoting issues)
>> >> >>
>> >> >> git checkout --detach M
>> >> >> git merge-recursive A -- M A'
>> >> >> tree=$(git write-tree)
>> >> >> git merge-recursive B -- $tree B'
>> >> >> tree=$(git write-tree)
>> >> >> git merge-recursive C -- $tree C'
>> >> >> tree=$(git write-tree)
>> >> >> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB' -pC')
>> >> >>
>> >> >> This should pull in all the changes from the parents while preserving
>> >> >> any evil conflict resolution in the original merge. It superficially
>> >> >> reminds me of incremental merging [1] but it's so long since I looked at
>> >> >> that I'm not sure if there are any significant similarities.
>> >> >>
>> >> >> [1] https://github.com/mhagger/git-imerge
>> >> > 
>> >> > Interesting, from quick test[3], this seems to produce the same 
>> >> > result as that other test I previously provided[2], where temporary 
>> >> > commits U1' and U2' are finally merged with original M as a base :)
>> >> > 
>> >> > Just that this looks like even more straight-forward approach...?
>> >> > 
>> >> > The only thing I wonder of here is how would we check if the 
>> >> > "rebased" merge M' was "clean", or should we stop for user amendment? 
>> >> > With that other approach Sergey described, we have U1'==U2' to test with.
>> >> 
>> >> I think (though I haven't rigorously proved to myself) that in the
>> >> absence of conflicts this scheme has well defined semantics (the merges
>> >> can be commuted), so the result should be predicable from the users
>> >> point of view so maybe it could just offer an option to stop.
>> >
>> > I am not so sure that the result is independent of the order of the
>> > merges. In other words, I am not necessarily certain that it is impossible
>> > to concoct A,A',B,B' commits where merging B'/B before A'/A has a
>> > different result than merging A'/A before B'/B.
>> >
>> > Remember, when constructing counter-examples to hypotheses, those
>> > counter-examples do not really *have* to make sense on their own. For
>> > example, A' could introduce *completely different* changes from A, and the
>> > same is true for B' and B.
>> >
>> > I could imagine, for example, that using a ton of consecutive empty lines,
>> > and using patches that insert something into these empty lines (and are
>> > thusly inherently ambiguous when said set of empty lines has changed),
>> > could even introduce a merge conflict in one order, but no conflict in the
>> > other.
>> >
>> > Even so, I think that merging in the order of the parents makes the most
>> > sense, and that using that strategy makes sense, too, because you really
>> > have to try hard to make it fail.
>> 
>> Alternatively, consider to adopt the original approach that has none of
>> these issues as it uses exactly the same method for rebasing merge
>> commits that you are already using for rebasing simple commits, not to
>> mention the advantage of the built-in consistency check.
>
> Surely I misunderstand?
>
> How can your approach -- which relies *very much* on having the original
> parent commits -- not *require* that consistency check?

I don't understand what you mean, sorry. Could you please point me
to the *require* you talk about in the original proposal?

> What would your approach (that still has no satisfyingly trivial
> explanation, in my mind)

Here is one-liner: rebase sides of the merge commit and then 3-way
merge them, using original merge commit as merge base.

> do if somebody edited a `merge` command and let it merge a completely
> unrelated commit?

Don't see a problem, sorry. The method should still work, provided you have
original merge commit and two new parents for the new merge.

You rebase sides of original merge to the new parents, then 3-way merge
the results using original merge as base.

Once again, if you can rebase simple commit, the method allows to rebase
the merge either.

-- Sergey