git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
@ 2018-02-16 13:08 Sergey Organov
  2018-02-18  4:16 ` Jacob Keller
                   ` (2 more replies)
  0 siblings, 3 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-16 13:08 UTC (permalink / raw)
  To: git; +Cc: Johannes Sixt, Junio C Hamano, Jacob Keller, Johannes Schindelin

Hi,

By accepting the challenges raised in recent discussion of advanced
support for history rebasing and editing in Git, I hopefully figured out
a clean and elegant method of rebasing merges that I think is "The Right
Way (TM)" to perform this so far troublesome operation. ["(TM)" here has
second meaning: a "Trivial Merge (TM)", see below.]

Let me begin by outlining the method in git terms, and special thanks
here must go to "Johannes Sixt" <j6t@kdbg.org> for his original bright
idea to use "cherry-pick -m1" to rebase merge commits.

End of preface -- here we go.

Given 2 original branches, b1 and b2, and a merge commit M that joins
them, suppose we've already rebased b1 to b1', and b2 to b2'. Suppose
also that B1' and B2' happen to be the tip commits on b1' and b2',
respectively.

To produce merge commit M' that joins b1' and b2', the following
operations will suffice:

1. Checkout b2' and cherry-pick -m2 M, to produce U2' (and new b2').
2. Checkout b1' and cherry-pick -m1 M, to produce U1' (and new b1').
3. Merge --no-ff new b2' to current new b1', to produce UM'.
4. Get rid of U1' and U2' by re-writing parent references of UM' from
   U1' and U2' to  B1' and B2', respectively, to produce M'.
5. Mission complete.

Let's now see why and how the method actually works.

Firs off, let me introduce you to my new friend, the Trivial Merge, or
(TM) for short. By definition, (TM) is a merge that introduces
absolutely no differences to the sides of the merge. (I also like to
sometimes call him "Angel Merge", both as the most beautiful of all
merges, and as direct antithesis to "evil merge".)

One very nice thing about (TM) is that to safely rebase it, it suffices
to merge its (rebased) parents. It is safe in this case, as (TM) itself
doesn't posses any content changes, and thus none could be missed by
replacing it with another merge commit.

I bet most of us have never seen (TM) in practice though, so let's see
how (TM) can help us handle general case of some random merge. What I'm
going to do is to create a virtual (TM) and see how it goes from there.

Let's start with this history:

  M
 / \
B1  B2

And let's transform it to the following one, contextually equivalent to
the original, by introducing 2 simple utility commits U1 and U2, and a
new utility merge commit UM:

  UM
 /  \
U1   U2
|    |
B1   B2

Here content of any of the created UM, U1, and U2 is the same, and is
exact copy of original content of M. I.e., provided [A] denotes
"content of commit A", we have:

[UM] = [U1] = [U2] = [M]

Stress again how these changes to the history preserve the exact content
of the original merge ([UM] = [M]), and how U1 an U2 represent content
changes due to merge on either side[*], and how neither preceding nor
subsequent commits content would be affected by the change of
representation.

Now observe that as [U1] = [UM], and [U2] = [UM], the UM happens to be
exactly our new friend -- the "Trivial Merge (TM)" his true self,
introducing zero changes to content.

Next we rebase our new representation of the history and we get:

  UM'
 /  \
U1'  U2'
|    |
B1'  B2'

Here UM' is bare merge of U1' and U2', in exact accordance with the
method of rebasing a (TM) we've already discussed above, and U1' and U2'
are rebased versions of U1 and U2, obtained by usual rebasing methods
for non-merge commits.

(Note, however, that at this point UM' is not necessarily a (TM)
anymore, so in real implementation it may make sense to check if UM' is
not a (TM) and stop for possible user amendment.)

Finally, to get to our required merge commit M', we get the content of
UM' and record two actual parents of the merge:

  M'
 / \
B1' B2'

Where [M'] = [UM'].

That's it. Mission complete.

I expect the method to have the following nice features:

- it carefully preserves user changes by rebasing the merge commit
itself, in a way that is semantically similar to rebasing simple
(non-merge) commits, yet it allows changes made to branches during
history editing to propagate over corresponding merge commit that joins
the branches, even automatically when the changes don't conflict, as
expected.

- it has provision for detection of even slightest chances of ending up
with surprising merge (just check if UM' is still (TM)), so that
implementation could stop for user inspection and amendment when
appropriate, yet it is capable of handling trivial cases smoothly and
automatically.

- it never falls back to simple invocation of merge operation on rebased
original branches themselves, thus avoiding the problem of lack of
knowledge of how the merge at hand has been performed in the first
place. It doesn't prevent implementation from letting user to manually
perform whatever merge she wishes when suspect result is automatically
detected though.

- it extends trivially to octopus merges.

- it appears shiny to the point that it will likely be able to handle
even darkest evil merges nicely, no special treatment required.

Footnote:

[*] We may as well consider the (UM,U1,U2) trio to be semantically split
representation of git merge commit, where U1 and U2 represent content
changes to the sides, and UM represents pure history joint. Or, the
other way around, we may consider git merge commit to be optimized
representation of this trio. I think this split representation could
help to simplify reasoning about git merges in general.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-16 13:08 [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Sergey Organov
@ 2018-02-18  4:16 ` Jacob Keller
  2018-02-19  5:28   ` Sergey Organov
  2018-02-19 23:44 ` Igor Djordjevic
  2018-03-06 13:26 ` [RFC v2] " Sergey Organov
  2 siblings, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-02-18  4:16 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Git mailing list, Johannes Sixt, Junio C Hamano,
	Johannes Schindelin

On Fri, Feb 16, 2018 at 5:08 AM, Sergey Organov <sorganov@gmail.com> wrote:
> Hi,
>
> By accepting the challenges raised in recent discussion of advanced
> support for history rebasing and editing in Git, I hopefully figured out
> a clean and elegant method of rebasing merges that I think is "The Right
> Way (TM)" to perform this so far troublesome operation. ["(TM)" here has
> second meaning: a "Trivial Merge (TM)", see below.]
>
> Let me begin by outlining the method in git terms, and special thanks
> here must go to "Johannes Sixt" <j6t@kdbg.org> for his original bright
> idea to use "cherry-pick -m1" to rebase merge commits.
>
> End of preface -- here we go.
>

I hope to take a more detailed look at this, also possibly with some
attempts at re-creating the process by hand to see it in practice.

> Given 2 original branches, b1 and b2, and a merge commit M that joins
> them, suppose we've already rebased b1 to b1', and b2 to b2'. Suppose
> also that B1' and B2' happen to be the tip commits on b1' and b2',
> respectively.
>
> To produce merge commit M' that joins b1' and b2', the following
> operations will suffice:
>
> 1. Checkout b2' and cherry-pick -m2 M, to produce U2' (and new b2').
> 2. Checkout b1' and cherry-pick -m1 M, to produce U1' (and new b1').
> 3. Merge --no-ff new b2' to current new b1', to produce UM'.
> 4. Get rid of U1' and U2' by re-writing parent references of UM' from
>    U1' and U2' to  B1' and B2', respectively, to produce M'.
> 5. Mission complete.
>

Seems pretty straight forward, go to each branch and cherry-pick the
merge respective to its relative parent, and then finally re-merge
everything, and consume the intermittent commits.

> Let's now see why and how the method actually works.
>
> Firs off, let me introduce you to my new friend, the Trivial Merge, or
> (TM) for short. By definition, (TM) is a merge that introduces
> absolutely no differences to the sides of the merge. (I also like to
> sometimes call him "Angel Merge", both as the most beautiful of all
> merges, and as direct antithesis to "evil merge".)
>
> One very nice thing about (TM) is that to safely rebase it, it suffices
> to merge its (rebased) parents. It is safe in this case, as (TM) itself
> doesn't posses any content changes, and thus none could be missed by
> replacing it with another merge commit.
>
> I bet most of us have never seen (TM) in practice though, so let's see
> how (TM) can help us handle general case of some random merge. What I'm
> going to do is to create a virtual (TM) and see how it goes from there.
>
> Let's start with this history:
>
>   M
>  / \
> B1  B2
>
> And let's transform it to the following one, contextually equivalent to
> the original, by introducing 2 simple utility commits U1 and U2, and a
> new utility merge commit UM:
>
>   UM
>  /  \
> U1   U2
> |    |
> B1   B2
>
> Here content of any of the created UM, U1, and U2 is the same, and is
> exact copy of original content of M. I.e., provided [A] denotes
> "content of commit A", we have:
>
> [UM] = [U1] = [U2] = [M]
>
> Stress again how these changes to the history preserve the exact content
> of the original merge ([UM] = [M]), and how U1 an U2 represent content
> changes due to merge on either side[*], and how neither preceding nor
> subsequent commits content would be affected by the change of
> representation.
>
> Now observe that as [U1] = [UM], and [U2] = [UM], the UM happens to be
> exactly our new friend -- the "Trivial Merge (TM)" his true self,
> introducing zero changes to content.
>
> Next we rebase our new representation of the history and we get:
>
>   UM'
>  /  \
> U1'  U2'
> |    |
> B1'  B2'
>
> Here UM' is bare merge of U1' and U2', in exact accordance with the
> method of rebasing a (TM) we've already discussed above, and U1' and U2'
> are rebased versions of U1 and U2, obtained by usual rebasing methods
> for non-merge commits.
>
> (Note, however, that at this point UM' is not necessarily a (TM)
> anymore, so in real implementation it may make sense to check if UM' is
> not a (TM) and stop for possible user amendment.)
>

This might be a bit tricky for a user to understand what the process
is, especially if they don't understand how it's creating special U1'
and U2' commits. However, it *is* the cleanest method I've either seen
or thought of for presenting the conflict to the user.

> Finally, to get to our required merge commit M', we get the content of
> UM' and record two actual parents of the merge:
>
>   M'
>  / \
> B1' B2'
>
> Where [M'] = [UM'].
>
> That's it. Mission complete.
>
> I expect the method to have the following nice features:
>
> - it carefully preserves user changes by rebasing the merge commit
> itself, in a way that is semantically similar to rebasing simple
> (non-merge) commits, yet it allows changes made to branches during
> history editing to propagate over corresponding merge commit that joins
> the branches, even automatically when the changes don't conflict, as
> expected.
>

Right.

> - it has provision for detection of even slightest chances of ending up
> with surprising merge (just check if UM' is still (TM)), so that
> implementation could stop for user inspection and amendment when
> appropriate, yet it is capable of handling trivial cases smoothly and
> automatically.

Nice!

>
> - it never falls back to simple invocation of merge operation on rebased
> original branches themselves, thus avoiding the problem of lack of
> knowledge of how the merge at hand has been performed in the first
> place. It doesn't prevent implementation from letting user to manually
> perform whatever merge she wishes when suspect result is automatically
> detected though.
>

Right, since we're re-creating the intermittent commits U1' and U2'
first based on the original merge, that's how we manage to maintain
the result of the merge. I like it.

> - it extends trivially to octopus merges.
>
> - it appears shiny to the point that it will likely be able to handle
> even darkest evil merges nicely, no special treatment required.
>

Yep, and I like that it has a pretty reasonable way of presenting
conflicts for resolution. It may be a bit tricky to explain the use of
the intermittent commits U1' and U2' though.

> Footnote:
>
> [*] We may as well consider the (UM,U1,U2) trio to be semantically split
> representation of git merge commit, where U1 and U2 represent content
> changes to the sides, and UM represents pure history joint. Or, the
> other way around, we may consider git merge commit to be optimized
> representation of this trio. I think this split representation could
> help to simplify reasoning about git merges in general.

Yes, I think this concept is pretty useful. I think it could be useful
as a way of showing how the merge worked.

Thanks,
Jake

>
> -- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-18  4:16 ` Jacob Keller
@ 2018-02-19  5:28   ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-19  5:28 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Git mailing list, Johannes Sixt, Junio C Hamano,
	Johannes Schindelin

Hi Jake,

Jacob Keller <jacob.keller@gmail.com> writes:
> On Fri, Feb 16, 2018 at 5:08 AM, Sergey Organov <sorganov@gmail.com> wrote:
>> Hi,
>>
>> By accepting the challenges raised in recent discussion of advanced
>> support for history rebasing and editing in Git, I hopefully figured out
>> a clean and elegant method of rebasing merges that I think is "The Right
>> Way (TM)" to perform this so far troublesome operation. ["(TM)" here has
>> second meaning: a "Trivial Merge (TM)", see below.]
>>
>> Let me begin by outlining the method in git terms, and special thanks
>> here must go to "Johannes Sixt" <j6t@kdbg.org> for his original bright
>> idea to use "cherry-pick -m1" to rebase merge commits.
>>
>> End of preface -- here we go.
>>
>
> I hope to take a more detailed look at this, also possibly with some
> attempts at re-creating the process by hand to see it in practice.

Thank you for your interest and for the review, and yes, some testing is
what the idea desperately needs. Unfortunately I don't have much time
for it right now, nor am I fluent enough in git internals to actually
make even a raw prototype really soon, sorry. Anybody who is interested
is very welcome to volunteer!

[...]
>
> This might be a bit tricky for a user to understand what the process
> is, especially if they don't understand how it's creating special U1'
> and U2' commits. However, it *is* the cleanest method I've either seen
> or thought of for presenting the conflict to the user.
>
[...]
>> - it appears shiny to the point that it will likely be able to handle
>> even darkest evil merges nicely, no special treatment required.
>>
>
> Yep, and I like that it has a pretty reasonable way of presenting
> conflicts for resolution. It may be a bit tricky to explain the use of
> the intermittent commits U1' and U2' though.

Yeah, I see how all this sends somewhat unique challenges to the
implementation to get user interaction in case of conflicts right, even
though all the basic concepts are old buddies and should be familiar to
the user.

That said, the recursive merge strategy comes to mind, where creating
virtual merge base may itself cause conflicts, so something similar
enough is likely to already exist.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-16 13:08 [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Sergey Organov
  2018-02-18  4:16 ` Jacob Keller
@ 2018-02-19 23:44 ` Igor Djordjevic
  2018-02-20 12:42   ` Sergey Organov
  2018-02-27  0:07   ` Johannes Schindelin
  2018-03-06 13:26 ` [RFC v2] " Sergey Organov
  2 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-19 23:44 UTC (permalink / raw)
  To: Sergey Organov, git
  Cc: Johannes Sixt, Junio C Hamano, Jacob Keller, Johannes Schindelin

Hi Sergey,

On 16/02/2018 14:08, Sergey Organov wrote:
> 
> By accepting the challenges raised in recent discussion of advanced
> support for history rebasing and editing in Git, I hopefully figured out
> a clean and elegant method of rebasing merges that I think is "The Right
> Way (TM)" to perform this so far troublesome operation. ["(TM)" here has
> second meaning: a "Trivial Merge (TM)", see below.]
> 
> Let me begin by outlining the method in git terms, and special thanks
> here must go to "Johannes Sixt" <j6t@kdbg.org> for his original bright
> idea to use "cherry-pick -m1" to rebase merge commits.
> 
> End of preface -- here we go.
> 
> Given 2 original branches, b1 and b2, and a merge commit M that joins
> them, suppose we've already rebased b1 to b1', and b2 to b2'. Suppose
> also that B1' and B2' happen to be the tip commits on b1' and b2',
> respectively.
> 
> To produce merge commit M' that joins b1' and b2', the following
> operations will suffice:
> 
> 1. Checkout b2' and cherry-pick -m2 M, to produce U2' (and new b2').
> 2. Checkout b1' and cherry-pick -m1 M, to produce U1' (and new b1').
> 3. Merge --no-ff new b2' to current new b1', to produce UM'.
> 4. Get rid of U1' and U2' by re-writing parent references of UM' from
>    U1' and U2' to  B1' and B2', respectively, to produce M'.
> 5. Mission complete.
> 
> Let's now see why and how the method actually works.
> 
> Firs off, let me introduce you to my new friend, the Trivial Merge, or
> (TM) for short. By definition, (TM) is a merge that introduces
> absolutely no differences to the sides of the merge. (I also like to
> sometimes call him "Angel Merge", both as the most beautiful of all
> merges, and as direct antithesis to "evil merge".)
> 
> One very nice thing about (TM) is that to safely rebase it, it suffices
> to merge its (rebased) parents. It is safe in this case, as (TM) itself
> doesn't posses any content changes, and thus none could be missed by
> replacing it with another merge commit.
> 
> I bet most of us have never seen (TM) in practice though, so let's see
> how (TM) can help us handle general case of some random merge. What I'm
> going to do is to create a virtual (TM) and see how it goes from there.
> 
> Let's start with this history:
> 
>   M
>  / \
> B1  B2
> 
> And let's transform it to the following one, contextually equivalent to
> the original, by introducing 2 simple utility commits U1 and U2, and a
> new utility merge commit UM:
> 
>   UM
>  /  \
> U1   U2
> |    |
> B1   B2
> 
> Here content of any of the created UM, U1, and U2 is the same, and is
> exact copy of original content of M. I.e., provided [A] denotes
> "content of commit A", we have:
> 
> [UM] = [U1] = [U2] = [M]
> 
> Stress again how these changes to the history preserve the exact content
> of the original merge ([UM] = [M]), and how U1 an U2 represent content
> changes due to merge on either side[*], and how neither preceding nor
> subsequent commits content would be affected by the change of
> representation.
> 
> Now observe that as [U1] = [UM], and [U2] = [UM], the UM happens to be
> exactly our new friend -- the "Trivial Merge (TM)" his true self,
> introducing zero changes to content.
> 
> Next we rebase our new representation of the history and we get:
> 
>   UM'
>  /  \
> U1'  U2'
> |    |
> B1'  B2'
> 
> Here UM' is bare merge of U1' and U2', in exact accordance with the
> method of rebasing a (TM) we've already discussed above, and U1' and U2'
> are rebased versions of U1 and U2, obtained by usual rebasing methods
> for non-merge commits.
> 
> (Note, however, that at this point UM' is not necessarily a (TM)
> anymore, so in real implementation it may make sense to check if UM' is
> not a (TM) and stop for possible user amendment.)
> 
> Finally, to get to our required merge commit M', we get the content of
> UM' and record two actual parents of the merge:
> 
>   M'
>  / \
> B1' B2'
> 
> Where [M'] = [UM'].
> 
> That's it. Mission complete.
> 
> I expect the method to have the following nice features:
> 
> - it carefully preserves user changes by rebasing the merge commit
> itself, in a way that is semantically similar to rebasing simple
> (non-merge) commits, yet it allows changes made to branches during
> history editing to propagate over corresponding merge commit that joins
> the branches, even automatically when the changes don't conflict, as
> expected.
> 
> - it has provision for detection of even slightest chances of ending up
> with surprising merge (just check if UM' is still (TM)), so that
> implementation could stop for user inspection and amendment when
> appropriate, yet it is capable of handling trivial cases smoothly and
> automatically.
> 
> - it never falls back to simple invocation of merge operation on rebased
> original branches themselves, thus avoiding the problem of lack of
> knowledge of how the merge at hand has been performed in the first
> place. It doesn't prevent implementation from letting user to manually
> perform whatever merge she wishes when suspect result is automatically
> detected though.
> 
> - it extends trivially to octopus merges.
> 
> - it appears shiny to the point that it will likely be able to handle
> even darkest evil merges nicely, no special treatment required.
> 
> Footnote:
> 
> [*] We may as well consider the (UM,U1,U2) trio to be semantically split
> representation of git merge commit, where U1 and U2 represent content
> changes to the sides, and UM represents pure history joint. Or, the
> other way around, we may consider git merge commit to be optimized
> representation of this trio. I think this split representation could
> help to simplify reasoning about git merges in general.

I`m far from a Git expert, but merely a bit advanced user, so please 
take my opinion with a grain of salt.

That said, I`m really interested in this topic, which seems to (try 
to) address the only "bad feeling" I had with rebasing merges - being 
afraid of silently losing amendments by actually trying to "replay" 
the merge (where additional and possibly important context is 
missing), instead of really "rebasing" it (somehow).

Even though this behavior is known and documented, it still left some 
to be desired.

Might be I`m missing something, but so far I like how described 
approach just "feels right" (to me, for now), being really simple, 
yet robust :)

As my humble contribution to the cause and discussion itself, I`m 
providing possibly naive, yet simple demonstration script[1], and 
clearly showing where current `git rebase --preserve-merges` is 
lacking (in my opinion, even though being expected and documented), 
and how your proposal seems to improve the situation here.

Thanks for your thoughts, and hoping to see this going somewhere :)

Regards, Buga

[1] Demonstration script:
--- 8< ---
#!/bin/sh

# rm -rf ./.git
# rm -f ./test.txt

git init

touch ./test.txt
git add -- test.txt

for i in {1..20}
do
	echo A$i >>test.txt
	git commit -am "A$i"
done

git checkout -b b1
sed -i '6iB1' test.txt
git commit -am "B1"

git checkout -b b2 HEAD^
sed -i '16iB2' test.txt
git commit -am "B2"

git checkout -b merge b1
git merge --no-commit b2
sed -i '12iX' test.txt # amend merge commit
git commit -am "M"
git tag original-merge

git checkout master
for i in {1..5}
do
	j=`expr "$i" + 20`
	sed -i "${i}iA${j}" test.txt
	git commit -am "A$j"
done

# (1) current merge rebasing logic,
# not preserving merge commit manual amendment, as documented
git rebase --preserve-merges master merge
git tag preserve-merges

# (2) simple/naive demonstration of proposed merge rebasing logic
# using described "Trivial Merge" (TM, or "Angel Merge"),
# preserving merge commit manual amendment :)
git checkout b1
git rebase master
git cherry-pick -m1 original-merge

git checkout b2
git rebase master
git cherry-pick -m2 original-merge

git branch -f merge b1
git checkout merge
git merge b2 --no-commit
git commit -a --reuse-message original-merge
git tag angel-merge

git reset --hard b1^
git read-tree --reset angel-merge
git update-ref refs/heads/merge "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse b1^)" -p "$(git rev-parse b2^)")"
git tag -f angel-merge
git branch -f b1 b1^
git branch -f b2 b2^

# show resulting graph
echo
git log --all --decorate --oneline --graph

# comparison between original merge and rebased merge (1),
# showing merge commit amendment "X" being silently lost during rebase
echo
echo 'diff original-merge..preserve-merges:'
git diff original-merge..preserve-merges

# comparison between original merge and rebased merge (2),
# showing merge commit amendment "X" being preserved during rebase
# (not shown in diff)
echo
echo 'diff original-merge..angel-merge:'
git diff original-merge..angel-merge

# direct comparison between two merge rebasing approaches,
# merge commit amendment difference showing up
echo
echo 'diff preserve-merges angel-merge:'
git diff preserve-merges angel-merge

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-19 23:44 ` Igor Djordjevic
@ 2018-02-20 12:42   ` Sergey Organov
  2018-02-27  0:07   ` Johannes Schindelin
  1 sibling, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-20 12:42 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Johannes Schindelin

Hi Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>

[...]

>
> Even though this behavior is known and documented, it still left some 
> to be desired.
>
> Might be I`m missing something, but so far I like how described 
> approach just "feels right" (to me, for now), being really simple, 
> yet robust :)
>
> As my humble contribution to the cause and discussion itself, I`m 
> providing possibly naive, yet simple demonstration script[1], and 
> clearly showing where current `git rebase --preserve-merges` is 
> lacking (in my opinion, even though being expected and documented), 
> and how your proposal seems to improve the situation here.

Thanks for the test-case, -- it's nice to see this thingy working!

>
> Thanks for your thoughts, and hoping to see this going somewhere :)

So do I. Even if nobody volunteers to adopt it, I hope I'll be able to
implement something myself, not very soon though.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-19 23:44 ` Igor Djordjevic
  2018-02-20 12:42   ` Sergey Organov
@ 2018-02-27  0:07   ` Johannes Schindelin
  2018-02-27  5:01     ` Sergey Organov
                       ` (2 more replies)
  1 sibling, 3 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-02-27  0:07 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller

Hi Buga,

On Tue, 20 Feb 2018, Igor Djordjevic wrote:

> I`m really interested in this topic, which seems to (try to) address the
> only "bad feeling" I had with rebasing merges - being afraid of silently
> losing amendments by actually trying to "replay" the merge (where
> additional and possibly important context is missing), instead of really
> "rebasing" it (somehow).

If those amendments are what you are worried about, why not address them
specifically?

In other words, rather than performing the equivalent of

	git show <merge>^! | git apply

(which would of course completely ignore if the rewritten <merge>^2
dropped a patch, amended a patch, or even added a new patch), what you
really want is to figure out what changes the user made when merging, and
what merge strategy was used to begin with.

To see what I mean, look at the output of `git show 0fd90daba8`: it shows
how conflicts were resolved. By necessity, this is more complicated than a
simple diff: it is *not* as simple as taking a diff between two revisions
and applying that diff to a third revision. There were (at least) three
revisions involved in the original merge commit, and recreating that merge
commit faithfully means to represent the essence of the merge commit
faithfully enough to be able to replay it on a new set of at least three
revisions.  That can be simplified to two-way diffs only in very, very
special circumstances, and in all other cases this simplification will
simply fall on its nose.

If the proposed solution was to extend `git apply` to process combined
diffs, I would agree that we're on to something. That is not the proposed
solution, though.

In short: while I am sympathetic to the desire to keep things simple,
the idea to somehow side-step replaying the original merge seems to be
*prone* to be flawed. Any system that cannot accommodate
dropped/changed/added commits on either side of a merge is likely to be
too limited to be useful.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27  0:07   ` Johannes Schindelin
@ 2018-02-27  5:01     ` Sergey Organov
  2018-02-27  5:30     ` Jacob Keller
  2018-02-27 11:57     ` Sergey Organov
  2 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-27  5:01 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Buga,
>
> On Tue, 20 Feb 2018, Igor Djordjevic wrote:
>
>> I`m really interested in this topic, which seems to (try to) address the
>> only "bad feeling" I had with rebasing merges - being afraid of silently
>> losing amendments by actually trying to "replay" the merge (where
>> additional and possibly important context is missing), instead of really
>> "rebasing" it (somehow).

[...]

> In short: while I am sympathetic to the desire to keep things simple,
> the idea to somehow side-step replaying the original merge seems to be
> *prone* to be flawed.

The proposed (TM) solution does replay the original merge.

> Any system that cannot accommodate dropped/changed/added commits on
> either side of a merge is likely to be too limited to be useful.

I believe the proposed (TM) solution handles all that nicely. It does
accommodate dropped/changed/added commits on either side of a merge,
symmetrically, and never silently drops user modifications.

If you think (TM) is flawed, please give us a test-case.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27  0:07   ` Johannes Schindelin
  2018-02-27  5:01     ` Sergey Organov
@ 2018-02-27  5:30     ` Jacob Keller
  2018-02-27 16:21       ` Johannes Schindelin
  2018-02-27 11:57     ` Sergey Organov
  2 siblings, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-02-27  5:30 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list, Johannes Sixt,
	Junio C Hamano

On Mon, Feb 26, 2018 at 4:07 PM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> Hi Buga,
>
> On Tue, 20 Feb 2018, Igor Djordjevic wrote:
>
>> I`m really interested in this topic, which seems to (try to) address the
>> only "bad feeling" I had with rebasing merges - being afraid of silently
>> losing amendments by actually trying to "replay" the merge (where
>> additional and possibly important context is missing), instead of really
>> "rebasing" it (somehow).
>
> If those amendments are what you are worried about, why not address them
> specifically?
>
> In other words, rather than performing the equivalent of
>
>         git show <merge>^! | git apply
>
> (which would of course completely ignore if the rewritten <merge>^2
> dropped a patch, amended a patch, or even added a new patch), what you
> really want is to figure out what changes the user made when merging, and
> what merge strategy was used to begin with.
>
> To see what I mean, look at the output of `git show 0fd90daba8`: it shows
> how conflicts were resolved. By necessity, this is more complicated than a
> simple diff: it is *not* as simple as taking a diff between two revisions
> and applying that diff to a third revision. There were (at least) three
> revisions involved in the original merge commit, and recreating that merge
> commit faithfully means to represent the essence of the merge commit
> faithfully enough to be able to replay it on a new set of at least three
> revisions.  That can be simplified to two-way diffs only in very, very
> special circumstances, and in all other cases this simplification will
> simply fall on its nose.
>
> If the proposed solution was to extend `git apply` to process combined
> diffs, I would agree that we're on to something. That is not the proposed
> solution, though.
>
> In short: while I am sympathetic to the desire to keep things simple,
> the idea to somehow side-step replaying the original merge seems to be
> *prone* to be flawed. Any system that cannot accommodate
> dropped/changed/added commits on either side of a merge is likely to be
> too limited to be useful.
>


The reason Sergey's solution works is because he cherry picks the
merge using each parent first, and then merges the result of those. So
each branch of the merge gets one, and then you merge the result of
those cherry-picks. This preservers amendments and changes properly,
and should result in a good solution.

I agree that making "git apply" work with combined diffs could also be
another solution, but it may be trickier.

If this *doesn't* work, a test case showing that it doesn't work would
be appreciated. I'm hoping to be able to put together something soon,
but I haven't had time due to $dayjob.

> Ciao,
> Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27  0:07   ` Johannes Schindelin
  2018-02-27  5:01     ` Sergey Organov
  2018-02-27  5:30     ` Jacob Keller
@ 2018-02-27 11:57     ` Sergey Organov
  2018-02-27 18:14       ` Junio C Hamano
  2 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-02-27 11:57 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Buga,
>
> On Tue, 20 Feb 2018, Igor Djordjevic wrote:
>
>> I`m really interested in this topic, which seems to (try to) address the
>> only "bad feeling" I had with rebasing merges - being afraid of silently
>> losing amendments by actually trying to "replay" the merge (where
>> additional and possibly important context is missing), instead of really
>> "rebasing" it (somehow).
>
> If those amendments are what you are worried about, why not address them
> specifically?
>
> In other words, rather than performing the equivalent of
>
> 	git show <merge>^! | git apply
>
> (which would of course completely ignore if the rewritten <merge>^2
> dropped a patch, amended a patch, or even added a new patch),

You've already bit this poor thingy to death. Please rather try your
teeth on the proposed Trivial Merge (TM) method. Sorry, you will need to
actually read the proposal should you decide to.

> what you really want is to figure out what changes the user made when
> merging,

Exactly! It's all and only about changes, I'm glad you are starting to
get it.

> and what merge strategy was used to begin with.

No. It's in Git core philosophy to store only result, independent on the
way the result has been achieved. Only result matters. I could have got
merge result out of thin air, and Git won't care, and will store it for
me safely.

The proposed (TM) method will ensure it's still stored safely after
rebasing.

> To see what I mean, look at the output of `git show 0fd90daba8`: it shows
> how conflicts were resolved. By necessity, this is more complicated than a
> simple diff: it is *not* as simple as taking a diff between two revisions
> and applying that diff to a third revision. There were (at least) three
> revisions involved in the original merge commit, and recreating that merge
> commit faithfully means to represent the essence of the merge commit
> faithfully enough to be able to replay it on a new set of at least three
> revisions.

Even better: we have at least 3 source revisions and 2 new revisions to
base 3-rd new revision upon. Proposed (TM) method will use all of them.

> That can be simplified to two-way diffs only in very, very special
> circumstances, and in all other cases this simplification will simply
> fall on its nose.

No, Done Right (TM) it won't fall as N-way-diff is nothing more than N-1
simple diffs, trivially combined.

> If the proposed solution was to extend `git apply` to process combined
> diffs, I would agree that we're on to something. That is not the proposed
> solution, though.

I'm glad you finally got the point. Proposed (TM) method effectively
'git apply' combined diffs, yes. I just defined it in terms of commits
rather than diffs, as it's simpler to understand and to reason about it
that way.

>
> In short: while I am sympathetic to the desire to keep things simple,

Proposed (TM) method is complex enough to solve the problem at hand. No
need to ask for more complexity.

> the idea to somehow side-step replaying the original merge seems to be
> *prone* to be flawed.

Exactly! That's why current approach to rebase merges is flawed, and
that's why proposed (TM) method does reply entire original merges.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27  5:30     ` Jacob Keller
@ 2018-02-27 16:21       ` Johannes Schindelin
  2018-02-27 18:55         ` Igor Djordjevic
  2018-02-28  0:29         ` Jacob Keller
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-02-27 16:21 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list, Johannes Sixt,
	Junio C Hamano

Hi Jake,

On Mon, 26 Feb 2018, Jacob Keller wrote:

> On Mon, Feb 26, 2018 at 4:07 PM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
> >
> > On Tue, 20 Feb 2018, Igor Djordjevic wrote:
> >
> >> I`m really interested in this topic, which seems to (try to) address the
> >> only "bad feeling" I had with rebasing merges - being afraid of silently
> >> losing amendments by actually trying to "replay" the merge (where
> >> additional and possibly important context is missing), instead of really
> >> "rebasing" it (somehow).
> >
> > If those amendments are what you are worried about, why not address them
> > specifically?
> >
> > In other words, rather than performing the equivalent of
> >
> >         git show <merge>^! | git apply
> >
> > (which would of course completely ignore if the rewritten <merge>^2
> > dropped a patch, amended a patch, or even added a new patch), what you
> > really want is to figure out what changes the user made when merging, and
> > what merge strategy was used to begin with.
> >
> > To see what I mean, look at the output of `git show 0fd90daba8`: it shows
> > how conflicts were resolved. By necessity, this is more complicated than a
> > simple diff: it is *not* as simple as taking a diff between two revisions
> > and applying that diff to a third revision. There were (at least) three
> > revisions involved in the original merge commit, and recreating that merge
> > commit faithfully means to represent the essence of the merge commit
> > faithfully enough to be able to replay it on a new set of at least three
> > revisions.  That can be simplified to two-way diffs only in very, very
> > special circumstances, and in all other cases this simplification will
> > simply fall on its nose.
> >
> > If the proposed solution was to extend `git apply` to process combined
> > diffs, I would agree that we're on to something. That is not the proposed
> > solution, though.
> >
> > In short: while I am sympathetic to the desire to keep things simple,
> > the idea to somehow side-step replaying the original merge seems to be
> > *prone* to be flawed. Any system that cannot accommodate
> > dropped/changed/added commits on either side of a merge is likely to be
> > too limited to be useful.
> >
> 
> 
> The reason Sergey's solution works is because he cherry picks the
> merge using each parent first, and then merges the result of those. So
> each branch of the merge gets one, and then you merge the result of
> those cherry-picks. This preservers amendments and changes properly,
> and should result in a good solution.

I saw your patch trying to add a minimal example, and I really want to run
away screaming.

Do you have any way to describe the idea in a simple, 3-5 lines long
paragraph?

So far, I just know that it is some sort of confusing criss-cross
cherry-picking and merging and stuff, but nothing in those steps shouts
out to me what the *idea* is.

If it would be something like "recreate the old merge, with merge
conflicts and all, then generate the diff to the actual tree of the merge
commit, then apply that to the newly-generated merge", I would understand.

I would still suspect that -s ours would be a hard nut for that method,
but I would understand that idea.

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 11:57     ` Sergey Organov
@ 2018-02-27 18:14       ` Junio C Hamano
  2018-02-28  0:30         ` Jacob Keller
  2018-02-28  4:53         ` Sergey Organov
  0 siblings, 2 replies; 173+ messages in thread
From: Junio C Hamano @ 2018-02-27 18:14 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Johannes Schindelin, Igor Djordjevic, git, Johannes Sixt,
	Jacob Keller

Sergey Organov <sorganov@gmail.com> writes:

> You've already bit this poor thingy to death. Please rather try your
> teeth on the proposed Trivial Merge (TM) method.

Whatever you do, do *NOT* call any part of your proposal "trivial
merge", unless you are actually using the term to mean what Git
calls "trivial merge".  The phrase has an established meaning in Git
and your attempt to abuse it to mean something entirely different is
adding unnecessary hindrance for other people to understand what you
want to perform.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 16:21       ` Johannes Schindelin
@ 2018-02-27 18:55         ` Igor Djordjevic
  2018-02-27 19:59           ` Igor Djordjevic
  2018-02-28  0:29         ` Jacob Keller
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-27 18:55 UTC (permalink / raw)
  To: Johannes Schindelin, Jacob Keller
  Cc: Sergey Organov, Git mailing list, Johannes Sixt, Junio C Hamano

Hi Dscho,

On 27/02/2018 17:21, Johannes Schindelin wrote:
> 
> Do you have any way to describe the idea in a simple, 3-5 lines long 
> paragraph?
> 
> So far, I just know that it is some sort of confusing criss-cross 
> cherry-picking and merging and stuff, but nothing in those steps
> shouts out to me what the *idea* is.
> 
> If it would be something like "recreate the old merge, with merge 
> conflicts and all, then generate the diff to the actual tree of the
> merge commit, then apply that to the newly-generated merge", I would
> understand.

It would be more along the lines of "(1) rebase old merge commit parents, 
(2) generate separate diff between old merge commit and each of its 
parents, (3) apply each diff to their corresponding newly rebased 
parent respectively (as a temporary commit, one per rebased parent), 
(4) merge these temporary commits to generate 'rebased' merge commit, 
(5) drop temporary commits, recording their parents as parents of 
'rebased' merge commit (instead of dropped temporary commits)".

Implementation wise, steps (2) and (3) could also be done by simply 
copying old merge commit _snapshot_ on top of each of its parents as 
a temporary, non-merge commit, then rebasing (cherry-picking) these 
temporary commits on top of their rebased parent commits to produce 
rebased temporary commits (to be merged for generating 'rebased' 
merge commit in step (4)).

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 18:55         ` Igor Djordjevic
@ 2018-02-27 19:59           ` Igor Djordjevic
  2018-02-27 23:27             ` Johannes Schindelin
  2018-02-27 23:40             ` Igor Djordjevic
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-27 19:59 UTC (permalink / raw)
  To: Git mailing list
  Cc: Johannes Schindelin, Jacob Keller, Sergey Organov, Johannes Sixt,
	Junio C Hamano

On 27/02/2018 19:55, Igor Djordjevic wrote:
> 
> It would be more along the lines of "(1) rebase old merge commit parents, 
> (2) generate separate diff between old merge commit and each of its 
> parents, (3) apply each diff to their corresponding newly rebased 
> parent respectively (as a temporary commit, one per rebased parent), 
> (4) merge these temporary commits to generate 'rebased' merge commit, 
> (5) drop temporary commits, recording their parents as parents of 
> 'rebased' merge commit (instead of dropped temporary commits)".
> 
> Implementation wise, steps (2) and (3) could also be done by simply 
> copying old merge commit _snapshot_ on top of each of its parents as 
> a temporary, non-merge commit, then rebasing (cherry-picking) these 
> temporary commits on top of their rebased parent commits to produce 
> rebased temporary commits (to be merged for generating 'rebased' 
> merge commit in step (4)).

For those still tagging along (and still confused), here are some 
diagrams (following what Sergey originally described). Note that 
actual implementation might be even simpler, but I believe it`s a bit 
easier to understand like this, using some "temporary" commits approach.

Here`s our starting position:

(0) ---X1---o---o---o---o---o---X2 (master)
       |\
       | A1---A2---A3
       |             \
       |              M (topic)
       |             /
       \-B1---B2---B3


Now, we want to rebase merge commit M from X1 onto X2. First, rebase
merge commit parents as usual:

(1) ---X1---o---o---o---o---o---X2
       |\                       |\
       | A1---A2---A3           | A1'--A2'--A3'
       |             \          |
       |              M         |
       |             /          |
       \-B1---B2---B3           \-B1'--B2'--B3'


That was commonly understandable part. Now, for "rebasing" the merge 
commit (keeping possible amendments), we do some extra work. First, 
we make two temporary commits on top of old merge parents, by using 
exact tree (snapshot) of commit M:

(2) ---X1---o---o---o---o---o---X2
       |\                       |\
       | A1---A2---A3---U1      | A1'--A2'--A3'
       |             \          |
       |              M         |
       |             /          |
       \-B1---B2---B3---U2      \-B1'--B2'--B3'


So here, in terms of _snapshots_ (trees, not diffs), U1 = U2 = M.

Now, we rebase these temporary commits, too:

(3) ---X1---o---o---o---o---o---X2
       |\                       |\
       | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
       |             \          |
       |              M         |
       |             /          |
       \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'


As a next step, we merge these temporary commits to produce our 
"rebased" merged commit M:

(4) ---X1---o---o---o---o---o---X2
       |\                       |\
       | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
       |             \          |                  \
       |              M         |                   M'
       |             /          |                  /
       \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'


Finally, we drop temporary commits, and record rebased commits A3' 
and B3' as our "rebased" merge commit parents instead (merge commit 
M' keeps its same tree/snapshot state, just gets parents replaced):

(5) ---X1---o---o---o---o---o---X2
       |\                       |\
       | A1---A2---A3---U1      | A1'--A2'--A3'
       |             \          |             \
       |              M         |              M'
       |             /          |             /
       \-B1---B2---B3---U2      \-B1'--B2'--B3'


And that`s it, our merge commit M has been "rebased" to M' :)

(6) ---X1---o---o---o---o---o---X2 (master)
                                |\
                                | A1'--A2'--A3'
                                |             \
                                |              M' (topic)
                                |             /
                                \-B1'--B2'--B3'


Important thing to note here is that in our step (3) above, still in 
terms of trees/snapshots (not diffs), U1' could still be equal to 
U2', produced merge commit M' tree thus being equal to both of them 
as well (merge commit introducing no changes to either of its 
parents, originally described by Sergey as "angel merge").

But it doesn`t have to be so - if any of the rebased commits A1 to A3 
or B1 to B3 was dropped or modified (or extra commits added, even), 
that would influence the trees (snapshots) produced after rebasing U1 
and U2 to U1' and U2', final merge M' reflecting all these changes as 
well, besides keeping original merge commit M amendments (preserving 
"evil merge").


Well, that`s some theory, now to hopefully confirm/test/polish all 
this... or trash it, if flawed beyond correction :P

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 19:59           ` Igor Djordjevic
@ 2018-02-27 23:27             ` Johannes Schindelin
  2018-02-28  2:12               ` Igor Djordjevic
  2018-02-28  5:44               ` Sergey Organov
  2018-02-27 23:40             ` Igor Djordjevic
  1 sibling, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-02-27 23:27 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Sergey Organov, Johannes Sixt,
	Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 10495 bytes --]

Hi Buga,

thank you for making this a lot more understandable to this thick
developer.

On Tue, 27 Feb 2018, Igor Djordjevic wrote:

> On 27/02/2018 19:55, Igor Djordjevic wrote:
> > 
> > It would be more along the lines of "(1) rebase old merge commit parents, 
> > (2) generate separate diff between old merge commit and each of its 
> > parents, (3) apply each diff to their corresponding newly rebased 
> > parent respectively (as a temporary commit, one per rebased parent), 
> > (4) merge these temporary commits to generate 'rebased' merge commit, 
> > (5) drop temporary commits, recording their parents as parents of 
> > 'rebased' merge commit (instead of dropped temporary commits)".
> > 
> > Implementation wise, steps (2) and (3) could also be done by simply 
> > copying old merge commit _snapshot_ on top of each of its parents as 
> > a temporary, non-merge commit, then rebasing (cherry-picking) these 
> > temporary commits on top of their rebased parent commits to produce 
> > rebased temporary commits (to be merged for generating 'rebased' 
> > merge commit in step (4)).
> 
> For those still tagging along (and still confused), here are some 
> diagrams (following what Sergey originally described). Note that 
> actual implementation might be even simpler, but I believe it`s a bit 
> easier to understand like this, using some "temporary" commits approach.
> 
> Here`s our starting position:
> 
> (0) ---X1---o---o---o---o---o---X2 (master)
>        |\
>        | A1---A2---A3
>        |             \
>        |              M (topic)
>        |             /
>        \-B1---B2---B3
> 
> 
> Now, we want to rebase merge commit M from X1 onto X2. First, rebase
> merge commit parents as usual:
> 
> (1) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3           | A1'--A2'--A3'
>        |             \          |
>        |              M         |
>        |             /          |
>        \-B1---B2---B3           \-B1'--B2'--B3'
> 
> 
> That was commonly understandable part.

Good. Let's assume that I want to do this interactively (because let's
face it, rebase is boring unless we shake up things a little). And let's
assume that A1 is my only change to the README, and that I realized that
it was incorrect and I do not want the world to see it, so I drop A1'.

Let's see how things go from here:

> Now, for "rebasing" the merge commit (keeping possible amendments), we
> do some extra work. First, we make two temporary commits on top of old
> merge parents, by using exact tree (snapshot) of commit M:
> 
> (2) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3---U1      | A1'--A2'--A3'
>        |             \          |
>        |              M         |
>        |             /          |
>        \-B1---B2---B3---U2      \-B1'--B2'--B3'

Okay, everything would still be the same except that I still have dropped
A1'.

> So here, in terms of _snapshots_ (trees, not diffs), U1 = U2 = M.
> 
> Now, we rebase these temporary commits, too:
> 
> (3) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>        |             \          |
>        |              M         |
>        |             /          |
>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'

I still want to drop A1 in this rebase, so A1' is still missing.

And now it starts to get interesting.

The diff between A3 and U1 does not touch the README, of course, as I said
that only A1 changed the README.  But the diff between B3 and U2 does
change the README, thanks to M containing A1 change.

Therefore, the diff between B3' and U2' will also have this change to the
README. That change that I wanted to drop.

> As a next step, we merge these temporary commits to produce our
> "rebased" merged commit M:
> 
> (4) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>        |             \          |                  \
>        |              M         |                   M'
>        |             /          |                  /
>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'

And here, thanks to B3'..U2' changing the README, M' will also have that
change that I wanted to see dropped.

Note that A1' is still dropped in my example.

> Finally, we drop temporary commits, and record rebased commits A3' 
> and B3' as our "rebased" merge commit parents instead (merge commit 
> M' keeps its same tree/snapshot state, just gets parents replaced):
> 
> (5) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3---U1      | A1'--A2'--A3'
>        |             \          |             \
>        |              M         |              M'
>        |             /          |             /
>        \-B1---B2---B3---U2      \-B1'--B2'--B3'

Now, thanks to U2' being dropped (and A1' *still* being dropped), the
change in the README that is still in M' is really only in M'. No other
rebased commit has it. That makes it look as if M' introduced this change
in addition to the changes that were merged between the merge parents.

This is what an "evil merge" is: it does more than just combine the
previously diverging branches. It introduces another change that was not
in any non-merge commits before it.

Sometimes, such an "evil merge" is necessary. For example, when master
converted a couple more `hash` references to `oid` including, say, in a
function signature, and the merged branch contains a new caller using that
old function signature: in this case, the merge commit must convert those
`hash` references to `oid` references, or the code won't compile.

In my example, where I dropped A1' specifically so that that embarrasingly
incorrect change to the README would not be seen by the world, though, the
evil merge would be truly evil: it would show said change to the world.
The exact opposite of what I wanted.

> And that`s it, our merge commit M has been "rebased" to M' :)
> 
> (6) ---X1---o---o---o---o---o---X2 (master)
>                                 |\
>                                 | A1'--A2'--A3'
>                                 |             \
>                                 |              M' (topic)
>                                 |             /
>                                 \-B1'--B2'--B3'
> 
> 
> Important thing to note here is that in our step (3) above, still in 
> terms of trees/snapshots (not diffs), U1' could still be equal to 
> U2', produced merge commit M' tree thus being equal to both of them 
> as well (merge commit introducing no changes to either of its 
> parents, originally described by Sergey as "angel merge").
> 
> But it doesn`t have to be so - if any of the rebased commits A1 to A3 
> or B1 to B3 was dropped or modified (or extra commits added, even), 
> that would influence the trees (snapshots) produced after rebasing U1 
> and U2 to U1' and U2', final merge M' reflecting all these changes as 
> well, besides keeping original merge commit M amendments (preserving 
> "evil merge").

Sadly, it would also introduce evil merges in certain circumstances, as I
demonstrated above.

> Well, that`s some theory, now to hopefully confirm/test/polish all 
> this... or trash it, if flawed beyond correction :P

It would have been nice to have such a simple solution ;-)

So the most obvious way to try to fix this design would be to recreate the
original merge first, even with merge conflicts, and then trying to use the
diff between that and the actual original merge commit. In your example,
this would look like that:

---X1---o---o---o---o---o---X2
   |\                       |\
   | A1---A2---A3--         | A1'--A2'--A3'
   |             \ \        |
   |              M M²      |
   |             / /        |
   \-B1---B2---B3--         \-B1'--B2'--B3'

Note that M² would be generated somewhat like this: `git checkout
A3; git merge -s recursive B3; git add -u; git commit`. If there are merge
conflicts, then the result would include the conflict markers.

Now we would generate something similar to those U1/U2 commits: a
single-parent commit R that reflects the diff between M and M²:

---X1---o---o---o---o---o---X2
   |\                       |\
   | A1---A2---A3           | A1'--A2'--A3'
   |             \          |
   |              M---R     |
   |             /          |
   \-B1---B2---B3           \-B1'--B2'--B3'

Note that the tree of R is identical to the tree of M². We can now
proceed to generate the merge between A3' and B3' (possibly with merge
conflicts) and then reverting R on top:

---X1---o---o---o---o---o---X2
   |\                       |\
   | A1---A2---A3           | A1'--A2'--A3'
   |             \          |              \
   |              M---R     |               M'---M³
   |             /          |              /
   \-B1---B2---B3           \-B1'--B2'--B3'

Of course, as before, the idea would be to squash the reverted
changes into the final merge commit.

Now, would this work?

I doubt it, for at least two reasons:

- if there are merge conflicts between A3/B3 and between A3'/B3', those
  merge conflicts will very likely look very different, and the conflicts
  when reverting R will contain those nested conflicts: utterly confusing.
  And those conflicts will look even more confusing if a patch (such as
  A1') was dropped during an interactive rebase.

- One of the promises was that the new way would also handle merge
  strategies other than recursive. What would happen, for example, if M
  was generated using `-s ours` (read: dropping the B* patches' changes)
  and if B1 had been cherry-picked into the history between X1..X2?

  Reverting R would obviously revert those B1 changes, even if B1' would
  obviously not even be part of the rebased history!

Yes, I agree that this `-s ours` example is quite concocted, but the point
of this example is not how plausible it is, but how easy it is to come up
with a scenario where this design to "rebase merge commits" results in
very, very unwanted behavior.

But maybe I missed something obvious, and the design can still be fixed
somehow?

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 19:59           ` Igor Djordjevic
  2018-02-27 23:27             ` Johannes Schindelin
@ 2018-02-27 23:40             ` Igor Djordjevic
  2018-02-28  0:10               ` Junio C Hamano
                                 ` (2 more replies)
  1 sibling, 3 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-27 23:40 UTC (permalink / raw)
  To: Git mailing list
  Cc: Sergey Organov, Jacob Keller, Johannes Schindelin, Johannes Sixt,
	Junio C Hamano

On 27/02/2018 20:59, Igor Djordjevic wrote:
> 
> (3) ---X1---o---o---o---o---o---X2
>        |\                       |\
>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>        |             \          |
>        |              M         |
>        |             /          |
>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> 

Meh, I hope I`m rushing it now, but for example, if we had decided to 
drop commit A2 during an interactive rebase (so losing A2' from 
diagram above), wouldn`t U2' still introduce those changes back, once 
U1' and U2' are merged, being incorrect/unwanted behavior...? :/

p.s. Looks like Johannes already elaborated on this in the meantime, 
let`s see... (goes reading that other e-mail[1])

[1] https://public-inbox.org/git/nycvar.QRO.7.76.6.1802272330290.56@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 23:40             ` Igor Djordjevic
@ 2018-02-28  0:10               ` Junio C Hamano
  2018-02-28  2:35                 ` Igor Djordjevic
  2018-02-28  5:27                 ` Sergey Organov
  2018-02-28  0:36               ` Jacob Keller
  2018-02-28  5:19               ` Sergey Organov
  2 siblings, 2 replies; 173+ messages in thread
From: Junio C Hamano @ 2018-02-28  0:10 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Sergey Organov, Jacob Keller,
	Johannes Schindelin, Johannes Sixt

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 27/02/2018 20:59, Igor Djordjevic wrote:
>> 
>> (3) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>> 
>
> Meh, I hope I`m rushing it now, but for example, if we had decided to 
> drop commit A2 during an interactive rebase (so losing A2' from 
> diagram above), wouldn`t U2' still introduce those changes back, once 
> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>
> p.s. Looks like Johannes already elaborated on this in the meantime, 
> let`s see... (goes reading that other e-mail[1])

As long as we are talking about rebase that allows us users to
adjust and make changes ("rebase -i" being the prime and most
flexible example), it is easy to imagine that A1'--A3' and B1'--B3'
have almost no relation to their original counterparts.  After all,
mechanical merge won't be able to guess the intention of the change
humans make, so depending on what happend during X1 and X2 that
happend outside of these two topics, what's required to bring series
A and B to series A' and B' can be unlimited.  So from that alone,
it should be clear that replaying difference between M and A3 (and M
and B3) on top of U1' and U2' is hopeless as a general solution.

It is acceptable as long as a solution fails loudly when it does the
wrong thing, but I do not think the apporach can produce incorrect
result silently, as your example shows above.

What you _COULD_ learn from an old merge is to compute:

    - a trial and mechanical merge between A3 and B3; call that pre-M

    - diff to bring pre-M to M (call that patch evil-M); which is
      what the person who made M did to resolve the textual and
      semantic conflicts necessary to merge these two topics.

Then when merging A3' and B3', you could try to mechanically merge
them (call that pre-M'), and apply evil-M, which may apply cleanly
on top of pre-M', or it may not.  When there aren't so huge a
difference between series A and A' (and series B and B'), the result
would probably be a moral equivalent of Sergay's "replay" (so this
approach will also silently produce a wrong result without human
supervision).  One edge the evil-M approach has over Sergey's "dual
cherry pick" is that it separates and highlights non-mechanical
conflict resolution out of mechanical merges in a human readable
form (i.e. the patch evil-M).






^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 16:21       ` Johannes Schindelin
  2018-02-27 18:55         ` Igor Djordjevic
@ 2018-02-28  0:29         ` Jacob Keller
  1 sibling, 0 replies; 173+ messages in thread
From: Jacob Keller @ 2018-02-28  0:29 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list, Johannes Sixt,
	Junio C Hamano

On Tue, Feb 27, 2018 at 8:21 AM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> Hi Jake,
>
> On Mon, 26 Feb 2018, Jacob Keller wrote:
>
>> On Mon, Feb 26, 2018 at 4:07 PM, Johannes Schindelin
>> <Johannes.Schindelin@gmx.de> wrote:
>> >
>> > On Tue, 20 Feb 2018, Igor Djordjevic wrote:
>> >
>> >> I`m really interested in this topic, which seems to (try to) address the
>> >> only "bad feeling" I had with rebasing merges - being afraid of silently
>> >> losing amendments by actually trying to "replay" the merge (where
>> >> additional and possibly important context is missing), instead of really
>> >> "rebasing" it (somehow).
>> >
>> > If those amendments are what you are worried about, why not address them
>> > specifically?
>> >
>> > In other words, rather than performing the equivalent of
>> >
>> >         git show <merge>^! | git apply
>> >
>> > (which would of course completely ignore if the rewritten <merge>^2
>> > dropped a patch, amended a patch, or even added a new patch), what you
>> > really want is to figure out what changes the user made when merging, and
>> > what merge strategy was used to begin with.
>> >
>> > To see what I mean, look at the output of `git show 0fd90daba8`: it shows
>> > how conflicts were resolved. By necessity, this is more complicated than a
>> > simple diff: it is *not* as simple as taking a diff between two revisions
>> > and applying that diff to a third revision. There were (at least) three
>> > revisions involved in the original merge commit, and recreating that merge
>> > commit faithfully means to represent the essence of the merge commit
>> > faithfully enough to be able to replay it on a new set of at least three
>> > revisions.  That can be simplified to two-way diffs only in very, very
>> > special circumstances, and in all other cases this simplification will
>> > simply fall on its nose.
>> >
>> > If the proposed solution was to extend `git apply` to process combined
>> > diffs, I would agree that we're on to something. That is not the proposed
>> > solution, though.
>> >
>> > In short: while I am sympathetic to the desire to keep things simple,
>> > the idea to somehow side-step replaying the original merge seems to be
>> > *prone* to be flawed. Any system that cannot accommodate
>> > dropped/changed/added commits on either side of a merge is likely to be
>> > too limited to be useful.
>> >
>>
>>
>> The reason Sergey's solution works is because he cherry picks the
>> merge using each parent first, and then merges the result of those. So
>> each branch of the merge gets one, and then you merge the result of
>> those cherry-picks. This preservers amendments and changes properly,
>> and should result in a good solution.
>
> I saw your patch trying to add a minimal example, and I really want to run
> away screaming.
>
> Do you have any way to describe the idea in a simple, 3-5 lines long
> paragraph?
>
> So far, I just know that it is some sort of confusing criss-cross
> cherry-picking and merging and stuff, but nothing in those steps shouts
> out to me what the *idea* is.
>

Sergey's posted explained it more in detail, at
https://public-inbox.org/git/87y3jtqdyg.fsf@javad.com/

I was mostly just attempting to re-create it in a test case to show
that it could work.

> If it would be something like "recreate the old merge, with merge
> conflicts and all, then generate the diff to the actual tree of the merge
> commit, then apply that to the newly-generated merge", I would understand.
>

It's more or less:

Rebase each parent, then cherry-pick -m<N> the original merge to that
parent, then you merge the result of each cherry-pick, then use the
resulting final merged tree to create the merge pointing at the real
parents instead of the cherry-pick merges.

> I would still suspect that -s ours would be a hard nut for that method,
> but I would understand that idea.
>

The goal of the process isn't to know or understand the "-s ours"
strategy, but simply re-create the contents of the original merge
faithfully, while still preserving the changes done when rebasing the
side branches. Thus it should re-create the contents generated by "-s
ours" the first time, but it doesn't need to do or know anything
special about how the content was created.

> Thanks,
> Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 18:14       ` Junio C Hamano
@ 2018-02-28  0:30         ` Jacob Keller
  2018-02-28  5:54           ` Sergey Organov
  2018-02-28  4:53         ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-02-28  0:30 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Sergey Organov, Johannes Schindelin, Igor Djordjevic,
	Git mailing list, Johannes Sixt

On Tue, Feb 27, 2018 at 10:14 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Sergey Organov <sorganov@gmail.com> writes:
>
>> You've already bit this poor thingy to death. Please rather try your
>> teeth on the proposed Trivial Merge (TM) method.
>
> Whatever you do, do *NOT* call any part of your proposal "trivial
> merge", unless you are actually using the term to mean what Git
> calls "trivial merge".  The phrase has an established meaning in Git
> and your attempt to abuse it to mean something entirely different is
> adding unnecessary hindrance for other people to understand what you
> want to perform.

Agreed, I think we need better terminology here, the current words for
(TM) are definitely *not* trivial merges. Same for "angel merge", I
don't think that term really works well either.

The goal of the process is to split the merge apart to its components
for each side branch and then bring them back together after applying
them to the newly rebased branches.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 23:40             ` Igor Djordjevic
  2018-02-28  0:10               ` Junio C Hamano
@ 2018-02-28  0:36               ` Jacob Keller
  2018-02-28  1:33                 ` Igor Djordjevic
  2018-02-28  5:19               ` Sergey Organov
  2 siblings, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-02-28  0:36 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Sergey Organov, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On Tue, Feb 27, 2018 at 3:40 PM, Igor Djordjevic
<igor.d.djordjevic@gmail.com> wrote:
> On 27/02/2018 20:59, Igor Djordjevic wrote:
>>
>> (3) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>
>
> Meh, I hope I`m rushing it now, but for example, if we had decided to
> drop commit A2 during an interactive rebase (so losing A2' from
> diagram above), wouldn`t U2' still introduce those changes back, once
> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>
> p.s. Looks like Johannes already elaborated on this in the meantime,
> let`s see... (goes reading that other e-mail[1])
>
> [1] https://public-inbox.org/git/nycvar.QRO.7.76.6.1802272330290.56@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/


In that case, the method won't work well at all, so I think we need a
different approach.

Thanks,
Jake

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  0:36               ` Jacob Keller
@ 2018-02-28  1:33                 ` Igor Djordjevic
  2018-02-28  1:43                   ` Igor Djordjevic
  2018-02-28  5:21                   ` Sergey Organov
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28  1:33 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Git mailing list, Sergey Organov, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 28/02/2018 01:36, Jacob Keller wrote:
> 
> > > (3) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> > >
> >
> > Meh, I hope I`m rushing it now, but for example, if we had decided to
> > drop commit A2 during an interactive rebase (so losing A2' from
> > diagram above), wouldn`t U2' still introduce those changes back, once
> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> 
> In that case, the method won't work well at all, so I think we need a
> different approach.
> 

Hmm, still rushing it, but what about adding an additional step, 
something like this: 
 
 (4) ---X1---o---o---o---o---o---X2
        |\                       |\
        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'--R1
        |             \          |
        |              M         |
        |             /          |
        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'--R2


... where:

  R1 = git diff U2 U2' | git apply --3way
  R2 = git diff U1 U1' | git apply --3way

(this is just to explain the point, might be there is a better way to 
produce Rx)

So, we still use Ux' to preserve merge commit M amendments, but also 
Rx to catch any changes happening between Ux and Ux' caused by 
interactive rebase commit manipulation (add/amend/drop).

Note that R*1* is produced by applying diff from U*2*' side, and vice 
versa (as it`s the other side that can erroneously introduce dropped 
commit changes back, like U2' in case of dropped A2').

From here we continue as before - merging R1 and R2, then rewriting 
merge commit parents to point to A3' and B3' (dropping Ux` and Rx).

This seems to be working inside my (too trivial?) test case, for 
interactive adding, dropping, and amending of rebased commits, 
resulting "rebased" merge containing all the added/modified/dropped 
changes, plus the original merge amendment, all as expected :P

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  1:33                 ` Igor Djordjevic
@ 2018-02-28  1:43                   ` Igor Djordjevic
  2018-02-28  5:21                   ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28  1:43 UTC (permalink / raw)
  To: Git mailing list
  Cc: Jacob Keller, Sergey Organov, Johannes Schindelin, Johannes Sixt,
	Junio C Hamano

On 28/02/2018 02:33, Igor Djordjevic wrote:
> 
> This seems to be working inside my (too trivial?) test case, for 
> interactive adding, dropping, and amending of rebased commits, 
> resulting "rebased" merge containing all the added/modified/dropped 
> changes, plus the original merge amendment, all as expected :P

In case anyone has a wish to examine my (now pretty messy) test 
script, here it is - sorry for not having time to clean it up! :(

What I get when I diff original and "rebased" merge is this:

    diff --git a/test.txt b/test.txt
    index a82470b..d458032 100644
    --- a/test.txt
    +++ b/test.txt
    @@ -1,10 +1,14 @@
    +A21
    +A22
    +A23
    +A24
    +A25
     A1
     A2
    -B11
    +B1111
     A3
     A4
     A5
    -B12
     A6
     A7
     A8
    @@ -14,6 +18,7 @@ A10
     A11
     A12
     A13
    +BX
     A14
     B2
     A15


... where A21 to A25 are additions due to new base, B11 was 
interactively amended to B1111, B12 was interactively dropped, and BX 
interactively added :)

We don`t see line X here, being an "evil merge" amendment being 
correctly preserved from original merge commit (thus not a 
difference). If we do `git show` of the "rebased" merge, we get this, 
as expected:

    diff --cc test.txt
    index b173cef,fad39a8..d458032
    --- a/test.txt
    +++ b/test.txt
    @@@ -13,6 -13,6 +13,7 @@@ A
      A7
      A8
      A9
    ++X
      A10
      A11
      A12


Regards, Buga

-- 8< --
#!/bin/sh

# rm -rf ./.git
# rm -f ./test.txt

git init

touch ./test.txt
git add -- test.txt

for i in {1..20}
do
	echo A$i >>test.txt
	git commit -am "A$i"
done

git checkout -b b1
sed -i '3iB11' test.txt
git commit -am "B11"
sed -i '7iB12' test.txt
git commit -am "B12"

git checkout -b b2 HEAD^
sed -i '16iB2' test.txt
git commit -am "B2"

git checkout -b merge b1
git merge --no-commit b2
sed -i '12iX' test.txt # amend merge commit
git commit -am "M"
git tag original-merge

git checkout master
for i in {1..5}
do
	j=`expr "$i" + 20`
	sed -i "${i}iA${j}" test.txt
	git commit -am "A$j"
done

# simple/naive demonstration of proposed merge rebasing logic
# using described "Trivial Merge" (TM, or "Angel Merge"),
# preserving merge commit manual amendments, but still respecting
# interactively rebased added/modified/dropped commits :)

# read -p "Press enter to continue"
git checkout b1
git cherry-pick -m1 original-merge && git tag U1
git reset --hard HEAD^^ # drop U1 and last b1 commit
sed -i '/B11/c\B1111' test.txt
git commit -a --amend --no-edit
git rebase master
git cherry-pick U1 && git tag U1-prime

# read -p "Press enter to continue"
git checkout b2
git cherry-pick -m2 original-merge && git tag U2
git reset --hard HEAD^ # drop U2
git rebase master
sed -i '20iBX' test.txt
git commit -am "BX" # add new commit
git cherry-pick U2 && git tag U2-prime

git diff U1 U1-prime | git apply --3way && git commit -m "U2-second" && git tag U2-second
git checkout b1
git diff U2 U2-prime | git apply --3way && git commit -m "U1-second" && git tag U1-second

# read -p "Press enter to continue"
git branch -f merge b1
git checkout merge
git merge b2 --no-commit
git commit -a --reuse-message original-merge
git tag angel-merge

# read -p "Press enter to continue"
git reset --hard b1^
git read-tree --reset angel-merge
git update-ref refs/heads/merge "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse b1^^)" -p "$(git rev-parse b2^^)")"
git tag -f angel-merge
git checkout angel-merge .
git branch -f b1 b1^^
git branch -f b2 b2^^

# show resulting graph
echo
git log --all --decorate --oneline --graph

# comparison between original merge and rebased merge,
# showing merge commit amendment "X" being preserved during rebase
# (not shown in diff)
echo
echo 'diff original-merge angel-merge:'
git diff original-merge angel-merge

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 23:27             ` Johannes Schindelin
@ 2018-02-28  2:12               ` Igor Djordjevic
  2018-02-28  4:35                 ` Igor Djordjevic
  2018-02-28  5:44               ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28  2:12 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Git mailing list, Jacob Keller, Sergey Organov, Johannes Sixt,
	Junio C Hamano

Hi Johannes,

On 28/02/2018 00:27, Johannes Schindelin wrote:
> 
> thank you for making this a lot more understandable to this thick
> developer.

Hehe, no problem, it primarily served fighting my own thickness ;)

> > Finally, we drop temporary commits, and record rebased commits A3' 
> > and B3' as our "rebased" merge commit parents instead (merge commit 
> > M' keeps its same tree/snapshot state, just gets parents replaced):
> >
> > (5) ---X1---o---o---o---o---o---X2
> >        |\                       |\
> >        | A1---A2---A3---U1      | A1'--A2'--A3'
> >        |             \          |             \
> >        |              M         |              M'
> >        |             /          |             /
> >        \-B1---B2---B3---U2      \-B1'--B2'--B3'
> 
> ...
> 
> In my example, where I dropped A1' specifically so that that embarrasingly
> incorrect change to the README would not be seen by the world, though, the
> evil merge would be truly evil: it would show said change to the world.
> The exact opposite of what I wanted.

Yeah, I`m afraid that`s what my testing produced as well :( Back to 
the drawing board...

> It would have been nice to have such a simple solution ;-)

Eh, the worst thing is the feeling I have, like it`s just around the 
corner, but we`re somehow missing it :P

> So the most obvious way to try to fix this design would be to recreate the
> original merge first, even with merge conflicts, and then trying to use the
> diff between that and the actual original merge commit.

For simplicity sake, this is something I would like to avoid (if 
possible), and also for the reasons you mentioned yourself:

> Now, would this work?
> 
> I doubt it, for at least two reasons:
> 
> - if there are merge conflicts between A3/B3 and between A3'/B3', those
>   merge conflicts will very likely look very different, and the conflicts
>   when reverting R will contain those nested conflicts: utterly confusing.
>   And those conflicts will look even more confusing if a patch (such as
>   A1') was dropped during an interactive rebase.
> 
> - One of the promises was that the new way would also handle merge
>   strategies other than recursive. What would happen, for example, if M
>   was generated using `-s ours` (read: dropping the B* patches' changes)
>   and if B1 had been cherry-picked into the history between X1..X2?
> 
>   Reverting R would obviously revert those B1 changes, even if B1' would
>   obviously not even be part of the rebased history!
> 
> ...
> 
> But maybe I missed something obvious, and the design can still be fixed
> somehow?

Would additional step as suggested in [1] (using R1 and R2 to "catch" 
interactive rebase additions/amendments/drops, on top of U1' and 
U2'), make more sense (or provide an additional clue, at least)?

It`s late here, and I`m really rushing it now, so please forgive me if 
it`s a stupid one... :$

Regards, Buga

[1] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  0:10               ` Junio C Hamano
@ 2018-02-28  2:35                 ` Igor Djordjevic
  2018-02-28  5:27                 ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28  2:35 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git mailing list, Sergey Organov, Jacob Keller,
	Johannes Schindelin, Johannes Sixt

Hi Junio,

On 28/02/2018 01:10, Junio C Hamano wrote:
> 
> > > (3) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> > >
> >
> > Meh, I hope I`m rushing it now, but for example, if we had decided to 
> > drop commit A2 during an interactive rebase (so losing A2' from 
> > diagram above), wouldn`t U2' still introduce those changes back, once 
> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> 
> As long as we are talking about rebase that allows us users to
> adjust and make changes ("rebase -i" being the prime and most
> flexible example), it is easy to imagine that A1'--A3' and B1'--B3'
> have almost no relation to their original counterparts.  After all,
> mechanical merge won't be able to guess the intention of the change
> humans make, so depending on what happend during X1 and X2 that
> happend outside of these two topics, what's required to bring series
> A and B to series A' and B' can be unlimited.  So from that alone,
> it should be clear that replaying difference between M and A3 (and M
> and B3) on top of U1' and U2' is hopeless as a general solution.

Yeah, I`ve encountered it in my (trivial) test case :(

> It is acceptable as long as a solution fails loudly when it does the
> wrong thing, but I do not think the apporach can produce incorrect
> result silently, as your example shows above.

Hmm, I think my example doesn`t even try to prevent failing, but it 
should otherwise be perfectly capable of doing so (and doing it 
loudly) - for example, it`s enough to diff U1' and U2' - if not the 
same, user might want to confirm the "rebased" merge outcome, as 
either something went wrong, or interactive rebase happened... or 
both :) (it`s what Sergey originally explained, seeming to be a solid 
safety net, though more testing would be good)

> What you _COULD_ learn from an old merge is to compute:
> 
>     - a trial and mechanical merge between A3 and B3; call that pre-M
> 
>     - diff to bring pre-M to M (call that patch evil-M); which is
>       what the person who made M did to resolve the textual and
>       semantic conflicts necessary to merge these two topics.
> 
> Then when merging A3' and B3', you could try to mechanically merge
> them (call that pre-M'), and apply evil-M, which may apply cleanly
> on top of pre-M', or it may not.  When there aren't so huge a
> difference between series A and A' (and series B and B'), the result
> would probably be a moral equivalent of Sergay's "replay" (so this
> approach will also silently produce a wrong result without human
> supervision).  One edge the evil-M approach has over Sergey's "dual
> cherry pick" is that it separates and highlights non-mechanical
> conflict resolution out of mechanical merges in a human readable
> form (i.e. the patch evil-M).

This seems to be what Johannes wrote about[1], too, but also 
something I think would be good to avoid, if possible, not 
complicating it too much :P

Maybe something like that latest thought[2] could help, using 
additional R1 and R2 commits to handle interactive rebase 
additions/amendments/drops, on top of U1' and U2'? Yeah, and 
that`s not complicating it... ;) :D

Regards, Buga

[1] https://public-inbox.org/git/nycvar.QRO.7.76.6.1802272330290.56@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/
[2] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  2:12               ` Igor Djordjevic
@ 2018-02-28  4:35                 ` Igor Djordjevic
  2018-02-28  6:14                   ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28  4:35 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Git mailing list, Jacob Keller, Sergey Organov, Johannes Sixt,
	Junio C Hamano

On 28/02/2018 03:12, Igor Djordjevic wrote:
> 
> Would additional step as suggested in [1] (using R1 and R2 to "catch" 
> interactive rebase additions/amendments/drops, on top of U1' and 
> U2'), make more sense (or provide an additional clue, at least)?
> 
> [1] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/

Heh, no sleeping tonight :P

Anyway, from (yet another) ad hoc test, additional step mentioned in 
[1] above seems to handle this case, too (merge with `-s ours` 
dropping B* patches, plus B1 cherry-picked between X1..X2):

On 28/02/2018 00:27, Johannes Schindelin wrote:
> 
> - One of the promises was that the new way would also handle merge
>   strategies other than recursive. What would happen, for example, if M
>   was generated using `-s ours` (read: dropping the B* patches' changes)
>   and if B1 had been cherry-picked into the history between X1..X2?
> 
>   Reverting R would obviously revert those B1 changes, even if B1' would
>   obviously not even be part of the rebased history!
> 
> Yes, I agree that this `-s ours` example is quite concocted, but the point
> of this example is not how plausible it is, but how easy it is to come up
> with a scenario where this design to "rebase merge commits" results in
> very, very unwanted behavior.

And not just that - it worked with additional interactive rebase 
adding, amending and removing commits, on top of all this still 
preserving original `-s ours` merge commit evil-merge amendment, too, 
all as expected (or so it seems) :P

Again, for the brave ones, here`s another messy test script (not 
tightly related to the last discussed diagrams, but based on my 
original "demonstration" script[2])... Hopefully I get to make a 
proper (and sane) sample soon... if not missing something here in the 
first place ;)

Regards, Buga

[2] https://public-inbox.org/git/bbe64321-4d3a-d3fe-8bb9-58b600fabf35@gmail.com/

-- 8< --
#!/bin/sh

# rm -rf ./.git
# rm -f ./test.txt

git init

touch ./test.txt
git add -- test.txt

for i in {1..20}
do
	echo A$i >>test.txt
	git commit -am "A$i"
done

git checkout -b b1
sed -i '3iB11' test.txt
git commit -am "B11"
sed -i '7iB12' test.txt
git commit -am "B12"

git checkout -b b2 HEAD^
sed -i '16iB21' test.txt
git commit -am "B21"
sed -i '18iB22' test.txt
git commit -am "B22"

git checkout -b merge b1
git merge -s ours --no-commit b2
sed -i '12iX' test.txt # amend merge commit
git commit -am "M"
git tag original-merge

git checkout master
git cherry-pick b2
for i in {1..5}
do
	j=`expr "$i" + 20`
	sed -i "${i}iA${j}" test.txt
	git commit -am "A$j"
done

# simple/naive demonstration of proposed merge rebasing logic
# using described "Trivial Merge" (TM, or "Angel Merge"),
# preserving merge commit manual amendments, but still respecting
# interactively rebased added/modified/dropped commits :)

# read -p "Press enter to continue"
git checkout b1
git cherry-pick -m1 original-merge && git tag U1
git reset --hard HEAD^^ # drop U1 and last b1 commit
sed -i '/B11/c\B1111' test.txt
git commit -a --amend --no-edit
git rebase master
git cherry-pick U1 && git tag U1-prime

# read -p "Press enter to continue"
git checkout b2
git cherry-pick -m2 original-merge && git tag U2
git reset --hard HEAD^ # drop U2
git rebase master
sed -i '20iBX' test.txt
git commit -am "BX" # add new commit
git cherry-pick U2 && git tag U2-prime

git diff U1 U1-prime | git apply --3way && git commit -m "U2-second" && git tag U2-second
git checkout b1
git diff U2 U2-prime | git apply --3way && git commit -m "U1-second" && git tag U1-second

# read -p "Press enter to continue"
git branch -f merge b1
git checkout merge
git merge b2 --no-commit
git commit -a --reuse-message original-merge
git tag angel-merge

# read -p "Press enter to continue"
git reset --hard b1^
git read-tree --reset angel-merge
git update-ref refs/heads/merge "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse b1^^)" -p "$(git rev-parse b2^^)")"
git tag -f angel-merge
git checkout angel-merge .
git branch -f b1 b1^^
git branch -f b2 b2^^

# show resulting graph
echo
git log --all --decorate --oneline --graph

# comparison between original merge and rebased merge,
# showing merge commit amendment "X" being preserved during rebase
# (not shown in diff)
echo
echo 'diff original-merge angel-merge:'
git diff original-merge angel-merge

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 18:14       ` Junio C Hamano
  2018-02-28  0:30         ` Jacob Keller
@ 2018-02-28  4:53         ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  4:53 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Igor Djordjevic, git, Johannes Sixt,
	Jacob Keller

Junio C Hamano <gitster@pobox.com> writes:

> Sergey Organov <sorganov@gmail.com> writes:
>
>> You've already bit this poor thingy to death. Please rather try your
>> teeth on the proposed Trivial Merge (TM) method.
>
> Whatever you do, do *NOT* call any part of your proposal "trivial
> merge", unless you are actually using the term to mean what Git
> calls "trivial merge".  The phrase has an established meaning in Git
> and your attempt to abuse it to mean something entirely different is
> adding unnecessary hindrance for other people to understand what you
> want to perform.

Yeah, got it. It's confusing indeed.

-- Sergey






^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 23:40             ` Igor Djordjevic
  2018-02-28  0:10               ` Junio C Hamano
  2018-02-28  0:36               ` Jacob Keller
@ 2018-02-28  5:19               ` Sergey Organov
  2018-02-28 20:25                 ` Igor Djordjevic
  2 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  5:19 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 27/02/2018 20:59, Igor Djordjevic wrote:
>> 
>> (3) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>> 
>
> Meh, I hope I`m rushing it now, but for example, if we had decided to 
> drop commit A2 during an interactive rebase (so losing A2' from 
> diagram above), wouldn`t U2' still introduce those changes back, once 
> U1' and U2' are merged, being incorrect/unwanted behavior...? :/

I think the method will handle this nicely.

When you "drop" A2, will A3' change, or stay intact?

If it changes, say, to A3'', the U1' will change to U1'', and the method
will propagate the change automatically.

If it A3' doesn't change, then there are no changes to take.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  1:33                 ` Igor Djordjevic
  2018-02-28  1:43                   ` Igor Djordjevic
@ 2018-02-28  5:21                   ` Sergey Organov
  2018-02-28 19:09                     ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  5:21 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Jacob Keller, Git mailing list, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 28/02/2018 01:36, Jacob Keller wrote:
>> 
>> > > (3) ---X1---o---o---o---o---o---X2
>> > >        |\                       |\
>> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>> > >        |             \          |
>> > >        |              M         |
>> > >        |             /          |
>> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>> > >
>> >
>> > Meh, I hope I`m rushing it now, but for example, if we had decided to
>> > drop commit A2 during an interactive rebase (so losing A2' from
>> > diagram above), wouldn`t U2' still introduce those changes back, once
>> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>> 
>> In that case, the method won't work well at all, so I think we need a
>> different approach.
>> 
>
> Hmm, still rushing it, but what about adding an additional step, 
> something like this:

I think it's unneeded, as it should work fine without it, see another
reply.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  0:10               ` Junio C Hamano
  2018-02-28  2:35                 ` Igor Djordjevic
@ 2018-02-28  5:27                 ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  5:27 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt

Junio C Hamano <gitster@pobox.com> writes:

> Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
>
>> On 27/02/2018 20:59, Igor Djordjevic wrote:
>>> 
>>> (3) ---X1---o---o---o---o---o---X2
>>>        |\                       |\
>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>>        |             \          |
>>>        |              M         |
>>>        |             /          |
>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>> 
>>
>> Meh, I hope I`m rushing it now, but for example, if we had decided to 
>> drop commit A2 during an interactive rebase (so losing A2' from 
>> diagram above), wouldn`t U2' still introduce those changes back, once 
>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>>
>> p.s. Looks like Johannes already elaborated on this in the meantime, 
>> let`s see... (goes reading that other e-mail[1])
>
> As long as we are talking about rebase that allows us users to
> adjust and make changes ("rebase -i" being the prime and most
> flexible example), it is easy to imagine that A1'--A3' and B1'--B3'
> have almost no relation to their original counterparts.  After all,
> mechanical merge won't be able to guess the intention of the change
> humans make, so depending on what happend during X1 and X2 that
> happend outside of these two topics, what's required to bring series
> A and B to series A' and B' can be unlimited.

You discuss some different method here. In my original proposal U1' and
U2' are to be merged. Nothing should be replayed on top of them. I.e.,
U1' is _already_ the result of replaying difference between M and A3 on
top of A3'.

> So from that alone, it should be clear that replaying difference
> between M and A3 (and M and B3) on top of U1' and U2' is hopeless as a
> general solution.

Getting U1' from U1 is the same complexity as getting A3' from A3, with
the same caveats. So, as general solution, it's as good as rebase of
non-merge commit.

> It is acceptable as long as a solution fails loudly when it does the
> wrong thing, but I do not think the apporach can produce incorrect
> result silently, as your example shows above.

I still believe the method handles simple cases automatically and
correctly and allows to immediately stop for amendment should something
suspect appear.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-27 23:27             ` Johannes Schindelin
  2018-02-28  2:12               ` Igor Djordjevic
@ 2018-02-28  5:44               ` Sergey Organov
  2018-02-28 19:42                 ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  5:44 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, Git mailing list, Jacob Keller, Johannes Sixt,
	Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Buga,
>
> thank you for making this a lot more understandable to this thick
> developer.
>
> On Tue, 27 Feb 2018, Igor Djordjevic wrote:
>
>> On 27/02/2018 19:55, Igor Djordjevic wrote:
>> > 
>> > It would be more along the lines of "(1) rebase old merge commit parents, 
>> > (2) generate separate diff between old merge commit and each of its 
>> > parents, (3) apply each diff to their corresponding newly rebased 
>> > parent respectively (as a temporary commit, one per rebased parent), 
>> > (4) merge these temporary commits to generate 'rebased' merge commit, 
>> > (5) drop temporary commits, recording their parents as parents of 
>> > 'rebased' merge commit (instead of dropped temporary commits)".
>> > 
>> > Implementation wise, steps (2) and (3) could also be done by simply 
>> > copying old merge commit _snapshot_ on top of each of its parents as 
>> > a temporary, non-merge commit, then rebasing (cherry-picking) these 
>> > temporary commits on top of their rebased parent commits to produce 
>> > rebased temporary commits (to be merged for generating 'rebased' 
>> > merge commit in step (4)).
>> 
>> For those still tagging along (and still confused), here are some 
>> diagrams (following what Sergey originally described). Note that 
>> actual implementation might be even simpler, but I believe it`s a bit 
>> easier to understand like this, using some "temporary" commits approach.
>> 
>> Here`s our starting position:
>> 
>> (0) ---X1---o---o---o---o---o---X2 (master)
>>        |\
>>        | A1---A2---A3
>>        |             \
>>        |              M (topic)
>>        |             /
>>        \-B1---B2---B3
>> 
>> 
>> Now, we want to rebase merge commit M from X1 onto X2. First, rebase
>> merge commit parents as usual:
>> 
>> (1) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3           | A1'--A2'--A3'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3           \-B1'--B2'--B3'
>> 
>> 
>> That was commonly understandable part.
>
> Good. Let's assume that I want to do this interactively (because let's
> face it, rebase is boring unless we shake up things a little). And let's
> assume that A1 is my only change to the README, and that I realized that
> it was incorrect and I do not want the world to see it, so I drop A1'.
>
> Let's see how things go from here:
>
>> Now, for "rebasing" the merge commit (keeping possible amendments), we
>> do some extra work. First, we make two temporary commits on top of old
>> merge parents, by using exact tree (snapshot) of commit M:
>> 
>> (2) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'
>
> Okay, everything would still be the same except that I still have dropped
> A1'.
>
>> So here, in terms of _snapshots_ (trees, not diffs), U1 = U2 = M.
>> 
>> Now, we rebase these temporary commits, too:
>> 
>> (3) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>        |             \          |
>>        |              M         |
>>        |             /          |
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>
> I still want to drop A1 in this rebase, so A1' is still missing.
>
> And now it starts to get interesting.
>
> The diff between A3 and U1 does not touch the README, of course, as I said
> that only A1 changed the README.  But the diff between B3 and U2 does
> change the README, thanks to M containing A1 change.
>
> Therefore, the diff between B3' and U2' will also have this change to the
> README. That change that I wanted to drop.
>
>> As a next step, we merge these temporary commits to produce our
>> "rebased" merged commit M:
>> 
>> (4) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>        |             \          |                  \
>>        |              M         |                   M'
>>        |             /          |                  /
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>
> And here, thanks to B3'..U2' changing the README, M' will also have that
> change that I wanted to see dropped.
>
> Note that A1' is still dropped in my example.
>
>> Finally, we drop temporary commits, and record rebased commits A3' 
>> and B3' as our "rebased" merge commit parents instead (merge commit 
>> M' keeps its same tree/snapshot state, just gets parents replaced):
>> 
>> (5) ---X1---o---o---o---o---o---X2
>>        |\                       |\
>>        | A1---A2---A3---U1      | A1'--A2'--A3'
>>        |             \          |             \
>>        |              M         |              M'
>>        |             /          |             /
>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'
>
> Now, thanks to U2' being dropped (and A1' *still* being dropped), the
> change in the README that is still in M' is really only in M'. No other
> rebased commit has it. That makes it look as if M' introduced this change
> in addition to the changes that were merged between the merge parents.

Except that original proposal suggests to cosider this a conflict and
to stop for amendment, as U1' and U2' now differ.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  0:30         ` Jacob Keller
@ 2018-02-28  5:54           ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  5:54 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Junio C Hamano, Johannes Schindelin, Igor Djordjevic,
	Git mailing list, Johannes Sixt

Jacob Keller <jacob.keller@gmail.com> writes:

> On Tue, Feb 27, 2018 at 10:14 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Sergey Organov <sorganov@gmail.com> writes:
>>
>>> You've already bit this poor thingy to death. Please rather try your
>>> teeth on the proposed Trivial Merge (TM) method.
>>
>> Whatever you do, do *NOT* call any part of your proposal "trivial
>> merge", unless you are actually using the term to mean what Git
>> calls "trivial merge".  The phrase has an established meaning in Git
>> and your attempt to abuse it to mean something entirely different is
>> adding unnecessary hindrance for other people to understand what you
>> want to perform.
>
> Agreed, I think we need better terminology here, the current words for
> (TM) are definitely *not* trivial merges. Same for "angel merge", I
> don't think that term really works well either.

Agreed.

How do we call a merge that introduces no differences on either side of
the merge then? Is there some English for even more trivial than what
Git calls "trivial merge"?

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  4:35                 ` Igor Djordjevic
@ 2018-02-28  6:14                   ` Sergey Organov
  2018-02-28 20:53                     ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-02-28  6:14 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 28/02/2018 03:12, Igor Djordjevic wrote:
>> 
>> Would additional step as suggested in [1] (using R1 and R2 to "catch" 
>> interactive rebase additions/amendments/drops, on top of U1' and 
>> U2'), make more sense (or provide an additional clue, at least)?
>> 
>> [1] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/
>
> Heh, no sleeping tonight :P
>
> Anyway, from (yet another) ad hoc test, additional step mentioned in 
> [1] above seems to handle this case, too (merge with `-s ours` 
> dropping B* patches, plus B1 cherry-picked between X1..X2):
>
> On 28/02/2018 00:27, Johannes Schindelin wrote:
>> 
>> - One of the promises was that the new way would also handle merge
>>   strategies other than recursive. What would happen, for example, if M
>>   was generated using `-s ours` (read: dropping the B* patches' changes)
>>   and if B1 had been cherry-picked into the history between X1..X2?
>> 
>>   Reverting R would obviously revert those B1 changes, even if B1' would
>>   obviously not even be part of the rebased history!
>> 
>> Yes, I agree that this `-s ours` example is quite concocted, but the point
>> of this example is not how plausible it is, but how easy it is to come up
>> with a scenario where this design to "rebase merge commits" results in
>> very, very unwanted behavior.
>
> And not just that - it worked with additional interactive rebase 
> adding, amending and removing commits, on top of all this still 
> preserving original `-s ours` merge commit evil-merge amendment, too, 
> all as expected (or so it seems) :P

Great! I do believe that once we start from some sensible approach, many
kinds of improvements are possible. I'll definitely need to take close
look at what you came up with, thanks!

I'd like to say though that nobody should expect miracles from automated
rebasing of merges when we get to complex history editing. It will need
to retreat to manual merge, sooner or later.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  5:21                   ` Sergey Organov
@ 2018-02-28 19:09                     ` Igor Djordjevic
  2018-03-01  5:27                       ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28 19:09 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Jacob Keller, Git mailing list, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 28/02/2018 06:21, Sergey Organov wrote:
> 
> > > > > (3) ---X1---o---o---o---o---o---X2
> > > > >        |\                       |\
> > > > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > > > >        |             \          |
> > > > >        |              M         |
> > > > >        |             /          |
> > > > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> > > > >
> > > >
> > > > Meh, I hope I`m rushing it now, but for example, if we had decided to
> > > > drop commit A2 during an interactive rebase (so losing A2' from
> > > > diagram above), wouldn`t U2' still introduce those changes back, once
> > > > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> > >
> > > In that case, the method won't work well at all, so I think we need a
> > > different approach.
> > >
> >
> > Hmm, still rushing it, but what about adding an additional step, 
> > something like this:
> 
> I think it's unneeded, as it should work fine without it, see another
> reply.

Unfortunately, I have a broken test case saying different - it could 
very well be a flawed test, too, but let`s elaborate in that 
other sub-thread[1], indeed.

Regards, Buga

[1] https://public-inbox.org/git/87606hoflx.fsf@javad.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  5:44               ` Sergey Organov
@ 2018-02-28 19:42                 ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28 19:42 UTC (permalink / raw)
  To: Sergey Organov, Johannes Schindelin
  Cc: Git mailing list, Jacob Keller, Johannes Sixt, Junio C Hamano

Hi Sergey,

On 28/02/2018 06:44, Sergey Organov wrote:
> 
> > > Here`s our starting position:
> > >
> > > (0) ---X1---o---o---o---o---o---X2 (master)
> > >        |\
> > >        | A1---A2---A3
> > >        |             \
> > >        |              M (topic)
> > >        |             /
> > >        \-B1---B2---B3
> > >
> > >
> > > Now, we want to rebase merge commit M from X1 onto X2. First, rebase
> > > merge commit parents as usual:
> > >
> > > (1) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3           | A1'--A2'--A3'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3           \-B1'--B2'--B3'
> > >
> > >
> > > That was commonly understandable part.
> >
> > Good. Let's assume that I want to do this interactively (because let's
> > face it, rebase is boring unless we shake up things a little). And let's
> > assume that A1 is my only change to the README, and that I realized that
> > it was incorrect and I do not want the world to see it, so I drop A1'.
> >
> > Let's see how things go from here:
> >
> > > Now, for "rebasing" the merge commit (keeping possible amendments), we
> > > do some extra work. First, we make two temporary commits on top of old
> > > merge parents, by using exact tree (snapshot) of commit M:
> > >
> > > (2) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'
> >
> > Okay, everything would still be the same except that I still have dropped
> > A1'.
> >
> > > So here, in terms of _snapshots_ (trees, not diffs), U1 = U2 = M.
> > >
> > > Now, we rebase these temporary commits, too:
> > >
> > > (3) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> >
> > I still want to drop A1 in this rebase, so A1' is still missing.
> >
> > And now it starts to get interesting.
> >
> > The diff between A3 and U1 does not touch the README, of course, as I said
> > that only A1 changed the README.  But the diff between B3 and U2 does
> > change the README, thanks to M containing A1 change.
> >
> > Therefore, the diff between B3' and U2' will also have this change to the
> > README. That change that I wanted to drop.
> >
> > > As a next step, we merge these temporary commits to produce our
> > > "rebased" merged commit M:
> > >
> > > (4) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |                  \
> > >        |              M         |                   M'
> > >        |             /          |                  /
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> >
> > And here, thanks to B3'..U2' changing the README, M' will also have that
> > change that I wanted to see dropped.
> >
> > Note that A1' is still dropped in my example.
> >
> > > Finally, we drop temporary commits, and record rebased commits A3' 
> > > and B3' as our "rebased" merge commit parents instead (merge commit 
> > > M' keeps its same tree/snapshot state, just gets parents replaced):
> > >
> > > (5) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'
> > >        |             \          |             \
> > >        |              M         |              M'
> > >        |             /          |             /
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'
> >
> > Now, thanks to U2' being dropped (and A1' *still* being dropped), the
> > change in the README that is still in M' is really only in M'. No other
> > rebased commit has it. That makes it look as if M' introduced this change
> > in addition to the changes that were merged between the merge parents.
> 
> Except that original proposal suggests to cosider this a conflict and
> to stop for amendment, as U1' and U2' now differ.

Thanks for bringing this one up again - I focused on a mere example 
of how the approach could work, not stressing enough the simplicity 
of recognizing when it really doesn`t, which is an important aspect 
to know as well, indeed, supporting much needed trust that no bad 
things could happen behind our back.

Either work as expected, or fail loudly, yes.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  5:19               ` Sergey Organov
@ 2018-02-28 20:25                 ` Igor Djordjevic
  2018-02-28 22:17                   ` Igor Djordjevic
  2018-03-01  5:39                   ` Sergey Organov
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28 20:25 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 28/02/2018 06:19, Sergey Organov wrote:
> 
> > > (3) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> > >
> >
> > Meh, I hope I`m rushing it now, but for example, if we had decided to 
> > drop commit A2 during an interactive rebase (so losing A2' from 
> > diagram above), wouldn`t U2' still introduce those changes back, once 
> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> 
> I think the method will handle this nicely.

That`s what I thought as well. And then I made a test. And then the 
test broke... Now, might be the test was flawed in the first place, 
but thinking about it a bit more, it does seem to make sense not to 
handle this case nicely :/

> When you "drop" A2, will A3' change, or stay intact?

When you "drop" commit A2 from rebasing, patch (diff) A3' does stay 
the same - but resulting tree (snapshot) after applying it does not.

By removing commit A2 from your rebase, you`re saying that changes 
introduced in that commit shouldn`t ever happen in the rebased tree, 
so trees/snapshots of all rebased commits coming after dropped A2 
will have these changes missing, in comparison to trees of original 
commits they`re being rebased from.

> If it changes, say, to A3'', the U1' will change to U1'', and the method
> will propagate the change automatically.
> 
> If it A3' doesn't change, then there are no changes to take.

All true, but note what I wrote in the original message - the issue 
of dropping A2 is not U1, but U2, and that one doesn`t change.

In this case, U1' will correctly represent U1 rebased on top of A3' 
(where A2 is now missing), no problem there.

But on the other end, as U2 holds changes between original merge and 
B3 (being A1, A2, A3 and evil-merge M), it will also still hold 
changes introduced by original A2.

Rebasing it onto B3' brings all these changes along, and once merged 
with U1' to produce "rebased" merge it unexpectedly introduces 
supposedly dropped commit A2 changes in their full glory.

Yes, considering this situation a conflict, as originally proposed, 
by simply noticing that U1' and U2' differ, helps this to fail 
loudly without doing the wrong thing.

But U1' and U2' are really to be expected to stay the same in 
non-interactive rebase case only, where it doesn`t help interactive 
rebase case at all if it is to fail most of the time (where U1' and 
U2' don`t have to be, but should be expected to possibly be different).

So while your original proposal currently seems like it could be 
working nicely for non-interactive rebase (and might be some simpler 
interactive ones), now hitting/acknowledging its first real use 
limit, my additional quick attempt[1] just tries to aid pretty 
interesting case of complicated interactive rebase, too, where we 
might be able to do better as well, still using you original proposal 
as a base idea :)

Regards, Buga

[1] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28  6:14                   ` Sergey Organov
@ 2018-02-28 20:53                     ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28 20:53 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Johannes Schindelin, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 28/02/2018 07:14, Sergey Organov wrote:
> 
> > > Would additional step as suggested in [1] (using R1 and R2 to "catch" 
> > > interactive rebase additions/amendments/drops, on top of U1' and 
> > > U2'), make more sense (or provide an additional clue, at least)?
> > > 
> > > [1] https://public-inbox.org/git/8829c395-fb84-2db0-9288-f7b28fa0d0d1@gmail.com/
> > 
> > Anyway, from (yet another) ad hoc test, additional step mentioned in 
> > [1] above seems to handle this case, too (merge with `-s ours` 
> > dropping B* patches, plus B1 cherry-picked between X1..X2)
> > 
> > ...
> > 
> > And not just that - it worked with additional interactive rebase 
> > adding, amending and removing commits, on top of all this still 
> > preserving original `-s ours` merge commit evil-merge amendment, too, 
> > all as expected (or so it seems) :P
> 
> Great! I do believe that once we start from some sensible approach, many
> kinds of improvements are possible. I'll definitely need to take close
> look at what you came up with, thanks!
> 
> I'd like to say though that nobody should expect miracles from automated
> rebasing of merges when we get to complex history editing. It will need
> to retreat to manual merge, sooner or later.

I agree, and as I really liked "the feeling" of the original approach 
you described, it felt bad to (presumably) see it failing in what 
doesn`t seem to be neither too complex nor rare situation of dropping 
a commit during an interactive rebase, thus motivation to try to 
improve it, if possible, wasn`t lacking :)
 
Eh, might be I`m just naively ignorant at the moment as well, but I`m 
trying to work my way through it... ;)

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28 20:25                 ` Igor Djordjevic
@ 2018-02-28 22:17                   ` Igor Djordjevic
  2018-03-01  5:19                     ` Sergey Organov
  2018-03-01  5:39                   ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-02-28 22:17 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 28/02/2018 21:25, Igor Djordjevic wrote:
> 
> But U1' and U2' are really to be expected to stay the same in 
> non-interactive rebase case only...

Just to rephrase to "could be expected" here, meaning still not 
necessarily in this case, either - I`ve just witnessed 
non-interactive rebase Johannes previously described[1], merge with 
`-s ours` (dropping B* patches), plus B1 cherry-picked between 
X1..X2, eventually coming up with different U1' and U2', too (which 
would produce a wrong end result, if continued).

But I guess this should go to the "complex history" pile, good thing 
still being that diff safety check between U1' and U2' works as 
expected, thus automatic merge rebase can be aborted and command 
given back to the user for closer inspection.

[1] https://public-inbox.org/git/nycvar.QRO.7.76.6.1802272330290.56@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28 22:17                   ` Igor Djordjevic
@ 2018-03-01  5:19                     ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-01  5:19 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 28/02/2018 21:25, Igor Djordjevic wrote:
>> 
>> But U1' and U2' are really to be expected to stay the same in 
>> non-interactive rebase case only...
>
> Just to rephrase to "could be expected" here, meaning still not 
> necessarily in this case, either - I`ve just witnessed 
> non-interactive rebase Johannes previously described[1], merge with 
> `-s ours` (dropping B* patches), plus B1 cherry-picked between 
> X1..X2, eventually coming up with different U1' and U2', too (which 
> would produce a wrong end result, if continued).

Even non-interactive rebase may bring arbitrary asymmetric changes on
both sides of the merge, especially likely when resolving conflicts
needs to take place. OTOH, interactive history editing session could
have merges that we don't intend to edit at all. Overall, neither
non-interactive case is in fact much simpler, nor interactive case is so
much different.

> But I guess this should go to the "complex history" pile, good thing 
> still being that diff safety check between U1' and U2' works as 
> expected, thus automatic merge rebase can be aborted and command 
> given back to the user for closer inspection.

Exactly. This fact hopefully won't stop us from looking for more
suitable automated handling of the general case though. It should still
be our goal to reduce the number of such aborts and to suggest better
merge result for amendment when we are still aborting.

Your proposal hopefully is such a valuable improvement.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28 19:09                     ` Igor Djordjevic
@ 2018-03-01  5:27                       ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-01  5:27 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Jacob Keller, Git mailing list, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

[...]
>> > Hmm, still rushing it, but what about adding an additional step, 
>> > something like this:
>> 
>> I think it's unneeded, as it should work fine without it, see another
>> reply.
>
> Unfortunately, I have a broken test case saying different - it could 
> very well be a flawed test, too, but let`s elaborate in that 
> other sub-thread[1], indeed.

Yeah, I was too fast to reply and I was wrong, sorry about it.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-28 20:25                 ` Igor Djordjevic
  2018-02-28 22:17                   ` Igor Djordjevic
@ 2018-03-01  5:39                   ` Sergey Organov
  2018-03-02  1:16                     ` Igor Djordjevic
  2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
  1 sibling, 2 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-01  5:39 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 28/02/2018 06:19, Sergey Organov wrote:
>> 
>> > > (3) ---X1---o---o---o---o---o---X2
>> > >        |\                       |\
>> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>> > >        |             \          |
>> > >        |              M         |
>> > >        |             /          |
>> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>> > >
>> >
>> > Meh, I hope I`m rushing it now, but for example, if we had decided to 
>> > drop commit A2 during an interactive rebase (so losing A2' from 
>> > diagram above), wouldn`t U2' still introduce those changes back, once 
>> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>> 
>> I think the method will handle this nicely.
>
> That`s what I thought as well. And then I made a test. And then the 
> test broke... Now, might be the test was flawed in the first place, 
> but thinking about it a bit more, it does seem to make sense not to 
> handle this case nicely :/

Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
this more carefully in the first place.

[...]

> So while your original proposal currently seems like it could be 
> working nicely for non-interactive rebase (and might be some simpler 
> interactive ones), now hitting/acknowledging its first real use 
> limit, my additional quick attempt[1] just tries to aid pretty 
> interesting case of complicated interactive rebase, too, where we 
> might be able to do better as well, still using you original proposal 
> as a base idea :)

Yes, thank you for pushing me back to reality! :-) The work and thoughts
you are putting into solving the puzzle are greatly appreciated!

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.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-01  5:39                   ` Sergey Organov
@ 2018-03-02  1:16                     ` Igor Djordjevic
  2018-03-02  5:40                       ` Sergey Organov
  2018-03-02 11:17                       ` [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear) Phillip Wood
  2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
  1 sibling, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-02  1:16 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 01/03/2018 06:39, Sergey Organov wrote:
> 
> > > (3) ---X1---o---o---o---o---o---X2
> > >        |\                       |\
> > >        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> > >        |             \          |
> > >        |              M         |
> > >        |             /          |
> > >        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> > >
> > 
> > Meh, I hope I`m rushing it now, but for example, if we had decided to 
> > drop commit A2 during an interactive rebase (so losing A2' from 
> > diagram above), wouldn`t U2' still introduce those changes back, once 
> > U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> > 
> > [...]
> 
> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
> this more carefully in the first place.

No problem, that`s why we`re discussing it, and I`m glad we`re 
aligned now, so we can move forward :)

> > So while your original proposal currently seems like it could be 
> > working nicely for non-interactive rebase (and might be some simpler 
> > interactive ones), now hitting/acknowledging its first real use 
> > limit, my additional quick attempt[1] just tries to aid pretty 
> > interesting case of complicated interactive rebase, too, where we 
> > might be able to do better as well, still using you original proposal 
> > as a base idea :)
> 
> Yes, thank you for pushing me back to reality! :-) The work and thoughts
> you are putting into solving the puzzle are greatly appreciated!

You`re welcome, and I am enjoying it :)

> 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.

Yes, this might be it...! ;)

To prove myself it works, I`ve assembled a pretty crazy `-s ours`  
merge interactive rebase scenario, and it seems this passes the test, 
ticking all the check boxes (I could think of) :P

Let`s see our starting situation:

 (0) ---X8--B2'--X9 (master)
        |\
        | A1---A2---A3 (A)
        |             \
        |              M (topic)
        |             /
        \-B1---B2---B3 (B)


Here, merge commit M is done with `-s ours` (obsoleting branch "B"), 
plus amended to make it an "evil merge", where a commit B2 from 
obsoleted branch "B" is cherry picked to "master".

Now, we want to rebase "topic" (M) onto updated "master" (X9), but to 
make things more interesting, we`ll do it interactively, with some 
amendments, drops, additions and even more cherry-picks!

This is what the final result looks like:

 (1) ---X8--B2'--X9 (master)
                 |\
                 | A12--A2'---B3' (A)
                 |             \
                 |              M' (topic)
                 |             /
                 \-B1'--B3'---B4  (B)


During interactive rebase, on branch "A", we amended A1 into A12, 
dropped A3 and cherry-picked B3. On branch "B", B4 is added, B2' being 
omitted automatically as already present in "master".

So... In comparison to original merge commit M, rebased merge commit 
M' is expected to:

 - Add X9, from updated "master"
 - Have A1 changed to A12, due to A12 commit amendment
 - Keep A2, rebased as A2'
 - Remove A3, due to dropped A3 commit
 - Keep amendment from original (evil) merge commit M
 - Miss B1' like M does B, due to original `-s ours` merge strategy
 - Add B2, cherry-picked as B2' into "master"
 - Add B3, cherry-picked as B3' into "A"
 - Add B4, added to "B"
 - Most important, provide safety mechanism to "fail loud", being 
   aware of non-trivial things going on, allowing to stop for user 
   inspection/decision


There, I hope I didn`t miss any expectation. And, it _seems_ to work 
exactly as expected :D

Not to leave this to imagination only, and hopefully helping others 
to get to speed and possibly discuss this, pointing to still possible 
flaws, I`m adding a demo script[1], showing how this exact example 
works.

Note that script _is_ coined to avoid rebase conflicts, as they`re not 
currently important for the point to be made here.

In real life, except for usual possibility for conflicts during 
commit rebasing, we might experience _three_ possible conflict 
situations once "rebased" merge itself is to be created - two when 
rebasing each of temporary merge helper commits, and one on the 
"rebased" merge itself. This is something where we might think about 
user experience, not introducing (too much) confusion...

Regards, Buga

[1] Demonstration script:
-- >8 --
#!/bin/sh

# rm -rf ./.git
# rm -f ./test.txt

git init

touch ./test.txt
git add -- test.txt

# prepare repository
for i in {1..8}
do
	echo X$i >>test.txt
	git commit -am "X$i"
done

# prepare branch A
git checkout -b A
sed -i '2iA1' test.txt
git commit -am "A1"
sed -i '4iA2' test.txt
git commit -am "A2"
sed -i '6iA3' test.txt
git commit -am "A3"

# prepare branch B
git checkout -b B master
sed -i '5iB1' test.txt
git commit -am "B1"
sed -i '7iB2' test.txt
git commit -am "B2"
sed -i '9iB3' test.txt
git commit -am "B3"

git checkout -b topic A
git merge -s ours --no-commit B # merge A and B with `-s ours`
sed -i '8iM' test.txt           # amend merge commit ("evil merge")
git commit -am "M"
git tag original-merge

# master moves on...
git checkout master
git cherry-pick B^     # cherry-pick B2 into master
sed -i "1iX9" test.txt # add X9
git commit -am "X9"

# (0) ---X8--B2'--X9 (master)
#        |\
#        | A1---A2---A3 (A)
#        |             \
#        |              M (topic)
#        |             /
#        \-B1---B2---B3 (B)

# simple/naive demonstration of proposed merge rebasing logic
# using described new approach, preserving merge commit manual
# amendments, testing `-s ours` merge with cherry-picking from
# obsoleted part, but still respecting interactively rebased
# added/modified/dropped/cherry-picked commits :)

git checkout A
git cherry-pick -m1 original-merge # prepare temporary helper commit U1
git tag U1
git reset --hard HEAD^^            # drop U1 and A3 from A
sed -i '/A1/c\A12' test.txt        # amend A1 to A12
git commit -a --amend --no-edit
git rebase master                  # rebase A onto master
git cherry-pick B                  # cherry-pick B3 into A
git cherry-pick U1                 # "rebase" temporary helper commit U1
git tag U1-prime

git checkout B
git cherry-pick -m2 original-merge # prepare temporary helper commit U2
git tag U2
git reset --hard HEAD^             # drop U2 from B
git rebase master                  # rebase B onto master
sed -i '12iB4' test.txt            # add B4
git commit -am "B4"
git cherry-pick U2                 # "rebase" temporary helper commit U2
git tag U2-prime

git branch -f topic A
git checkout topic
# merge rebased temporary commits U1' and U2',
# using original merge commit as a merge base,
# producing "rebased" merge commit M'
git read-tree -m --aggressive original-merge A B
git merge-index -o git-merge-one-file -a

# recognize complex stuff going on during rebasing merge commit,
# allowing user to inspect result, edit, and continue or abort
git diff --quiet U1-prime U2-prime
if test $? -ne 0
then
	# PLACEHOLDER
	# chance to inspect result, like:
	git diff original-merge
	# edit if needed, continue or abort
fi

# drop rebased temporary commits U1' and U2'
git branch -f A A^
git branch -f B B^

# record branches A and B as parents of "rebased" merge commit M',
# updating topic branch
git update-ref refs/heads/topic "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse A)" -p "$(git rev-parse B)")"
git tag angel-merge

# (1) ---X8--B2'--X9 (master)
#                 |\
#                 | A12--A2'---B3' (A)
#                 |             \
#                 |              M' (topic)
#                 |             /
#                 \-B1'--B3'---B4  (B)

# show resulting graph
# echo
# git log --all --decorate --oneline --graph

# in comparison to original merge commit M, rebased merge commit 
# M' is expected to:
#
# - Add X9, from updated "master"
# - Have A1 changed to A12, due to A12 commit amendment
# - Keep A2, rebased as A2'
# - Remove A3, due to dropped A3 commit
# - Keep amendment from original (evil) merge commit M
# - Miss B1' like M does B, due to original `-s ours` merge strategy
# - Add B2, cherry-picked as B2' into "master"
# - Add B3, cherry-picked as B3' into "A"
# - Add B4, added to "B"
#
# echo
# echo 'diff original-merge angel-merge:'
# git diff original-merge angel-merge

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-02  1:16                     ` Igor Djordjevic
@ 2018-03-02  5:40                       ` Sergey Organov
  2018-03-02 17:45                         ` Igor Djordjevic
  2018-03-02 11:17                       ` [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear) Phillip Wood
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-02  5:40 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 01/03/2018 06:39, Sergey Organov wrote:

[...]

>> 
>> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
>> this more carefully in the first place.
>
> No problem, that`s why we`re discussing it, and I`m glad we`re 
> aligned now, so we can move forward :)
>
>> > So while your original proposal currently seems like it could be 
>> > working nicely for non-interactive rebase (and might be some simpler 
>> > interactive ones), now hitting/acknowledging its first real use 
>> > limit, my additional quick attempt[1] just tries to aid pretty 
>> > interesting case of complicated interactive rebase, too, where we 
>> > might be able to do better as well, still using you original proposal 
>> > as a base idea :)
>> 
>> Yes, thank you for pushing me back to reality! :-) The work and thoughts
>> you are putting into solving the puzzle are greatly appreciated!
>
> You`re welcome, and I am enjoying it :)
>
>> 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.
>
> Yes, this might be it...! ;)
>
> To prove myself it works, I`ve assembled a pretty crazy `-s ours`  
> merge interactive rebase scenario, and it seems this passes the test, 
> ticking all the check boxes (I could think of) :P

I must admit it's quite a relief to hear this. What we now have is so
simple and obvious that it'd be a huge disappointment if didn't work!

> Here, merge commit M is done with `-s ours` (obsoleting branch "B"), 
> plus amended to make it an "evil merge", where a commit B2 from 
> obsoleted branch "B" is cherry picked to "master".

[...]

> There, I hope I didn`t miss any expectation. And, it _seems_ to work 
> exactly as expected :D

That's very nice, to the level of being even suspect! :-)

To avoid falling into euphoria though, we need to keep in mind that
"expectations" is rather vague concept, and we likely still need to stop
for user amendment unless we absolutely sure nothing surprising happens.
I.e., we better require U1'==U2' test to succeed to proceed non-stop
automatically. Besides, it will be somewhat inline with what 'rerere'
does.

> In real life, except for usual possibility for conflicts during 
> commit rebasing, we might experience _three_ possible conflict 
> situations once "rebased" merge itself is to be created - two when 
> rebasing each of temporary merge helper commits, and one on the 
> "rebased" merge itself. This is something where we might think about 
> user experience, not introducing (too much) confusion...

Yeah, this is terribly important issue to take care of! Relative
simplicity of the concept itself raises the chances of finding a
suitable solution, I hope.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02  1:16                     ` Igor Djordjevic
  2018-03-02  5:40                       ` Sergey Organov
@ 2018-03-02 11:17                       ` Phillip Wood
  2018-03-02 12:36                         ` Phillip Wood
  2018-03-02 16:00                         ` Jacob Keller
  1 sibling, 2 replies; 173+ messages in thread
From: Phillip Wood @ 2018-03-02 11:17 UTC (permalink / raw)
  To: Igor Djordjevic, Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano


On 02/03/18 01:16, Igor Djordjevic wrote:
> 
> Hi Sergey,
> 
> On 01/03/2018 06:39, Sergey Organov wrote:
>>
>>>> (3) ---X1---o---o---o---o---o---X2
>>>>        |\                       |\
>>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>>>        |             \          |
>>>>        |              M         |
>>>>        |             /          |
>>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>>>
>>>
>>> Meh, I hope I`m rushing it now, but for example, if we had decided to 
>>> drop commit A2 during an interactive rebase (so losing A2' from 
>>> diagram above), wouldn`t U2' still introduce those changes back, once 
>>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>>>
>>> [...]
>>
>> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
>> this more carefully in the first place.
> 
> No problem, that`s why we`re discussing it, and I`m glad we`re 
> aligned now, so we can move forward :)
> 
>>> So while your original proposal currently seems like it could be 
>>> working nicely for non-interactive rebase (and might be some simpler 
>>> interactive ones), now hitting/acknowledging its first real use 
>>> limit, my additional quick attempt[1] just tries to aid pretty 
>>> interesting case of complicated interactive rebase, too, where we 
>>> might be able to do better as well, still using you original proposal 
>>> as a base idea :)
>>
>> Yes, thank you for pushing me back to reality! :-) The work and thoughts
>> you are putting into solving the puzzle are greatly appreciated!
> 
> You`re welcome, and I am enjoying it :)
> 
>> 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.
> 
> Yes, this might be it...! ;)
> 
> To prove myself it works, I`ve assembled a pretty crazy `-s ours`  
> merge interactive rebase scenario, and it seems this passes the test, 
> ticking all the check boxes (I could think of) :P

It is interesting to think what it means to faithfully rebase a '-s
ours' merge. In your example the rebase does not introduce any new
changes into branch B that it doesn't introduce to branch A. Had it
added a fixup to branch B1 for example or if the topology was more
complex so that B ended up with some other changes that the rebase did
not introduce into A, then M' would contain those extra changes whereas
'--recreate-merges' with '-s ours' (once it supports it) would not.

> 
> Let`s see our starting situation:
> 
>  (0) ---X8--B2'--X9 (master)
>         |\
>         | A1---A2---A3 (A)
>         |             \
>         |              M (topic)
>         |             /
>         \-B1---B2---B3 (B)
> 
> 
> Here, merge commit M is done with `-s ours` (obsoleting branch "B"), 
> plus amended to make it an "evil merge", where a commit B2 from 
> obsoleted branch "B" is cherry picked to "master".
> 
> Now, we want to rebase "topic" (M) onto updated "master" (X9), but to 
> make things more interesting, we`ll do it interactively, with some 
> amendments, drops, additions and even more cherry-picks!
> 
> This is what the final result looks like:
> 
>  (1) ---X8--B2'--X9 (master)
>                  |\
>                  | A12--A2'---B3' (A)
>                  |             \
>                  |              M' (topic)
>                  |             /
>                  \-B1'--B3'---B4  (B)
> 
> 
> During interactive rebase, on branch "A", we amended A1 into A12, 
> dropped A3 and cherry-picked B3. On branch "B", B4 is added, B2' being 
> omitted automatically as already present in "master".
> 
> So... In comparison to original merge commit M, rebased merge commit 
> M' is expected to:
> 
>  - Add X9, from updated "master"
>  - Have A1 changed to A12, due to A12 commit amendment
>  - Keep A2, rebased as A2'
>  - Remove A3, due to dropped A3 commit
>  - Keep amendment from original (evil) merge commit M
>  - Miss B1' like M does B, due to original `-s ours` merge strategy
>  - Add B2, cherry-picked as B2' into "master"
>  - Add B3, cherry-picked as B3' into "A"
>  - Add B4, added to "B"
>  - Most important, provide safety mechanism to "fail loud", being 
>    aware of non-trivial things going on, allowing to stop for user 
>    inspection/decision
> 
> 
> There, I hope I didn`t miss any expectation. And, it _seems_ to work 
> exactly as expected :D
> 
> Not to leave this to imagination only, and hopefully helping others 
> to get to speed and possibly discuss this, pointing to still possible 
> flaws, I`m adding a demo script[1], showing how this exact example 
> works.
> 
> Note that script _is_ coined to avoid rebase conflicts, as they`re not 
> currently important for the point to be made here.
> 
> In real life, except for usual possibility for conflicts during 
> commit rebasing, we might experience _three_ possible conflict 
> situations once "rebased" merge itself is to be created - two when 
> rebasing each of temporary merge helper commits, and one on the 
> "rebased" merge itself. This is something where we might think about 
> user experience, not introducing (too much) confusion...
> 
> Regards, Buga
> 
> [1] Demonstration script:
> -- >8 --
> #!/bin/sh
> 
> # rm -rf ./.git
> # rm -f ./test.txt
> 
> git init
> 
> touch ./test.txt
> git add -- test.txt
> 
> # prepare repository
> for i in {1..8}
> do
> 	echo X$i >>test.txt
> 	git commit -am "X$i"
> done
> 
> # prepare branch A
> git checkout -b A
> sed -i '2iA1' test.txt
> git commit -am "A1"
> sed -i '4iA2' test.txt
> git commit -am "A2"
> sed -i '6iA3' test.txt
> git commit -am "A3"
> 
> # prepare branch B
> git checkout -b B master
> sed -i '5iB1' test.txt
> git commit -am "B1"
> sed -i '7iB2' test.txt
> git commit -am "B2"
> sed -i '9iB3' test.txt
> git commit -am "B3"
> 
> git checkout -b topic A
> git merge -s ours --no-commit B # merge A and B with `-s ours`
> sed -i '8iM' test.txt           # amend merge commit ("evil merge")
> git commit -am "M"
> git tag original-merge
> 
> # master moves on...
> git checkout master
> git cherry-pick B^     # cherry-pick B2 into master
> sed -i "1iX9" test.txt # add X9
> git commit -am "X9"
> 
> # (0) ---X8--B2'--X9 (master)
> #        |\
> #        | A1---A2---A3 (A)
> #        |             \
> #        |              M (topic)
> #        |             /
> #        \-B1---B2---B3 (B)
> 
> # simple/naive demonstration of proposed merge rebasing logic
> # using described new approach, preserving merge commit manual
> # amendments, testing `-s ours` merge with cherry-picking from
> # obsoleted part, but still respecting interactively rebased
> # added/modified/dropped/cherry-picked commits :)
> 
> git checkout A
> git cherry-pick -m1 original-merge # prepare temporary helper commit U1
> git tag U1
> git reset --hard HEAD^^            # drop U1 and A3 from A
> sed -i '/A1/c\A12' test.txt        # amend A1 to A12
> git commit -a --amend --no-edit
> git rebase master                  # rebase A onto master
> git cherry-pick B                  # cherry-pick B3 into A
> git cherry-pick U1                 # "rebase" temporary helper commit U1
> git tag U1-prime
> 
> git checkout B
> git cherry-pick -m2 original-merge # prepare temporary helper commit U2
> git tag U2
> git reset --hard HEAD^             # drop U2 from B
> git rebase master                  # rebase B onto master
> sed -i '12iB4' test.txt            # add B4
> git commit -am "B4"
> git cherry-pick U2                 # "rebase" temporary helper commit U2
> git tag U2-prime
> 
> git branch -f topic A
> git checkout topic
> # merge rebased temporary commits U1' and U2',
> # using original merge commit as a merge base,
> # producing "rebased" merge commit M'
> git read-tree -m --aggressive original-merge A B
> git merge-index -o git-merge-one-file -a
> 
> # recognize complex stuff going on during rebasing merge commit,
> # allowing user to inspect result, edit, and continue or abort
> git diff --quiet U1-prime U2-prime
> if test $? -ne 0
> then
> 	# PLACEHOLDER
> 	# chance to inspect result, like:
> 	git diff original-merge
> 	# edit if needed, continue or abort
> fi
> 
> # drop rebased temporary commits U1' and U2'
> git branch -f A A^
> git branch -f B B^
> 
> # record branches A and B as parents of "rebased" merge commit M',
> # updating topic branch
> git update-ref refs/heads/topic "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse A)" -p "$(git rev-parse B)")"
> git tag angel-merge
> 
> # (1) ---X8--B2'--X9 (master)
> #                 |\
> #                 | A12--A2'---B3' (A)
> #                 |             \
> #                 |              M' (topic)
> #                 |             /
> #                 \-B1'--B3'---B4  (B)
> 
> # show resulting graph
> # echo
> # git log --all --decorate --oneline --graph
> 
> # in comparison to original merge commit M, rebased merge commit 
> # M' is expected to:
> #
> # - Add X9, from updated "master"
> # - Have A1 changed to A12, due to A12 commit amendment
> # - Keep A2, rebased as A2'
> # - Remove A3, due to dropped A3 commit
> # - Keep amendment from original (evil) merge commit M
> # - Miss B1' like M does B, due to original `-s ours` merge strategy
> # - Add B2, cherry-picked as B2' into "master"
> # - Add B3, cherry-picked as B3' into "A"
> # - Add B4, added to "B"
> #
> # echo
> # echo 'diff original-merge angel-merge:'
> # git diff original-merge angel-merge
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-01  5:39                   ` Sergey Organov
  2018-03-02  1:16                     ` Igor Djordjevic
@ 2018-03-02 11:31                     ` Phillip Wood
  2018-03-03  0:29                       ` Igor Djordjevic
                                         ` (2 more replies)
  1 sibling, 3 replies; 173+ messages in thread
From: Phillip Wood @ 2018-03-02 11:31 UTC (permalink / raw)
  To: Sergey Organov, Igor Djordjevic
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 01/03/18 05:39, Sergey Organov wrote:
> 
> Hi Igor,
> 
> Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
> 
>> Hi Sergey,
>>
>> On 28/02/2018 06:19, Sergey Organov wrote:
>>>
>>>>> (3) ---X1---o---o---o---o---o---X2
>>>>>        |\                       |\
>>>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>>>>        |             \          |
>>>>>        |              M         |
>>>>>        |             /          |
>>>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>>>>
>>>>
>>>> Meh, I hope I`m rushing it now, but for example, if we had decided to 
>>>> drop commit A2 during an interactive rebase (so losing A2' from 
>>>> diagram above), wouldn`t U2' still introduce those changes back, once 
>>>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>>>
>>> I think the method will handle this nicely.
>>
>> That`s what I thought as well. And then I made a test. And then the 
>> test broke... Now, might be the test was flawed in the first place, 
>> but thinking about it a bit more, it does seem to make sense not to 
>> handle this case nicely :/
> 
> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
> this more carefully in the first place.
> 
> [...]
> 
>> So while your original proposal currently seems like it could be 
>> working nicely for non-interactive rebase (and might be some simpler 
>> interactive ones), now hitting/acknowledging its first real use 
>> limit, my additional quick attempt[1] just tries to aid pretty 
>> interesting case of complicated interactive rebase, too, where we 
>> might be able to do better as well, still using you original proposal 
>> as a base idea :)
> 
> Yes, thank you for pushing me back to reality! :-) The work and thoughts
> you are putting into solving the puzzle are greatly appreciated!
> 
> 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.

Best Wishes

Phillip

[1] https://github.com/mhagger/git-imerge


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 11:17                       ` [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear) Phillip Wood
@ 2018-03-02 12:36                         ` Phillip Wood
  2018-03-02 16:02                           ` Jacob Keller
  2018-03-02 16:00                         ` Jacob Keller
  1 sibling, 1 reply; 173+ messages in thread
From: Phillip Wood @ 2018-03-02 12:36 UTC (permalink / raw)
  To: Igor Djordjevic, Sergey Organov, Git Mailing List
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 02/03/18 11:17, Phillip Wood wrote:
> 
> On 02/03/18 01:16, Igor Djordjevic wrote:
>>
>> Hi Sergey,
>>
>> On 01/03/2018 06:39, Sergey Organov wrote:
>>>
>>>>> (3) ---X1---o---o---o---o---o---X2
>>>>>        |\                       |\
>>>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>>>>        |             \          |
>>>>>        |              M         |
>>>>>        |             /          |
>>>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>>>>
>>>>
>>>> Meh, I hope I`m rushing it now, but for example, if we had decided to 
>>>> drop commit A2 during an interactive rebase (so losing A2' from 
>>>> diagram above), wouldn`t U2' still introduce those changes back, once 
>>>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>>>>
>>>> [...]
>>>
>>> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
>>> this more carefully in the first place.
>>
>> No problem, that`s why we`re discussing it, and I`m glad we`re 
>> aligned now, so we can move forward :)
>>
>>>> So while your original proposal currently seems like it could be 
>>>> working nicely for non-interactive rebase (and might be some simpler 
>>>> interactive ones), now hitting/acknowledging its first real use 
>>>> limit, my additional quick attempt[1] just tries to aid pretty 
>>>> interesting case of complicated interactive rebase, too, where we 
>>>> might be able to do better as well, still using you original proposal 
>>>> as a base idea :)
>>>
>>> Yes, thank you for pushing me back to reality! :-) The work and thoughts
>>> you are putting into solving the puzzle are greatly appreciated!
>>
>> You`re welcome, and I am enjoying it :)
>>
>>> 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.
>>
>> Yes, this might be it...! ;)
>>
>> To prove myself it works, I`ve assembled a pretty crazy `-s ours`  
>> merge interactive rebase scenario, and it seems this passes the test, 
>> ticking all the check boxes (I could think of) :P

Hi Igor

> It is interesting to think what it means to faithfully rebase a '-s
> ours' merge.
I should have explained that I mean is a faithful rebase one that
adheres to the semantics of '-s ours' (i.e. ignores any changes in the
side branch) or one that merges new changes from the side branch into
the content of the original merge? In your example you add B4 to B. If
M' preserves the semantics of '-s ours' then it will not contain the
changes in B4. I think your result does (correct me if I'm wrong) so it
is preserving the content of the original merge rather than the
semantics of it.

Best Wishes

Phillip

(ignore the rest of what I wrote earlier I don't think it's correct)
 In your example the rebase does not introduce any new
> changes into branch B that it doesn't introduce to branch A. Had it
> added a fixup to branch B1 for example or if the topology was more
> complex so that B ended up with some other changes that the rebase did
> not introduce into A, then M' would contain those extra changes whereas
> '--recreate-merges' with '-s ours' (once it supports it) would not.
> 
>>
>> Let`s see our starting situation:
>>
>>  (0) ---X8--B2'--X9 (master)
>>         |\
>>         | A1---A2---A3 (A)
>>         |             \
>>         |              M (topic)
>>         |             /
>>         \-B1---B2---B3 (B)
>>
>>
>> Here, merge commit M is done with `-s ours` (obsoleting branch "B"), 
>> plus amended to make it an "evil merge", where a commit B2 from 
>> obsoleted branch "B" is cherry picked to "master".
>>
>> Now, we want to rebase "topic" (M) onto updated "master" (X9), but to 
>> make things more interesting, we`ll do it interactively, with some 
>> amendments, drops, additions and even more cherry-picks!
>>
>> This is what the final result looks like:
>>
>>  (1) ---X8--B2'--X9 (master)
>>                  |\
>>                  | A12--A2'---B3' (A)
>>                  |             \
>>                  |              M' (topic)
>>                  |             /
>>                  \-B1'--B3'---B4  (B)
>>
>>
>> During interactive rebase, on branch "A", we amended A1 into A12, 
>> dropped A3 and cherry-picked B3. On branch "B", B4 is added, B2' being 
>> omitted automatically as already present in "master".
>>
>> So... In comparison to original merge commit M, rebased merge commit 
>> M' is expected to:
>>
>>  - Add X9, from updated "master"
>>  - Have A1 changed to A12, due to A12 commit amendment
>>  - Keep A2, rebased as A2'
>>  - Remove A3, due to dropped A3 commit
>>  - Keep amendment from original (evil) merge commit M
>>  - Miss B1' like M does B, due to original `-s ours` merge strategy
>>  - Add B2, cherry-picked as B2' into "master"
>>  - Add B3, cherry-picked as B3' into "A"
>>  - Add B4, added to "B"
>>  - Most important, provide safety mechanism to "fail loud", being 
>>    aware of non-trivial things going on, allowing to stop for user 
>>    inspection/decision
>>
>>
>> There, I hope I didn`t miss any expectation. And, it _seems_ to work 
>> exactly as expected :D
>>
>> Not to leave this to imagination only, and hopefully helping others 
>> to get to speed and possibly discuss this, pointing to still possible 
>> flaws, I`m adding a demo script[1], showing how this exact example 
>> works.
>>
>> Note that script _is_ coined to avoid rebase conflicts, as they`re not 
>> currently important for the point to be made here.
>>
>> In real life, except for usual possibility for conflicts during 
>> commit rebasing, we might experience _three_ possible conflict 
>> situations once "rebased" merge itself is to be created - two when 
>> rebasing each of temporary merge helper commits, and one on the 
>> "rebased" merge itself. This is something where we might think about 
>> user experience, not introducing (too much) confusion...
>>
>> Regards, Buga
>>
>> [1] Demonstration script:
>> -- >8 --
>> #!/bin/sh
>>
>> # rm -rf ./.git
>> # rm -f ./test.txt
>>
>> git init
>>
>> touch ./test.txt
>> git add -- test.txt
>>
>> # prepare repository
>> for i in {1..8}
>> do
>> 	echo X$i >>test.txt
>> 	git commit -am "X$i"
>> done
>>
>> # prepare branch A
>> git checkout -b A
>> sed -i '2iA1' test.txt
>> git commit -am "A1"
>> sed -i '4iA2' test.txt
>> git commit -am "A2"
>> sed -i '6iA3' test.txt
>> git commit -am "A3"
>>
>> # prepare branch B
>> git checkout -b B master
>> sed -i '5iB1' test.txt
>> git commit -am "B1"
>> sed -i '7iB2' test.txt
>> git commit -am "B2"
>> sed -i '9iB3' test.txt
>> git commit -am "B3"
>>
>> git checkout -b topic A
>> git merge -s ours --no-commit B # merge A and B with `-s ours`
>> sed -i '8iM' test.txt           # amend merge commit ("evil merge")
>> git commit -am "M"
>> git tag original-merge
>>
>> # master moves on...
>> git checkout master
>> git cherry-pick B^     # cherry-pick B2 into master
>> sed -i "1iX9" test.txt # add X9
>> git commit -am "X9"
>>
>> # (0) ---X8--B2'--X9 (master)
>> #        |\
>> #        | A1---A2---A3 (A)
>> #        |             \
>> #        |              M (topic)
>> #        |             /
>> #        \-B1---B2---B3 (B)
>>
>> # simple/naive demonstration of proposed merge rebasing logic
>> # using described new approach, preserving merge commit manual
>> # amendments, testing `-s ours` merge with cherry-picking from
>> # obsoleted part, but still respecting interactively rebased
>> # added/modified/dropped/cherry-picked commits :)
>>
>> git checkout A
>> git cherry-pick -m1 original-merge # prepare temporary helper commit U1
>> git tag U1
>> git reset --hard HEAD^^            # drop U1 and A3 from A
>> sed -i '/A1/c\A12' test.txt        # amend A1 to A12
>> git commit -a --amend --no-edit
>> git rebase master                  # rebase A onto master
>> git cherry-pick B                  # cherry-pick B3 into A
>> git cherry-pick U1                 # "rebase" temporary helper commit U1
>> git tag U1-prime
>>
>> git checkout B
>> git cherry-pick -m2 original-merge # prepare temporary helper commit U2
>> git tag U2
>> git reset --hard HEAD^             # drop U2 from B
>> git rebase master                  # rebase B onto master
>> sed -i '12iB4' test.txt            # add B4
>> git commit -am "B4"
>> git cherry-pick U2                 # "rebase" temporary helper commit U2
>> git tag U2-prime
>>
>> git branch -f topic A
>> git checkout topic
>> # merge rebased temporary commits U1' and U2',
>> # using original merge commit as a merge base,
>> # producing "rebased" merge commit M'
>> git read-tree -m --aggressive original-merge A B
>> git merge-index -o git-merge-one-file -a
>>
>> # recognize complex stuff going on during rebasing merge commit,
>> # allowing user to inspect result, edit, and continue or abort
>> git diff --quiet U1-prime U2-prime
>> if test $? -ne 0
>> then
>> 	# PLACEHOLDER
>> 	# chance to inspect result, like:
>> 	git diff original-merge
>> 	# edit if needed, continue or abort
>> fi
>>
>> # drop rebased temporary commits U1' and U2'
>> git branch -f A A^
>> git branch -f B B^
>>
>> # record branches A and B as parents of "rebased" merge commit M',
>> # updating topic branch
>> git update-ref refs/heads/topic "$(git show -s --format=%B original-merge | git commit-tree "$(git write-tree)" -p "$(git rev-parse A)" -p "$(git rev-parse B)")"
>> git tag angel-merge
>>
>> # (1) ---X8--B2'--X9 (master)
>> #                 |\
>> #                 | A12--A2'---B3' (A)
>> #                 |             \
>> #                 |              M' (topic)
>> #                 |             /
>> #                 \-B1'--B3'---B4  (B)
>>
>> # show resulting graph
>> # echo
>> # git log --all --decorate --oneline --graph
>>
>> # in comparison to original merge commit M, rebased merge commit 
>> # M' is expected to:
>> #
>> # - Add X9, from updated "master"
>> # - Have A1 changed to A12, due to A12 commit amendment
>> # - Keep A2, rebased as A2'
>> # - Remove A3, due to dropped A3 commit
>> # - Keep amendment from original (evil) merge commit M
>> # - Miss B1' like M does B, due to original `-s ours` merge strategy
>> # - Add B2, cherry-picked as B2' into "master"
>> # - Add B3, cherry-picked as B3' into "A"
>> # - Add B4, added to "B"
>> #
>> # echo
>> # echo 'diff original-merge angel-merge:'
>> # git diff original-merge angel-merge
>>
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 11:17                       ` [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear) Phillip Wood
  2018-03-02 12:36                         ` Phillip Wood
@ 2018-03-02 16:00                         ` Jacob Keller
  2018-03-02 18:14                           ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-03-02 16:00 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

On Fri, Mar 2, 2018 at 3:17 AM, Phillip Wood <phil@philandanna.no-ip.org> wrote:
>
> It is interesting to think what it means to faithfully rebase a '-s
> ours' merge. In your example the rebase does not introduce any new
> changes into branch B that it doesn't introduce to branch A. Had it
> added a fixup to branch B1 for example or if the topology was more
> complex so that B ended up with some other changes that the rebase did
> not introduce into A, then M' would contain those extra changes whereas
> '--recreate-merges' with '-s ours' (once it supports it) would not.
>

Unless the method of merging was stored, I don't think we *can*
correctly automate resolving of "-s ours" because all we store is the
resulting content, and we don't know how or why the user generated it
as such. I believe the "correct" solution in any case would be to take
the content we DO know and then ask the user to stop for amendments.

Thanks,
Jake

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 12:36                         ` Phillip Wood
@ 2018-03-02 16:02                           ` Jacob Keller
  2018-03-02 23:33                             ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-03-02 16:02 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Igor Djordjevic, Sergey Organov, Git Mailing List,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

On Fri, Mar 2, 2018 at 4:36 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:
> On 02/03/18 11:17, Phillip Wood wrote:
>>
>> On 02/03/18 01:16, Igor Djordjevic wrote:
>>>
>>> Hi Sergey,
>>>
>>> On 01/03/2018 06:39, Sergey Organov wrote:
>>>>
>>>>>> (3) ---X1---o---o---o---o---o---X2
>>>>>>        |\                       |\
>>>>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
>>>>>>        |             \          |
>>>>>>        |              M         |
>>>>>>        |             /          |
>>>>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
>>>>>>
>>>>>
>>>>> Meh, I hope I`m rushing it now, but for example, if we had decided to
>>>>> drop commit A2 during an interactive rebase (so losing A2' from
>>>>> diagram above), wouldn`t U2' still introduce those changes back, once
>>>>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
>>>>>
>>>>> [...]
>>>>
>>>> Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
>>>> this more carefully in the first place.
>>>
>>> No problem, that`s why we`re discussing it, and I`m glad we`re
>>> aligned now, so we can move forward :)
>>>
>>>>> So while your original proposal currently seems like it could be
>>>>> working nicely for non-interactive rebase (and might be some simpler
>>>>> interactive ones), now hitting/acknowledging its first real use
>>>>> limit, my additional quick attempt[1] just tries to aid pretty
>>>>> interesting case of complicated interactive rebase, too, where we
>>>>> might be able to do better as well, still using you original proposal
>>>>> as a base idea :)
>>>>
>>>> Yes, thank you for pushing me back to reality! :-) The work and thoughts
>>>> you are putting into solving the puzzle are greatly appreciated!
>>>
>>> You`re welcome, and I am enjoying it :)
>>>
>>>> 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.
>>>
>>> Yes, this might be it...! ;)
>>>
>>> To prove myself it works, I`ve assembled a pretty crazy `-s ours`
>>> merge interactive rebase scenario, and it seems this passes the test,
>>> ticking all the check boxes (I could think of) :P
>
> Hi Igor
>
>> It is interesting to think what it means to faithfully rebase a '-s
>> ours' merge.
> I should have explained that I mean is a faithful rebase one that
> adheres to the semantics of '-s ours' (i.e. ignores any changes in the
> side branch) or one that merges new changes from the side branch into
> the content of the original merge? In your example you add B4 to B. If
> M' preserves the semantics of '-s ours' then it will not contain the
> changes in B4. I think your result does (correct me if I'm wrong) so it
> is preserving the content of the original merge rather than the
> semantics of it.
>
> Best Wishes
>
> Phillip
>

I believe this was always the outline of this type of approach, as per
Sergey's original email.

We only have the content, and we don't know the semantics (nor, I
think, should we attempt to understand or figure out the semantics).

Regards,
Jake

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-02  5:40                       ` Sergey Organov
@ 2018-03-02 17:45                         ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-02 17:45 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 02/03/2018 06:40, Sergey Organov wrote:
> 
> > So... In comparison to original merge commit M, rebased merge commit 
> > M' is expected to:
> > 
> >  - Add X9, from updated "master"
> >  - Have A1 changed to A12, due to A12 commit amendment
> >  - Keep A2, rebased as A2'
> >  - Remove A3, due to dropped A3 commit
> >  - Keep amendment from original (evil) merge commit M
> >  - Miss B1' like M does B, due to original `-s ours` merge strategy
> >  - Add B2, cherry-picked as B2' into "master"
> >  - Add B3, cherry-picked as B3' into "A"
> >  - Add B4, added to "B"
> >  - Most important, provide safety mechanism to "fail loud", being 
> >    aware of non-trivial things going on, allowing to stop for user 
> >    inspection/decision
> > 
> > 
> > There, I hope I didn`t miss any expectation. And, it _seems_ to work 
> > exactly as expected :D
> 
> That's very nice, to the level of being even suspect! :-)

Heh, indeed :) I`m already thinking of some even more complex 
situations. The more use/test cases, the merrier.

Like if number of merge parents (branches) gets changed during 
interactive rebase...

> To avoid falling into euphoria though, we need to keep in mind that
> "expectations" is rather vague concept, and we likely still need to stop
> for user amendment unless we absolutely sure nothing surprising happens.
> I.e., we better require U1'==U2' test to succeed to proceed non-stop
> automatically. Besides, it will be somewhat inline with what 'rerere'
> does.

I totally agree, and I think whatever we come up with, we`ll always 
be missing some deeper context of the original merge, so U1'==U2' 
test is a must - if it fails, even if we didn`t get any conflicts and 
could otherwise proceed automatically, better stop for user sanity check.

I`m still thinking if there could be a situation where test passes, 
but result might still be suspicious (and worth inspecting), though.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 16:00                         ` Jacob Keller
@ 2018-03-02 18:14                           ` Igor Djordjevic
  2018-03-03 17:29                             ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-02 18:14 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Jacob Keller, Sergey Organov, Git mailing list,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Phillip,

On 02/03/2018 17:00, Jacob Keller wrote:
> 
> > It is interesting to think what it means to faithfully rebase a '-s
> > ours' merge. In your example the rebase does not introduce any new
> > changes into branch B that it doesn't introduce to branch A. Had it
> > added a fixup to branch B1 for example or if the topology was more
> > complex so that B ended up with some other changes that the rebase did
> > not introduce into A, then M' would contain those extra changes whereas
> > '--recreate-merges' with '-s ours' (once it supports it) would not.
> 
> Unless the method of merging was stored, I don't think we *can*
> correctly automate resolving of "-s ours" because all we store is the
> resulting content, and we don't know how or why the user generated it
> as such. I believe the "correct" solution in any case would be to take
> the content we DO know and then ask the user to stop for amendments.
 
I agree with Jake, and for the exact same reasons.

That said, I`d like to see what mentioned '--recreate-merges' with 
'-s ours' does (or would do, once it supports it), would you have a 
pointer for me where to look at?

But if that`s something yet to come, might be it`s still open for 
discussion. I mean, even this topic started inside original 
`--recreate-merges` one[1], and hopefully it can still bring 
improvements there (sooner or later).

Thanks, Buga

[1] https://public-inbox.org/git/cover.1516225925.git.johannes.schindelin@gmx.de/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 16:02                           ` Jacob Keller
@ 2018-03-02 23:33                             ` Igor Djordjevic
  2018-03-06 10:36                               ` Phillip Wood
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-02 23:33 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Phillip,

> On Fri, Mar 2, 2018 at 4:36 AM, Phillip Wood wrote:
> > 
> > > It is interesting to think what it means to faithfully rebase a '-s
> > > ours' merge.
> > 
> > I should have explained that I mean is a faithful rebase one that
> > adheres to the semantics of '-s ours' (i.e. ignores any changes in the
> > side branch) or one that merges new changes from the side branch into
> > the content of the original merge? In your example you add B4 to B. If
> > M' preserves the semantics of '-s ours' then it will not contain the
> > changes in B4. I think your result does (correct me if I'm wrong) so it
> > is preserving the content of the original merge rather than the
> > semantics of it.

Yeah, I understood what you mean, and I see you noticed that B4 
commit, for which I did anticipate possibly bringing up a discussion 
like this ;)

I agree with Jake here, my thoughts exactly (what I wrote in that 
other subthread[1], too):

On 02/03/2018 17:02, Jacob Keller wrote:
> 
> We only have the content, and we don't know the semantics (nor, I
> think, should we attempt to understand or figure out the semantics).

Hmm, I wanted to elaborate a bit here, but that sentence seems to 
summarize the pure essence of it, and whatever I write looks like 
just repeating the same stuff again...

That`s just it. And stopping to give the user a chance to 
review/amend the result, where he might decide he actually did want 
something else - so all good.

Otherwise, I would be interested to learn how context/semantics 
guessing could provide a better default action (without introducing 
more complexity for might not be much benefit, if any).

But in the end, I guess we can just discuss the "most sane default" 
to present to the user (introduce or obsolete that new commit B4, in 
the discussed example[2]), as we should definitely stop for amending 
anyway, not proceeding automatically whenever U1' != U2'.

Oh, and what about situations where we introduce new or drop existing 
branches (which should be possible with new `--recreate-merges`)...? 
"Preserving old branch semantics" may have even less sense here - the 
(possibly heavily reorganized) content is the only thing we have, 
where context will (and should) be provided by the user.

And I guess being consistent is pretty important, too - if you add 
new content during merge rebase, it should always show up in the 
merge, period. It seems pretty confusing to find out one of the 
branches "declared special" (even more if it`s based on uncertain 
guess-work), so when you add something to it it`s just "swallowed", 
as the whole branch is always obsoleted, for now and ever.

I might even see a value in such behavior, but only as a conscious 
user action, not something done automatically... I guess? :)

Regards, Buga

[1] https://public-inbox.org/git/f26cdbe2-1bc3-02ff-7b99-12a6ebab5a70@gmail.com/
[2] https://public-inbox.org/git/f1a960dc-cc5c-e7b0-10b6-39e5516655b3@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
@ 2018-03-03  0:29                       ` Igor Djordjevic
  2018-03-05  5:00                         ` Sergey Organov
                                           ` (2 more replies)
  2018-03-05 17:52                       ` Johannes Schindelin
  2018-03-13 16:10                       ` Sergey Organov
  2 siblings, 3 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-03  0:29 UTC (permalink / raw)
  To: phillip.wood, Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Phillip,

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.

By the way, is there documentation for `git merge-recursive` 
anywhere, besides the code itself...? :$

Thanks, Buga

[2] https://public-inbox.org/git/f1a960dc-cc5c-e7b0-10b6-39e5516655b3@gmail.com/
[3] Quick test script:
-- >8 --
#!/bin/sh

# rm -rf ./.git
# rm -f ./test.txt

git init

touch ./test.txt
git add -- test.txt

# prepare repository
for i in {1..8}
do
	echo X$i >>test.txt
	git commit -am "X$i"
done

# prepare branch A
git checkout -b A
sed -i '2iA1' test.txt
git commit -am "A1"
sed -i '4iA2' test.txt
git commit -am "A2"
sed -i '6iA3' test.txt
git commit -am "A3"

# prepare branch B
git checkout -b B master
sed -i '5iB1' test.txt
git commit -am "B1"
sed -i '7iB2' test.txt
git commit -am "B2"
sed -i '9iB3' test.txt
git commit -am "B3"

git checkout -b topic A
git merge -s ours --no-commit B # merge A and B with `-s ours`
sed -i '8iM' test.txt           # amend merge commit ("evil merge")
git commit -am "M"
git tag M #original-merge

# master moves on...
git checkout master
git cherry-pick B^     # cherry-pick B2 into master
sed -i "1iX9" test.txt # add X9
git commit -am "X9"

# (0) ---X8--B2'--X9 (master)
#        |\
#        | A1---A2---A3 (A)
#        |             \
#        |              M (topic)
#        |             /
#        \-B1---B2---B3 (B)

# simple/naive demonstration of proposed merge rebasing logic
# using iterative merge-recursive, preserving merge commit manual
# amendments, testing `-s ours` merge with cherry-picking from
# obsoleted part, but still respecting interactively rebased
# added/modified/dropped/cherry-picked commits :)

git checkout -b A-prime A
git reset --hard HEAD^             # drop A3 from A
sed -i '/A1/c\A12' test.txt        # amend A1 to A12
git commit -a --amend --no-edit
git rebase master                  # rebase A onto master
git cherry-pick B                  # cherry-pick B3 into A

git checkout -b B-prime B
git rebase master                  # rebase B onto master
sed -i '12iB4' test.txt            # add B4
git commit -am "B4"

git checkout --detach M
git merge-recursive A -- M A-prime
tree="$(git write-tree)"
git merge-recursive B -- $tree B-prime
tree="$(git write-tree)"
git tag M-prime "$(git log --pretty=%B -1 M | git commit-tree $tree -p A-prime -p B-prime)"

git update-ref refs/heads/topic "$(git rev-parse M-prime)"
git checkout topic

# (1) ---X8--B2'--X9 (master)
#                 |\
#                 | A12--A2'---B3' (A)
#                 |             \
#                 |              M' (topic)
#                 |             /
#                 \-B1'--B3'---B4  (B)

# show resulting graph
# echo
# git log --all --decorate --oneline --graph

# in comparison to original merge commit M, rebased merge commit 
# M' is expected to:
#
# - Add X9, from updated "master"
# - Have A1 changed to A12, due to A12 commit amendment
# - Keep A2, rebased as A2'
# - Remove A3, due to dropped A3 commit
# - Keep amendment from original (evil) merge commit M
# - Miss B1' like M does B, due to original `-s ours` merge strategy
# - Add B2, cherry-picked as B2' into "master"
# - Add B3, cherry-picked as B3' into "A"
# - Add B4, added to "B"
#
# echo
# echo 'diff M M-prime:'
# git diff M M-prime

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 18:14                           ` Igor Djordjevic
@ 2018-03-03 17:29                             ` Igor Djordjevic
  2018-03-05  5:35                               ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-03 17:29 UTC (permalink / raw)
  To: Phillip Wood, Johannes Schindelin
  Cc: Jacob Keller, Sergey Organov, Git mailing list, Johannes Sixt,
	Junio C Hamano

On 02/03/2018 19:14, Igor Djordjevic wrote:
> 
> > > It is interesting to think what it means to faithfully rebase a '-s
> > > ours' merge. In your example the rebase does not introduce any new
> > > changes into branch B that it doesn't introduce to branch A. Had it
> > > added a fixup to branch B1 for example or if the topology was more
> > > complex so that B ended up with some other changes that the rebase did
> > > not introduce into A, then M' would contain those extra changes whereas
> > > '--recreate-merges' with '-s ours' (once it supports it) would not.
> >
> > Unless the method of merging was stored, I don't think we *can*
> > correctly automate resolving of "-s ours" because all we store is the
> > resulting content, and we don't know how or why the user generated it
> > as such. I believe the "correct" solution in any case would be to take
> > the content we DO know and then ask the user to stop for amendments.
>  
> I agree with Jake, and for the exact same reasons.
> 
> That said, I`d like to see what mentioned '--recreate-merges' with 
> '-s ours' does (or would do, once it supports it), would you have a 
> pointer for me where to look at?
> 
> But if that`s something yet to come, might be it`s still open for 
> discussion. I mean, even this topic started inside original 
> `--recreate-merges` one[1], and hopefully it can still bring 
> improvements there (sooner or later).
> 
> [1] https://public-inbox.org/git/cover.1516225925.git.johannes.schindelin@gmx.de/

Ok, I went through mentioned `--recreate-merges` topic again, and I 
think I see one crucial merge commit handling difference.

In there, as it is at the moment, merge commits are really to be 
_recreated_... as the option name seems to imply ;)

In terms of interactive rebasing, it actually comes from "todo list" 
becoming much more powerful, gaining ability to create (new) merges, 
which is wonderful from the aspect of history rewriting (what rebase 
is all about).

But, I would argue it is a different concept from actually _rebasing_ 
existing merge commits, being what we`re discussing about here.

Yes, you can use merge commit (re)creation to "rebase" existing merge 
commit so the end result is the same, being what `--recreate-merges` 
now does, but that only goes for some (uninteresting?) merge commits 
where not knowing the deeper context of how the merge commit is 
originally created is not important as no content is to be lost (due 
to missing that deeper and utterly unknown context).

But as soon as you try to do that with more complex merge commits, 
like holding manual amendments and conflict resolutions, it doesn`t 
really work as expected - which I demonstrated in my original example 
script[1] in this topic, original merge commit amendment lost on 
rebase, and even worse - that happened silently.

Now, not to get misinterpreted to pick sides in "(re)create" vs 
"rebase" merge commit discussion, I just think these two (should) have 
a different purpose, and actually having both inside interactive rebase 
is what we should be aiming for.

And that`s what I think is important to understand before any further 
discussion - _(re)creating_ existing merge commits is not the same as 
_rebasing_ them, even though the former can sometimes be used to 
achieve the latter.

Regards, Buga

[1] https://public-inbox.org/git/bbe64321-4d3a-d3fe-8bb9-58b600fabf35@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-03  0:29                       ` Igor Djordjevic
@ 2018-03-05  5:00                         ` Sergey Organov
  2018-03-06 10:52                           ` Phillip Wood
  2018-03-05 17:29                         ` Johannes Schindelin
  2018-03-06 10:45                         ` Phillip Wood
  2 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-05  5:00 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Plillip and Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
> Hi Phillip,
>
> 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 :)

Looks like sound approach and it's interesting if these 2 methods do in
fact always bring the same result. Because if we look at the (now fixed)
original approach closely, it also just gathers the changes in merge
parents into U1' and U2', then merges the changes back into the original
M (=U1=U2=UM).

Overall, this one looks like another implementation of essentially the
same method and confirms that we all have the right thought direction
here.

>
> 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.

That's an advantage of the original, yes.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-03 17:29                             ` Igor Djordjevic
@ 2018-03-05  5:35                               ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-05  5:35 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, Johannes Schindelin, Jacob Keller, Git mailing list,
	Johannes Sixt, Junio C Hamano

Hi Igor,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

[...]

> Now, not to get misinterpreted to pick sides in "(re)create" vs 
> "rebase" merge commit discussion, I just think these two (should) have 
> a different purpose, and actually having both inside interactive rebase 
> is what we should be aiming for.

Yes, if the user has an existing merge that he intends to throw away by
re-merging from scratch, he should be given a way to do it during
history editing session, no argues.

What I argue against is that this mode of operation is the default one,
let alone the only one.

> And that`s what I think is important to understand before any further 
> discussion - _(re)creating_ existing merge commits is not the same as 
> _rebasing_ them, even though the former can sometimes be used to 
> achieve the latter.

Yes, indeed. Sometimes creating new merge instead of original does the
job of rebasing the original, only it does it by pure accident.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-03  0:29                       ` Igor Djordjevic
  2018-03-05  5:00                         ` Sergey Organov
@ 2018-03-05 17:29                         ` Johannes Schindelin
  2018-03-06 23:21                           ` Igor Djordjevic
  2018-03-06 10:45                         ` Phillip Wood
  2 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-05 17:29 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Sergey Organov, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Sat, 3 Mar 2018, Igor Djordjevic wrote:

> By the way, is there documentation for `git merge-recursive` 
> anywhere, besides the code itself...? :$

I am not aware of any. The commit message adding the command is not very
illuminating (https://github.com/git-for-windows/git/commit/720d150c4):

    Add a new merge strategy by Fredrik Kuivinen.

    I really wanted to try this out, instead of asking for an adjustment
    to the 'git merge' driver and waiting.  For now the new strategy is
    called 'fredrik' and not in the list of default strategies to be tried.

    The script wants Python 2.4 so this commit also adjusts Debian and RPM
    build procecure files.

Digging through https://public-inbox.org/git/ during that time frame comes
up with this hit, though:

https://public-inbox.org/git/20050907164734.GA20198@c165.ib.student.liu.se/

which is still not a good documentation of the algorithm. You can probably
dig further yourself, but I think I can describe it very quickly here:

To merge two commits recursively, you first have to find their "merge
bases". If there was an obvious branch point, then that is the merge base.
But when you start a branch off of master, then work a bit, then merge
master, you already have two merge bases.

The trick about the recursive merge is to reduce the number of merge bases
iteratively to one. It does that by taking two merge bases, and performing
a recursive merge on them, which generates a "virtual" commit, the
condensed merge base. That one is then merged recursively with the next
merge base, until there is only one left.

A recursive merge of two commits with exactly one merge base is simply a
three-way merge.

I vaguely remember that there was something funny about the order in which
order you want to process the merge bases: if you did it in one
(chronological) direction, it worked beautifully, in the other direction
it would generate tons of merge conflicts or something like that.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
  2018-03-03  0:29                       ` Igor Djordjevic
@ 2018-03-05 17:52                       ` Johannes Schindelin
  2018-03-13 16:10                       ` Sergey Organov
  2 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-05 17:52 UTC (permalink / raw)
  To: phillip.wood
  Cc: Sergey Organov, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Phillip,

On Fri, 2 Mar 2018, Phillip Wood wrote:

> On 01/03/18 05:39, Sergey Organov wrote:
> > 
> > Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
> > 
> >> On 28/02/2018 06:19, Sergey Organov wrote:
> >>>
> >>>>> (3) ---X1---o---o---o---o---o---X2
> >>>>>        |\                       |\
> >>>>>        | A1---A2---A3---U1      | A1'--A2'--A3'--U1'
> >>>>>        |             \          |
> >>>>>        |              M         |
> >>>>>        |             /          |
> >>>>>        \-B1---B2---B3---U2      \-B1'--B2'--B3'--U2'
> >>>>>
> >>>>
> >>>> Meh, I hope I`m rushing it now, but for example, if we had decided to 
> >>>> drop commit A2 during an interactive rebase (so losing A2' from 
> >>>> diagram above), wouldn`t U2' still introduce those changes back, once 
> >>>> U1' and U2' are merged, being incorrect/unwanted behavior...? :/
> >>>
> >>> I think the method will handle this nicely.
> >>
> >> That`s what I thought as well. And then I made a test. And then the 
> >> test broke... Now, might be the test was flawed in the first place, 
> >> but thinking about it a bit more, it does seem to make sense not to 
> >> handle this case nicely :/
> > 
> > Yeah, I now see it myself. I'm sorry for being lazy and not inspecting
> > this more carefully in the first place.
> > 
> > [...]
> > 
> >> So while your original proposal currently seems like it could be 
> >> working nicely for non-interactive rebase (and might be some simpler 
> >> interactive ones), now hitting/acknowledging its first real use 
> >> limit, my additional quick attempt[1] just tries to aid pretty 
> >> interesting case of complicated interactive rebase, too, where we 
> >> might be able to do better as well, still using you original proposal 
> >> as a base idea :)
> > 
> > Yes, thank you for pushing me back to reality! :-) The work and thoughts
> > you are putting into solving the puzzle are greatly appreciated!
> > 
> > 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')

(The $tree obviously wants to be passed as parameter to commit-tree, but
it's easy to get the idea.)

> 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.

Interesting.

Basically, this is pretending that A'/B'/C' were not the result of
rebases, but of merges between A/B/C and upstream, and then performing an
octopus merge of M, A', B' and C'.

It is a beautiful application of the duality between merges and rebases:
ideally, they both result in the same tree [*1*].

That is a pretty clean and simple-to-describe paradigm to work off of, and
to reason about.

Ciao,
Dscho

Footnote *1*: I frequently use that duality in work to rebase "clean"
patches on top of upstream, and use that rebased branch to figure out how
to resolve merge conflicts when merging upstream into a long-running
branch (whose tree is identical to the pre-rebase version of the clean
patches). And yes, I *think* that this is essentially Michael Haggerty's
`git-imerge` idea.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-02 23:33                             ` Igor Djordjevic
@ 2018-03-06 10:36                               ` Phillip Wood
  2018-03-06 18:12                                 ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Phillip Wood @ 2018-03-06 10:36 UTC (permalink / raw)
  To: Igor Djordjevic, Phillip Wood
  Cc: Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

On 02/03/18 23:33, Igor Djordjevic wrote:
> Hi Phillip,
> 
>> On Fri, Mar 2, 2018 at 4:36 AM, Phillip Wood wrote:
>>>
>>>> It is interesting to think what it means to faithfully rebase a '-s
>>>> ours' merge.
>>>
>>> I should have explained that I mean is a faithful rebase one that
>>> adheres to the semantics of '-s ours' (i.e. ignores any changes in the
>>> side branch) or one that merges new changes from the side branch into
>>> the content of the original merge? In your example you add B4 to B. If
>>> M' preserves the semantics of '-s ours' then it will not contain the
>>> changes in B4. I think your result does (correct me if I'm wrong) so it
>>> is preserving the content of the original merge rather than the
>>> semantics of it.
> 
> Yeah, I understood what you mean, and I see you noticed that B4 
> commit, for which I did anticipate possibly bringing up a discussion 
> like this ;)
> 
> I agree with Jake here, my thoughts exactly (what I wrote in that 
> other subthread[1], too):
> 
> On 02/03/2018 17:02, Jacob Keller wrote:
>>
>> We only have the content, and we don't know the semantics (nor, I
>> think, should we attempt to understand or figure out the semantics).
> 
> Hmm, I wanted to elaborate a bit here, but that sentence seems to 
> summarize the pure essence of it, and whatever I write looks like 
> just repeating the same stuff again...
> 
> That`s just it. And stopping to give the user a chance to 
> review/amend the result, where he might decide he actually did want 
> something else - so all good.
> 
> Otherwise, I would be interested to learn how context/semantics 
> guessing could provide a better default action (without introducing 
> more complexity for might not be much benefit, if any).

I don't think its possible to guess the semantics of the original merge
as users can use custom merge strategies and amend the result. It would
be possible to detect and unamended '-s ours' merge but special casing
that may end up causing users more confusion rather than helping them.

> But in the end, I guess we can just discuss the "most sane default" 
> to present to the user (introduce or obsolete that new commit B4, in 
> the discussed example[2]), as we should definitely stop for amending 
> anyway, not proceeding automatically whenever U1' != U2'.

I can see the reason for that but I'm concerned that it might get
annoying with an interactive rebase as it would stop whenever one of the
commits on a topic branch that is a parent of a merge gets amended.
(squashing and reordering existing commits on a topic branch would be OK
though)

> Oh, and what about situations where we introduce new or drop existing 
> branches (which should be possible with new `--recreate-merges`)...? 
> "Preserving old branch semantics" may have even less sense here - the 
> (possibly heavily reorganized) content is the only thing we have, 
> where context will (and should) be provided by the user.

In this scheme there is now way to change the parents of a merge so
preserving the old branch sementics is well defined. If the user wants
to change the parents of the merge then this scheme wont help them.

> And I guess being consistent is pretty important, too - if you add 
> new content during merge rebase, it should always show up in the 
> merge, period. 

Yes, that should make it easy for the user to know what to expect from
rebase.

> It seems pretty confusing to find out one of the 
> branches "declared special" (even more if it`s based on uncertain 
> guess-work), so when you add something to it it`s just "swallowed", 
> as the whole branch is always obsoleted, for now and ever.
> 
> I might even see a value in such behavior, but only as a conscious 
> user action, not something done automatically... I guess? :)
> 
> Regards, Buga
> 
> [1] https://public-inbox.org/git/f26cdbe2-1bc3-02ff-7b99-12a6ebab5a70@gmail.com/
> [2] https://public-inbox.org/git/f1a960dc-cc5c-e7b0-10b6-39e5516655b3@gmail.com/
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-03  0:29                       ` Igor Djordjevic
  2018-03-05  5:00                         ` Sergey Organov
  2018-03-05 17:29                         ` Johannes Schindelin
@ 2018-03-06 10:45                         ` Phillip Wood
  2018-03-06 11:45                           ` Sergey Organov
  2018-03-06 18:12                           ` Johannes Schindelin
  2 siblings, 2 replies; 173+ messages in thread
From: Phillip Wood @ 2018-03-06 10:45 UTC (permalink / raw)
  To: Igor Djordjevic, phillip.wood, Sergey Organov
  Cc: Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 03/03/18 00:29, Igor Djordjevic wrote:
> Hi Phillip,
> 
> 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.

> 
> By the way, is there documentation for `git merge-recursive` 
> anywhere, besides the code itself...? :$

Not that I'm aware of unfortunately. It's pretty simple though

git merge-recursive [<options>] <merge-bases> -- <our-head> <their-head>

The options are listed on the 'git merge' man page but you specify them
as '--option' rather than '-Xoption'. I'm not sure what the exact
requirements are for the index, having it match <our-head> should always
be safe.

> 
> Thanks, Buga
> 
> [2] https://public-inbox.org/git/f1a960dc-cc5c-e7b0-10b6-39e5516655b3@gmail.com/
> [3] Quick test script:
> -- >8 --
> #!/bin/sh
> 
> # rm -rf ./.git
> # rm -f ./test.txt
> 
> git init
> 
> touch ./test.txt
> git add -- test.txt
> 
> # prepare repository
> for i in {1..8}
> do
> 	echo X$i >>test.txt
> 	git commit -am "X$i"
> done
> 
> # prepare branch A
> git checkout -b A
> sed -i '2iA1' test.txt
> git commit -am "A1"
> sed -i '4iA2' test.txt
> git commit -am "A2"
> sed -i '6iA3' test.txt
> git commit -am "A3"
> 
> # prepare branch B
> git checkout -b B master
> sed -i '5iB1' test.txt
> git commit -am "B1"
> sed -i '7iB2' test.txt
> git commit -am "B2"
> sed -i '9iB3' test.txt
> git commit -am "B3"
> 
> git checkout -b topic A
> git merge -s ours --no-commit B # merge A and B with `-s ours`
> sed -i '8iM' test.txt           # amend merge commit ("evil merge")
> git commit -am "M"
> git tag M #original-merge
> 
> # master moves on...
> git checkout master
> git cherry-pick B^     # cherry-pick B2 into master
> sed -i "1iX9" test.txt # add X9
> git commit -am "X9"
> 
> # (0) ---X8--B2'--X9 (master)
> #        |\
> #        | A1---A2---A3 (A)
> #        |             \
> #        |              M (topic)
> #        |             /
> #        \-B1---B2---B3 (B)
> 
> # simple/naive demonstration of proposed merge rebasing logic
> # using iterative merge-recursive, preserving merge commit manual
> # amendments, testing `-s ours` merge with cherry-picking from
> # obsoleted part, but still respecting interactively rebased
> # added/modified/dropped/cherry-picked commits :)
> 
> git checkout -b A-prime A
> git reset --hard HEAD^             # drop A3 from A
> sed -i '/A1/c\A12' test.txt        # amend A1 to A12
> git commit -a --amend --no-edit
> git rebase master                  # rebase A onto master
> git cherry-pick B                  # cherry-pick B3 into A
> 
> git checkout -b B-prime B
> git rebase master                  # rebase B onto master
> sed -i '12iB4' test.txt            # add B4
> git commit -am "B4"
> 
> git checkout --detach M
> git merge-recursive A -- M A-prime
> tree="$(git write-tree)"
> git merge-recursive B -- $tree B-prime
> tree="$(git write-tree)"
> git tag M-prime "$(git log --pretty=%B -1 M | git commit-tree $tree -p A-prime -p B-prime)"
> 
> git update-ref refs/heads/topic "$(git rev-parse M-prime)"
> git checkout topic
> 
> # (1) ---X8--B2'--X9 (master)
> #                 |\
> #                 | A12--A2'---B3' (A)
> #                 |             \
> #                 |              M' (topic)
> #                 |             /
> #                 \-B1'--B3'---B4  (B)
> 
> # show resulting graph
> # echo
> # git log --all --decorate --oneline --graph
> 
> # in comparison to original merge commit M, rebased merge commit 
> # M' is expected to:
> #
> # - Add X9, from updated "master"
> # - Have A1 changed to A12, due to A12 commit amendment
> # - Keep A2, rebased as A2'
> # - Remove A3, due to dropped A3 commit
> # - Keep amendment from original (evil) merge commit M
> # - Miss B1' like M does B, due to original `-s ours` merge strategy
> # - Add B2, cherry-picked as B2' into "master"
> # - Add B3, cherry-picked as B3' into "A"
> # - Add B4, added to "B"
> #
> # echo
> # echo 'diff M M-prime:'
> # git diff M M-prime
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-05  5:00                         ` Sergey Organov
@ 2018-03-06 10:52                           ` Phillip Wood
  2018-03-06 16:56                             ` Junio C Hamano
  0 siblings, 1 reply; 173+ messages in thread
From: Phillip Wood @ 2018-03-06 10:52 UTC (permalink / raw)
  To: Sergey Organov, Igor Djordjevic
  Cc: phillip.wood, Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 05/03/18 05:00, Sergey Organov wrote:
> Hi Plillip and Igor,
> 
> Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
>> Hi Phillip,
>>
>> 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 :)
> 
> Looks like sound approach and it's interesting if these 2 methods do in
> fact always bring the same result. Because if we look at the (now fixed)
> original approach closely, it also just gathers the changes in merge
> parents into U1' and U2', then merges the changes back into the original
> M (=U1=U2=UM).
> 
> Overall, this one looks like another implementation of essentially the
> same method and confirms that we all have the right thought direction
> here.
> 

Yes, I think they are doing the same thing. If there are no conflicts
then U1' is the should as "git merge-recursive A -- M A'". My patch
algebra isn't very good, but I think one ought to be able to show that
in the absence of conflicts the two approaches are equivalent.

>>
>> 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.
> 
> That's an advantage of the original, yes.

I wonder if just having a predicable result rather than forcing the
rebase to stop if the user just squashes a fixup commit into a topic
branch that is the parent of a merge might be more convenient in practice.

Best Wishes

Phillip

> -- Sergey
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 10:45                         ` Phillip Wood
@ 2018-03-06 11:45                           ` Sergey Organov
  2018-03-08 16:30                             ` Igor Djordjevic
  2018-03-06 18:12                           ` Johannes Schindelin
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-06 11:45 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Igor Djordjevic, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Phillip,

Phillip Wood <phillip.wood@talktalk.net> writes:

> On 03/03/18 00:29, Igor Djordjevic wrote:
>> Hi Phillip,

[...]

>> 
>> 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.

Yes, hopefully it's predictable, but is it the intended one? We don't
know, so there is still some level of uncertainty.

When in doubt, I try to find similar cases. There are two I'm aware of:

1. "git merge" just commits the result when there are no conflicts.
However, it supposedly has been run by the user just now, and thus user
can amend what he gets. That's effectively a stop for amendment from our
POV.

2. When rebasing, "rerere", when fires, stages the changes, and rebasing
stops for amendment. For me "rerere" behavior is rather annoying (I've
never in fact amended what it prepared), but I always assumed there are
good reasons it behaves this way.

Overall, to be consistent, it seems we do need to stop at U1' != U2', at
least by default. Additional options could be supported then to specify
user intentions, both on the command level and in the todo list,
provided it proves to be useful.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-02-16 13:08 [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Sergey Organov
  2018-02-18  4:16 ` Jacob Keller
  2018-02-19 23:44 ` Igor Djordjevic
@ 2018-03-06 13:26 ` Sergey Organov
  2018-03-07  6:46   ` Johannes Schindelin
  2 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-06 13:26 UTC (permalink / raw)
  To: git
  Cc: Johannes Sixt, Junio C Hamano, Jacob Keller, Johannes Schindelin,
	Igor Djordjevic, Phillip Wood

Hi,

This is v2 of my "Rebasing merges" proposal.

Significant changes are:

1. Fixed mistake in the final merge step in the original proposal: wrong
   merge base was used. Thanks everybody who provided test-cases, and
   special thanks to Igor Djordjevic <igor.d.djordjevic@gmail.com> for
   implementing and testing a few variants of the method.

2. Added discussion of the exact place where handling of special
   frequent cases such as "git merge -ours", if any, should go.

3. I now use "True Merge" name instead of former "Trivial Merge", to
   avoid confusion with what Git documentation calls "trivial merge",
   thanks to Junio C Hamano <gitster@pobox.com> for pointing this out.

During discussion of the original proposal, yet another way of
implementing a true rebase of a merge commit has been suggested by
Phillip Wood <phillip.wood@dunelm.org.uk>[1]. His method also gathers
the changes on both sides of the merge and then merges them back to the
original merge, so both methods have similar concept and differ in
implementation. It looks like both implementations bring the same
result, at least it was so in the limited testing that Igor performed.

[1] https://public-inbox.org/git/6c8749ca-ec5d-b4b7-f1a0-50d9ad2949a5@talktalk.net/


-------8<-------------8<------

By accepting the challenges raised in recent discussion of advanced
support for history rebasing and editing in Git, I hopefully figured out
a clean and elegant method of rebasing merges that I think is "The Right
Way (TM)" to perform this so far troublesome operation. ["(TM)" here has
second meaning: "True Merge" (TM), see below.]

Let me begin with quick outline of the method, to illustrate the
simplicity of the approach, and special thanks here must go to "Johannes
Sixt" <j6t@kdbg.org> for his original bright idea to use "cherry-pick
-m1" to rebase merge commits.

Given 2 original branches, b1 and b2, and a merge commit M that joins
them, suppose we've already rebased b1 to b1', and b2 to b2'. Suppose
also that B1' and B2' happen to be the tip commits on b1' and b2',
respectively.

To produce merge commit M' that joins b1' and b2', the following
operations will suffice:

1. Checkout b2' and cherry-pick -m2 M, to produce U2' (and new b2').
2. Checkout b1' and cherry-pick -m1 M, to produce U1' (and new b1').
3. Perform 3-way merge of U1' and U2' using original M as merge base,
   to get UM'.
4. Get rid of U1' and U2' by re-writing parent references of UM' from
   U1' and U2' to  B1' and B2', respectively, to produce M'.
5. Mission complete.

Let's now turn to the method itself and see why and how it actually
works.

First off, let me introduce you to my new friend, the True Merge, or
(TM) for short. By definition, (TM) is a merge that brings absolutely
no differences to the sides of the merge. (I also like to call him
"Angel Merge" (AM), both as being the most beautiful of all merges, and
as direct antithesis to "[d]evil merge"; or even "Perfect Merge" (PM),
but the latter goes after lunch time.)

Being trivial history joint and nothing else, (TM)/(AM)/(PM) is safe and
easy to be rebased (see below). However, since most of us have never met
(TM) in practice, you probably wonder how (TM) can actually help us
handle general case of rebasing of some random merge.

Let's start with this history:

    M
   / \
  B1  B2

where B1 and B2 are tip commits of 2 branches, and M is the merge commit
that joins them. Let's transform this history to the following one,
contextually equivalent to the original, by introducing 2 non-merge
utility commits U1 and U2, and a new utility merge commit UM:

    UM
   /  \
  U1   U2
  |    |
  B1   B2

were contents of all the created commits match, and are exact copies of
the original content of M. I.e., provided [A] denotes "content of commit
A", we have:

  [UM] = [U1] = [U2] = [M]

Stress again how these changes to the history preserve the exact content
of the original merge ([UM] = [M]), how U1 an U2 represent content
changes due to the merge on either side[*], and how content of neither
preceding nor subsequent commits is affected by the change of
representation.

Now observe that as [U1] = [UM], and [U2] = [UM], the UM happens to be
exactly our new friend -- the "True Merge (TM)" his true self,
introducing exactly zero changes to content. Pure history joint.

Next, we separately rebase both branches of our new representation of
the history to whatever new base we need, and we get:

  U1'  U2'
  |    |
  B1'  B2'

where U1' and U2' are rebased versions of U1 and U2, obtained by usual
rebasing methods for non-merge commits.

Finally, let's merge back our branches.

To perform the right kind of merge, notice that U1' and U2' have
diverged from U1 and U2, respectively. Further, provided [U1] = [U2] =
[UM] = [M], they have both diverged from the original merge commit
M. Therefore, to merge U1' and U2' into UM', it suffices to use 3-way
merge using original M as the merge base:

    UM'
   /  \
  U1'  U2'
   \  /
    M

Note that unlike merging of B1' and B2' that current "git rebase
--preserve-merges/--recreate-merges" performs, merging of U1' and U2' is
safe, as original UM, being (TM), doesn't itself carry any content
changes, and thus none could be missed by ignoring the UM. Essentially,
reproducing a (TM) is just joining corresponding histories back, and
that's what merge operation is all about.

If the resulting U1' and U2' happen to match ([U1'] = [U2']), we have
got our lovely (TM) back and we are clear to proceed automatically.

OTOH, if the resulting U1' and U2' differ, we should better stop for
user inspection and possible amendment of the resulting UM'. At this
point special treatment of any rather frequent cases (such as "git merge
-ours") could be applied before amendment, if need to be. Amendment by
replacing UM' with re-merge of B2' and B1' could be suggested to the
user as well. Options are plenty.

Finally, to get to our required merge commit M', we get the content of
UM' (possibly amended by the user), and record two actual parents of the
merge:

    M'
   / \
  B1' B2'

where [M'] = [UM'].

That's it. Mission complete.

The method is expected to have the following nice features:

- it carefully preserves user changes by rebasing the merge commit
itself, in a way that is semantically similar to rebasing simple
(non-merge) commits, yet it allows changes made to branches during
history editing to propagate over corresponding merge commit that joins
the branches, even automatically when the changes don't conflict, as
expected.

- it has provision for detection of even slightest chances of ending up
with surprising merge (just check if UM' is still (TM)), so that
implementation could stop for user inspection and amendment when
appropriate, yet it is capable of handling trivial cases smoothly and
automatically.

- it never falls back to simple invocation of merge operation on rebased
original branches themselves, thus avoiding the problem of lack of
knowledge of how the merge at hand has been performed in the first
place.

- it allows implementation to let user manually perform whatever merge
she wishes when suspect result is automatically detected.

- it extends trivially to octopus merges.

- it appears to be shiny to the point that it will likely be able to
handle even darkest [d]evil merges nicely, no special treatment
required.

Footnote:

[*] We may as well consider the (UM,U1,U2) trio to be semantically split
representation of git merge commit, where U1 and U2 represent content
changes to the sides, and UM represents pure history joint. Or, the
other way around, we may consider git merge commit to be optimized
representation of this trio. I think this split representation could
help to simplify reasoning about git merges in general.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 10:52                           ` Phillip Wood
@ 2018-03-06 16:56                             ` Junio C Hamano
  2018-03-08  7:05                               ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-06 16:56 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Sergey Organov, Igor Djordjevic, phillip.wood, Git mailing list,
	Jacob Keller, Johannes Schindelin, Johannes Sixt

Phillip Wood <phillip.wood@talktalk.net> writes:

> I wonder if just having a predicable result rather than forcing the
> rebase to stop if the user just squashes a fixup commit into a topic
> branch that is the parent of a merge might be more convenient in practice.

Unless I am misunderstanding what you are saying, that is pretty
much what I have automated for my daily rebuild of the 'pu' branch

Non-textual semantic conflicts are made (in the best case just once)
as a separate commit on top of mechanical auto-merge whose focus is
predictability (rather than cleverness) done by Git, and then that
separate commit is kept outside the history.  When replaying these
merges to rebuild the 'pu' branch, after resetting the tip to
'master', each topic is merged mechanically, and if such a fix-up
commit is present, "cherry-pick --no-commit" applies it and then
"commit --amend --no-edit" to adjust the merge.  I find it quite
valuable to have a separate record of what "evil" non-mechanical
adjustment was done, which I know won't be lost in the noise when
these merges need to be redone daily or more often.

The Appendix in Documentation/howto/maintain-git.txt talks about
this process.  You can see what topics have such merge-fix defined
by peeking https://github.com/gitster/git/ repository.


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 10:45                         ` Phillip Wood
  2018-03-06 11:45                           ` Sergey Organov
@ 2018-03-06 18:12                           ` Johannes Schindelin
  2018-03-07  5:08                             ` Sergey Organov
  2018-03-08 11:08                             ` Phillip Wood
  1 sibling, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-06 18:12 UTC (permalink / raw)
  To: phillip.wood
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Phillip,

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.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-06 10:36                               ` Phillip Wood
@ 2018-03-06 18:12                                 ` Johannes Schindelin
  2018-03-06 19:43                                   ` Igor Djordjevic
  2018-03-06 23:24                                   ` Junio C Hamano
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-06 18:12 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Igor Djordjevic, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Phillip & Buga,

On Tue, 6 Mar 2018, Phillip Wood wrote:

> On 02/03/18 23:33, Igor Djordjevic wrote:
> > 
> > [...]
> > Otherwise, I would be interested to learn how context/semantics 
> > guessing could provide a better default action (without introducing 
> > more complexity for might not be much benefit, if any).
> 
> I don't think its possible to guess the semantics of the original merge
> as users can use custom merge strategies and amend the result. It would
> be possible to detect and unamended '-s ours' merge but special casing
> that may end up causing users more confusion rather than helping them.

FWIW I agree.

My original plan was to always merge recursively and suggest to use `exec`
commands if anything else is needed.

But now with that excellent new idea to perform successive three-way
merges of the original merge commit with the new tips, using the old tips
as merge base, I am considering to change that.

There is a big problem here, though: consistency. See below for more
musings about that.

> > And I guess being consistent is pretty important, too - if you add new
> > content during merge rebase, it should always show up in the merge,
> > period. 
> 
> Yes, that should make it easy for the user to know what to expect from
> rebase.

Indeed. We have seen time and time again that consistent behavior is the
only thing that lets us adhere to the Law of Least Surprise.

And here lies the rub: do we really want to let `merge -C <commit>` behave
completely differently than `merge`? Granted, in one case we provide a
template merge commit, in the other case, we do not. And the idea is
already to behave differently, although that difference only extends to
the commit message so far.

But given the benefit (i.e. that the strategy to transform the original
merge commit into the new merge commit), I am willing to run that risk,
especially since I foresee only few users wanting to create new merge
commits from scratch using the `merge` todo command.

Of course, even then we need to be careful: the user might have
*changed* or *moved* the original `merge` command. For example, if the
merge command read:

	merge -C deadbee cafecafe bedbedbed

and the user switched the order of the merged branches into

	merge -C deadbee bedbedbed cafecafe

we would have to detect the changed order of the arguments so that we
could still find the original branch tips.

But the user might also have changed the branch(es) to merge completely,
in which case we might not even be able to find original branch tips.

My preferred solution would be to let the `merge` command figure out
whether the passed arguments correspond to the rewritten versions of the
original merge parents. And only in that case would we use the fancy
strategy, in all other cases we would fall back to performing a regular
recursive (or octopus) merge.

How does that sound?

It will be slightly inconsistent. But in a defendable way, I think.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-06 18:12                                 ` Johannes Schindelin
@ 2018-03-06 19:43                                   ` Igor Djordjevic
  2018-03-07  7:26                                     ` Johannes Schindelin
  2018-03-06 23:24                                   ` Junio C Hamano
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-06 19:43 UTC (permalink / raw)
  To: Johannes Schindelin, Phillip Wood
  Cc: Jacob Keller, Sergey Organov, Git Mailing List, Johannes Sixt,
	Junio C Hamano

On 06/03/2018 19:12, Johannes Schindelin wrote:
> 
> > > And I guess being consistent is pretty important, too - if you add new
> > > content during merge rebase, it should always show up in the merge,
> > > period. 
> >
> > Yes, that should make it easy for the user to know what to expect from
> > rebase.
> 
> Indeed. We have seen time and time again that consistent behavior is the
> only thing that lets us adhere to the Law of Least Surprise.
> 
> And here lies the rub: do we really want to let `merge -C <commit>` behave
> completely differently than `merge`? Granted, in one case we provide a
> template merge commit, in the other case, we do not. And the idea is
> already to behave differently, although that difference only extends to
> the commit message so far.
> 
> But given the benefit (i.e. that the strategy to transform the original
> merge commit into the new merge commit), I am willing to run that risk,
> especially since I foresee only few users wanting to create new merge
> commits from scratch using the `merge` todo command.
> 
> Of course, even then we need to be careful: the user might have
> *changed* or *moved* the original `merge` command. For example, if the
> merge command read:
> 
> 	merge -C deadbee cafecafe bedbedbed
> 
> and the user switched the order of the merged branches into
> 
> 	merge -C deadbee bedbedbed cafecafe
> 
> we would have to detect the changed order of the arguments so that we
> could still find the original branch tips.
> 
> But the user might also have changed the branch(es) to merge completely,
> in which case we might not even be able to find original branch tips.
> 
> My preferred solution would be to let the `merge` command figure out
> whether the passed arguments correspond to the rewritten versions of the
> original merge parents. And only in that case would we use the fancy
> strategy, in all other cases we would fall back to performing a regular
> recursive (or octopus) merge.
> 
> How does that sound?
> 
> It will be slightly inconsistent. But in a defendable way, I think.

I like where this discussion is heading, and here`s what I thought 
about it :)

First, starting from non-interactive rebase, I guess we may now agree 
that _rebasing_ merges is an actually expected behavior, not recreating 
them (thus keeping manual conflict resolutions and amendments, not 
losing them).

Now, interactive rebase is a totally different story, we already said 
user can change pretty much about everything, making merge 
_recreation_ to be a more sane choice, but let`s leave this other 
extreme for a brief moment.

In the least interesting situation, though, user could just review 
and close todo list, without changing anything - and in that case it 
would be important, consistency wise, to behave exactly like in case 
of non-interactive rebase, meaning still rebasing merges, not 
recreating them.

Ok, so that still aligns with what`s written so far - we need to be 
able to rebase merges interactively, too (not just recreate them), to 
stay consistent in less complex interactive rebases.

But, what if user really wants to _recreate_ merges, for whatever 
reason? Come on, this is interactive rebase we`re talking about, why 
being restrictive? :)

Here`s a twist - not letting `merge` trying to be too smart by 
figuring out whether passed arguments correspond to rewritten 
versions of the original merge parents (which would be too 
restrictive, too, I`m afraid), but just be explicit about it, instead!

So, it could be something like:

	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed


The format is still something to think about, but the point is rather 
simple - explicitly map old and new merge parents, showing this 
inside todo list by default.

This makes it much easier for later processing (and correct, no need 
to guess which one goes where), but also gives more power to the 
user, being able to decide which merge parents get "rebased", and 
which ones should go into the merge just like "new".

So if a user gets an interactive todo list like that and just closes 
it, we still have exact situation like non-interactive rebase (and no 
guessing on implementation side).

But, user might still decide to introduce new merge parents into the 
mix, even, where we could then just be merging those (as there is no 
old merge parent to actually rebase from):

	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed new-branch
	
Here, "new-branch" is something new, introduced inside interactive 
rebase, and it will be just merged into the other two (which are 
still being rebased).

Also, another example - if original merge parent "123abc" was merged 
from the other side using `-s ours` strategy, that means all the 
content this branch originally had will still be missing from the 
rebased merge (expect for what`s been cherry-picked elsewhere).

But, I would argue it`s quite legit to want to revise that decision, 
and let that content in this time. To make that happen, one would 
just remove "123abc:" from the todo list:

	merge -C deadbee cafecafe 234bcd:bedbedbed new-branch

..., meaning that only "bedbedbed" should be rebased according 
original merge parent "234bcd", where both "cafecafe" and "new-branch" 
should be just merged in, no previous context existing (no rebase).

In the end, user might even decide to swap old/new parent mapping, 
and that should be possible, too (might be pretty strange, though, 
causing conflicts, but we shouldn`t judge).

Or, one could map old merge parent to some totally new merge parent, 
like "new-branch" in that example above, all this being a fair game.

As one might suspect, to recreate merge from scratch instead, just 
drop all the old merge parents mappings:

	merge -C deadbee cafecafe bedbedbed


There, in my opinion, something like this provides the most 
consistent user experience (we always behave the same), while adding 
additional possibilities on top (getting to actually decide which 
merge parents get rebased, and which just merged), plus avoids all 
guess work (old and new merge parents, to be used for merge rebasing, 
are explicitly mapped).

What do you think? (unrelated to the parent mapping format itself, 
which could/should probably be made better, if possible)

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-05 17:29                         ` Johannes Schindelin
@ 2018-03-06 23:21                           ` Igor Djordjevic
  2018-03-07  7:04                             ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-06 23:21 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Sergey Organov, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

On 05/03/2018 18:29, Johannes Schindelin wrote:
> 
> > By the way, is there documentation for `git merge-recursive` 
> > anywhere, besides the code itself...? :$
> 
> I am not aware of any. The commit message adding the command is not very
> illuminating (https://github.com/git-for-windows/git/commit/720d150c4):
> 
>     Add a new merge strategy by Fredrik Kuivinen.
> 
>     I really wanted to try this out, instead of asking for an adjustment
>     to the 'git merge' driver and waiting.  For now the new strategy is
>     called 'fredrik' and not in the list of default strategies to be tried.
> 
>     The script wants Python 2.4 so this commit also adjusts Debian and RPM
>     build procecure files.
> 
> Digging through https://public-inbox.org/git/ during that time frame comes
> up with this hit, though:
> 
> https://public-inbox.org/git/20050907164734.GA20198@c165.ib.student.liu.se/
> 
> which is still not a good documentation of the algorithm. You can probably
> dig further yourself, but I think I can describe it very quickly here:
> 
> To merge two commits recursively, you first have to find their "merge
> bases". If there was an obvious branch point, then that is the merge base.
> But when you start a branch off of master, then work a bit, then merge
> master, you already have two merge bases.
> 
> The trick about the recursive merge is to reduce the number of merge bases
> iteratively to one. It does that by taking two merge bases, and performing
> a recursive merge on them, which generates a "virtual" commit, the
> condensed merge base. That one is then merged recursively with the next
> merge base, until there is only one left.
> 
> A recursive merge of two commits with exactly one merge base is simply a
> three-way merge.
> 
> I vaguely remember that there was something funny about the order in which
> order you want to process the merge bases: if you did it in one
> (chronological) direction, it worked beautifully, in the other direction
> it would generate tons of merge conflicts or something like that.

Thanks, this is very informative (together with the linked discussion).

Not remembering seeing this before, I wasn`t really sure if this was 
some undocumented core Git (plumbing?) utility (used withing Git 
itself, too), or just a leftover (yet still useful) sample tool.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-06 18:12                                 ` Johannes Schindelin
  2018-03-06 19:43                                   ` Igor Djordjevic
@ 2018-03-06 23:24                                   ` Junio C Hamano
  2018-03-07  7:09                                     ` Johannes Schindelin
  1 sibling, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-06 23:24 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> I don't think its possible to guess the semantics of the original merge
>> as users can use custom merge strategies and amend the result. It would
>> be possible to detect and unamended '-s ours' merge but special casing
>> that may end up causing users more confusion rather than helping them.
>
> FWIW I agree.

I think it is a mistake to sacrifice predictability only to add
cleverness that sometimes work.  Elsewhere in the thread, I think I
saw an argument to treat interactive and non-interactive something
very different, but there is no fundamental difference between them
(it is far easier with interactive to force the command to "port"
each change to a vastly different context) so having consistent
behaviour between the two cases is important, too.

>
> My original plan was to always merge recursively and suggest to use `exec`
> commands if anything else is needed.
>
> But now with that excellent new idea to perform successive three-way
> merges of the original merge commit with the new tips, using the old tips
> as merge base, I am considering to change that.

OK, does this mean we want to wait before merging the "recreate
merge" topic down to 'next'?  For more than a few weeks, it has been
slated for 'next'.


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 18:12                           ` Johannes Schindelin
@ 2018-03-07  5:08                             ` Sergey Organov
  2018-03-07  6:58                               ` Johannes Schindelin
  2018-03-08 11:08                             ` Phillip Wood
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-07  5:08 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Phillip,
>
> 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.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 13:26 ` [RFC v2] " Sergey Organov
@ 2018-03-07  6:46   ` Johannes Schindelin
  2018-03-07 13:27     ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07  6:46 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Sergey,

On Tue, 6 Mar 2018, Sergey Organov wrote:

> This is v2 of my "Rebasing merges" proposal.

Didn't we settle on Phillip's "perform successive three-way merges between
the original merge commit and the new tips with the old tips as base"
strategy? It has the following advantages:

- it is *very simple* to describe

- it is *very easy* to reason about, once it is pointed out that rebases
  and merges result in the same trees.

... and BTW...

> 3. I now use "True Merge" name instead of former "Trivial Merge", to
>    avoid confusion with what Git documentation calls "trivial merge",
>    thanks to Junio C Hamano <gitster@pobox.com> for pointing this out.

"True Merge" is probably also a candidate for improvement. If what you
refer to is a "true" merge, that means all others are "untrue" merges???

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07  5:08                             ` Sergey Organov
@ 2018-03-07  6:58                               ` Johannes Schindelin
  2018-03-07 14:34                                 ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07  6:58 UTC (permalink / raw)
  To: Sergey Organov
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Wed, 7 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> 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?

What would your approach (that still has no satisfyingly trivial
explanation, in my mind) do if somebody edited a `merge` command and let
it merge a completely unrelated commit?

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 23:21                           ` Igor Djordjevic
@ 2018-03-07  7:04                             ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07  7:04 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Sergey Organov, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Wed, 7 Mar 2018, Igor Djordjevic wrote:

> On 05/03/2018 18:29, Johannes Schindelin wrote:
> > 
> > > By the way, is there documentation for `git merge-recursive`
> > > anywhere, besides the code itself...? :$
> > 
> > I am not aware of any. The commit message adding the command is not very
> > illuminating (https://github.com/git-for-windows/git/commit/720d150c4):
> > 
> >     Add a new merge strategy by Fredrik Kuivinen.
> > 
> >     I really wanted to try this out, instead of asking for an adjustment
> >     to the 'git merge' driver and waiting.  For now the new strategy is
> >     called 'fredrik' and not in the list of default strategies to be tried.
> > 
> >     The script wants Python 2.4 so this commit also adjusts Debian and RPM
> >     build procecure files.
> > 
> > Digging through https://public-inbox.org/git/ during that time frame comes
> > up with this hit, though:
> > 
> > https://public-inbox.org/git/20050907164734.GA20198@c165.ib.student.liu.se/
> > 
> > which is still not a good documentation of the algorithm. You can probably
> > dig further yourself, but I think I can describe it very quickly here:
> > 
> > To merge two commits recursively, you first have to find their "merge
> > bases". If there was an obvious branch point, then that is the merge base.
> > But when you start a branch off of master, then work a bit, then merge
> > master, you already have two merge bases.
> > 
> > The trick about the recursive merge is to reduce the number of merge bases
> > iteratively to one. It does that by taking two merge bases, and performing
> > a recursive merge on them, which generates a "virtual" commit, the
> > condensed merge base. That one is then merged recursively with the next
> > merge base, until there is only one left.
> > 
> > A recursive merge of two commits with exactly one merge base is simply a
> > three-way merge.
> > 
> > I vaguely remember that there was something funny about the order in which
> > order you want to process the merge bases: if you did it in one
> > (chronological) direction, it worked beautifully, in the other direction
> > it would generate tons of merge conflicts or something like that.
> 
> Thanks, this is very informative (together with the linked discussion).
> 
> Not remembering seeing this before, I wasn`t really sure if this was 
> some undocumented core Git (plumbing?) utility (used withing Git 
> itself, too), or just a leftover (yet still useful) sample tool.

You raise a good point. The "recursive merge" is not really documented
properly. Maybe you can enhance my vague description and provide a patch
for Documentation/technical/?

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-06 23:24                                   ` Junio C Hamano
@ 2018-03-07  7:09                                     ` Johannes Schindelin
  2018-03-07 18:20                                       ` Junio C Hamano
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07  7:09 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Hi Junio,

On Tue, 6 Mar 2018, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> I don't think its possible to guess the semantics of the original merge
> >> as users can use custom merge strategies and amend the result. It would
> >> be possible to detect and unamended '-s ours' merge but special casing
> >> that may end up causing users more confusion rather than helping them.
> >
> > FWIW I agree.
> 
> I think it is a mistake to sacrifice predictability only to add
> cleverness that sometimes work.  Elsewhere in the thread, I think I
> saw an argument to treat interactive and non-interactive something
> very different, but there is no fundamental difference between them
> (it is far easier with interactive to force the command to "port"
> each change to a vastly different context) so having consistent
> behaviour between the two cases is important, too.

I could be swayed both ways, but Buga already pointed out that we do not
have to compromise any consistency, simply by adding some syntactic sugar
to the `merge` command so that the different behavior is *explicit*.

> > My original plan was to always merge recursively and suggest to use `exec`
> > commands if anything else is needed.
> >
> > But now with that excellent new idea to perform successive three-way
> > merges of the original merge commit with the new tips, using the old tips
> > as merge base, I am considering to change that.
> 
> OK, does this mean we want to wait before merging the "recreate
> merge" topic down to 'next'?  For more than a few weeks, it has been
> slated for 'next'.

Maybe a few more days.

My current thinking is to rework the handling of -c vs -C and *not* have
two different todo_command enum values, but rather introduce an unsigned
integer that has flags such as TODO_MERGE_EDIT.

And for this new behavior, we could introduce a new flag
(TODO_MERGE_REBASE_MERGE_COMMIT or something less unwieldy) and set that
flag via

	merge -R -C <commit> <merge>...

(i.e. via a new flag `-R`).

I want to discuss this in the other subthread, though.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-06 19:43                                   ` Igor Djordjevic
@ 2018-03-07  7:26                                     ` Johannes Schindelin
  2018-03-08 11:20                                       ` Phillip Wood
  2018-03-08 15:16                                       ` Igor Djordjevic
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07  7:26 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Tue, 6 Mar 2018, Igor Djordjevic wrote:

> On 06/03/2018 19:12, Johannes Schindelin wrote:
> > 
> > > > And I guess being consistent is pretty important, too - if you add new
> > > > content during merge rebase, it should always show up in the merge,
> > > > period. 
> > >
> > > Yes, that should make it easy for the user to know what to expect from
> > > rebase.
> > 
> > [...]
> > 
> > It will be slightly inconsistent. But in a defendable way, I think.
> 
> I like where this discussion is heading, and here`s what I thought 
> about it :)
> 
> [...]
> 
> Here`s a twist - not letting `merge` trying to be too smart by 
> figuring out whether passed arguments correspond to rewritten 
> versions of the original merge parents (which would be too 
> restrictive, too, I`m afraid), but just be explicit about it, instead!

That's the missing piece, I think.

> So, it could be something like:
> 
> 	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed

I like where this is heading, too, but I do not think that we can do this
on a per-MERGE_HEAD basis. The vast majority of merge commits, in
practice, have two parents. So the `merge` command would actually only
have one revision to merge (because HEAD is the implicit first parent). So
that is easy.

But as soon as you go octopus, you can either perform an octopus merge, or
rebase the original merge commit. You cannot really mix and match here.

Unless we reimplement the octopus merge (which works quite a bit
differently from the "rebase merge commit" strategy, even if it is
incremental, too), which has its own challenges: if there are merge
conflicts before merging the last MERGE_HEAD, the octopus merge will exit
with status 2, telling you "Should not be doing an octopus.". While we
will want to keep merge conflict markers and continue with the "rebase the
original merge commit" strategy.

And it would slam the door shut for adding support for *other* merge
strategies to perform a more-than-two-parents merge.

Also, I do not think that it makes a whole lot of sense in practice to let
users edit what will be used for "original parent". If the user wants to
do complicated stuff, they can already do that, via `exec`. The `merge`
command really should be about facilitating common workflows, guiding the
user to what is sane.

Currently my favorite idea is to introduce a new flag: -R (for "rebase the
original merge commit"). It would look like this:

	merge -R -C <original-merge> <merge-head> # <oneline>

This flag would of course trigger the consistency check (does the number
of parents of the original merge commit agree with the parameter list? Was
an original merge commit specified to begin with?), and it would not fall
back to the recursive merge, but error out if that check failed.

Side note: I wonder whether we really need to perform the additional check
that ensures that the <merge-head> refers to the rewritten version of the
original merge commit's parent.

Second side note: if we can fast-forward, currently we prefer that, and I
think we should keep that behavior with -R, too.

If the user wants to force a new merge, they simply remove that -R flag.

What do you think?

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07  6:46   ` Johannes Schindelin
@ 2018-03-07 13:27     ` Sergey Organov
  2018-03-07 14:08       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-07 13:27 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Tue, 6 Mar 2018, Sergey Organov wrote:
>
>> This is v2 of my "Rebasing merges" proposal.
>
> Didn't we settle on Phillip's "perform successive three-way merges between
> the original merge commit and the new tips with the old tips as base"
> strategy?

It seems you did, dunno exactly why.

The main problem with this decision is that we still don't see how and
when to stop for user amendment using this method. OTOH, the original
has this issue carefully discussed.

> It has the following advantages:
>
> - it is *very simple* to describe

The original is as simple if not simpler:

"rebase sides of the merge commit and then three-way merge them back
using original merge commit as base"

No problems with octopuses, and no virtual merge bases of recursive
merges to reason about.

> - it is *very easy* to reason about, once it is pointed out that rebases
>   and merges result in the same trees.

The original is as easy to reason about, if not easier, especially
as recursive merge strategy is not being used there in new ways.

I honestly don't see any advantages of Phillip's method over the
original, except personal preferences. At the same time, I have no
objection of using it either, provided consistency check problem is
solved there as well.

>
> ... and BTW...
>
>> 3. I now use "True Merge" name instead of former "Trivial Merge", to
>>    avoid confusion with what Git documentation calls "trivial merge",
>>    thanks to Junio C Hamano <gitster@pobox.com> for pointing this out.
>
> "True Merge" is probably also a candidate for improvement. If what you
> refer to is a "true" merge, that means all others are "untrue"
> merges???

[d]evil merges, obviously.

Seriously, it's pure history joint. Just "joint' will do.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07 13:27     ` Sergey Organov
@ 2018-03-07 14:08       ` Johannes Schindelin
  2018-03-07 15:16         ` Sergey Organov
  2018-03-08 19:58         ` Igor Djordjevic
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-07 14:08 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Sergey,

On Wed, 7 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > On Tue, 6 Mar 2018, Sergey Organov wrote:
> >
> >> This is v2 of my "Rebasing merges" proposal.
> >
> > Didn't we settle on Phillip's "perform successive three-way merges
> > between the original merge commit and the new tips with the old tips
> > as base" strategy?
> 
> It seems you did, dunno exactly why.

That is not true. You make it sound like I was the only one who liked
this, and not Phillip and Buga, too.

Are you interested in the best solution, or in your solution :-)

> The main problem with this decision is that we still don't see how and
> when to stop for user amendment using this method. OTOH, the original
> has this issue carefully discussed.

Why would we want to stop, unless there are merge conflicts?

> > It has the following advantages:
> >
> > - it is *very simple* to describe
> 
> The original is as simple if not simpler:
> 
> "rebase sides of the merge commit and then three-way merge them back
> using original merge commit as base"

And that is also wrong, as I had proved already! Only Buga's addition made
it robust against dropping/modifying commits, and that addition also makes
it more complicated.

And it still has no satisfactory simple explanation why it works.

> No problems with octopuses, and no virtual merge bases of recursive
> merges to reason about.

But those are no problems for Phillip's strategy, either!

So your point is...?

> > - it is *very easy* to reason about, once it is pointed out that
> > rebases and merges result in the same trees.
> 
> The original is as easy to reason about, if not easier, especially as
> recursive merge strategy is not being used there in new ways.

So do it. I still have to hear a single-sentence, clear and obvious
explanation why it works.

And please do not describe why your original version works, because it
does not work. Describe why the one amended with Buga's hack works.

> I honestly don't see any advantages of Phillip's method over the
> original, except personal preferences. At the same time, I have no
> objection of using it either, provided consistency check problem is
> solved there as well.

Okay, let me reiterate then, because I do not want this point to be
missed:

Phillip's method is essentially merging the new tips into the original
merge, pretending that the new tips were not rebased but merged into
upstream.

So it exploits the duality of the rebase and merge operation, which both
result in identical trees (potentially after resolving merge conflicts).

I cannot think of any such interpretation for your proposal augmented by
Buga's fix-ups. And I haven't heard any such interpretation from your
side, either.

> >> 3. I now use "True Merge" name instead of former "Trivial Merge", to
> >>    avoid confusion with what Git documentation calls "trivial merge",
> >>    thanks to Junio C Hamano <gitster@pobox.com> for pointing this out.
> >
> > "True Merge" is probably also a candidate for improvement. If what you
> > refer to is a "true" merge, that means all others are "untrue"
> > merges???
> 
> [d]evil merges, obviously.
> 
> Seriously, it's pure history joint. Just "joint' will do.

You might want to try harder to stick with the existing nomenclature. That
would make it a lot easier to discuss.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07  6:58                               ` Johannes Schindelin
@ 2018-03-07 14:34                                 ` Sergey Organov
  2018-03-08  6:45                                   ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-07 14:34 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Wed, 7 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> 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

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07 14:08       ` Johannes Schindelin
@ 2018-03-07 15:16         ` Sergey Organov
  2018-03-08  7:01           ` Johannes Schindelin
  2018-03-08 19:58         ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-07 15:16 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Sergey,
>
> On Wed, 7 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > On Tue, 6 Mar 2018, Sergey Organov wrote:
>> >
>> >> This is v2 of my "Rebasing merges" proposal.
>> >
>> > Didn't we settle on Phillip's "perform successive three-way merges
>> > between the original merge commit and the new tips with the old tips
>> > as base" strategy?
>> 
>> It seems you did, dunno exactly why.
>
> That is not true. You make it sound like I was the only one who liked
> this, and not Phillip and Buga, too.
>
> Are you interested in the best solution, or in your solution :-)

I'm interested in any that works, and only you say that those suggested
by Phillip is somehow superior. I still believe it's mine that superior,
even if slightly.

>
>> The main problem with this decision is that we still don't see how and
>> when to stop for user amendment using this method. OTOH, the original
>> has this issue carefully discussed.
>
> Why would we want to stop, unless there are merge conflicts?

There is somewhat lengthy discussion about it that you probably missed.
Not to repeat it, just see how 'rerere' works when it fires during
rebase, even with no conflicts.

>
>> > It has the following advantages:
>> >
>> > - it is *very simple* to describe
>> 
>> The original is as simple if not simpler:
>> 
>> "rebase sides of the merge commit and then three-way merge them back
>> using original merge commit as base"
>
> And that is also wrong, as I had proved already! Only Buga's addition made
> it robust against dropping/modifying commits, and that addition also makes
> it more complicated.

No. Get your facts straight. The [RFC v2] already fixed that original
mistake. Could you please finally read it?

> And it still has no satisfactory simple explanation why it works.

It has. It's there in the [RFC v2]. You seem to be the only one who
doesn't get it. I suppose you just didn't bother to read.

>> No problems with octopuses, and no virtual merge bases of recursive
>> merges to reason about.
>
> But those are no problems for Phillip's strategy, either!

I thought it was you who started to discuss virtual merge bases and
related problems, as well as how it's difficult to support octopus
merges, but it's fine with me if there are none of these problems.

>
> So your point is...?

Still the same -- use what's better, the [RFC v2].

>
>> > - it is *very easy* to reason about, once it is pointed out that
>> > rebases and merges result in the same trees.
>> 
>> The original is as easy to reason about, if not easier, especially as
>> recursive merge strategy is not being used there in new ways.
>
> So do it. I still have to hear a single-sentence, clear and obvious
> explanation why it works.
>
> And please do not describe why your original version works, because it
> does not work.

Original [RFC] didn't work because of rather simple mistake that I've
already admitted and fixed. [RFC v2] has got the fix. Read [RFC v2] and
get your facts straight.

> Describe why the one amended with Buga's hack works.

It doesn't matter as these hacks are not needed anymore.

>
>> I honestly don't see any advantages of Phillip's method over the
>> original, except personal preferences. At the same time, I have no
>> objection of using it either, provided consistency check problem is
>> solved there as well.
>
> Okay, let me reiterate then, because I do not want this point to be
> missed:
>
> Phillip's method is essentially merging the new tips into the original
> merge, pretending that the new tips were not rebased but merged into
> upstream.
>
> So it exploits the duality of the rebase and merge operation, which both
> result in identical trees (potentially after resolving merge
> conflicts).
>
> I cannot think of any such interpretation for your proposal augmented by
> Buga's fix-ups. And I haven't heard any such interpretation from your
> side, either.

No fix-ups or augmentations are needed. It was a mistake that has been
fixed in [RFC v2]. You've missed essential part of the discussion.

Read the [RFC v2], please:

Significant changes are:

1. Fixed mistake in the final merge step in the original proposal: wrong
   merge base was used.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-07  7:09                                     ` Johannes Schindelin
@ 2018-03-07 18:20                                       ` Junio C Hamano
  2018-03-08  7:03                                         ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-07 18:20 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> OK, does this mean we want to wait before merging the "recreate
>> merge" topic down to 'next'?  For more than a few weeks, it has been
>> slated for 'next'.
>
> Maybe a few more days.
> ...
> I want to discuss this in the other subthread, though.

If we are talking about a drastic change, a few more days may not be
sufficient, but we are not in a hurry, as this already sounds like a
2.18 material anyway.  As you made it clear that it is OK not to
merge the current one for now, my objective of asking the question
is already satisfied ;-)


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07 14:34                                 ` Sergey Organov
@ 2018-03-08  6:45                                   ` Johannes Schindelin
  2018-03-12 12:31                                     ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-08  6:45 UTC (permalink / raw)
  To: Sergey Organov
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Wed, 7 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > 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?

Imagine a todo list that contains this line

	merge -C abcdef 123456

and now the user edits it (this is an interactive rebase, after all),
adding another merge head:

	merge -C abcdef 987654 123456

Now your strategy would have a serious problem: to find the original
version of 987654. If there was one.

> > 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.

But I already pointed out how that would undo a commit having been
dropped.

> > 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.

That is assuming a lot. That is exactly what this consistency check is
for, that I mentioned earlier, and which you listed as a downside of
Phillip's strategy (forgetting that your strategy has the same downside,
so...).

But I guess that you are still talking about the non-interactive version
of the rebase, and missed that our conversation proceeded to the point
where we want that same strategy to work *also* in the interactive version
(and not have a completely different functionality depending whether you
use --interactive or not)?

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07 15:16         ` Sergey Organov
@ 2018-03-08  7:01           ` Johannes Schindelin
  2018-03-12 12:42             ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-08  7:01 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Sergey,

On Wed, 7 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >
> > On Wed, 7 Mar 2018, Sergey Organov wrote:
> >
> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >> 
> >> > On Tue, 6 Mar 2018, Sergey Organov wrote:
> >> >
> >> >> This is v2 of my "Rebasing merges" proposal.
> >> >
> >> > Didn't we settle on Phillip's "perform successive three-way merges
> >> > between the original merge commit and the new tips with the old
> >> > tips as base" strategy?
> >> 
> >> It seems you did, dunno exactly why.
> >
> > That is not true. You make it sound like I was the only one who liked
> > this, and not Phillip and Buga, too.
> >
> > Are you interested in the best solution, or in your solution :-)
> 
> I'm interested in any that works, and only you say that those suggested
> by Phillip is somehow superior. I still believe it's mine that superior,
> even if slightly.

That is misrepresenting what happened.

First, you came up with a strategy. I pointed out shortcomings that
implied that we cannot use it unchanged. Then, Buga fixed your strategy by
using additional steps (making the process more complicated than before,
still without a simple-enough explanation for my liking, fixing the
shortcomings). Then, Phillip presented a super-simple strategy and Buga
confirmed that it also fixes the shortcomings I pointed out.

I am very excited that we finally found something that works *and* is easy
to reason about.

Let's focus on that strategy rather than going back to the strategy which
has known flaws and only an unsatisfyingly complex explanation.

> >> The main problem with this decision is that we still don't see how
> >> and when to stop for user amendment using this method. OTOH, the
> >> original has this issue carefully discussed.
> >
> > Why would we want to stop, unless there are merge conflicts?
> 
> There is somewhat lengthy discussion about it that you probably missed.
> Not to repeat it, just see how 'rerere' works when it fires during
> rebase, even with no conflicts.

I did not miss that discussion. My question was a follow-up: Why would we
want to stop, unless there are merge conflicts?

> >> > It has the following advantages:
> >> >
> >> > - it is *very simple* to describe
> >> 
> >> The original is as simple if not simpler:
> >> 
> >> "rebase sides of the merge commit and then three-way merge them back
> >> using original merge commit as base"
> >
> > And that is also wrong, as I had proved already! Only Buga's addition made
> > it robust against dropping/modifying commits, and that addition also makes
> > it more complicated.
> 
> No. Get your facts straight. The [RFC v2] already fixed that original
> mistake. Could you please finally read it?

I do not see Buga's additions, and it is still a lengthy document that is
hard to understand.

Phillip's alternative, in contrast, fit in at most two 80x25 pages and was
intuitive (at least after seeing that the tree resulting from a merge is
identical to the tree resulting from a rebase, once all merge conflicts
are handled appropriately).

> > And it still has no satisfactory simple explanation why it works.
> 
> It has. It's there in the [RFC v2]. You seem to be the only one who
> doesn't get it. I suppose you just didn't bother to read.

I tried to read it, and got lost in all those figures that really do not
do anything to make this strategy obvious to me.

Granted, I now know *how* it works.

I gave up understanding *why* it is supposed to work.

With Phillip's mail, it only took me 5 minutes to get a rudimentary
understanding why it works.

> >> No problems with octopuses, and no virtual merge bases of recursive
> >> merges to reason about.
> >
> > But those are no problems for Phillip's strategy, either!
> 
> I thought it was you who started to discuss virtual merge bases and
> related problems, as well as how it's difficult to support octopus
> merges, but it's fine with me if there are none of these problems.

Yes, I started explaining virtual merge bases, and how they are used in
the recursive merge. Because I was asked how the recursive merge works,
and I happen to know how it works very intimately.

> > So your point is...?
> 
> Still the same -- use what's better, the [RFC v2].

I strongly disagree that your approach is superior. It is more complex,
and still has no simple answer to the question "why is this supposed to do
what I want it to do?"

It does a lot of criss-crossing, and when I see that, I immediately
suspect that it would lose information e.g. when commits were amended
during the rebase. There are just way too many paths for obsolete changes
to creep in again.

> >> > - it is *very easy* to reason about, once it is pointed out that
> >> > rebases and merges result in the same trees.
> >> 
> >> The original is as easy to reason about, if not easier, especially as
> >> recursive merge strategy is not being used there in new ways.
> >
> > So do it. I still have to hear a single-sentence, clear and obvious
> > explanation why it works.

Please.

> > And please do not describe why your original version works, because it
> > does not work.
> 
> Original [RFC] didn't work because of rather simple mistake that I've
> already admitted and fixed. [RFC v2] has got the fix. Read [RFC v2] and
> get your facts straight.

I don't care how many mistakes you made, and where the original ideas had
to be enhanced. I am only interested in the outcome.

We already have a nice, simple outcome, and to be quite honest: I find
this discussion here a bit pointless. Why would I abandon a simple
strategy that obviously works for a complex strategy where it still is not
obvious under what circumstances it works, and why?

> > Describe why the one amended with Buga's hack works.
> 
> It doesn't matter as these hacks are not needed anymore.

So maybe you want to also describe the "interdiff", and not expect anybody
to read two lengthy documents and figure out where the changes are.

> >> I honestly don't see any advantages of Phillip's method over the
> >> original, except personal preferences. At the same time, I have no
> >> objection of using it either, provided consistency check problem is
> >> solved there as well.
> >
> > Okay, let me reiterate then, because I do not want this point to be
> > missed:
> >
> > Phillip's method is essentially merging the new tips into the original
> > merge, pretending that the new tips were not rebased but merged into
> > upstream.
> >
> > So it exploits the duality of the rebase and merge operation, which both
> > result in identical trees (potentially after resolving merge
> > conflicts).
> >
> > I cannot think of any such interpretation for your proposal augmented by
> > Buga's fix-ups. And I haven't heard any such interpretation from your
> > side, either.
> 
> No fix-ups or augmentations are needed. It was a mistake that has been
> fixed in [RFC v2]. You've missed essential part of the discussion.
> 
> Read the [RFC v2], please:

Okay, I am done here. If all my questions and all my concerns are answered
with the suggestion to spend an hour pouring over a lengthy description of
your strategy that is so much more complex than my preferred alternative,
asking me to figure out from that long document what the answers to my
questions are, then I respectfully decline. I do have other things to care
about, too, and I will *not* spend an hour trying to figure out answers
only because you refuse to give them directly.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-07 18:20                                       ` Junio C Hamano
@ 2018-03-08  7:03                                         ` Johannes Schindelin
  2018-03-08  8:11                                           ` Junio C Hamano
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-08  7:03 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Hi Junio,

On Wed, 7 Mar 2018, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> OK, does this mean we want to wait before merging the "recreate
> >> merge" topic down to 'next'?  For more than a few weeks, it has been
> >> slated for 'next'.
> >
> > Maybe a few more days.
> > ...
> > I want to discuss this in the other subthread, though.
> 
> If we are talking about a drastic change, a few more days may not be
> sufficient, but we are not in a hurry, as this already sounds like a
> 2.18 material anyway.

It is not at all a drastic change. It will actually make the current patch
series better (simplifying the "can we fast-forward?" check).

I just want to make sure that I already have Phillip's strategy working,
but it will be yet another topic branch on top of the topic branch that
will add support for octopus merges *after* the current --recreate-merges
topic branch ;-)

> As you made it clear that it is OK not to merge the current one for now,
> my objective of asking the question is already satisfied ;-)

Depending how much GitMerge will occupy my time, I hope to have something
polished by tomorrow.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 16:56                             ` Junio C Hamano
@ 2018-03-08  7:05                               ` Johannes Schindelin
  2018-03-08  8:18                                 ` Junio C Hamano
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-08  7:05 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Sergey Organov, Igor Djordjevic, phillip.wood,
	Git mailing list, Jacob Keller, Johannes Sixt

Hi Junio,

On Tue, 6 Mar 2018, Junio C Hamano wrote:

> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
> > I wonder if just having a predicable result rather than forcing the
> > rebase to stop if the user just squashes a fixup commit into a topic
> > branch that is the parent of a merge might be more convenient in
> > practice.
> 
> Unless I am misunderstanding what you are saying, that is pretty much
> what I have automated for my daily rebuild of the 'pu' branch
> 
> Non-textual semantic conflicts are made (in the best case just once)
> as a separate commit on top of mechanical auto-merge whose focus is
> predictability (rather than cleverness) done by Git, and then that
> separate commit is kept outside the history.  When replaying these
> merges to rebuild the 'pu' branch, after resetting the tip to
> 'master', each topic is merged mechanically, and if such a fix-up
> commit is present, "cherry-pick --no-commit" applies it and then
> "commit --amend --no-edit" to adjust the merge.  I find it quite
> valuable to have a separate record of what "evil" non-mechanical
> adjustment was done, which I know won't be lost in the noise when
> these merges need to be redone daily or more often.

So essentially, you have something that `git rerere` would have learned,
but as a commit?

Might make sense to make that procedure more accessible to others, too ;-)
I know that *I* would use it.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08  7:03                                         ` Johannes Schindelin
@ 2018-03-08  8:11                                           ` Junio C Hamano
  2018-03-09 17:09                                             ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-08  8:11 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> If we are talking about a drastic change, a few more days may not be
>> sufficient, but we are not in a hurry, as this already sounds like a
>> 2.18 material anyway.
>
> It is not at all a drastic change. It will actually make the current patch
> series better (simplifying the "can we fast-forward?" check).
>
> I just want to make sure that I already have Phillip's strategy working,
> but it will be yet another topic branch on top of the topic branch that
> will add support for octopus merges *after* the current --recreate-merges
> topic branch ;-)

Oh, if the "not redoing the merge afresh, but attempt to reuse the
previous merge" that was discussed is going to be done as an
update/addition to the "redo the merge afresh" you already had in
production forever (and I had in 'pu' for quite a while in various
polished-ness during iteration), then I do prefer merging down what
has already proven to be 'next' worthy without waiting for the
discussion and your local verification of Phillip's new thing,
especially given that you'll be giving an explicit control to the
users which variant of "merge" insn will be used and the addition
of the Phillip's thing won't be a backward-compatibility issue when
it comes later.

>> As you made it clear that it is OK not to merge the current one for now,
>> my objective of asking the question is already satisfied ;-)
>
> Depending how much GitMerge will occupy my time, I hope to have something
> polished by tomorrow.

My "for now" above was just for the coming few days.  Don't rush
things, and use valuable in-person time wisely, and have fun.

Thanks.


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08  7:05                               ` Johannes Schindelin
@ 2018-03-08  8:18                                 ` Junio C Hamano
  2018-03-11 11:56                                   ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-08  8:18 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Sergey Organov, Igor Djordjevic, phillip.wood,
	Git mailing list, Jacob Keller, Johannes Sixt

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> Non-textual semantic conflicts are made (in the best case just once)
>> as a separate commit on top of mechanical auto-merge whose focus is
>> predictability (rather than cleverness) done by Git, and then that
>> separate commit is kept outside the history.  When replaying these
>> merges to rebuild the 'pu' branch, after resetting the tip to
>> 'master', each topic is merged mechanically, and if such a fix-up
>> commit is present, "cherry-pick --no-commit" applies it and then
>> "commit --amend --no-edit" to adjust the merge.  I find it quite
>> valuable to have a separate record of what "evil" non-mechanical
>> adjustment was done, which I know won't be lost in the noise when
>> these merges need to be redone daily or more often.
>
> So essentially, you have something that `git rerere` would have learned,
> but as a commit?

You probably wouldn't be asking that if you read what you cut out
when you quoted above ;-) 

There are a collection of cherry-pickable commits in hierarchy under
refs/merge-fix.  They are indexed by the branch that will cause
semantic conflicts that do not involve textual conflicts at all (the
important implication of which is that 'rerere' fundamentally will
not trigger to help resolving them) [*1*], and are used to create
evil merge when a corresponding branch is merged to 'pu' (and down).

[Footnote]

*1* One topic adds an extra parameter to read_index_from() that has
been and still is defined in a file and merged to 'pu' first, while
another topic adds a new callsite for the same function in a file
that the former topic does not touch, hence a merge of the latter
topic has no textual conflict to the file with a new callsite, but
still needs adjusting.  That sort of think.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 18:12                           ` Johannes Schindelin
  2018-03-07  5:08                             ` Sergey Organov
@ 2018-03-08 11:08                             ` Phillip Wood
  1 sibling, 0 replies; 173+ messages in thread
From: Phillip Wood @ 2018-03-08 11:08 UTC (permalink / raw)
  To: Johannes Schindelin, phillip.wood
  Cc: Igor Djordjevic, Sergey Organov, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

On 06/03/18 18:12, Johannes Schindelin wrote:
> Hi Phillip,
> 
> 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.

Yes I should have thought of that given I've just been working on making
'add -p' more robust when there are lots of identical lines.

> 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.
> 
> Ciao,
> Dscho
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-07  7:26                                     ` Johannes Schindelin
@ 2018-03-08 11:20                                       ` Phillip Wood
  2018-03-08 12:16                                         ` Phillip Wood
                                                           ` (2 more replies)
  2018-03-08 15:16                                       ` Igor Djordjevic
  1 sibling, 3 replies; 173+ messages in thread
From: Phillip Wood @ 2018-03-08 11:20 UTC (permalink / raw)
  To: Johannes Schindelin, Igor Djordjevic
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

On 07/03/18 07:26, Johannes Schindelin wrote:
> Hi Buga,
> 
> On Tue, 6 Mar 2018, Igor Djordjevic wrote:
> 
>> On 06/03/2018 19:12, Johannes Schindelin wrote:
>>>
>>>>> And I guess being consistent is pretty important, too - if you add new
>>>>> content during merge rebase, it should always show up in the merge,
>>>>> period. 
>>>>
>>>> Yes, that should make it easy for the user to know what to expect from
>>>> rebase.
>>>
>>> [...]
>>>
>>> It will be slightly inconsistent. But in a defendable way, I think.
>>
>> I like where this discussion is heading, and here`s what I thought 
>> about it :)
>>
>> [...]
>>
>> Here`s a twist - not letting `merge` trying to be too smart by 
>> figuring out whether passed arguments correspond to rewritten 
>> versions of the original merge parents (which would be too 
>> restrictive, too, I`m afraid), but just be explicit about it, instead!
> 
> That's the missing piece, I think.
> 
>> So, it could be something like:
>>
>> 	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed
> 
> I like where this is heading, too, but I do not think that we can do this
> on a per-MERGE_HEAD basis. The vast majority of merge commits, in
> practice, have two parents. So the `merge` command would actually only
> have one revision to merge (because HEAD is the implicit first parent). So
> that is easy.
> 
> But as soon as you go octopus, you can either perform an octopus merge, or
> rebase the original merge commit. You cannot really mix and match here.
> 
> Unless we reimplement the octopus merge (which works quite a bit
> differently from the "rebase merge commit" strategy, even if it is
> incremental, too), which has its own challenges: if there are merge
> conflicts before merging the last MERGE_HEAD, the octopus merge will exit
> with status 2, telling you "Should not be doing an octopus.". While we
> will want to keep merge conflict markers and continue with the "rebase the
> original merge commit" strategy.
> 
> And it would slam the door shut for adding support for *other* merge
> strategies to perform a more-than-two-parents merge.
> 
> Also, I do not think that it makes a whole lot of sense in practice to let
> users edit what will be used for "original parent". If the user wants to
> do complicated stuff, they can already do that, via `exec`. The `merge`
> command really should be about facilitating common workflows, guiding the
> user to what is sane.
> 
> Currently my favorite idea is to introduce a new flag: -R (for "rebase the
> original merge commit"). It would look like this:
> 
> 	merge -R -C <original-merge> <merge-head> # <oneline>
>
> This flag would of course trigger the consistency check (does the number
> of parents of the original merge commit agree with the parameter list? Was
> an original merge commit specified to begin with?), and it would not fall
> back to the recursive merge, but error out if that check failed.
> 
> Side note: I wonder whether we really need to perform the additional check
> that ensures that the <merge-head> refers to the rewritten version of the
> original merge commit's parent.
> 
> Second side note: if we can fast-forward, currently we prefer that, and I
> think we should keep that behavior with -R, too.

I think that would be a good idea to avoid unpleasant surprises.

> If the user wants to force a new merge, they simply remove that -R flag.
> 
> What do you think?

I did wonder about using 'pick <original-merge>' for rebasing merges and
keeping 'merge ...' for recreating them but I'm not sure if that is a
good idea. It has the advantage that the user cannot specify the wrong
parents for the merge to be rebased as 'git rebase' would work out if
the parents have been rebased, but maybe it's a bit magical to use pick
for merge commits. Also there isn't such a simple way for the user to go
from 'rabase this merge' to 'recreate this merge' as they'd have to
write the whole merge line themselves (though I guess something like
emacs' git-rebase.el would be able to help with that)

Best Wishes

Phillip


> Ciao,
> Dscho
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 11:20                                       ` Phillip Wood
@ 2018-03-08 12:16                                         ` Phillip Wood
  2018-03-08 16:05                                           ` Igor Djordjevic
  2018-03-08 15:56                                         ` Igor Djordjevic
  2018-03-08 16:07                                         ` Jacob Keller
  2 siblings, 1 reply; 173+ messages in thread
From: Phillip Wood @ 2018-03-08 12:16 UTC (permalink / raw)
  To: Johannes Schindelin, Igor Djordjevic
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

On 08/03/18 11:20, Phillip Wood wrote:
> On 07/03/18 07:26, Johannes Schindelin wrote:
>> Hi Buga,
>>
>> On Tue, 6 Mar 2018, Igor Djordjevic wrote:
>>
>>> On 06/03/2018 19:12, Johannes Schindelin wrote:
>>>>
>>>>>> And I guess being consistent is pretty important, too - if you add new
>>>>>> content during merge rebase, it should always show up in the merge,
>>>>>> period. 
>>>>>
>>>>> Yes, that should make it easy for the user to know what to expect from
>>>>> rebase.
>>>>
>>>> [...]
>>>>
>>>> It will be slightly inconsistent. But in a defendable way, I think.
>>>
>>> I like where this discussion is heading, and here`s what I thought 
>>> about it :)
>>>
>>> [...]
>>>
>>> Here`s a twist - not letting `merge` trying to be too smart by 
>>> figuring out whether passed arguments correspond to rewritten 
>>> versions of the original merge parents (which would be too 
>>> restrictive, too, I`m afraid), but just be explicit about it, instead!
>>
>> That's the missing piece, I think.
>>
>>> So, it could be something like:
>>>
>>> 	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed
>>
>> I like where this is heading, too, but I do not think that we can do this
>> on a per-MERGE_HEAD basis. The vast majority of merge commits, in
>> practice, have two parents. So the `merge` command would actually only
>> have one revision to merge (because HEAD is the implicit first parent). So
>> that is easy.
>>
>> But as soon as you go octopus, you can either perform an octopus merge, or
>> rebase the original merge commit. You cannot really mix and match here.
>>
>> Unless we reimplement the octopus merge (which works quite a bit
>> differently from the "rebase merge commit" strategy, even if it is
>> incremental, too), which has its own challenges: if there are merge
>> conflicts before merging the last MERGE_HEAD, the octopus merge will exit
>> with status 2, telling you "Should not be doing an octopus.". While we
>> will want to keep merge conflict markers and continue with the "rebase the
>> original merge commit" strategy.
>>
>> And it would slam the door shut for adding support for *other* merge
>> strategies to perform a more-than-two-parents merge.
>>
>> Also, I do not think that it makes a whole lot of sense in practice to let
>> users edit what will be used for "original parent". If the user wants to
>> do complicated stuff, they can already do that, via `exec`. The `merge`
>> command really should be about facilitating common workflows, guiding the
>> user to what is sane.
>>
>> Currently my favorite idea is to introduce a new flag: -R (for "rebase the
>> original merge commit"). It would look like this:
>>
>> 	merge -R -C <original-merge> <merge-head> # <oneline>
>>
>> This flag would of course trigger the consistency check (does the number
>> of parents of the original merge commit agree with the parameter list? Was
>> an original merge commit specified to begin with?), and it would not fall
>> back to the recursive merge, but error out if that check failed.
>>
>> Side note: I wonder whether we really need to perform the additional check
>> that ensures that the <merge-head> refers to the rewritten version of the
>> original merge commit's parent.
>>
>> Second side note: if we can fast-forward, currently we prefer that, and I
>> think we should keep that behavior with -R, too.
> 
> I think that would be a good idea to avoid unpleasant surprises.

Oops that was referring to the first side note. I think fast forwarding
is a good idea. I'm not so sure about checking that <merge-head> refers
to the rewritten version of the original merge commit's parent any more
though. Having thought some more, I think we would want to allow the
user to rearrange a topic branch that is the parent of a merge and that
would require allowing a different parent as the old parent could be
dropped or swapped with another commit in the branch. I can't think of a
way to mechanically check that the new parent is 'somehow derived from'
the old one.

>> If the user wants to force a new merge, they simply remove that -R flag.
>>
>> What do you think?
> 
> I did wonder about using 'pick <original-merge>' for rebasing merges and
> keeping 'merge ...' for recreating them but I'm not sure if that is a
> good idea. It has the advantage that the user cannot specify the wrong
> parents for the merge to be rebased as 'git rebase' would work out if
> the parents have been rebased, but maybe it's a bit magical to use pick
> for merge commits. Also there isn't such a simple way for the user to go
> from 'rabase this merge' to 'recreate this merge' as they'd have to
> write the whole merge line themselves (though I guess something like
> emacs' git-rebase.el would be able to help with that)

Scrub that, it is too magical and I don't think it would work with
rearranged commits - it's making the --preserve-merges mistake all over
again. It's a shame to have 'merge' mean 'recreate the merge' and
'rebase the merge' but I don't think there is an easy way round that.

> Best Wishes
> 
> Phillip
> 
> 
>> Ciao,
>> Dscho
>>
> 


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-07  7:26                                     ` Johannes Schindelin
  2018-03-08 11:20                                       ` Phillip Wood
@ 2018-03-08 15:16                                       ` Igor Djordjevic
  2018-03-08 16:21                                         ` Igor Djordjevic
  2018-03-14 14:24                                         ` Sergey Organov
  1 sibling, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 15:16 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Dscho,

On 07/03/2018 08:26, Johannes Schindelin wrote:
> 
> > So, it could be something like:
> >
> > 	merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed
> 
> I like where this is heading, too, but I do not think that we can do this
> on a per-MERGE_HEAD basis. The vast majority of merge commits, in
> practice, have two parents. So the `merge` command would actually only
> have one revision to merge (because HEAD is the implicit first parent). So
> that is easy.
> 
> But as soon as you go octopus, you can either perform an octopus merge, or
> rebase the original merge commit. You cannot really mix and match here.
> 
> Unless we reimplement the octopus merge (which works quite a bit
> differently from the "rebase merge commit" strategy, even if it is
> incremental, too), which has its own challenges: if there are merge
> conflicts before merging the last MERGE_HEAD, the octopus merge will exit
> with status 2, telling you "Should not be doing an octopus.". While we
> will want to keep merge conflict markers and continue with the "rebase the
> original merge commit" strategy.
> 
> And it would slam the door shut for adding support for *other* merge
> strategies to perform a more-than-two-parents merge.

The thing is, in my opinion, as long as we are _rebasing_, you can`t 
pick any merge strategy, as it doesn`t really make much sense. If you 
do want a specific strategy, than that`s _recreating_ a merge, and it 
goes fine with what you already have for `--recreate-merges`.

On merge rebasing, the underline strategy we decide to use is just an 
implementation detail, picking the one that works best (or the only 
one that works, even), user should have nothing to do with it.

> Also, I do not think that it makes a whole lot of sense in practice to let
> users edit what will be used for "original parent". If the user wants to
> do complicated stuff, they can already do that, via `exec`. The `merge`
> command really should be about facilitating common workflows, guiding the
> user to what is sane.

I thought of a situation like this:

(1) ---o---o---o---M--- (master)
        \         /
         X1--X2--X3 (topic)


Merge M was done with `-s ours`, obsoleting "topic" branch. But, I 
later realized that I actually do want that X2 commit in master.

Now, I guess the most obvious approach is just cherry-picking it, but 
what if I would like to do something like this instead, with an 
interactive rebase (and rebasing the merge, not recreating it):

(2) ---o---o---o---M'--- (master)
       |\         /|
       | X1'-X3'-/ | (topic)
       |           |
       \--X2'------/ (new)


This way, and having "topic" inherit original merge behavior due to 
merge rebasing, X1' and X3' would still be missing from M' as they 
did originally from M, but X2' would now be included, as it`s coming 
from a new branch, forged during interactive rebase.

(note - implementation wise, this still wouldn`t be an octopus merge ;)

> The vast majority of merge commits, in practice, have two parents. So
> the `merge` command would actually only have one revision to merge
> (because HEAD is the implicit first parent).

Now, this is something I actually overlooked :( 

I guess order of parent commits could then be used to map to old 
commit parents, being a limitation in comparison to direct 
old-parent:new-parent mapping, but might be a more straightforward 
user experience...

Though in case of octopus merge, where one would like to drop a 
branch from the middle, being merged with `-s ours`, that would be 
impossible, as then the next branch would be taking over dropped 
branch merge parent, yielding an incorrect result.

So in this case:

(3) ---o---o---o---M--- (master)
       |\         /|
       | X1--X3--/ | (topic)
       |           |
       \--X2-------/ (new)

... where "topic" was merged using `-s ours`, we wouldn`t be able to 
just remove whole "topic" branch from the rebased merge without 
influencing it incorrectly.

With (any kind of) explicit old-parent:new-parent mapping, this is 
possible (and shouldn`t be any harder, implementation wise).

Now, it`s a different story if we`re interested in such exotic 
scenarios in the first place, but if possible, I would be all for 
it... :)

> Currently my favorite idea is to introduce a new flag: -R (for "rebase the
> original merge commit"). It would look like this:
> 
> 	merge -R -C <original-merge> <merge-head> # <oneline>
> 
> This flag would of course trigger the consistency check (does the number
> of parents of the original merge commit agree with the parameter list? Was
> an original merge commit specified to begin with?), and it would not fall
> back to the recursive merge, but error out if that check failed.
> 
> Side note: I wonder whether we really need to perform the additional check
> that ensures that the <merge-head> refers to the rewritten version of the
> original merge commit's parent.

No, and even worse - I think we must not do that, as that merge 
parent might be moved elsewhere, which should be allowed.

When I say "merge parent", I really mean "merge parent _branch_" (or 
so to say, merge parent context), I was deliberately avoiding term 
"merge parent commit" as it`s more than that.

> Second side note: if we can fast-forward, currently we prefer that, and I
> think we should keep that behavior with -R, too.

I agree.

> If the user wants to force a new merge, they simply remove that -R flag.
> 
> What do you think?

Having more replies in the thread, I`ll comment the format there, 
might be more appropriate, as discussion already evolved since the 
last time I`ve checked, and I might have a new idea... ;) :P

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 11:20                                       ` Phillip Wood
  2018-03-08 12:16                                         ` Phillip Wood
@ 2018-03-08 15:56                                         ` Igor Djordjevic
  2018-03-11 12:08                                           ` Johannes Schindelin
  2018-03-08 16:07                                         ` Jacob Keller
  2 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 15:56 UTC (permalink / raw)
  To: phillip.wood, Johannes Schindelin
  Cc: Jacob Keller, Sergey Organov, Git Mailing List, Johannes Sixt,
	Junio C Hamano

Hi Phillip and Johannes, 

On 08/03/2018 12:20, Phillip Wood wrote:
> 
> I did wonder about using 'pick <original-merge>' for rebasing merges and
> keeping 'merge ...' for recreating them but I'm not sure if that is a
> good idea. It has the advantage that the user cannot specify the wrong
> parents for the merge to be rebased as 'git rebase' would work out if
> the parents have been rebased, but maybe it's a bit magical to use pick
> for merge commits. Also there isn't such a simple way for the user to go
> from 'rabase this merge' to 'recreate this merge' as they'd have to
> write the whole merge line themselves (though I guess something like
> emacs' git-rebase.el would be able to help with that)

Hmm, funny enough, `pick <original merge>` was something I though 
about originally, too, feeling that it might make more sense in terms 
on what`s really going on, but I guess I wanted it incorporated into 
`--recreate-merges` too much that I tried really hard to fit it in, 
without changing it much :/

And now that I said this in a previous reply:

> The thing is, in my opinion, as long as we are _rebasing_, you can`t 
> pick any merge strategy, as it doesn`t really make much sense. If you 
> do want a specific strategy, than that`s _recreating_ a merge, and it 
> goes fine with what you already have for `--recreate-merges`.
> 
> On merge rebasing, the underline strategy we decide to use is just an 
> implementation detail, picking the one that works best (or the only 
> one that works, even), user should have nothing to do with it.

The difference between "rebase merge commit" and "recreate merge 
commit" might starting to be more evident.

So... I might actually go for this one now. And (trying to stick with 
explicit mappings, still :P), now that we`re not married to `merge` 
expectations a user may already have, maybe a format like this:

  pick <original-merge> <original-parent1>:HEAD <original-parent2>:<new-parent2>


Here, original-merge is a _commit_, where original-parent and 
new-parent are _labels_ (in terms of `--recreate-merges`).

Everything else I previously said still holds - one is allowed to 
change or drop mappings, and add or drop new merge parents. Yes, in 
case user does something "stupid", he`ll get a lot of conflicts, but 
hey, we shouldn`t judge.

p.s. Are we moving towards `--rebase-merges` I mentioned in that 
other topic[1], as an add-on series after `--recreate-merges` hits 
the mainstream (as-is)...? :P

Regards, Buga

[1] https://public-inbox.org/git/bc9f82fb-fd18-ee45-36a4-921a1381b32e@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 12:16                                         ` Phillip Wood
@ 2018-03-08 16:05                                           ` Igor Djordjevic
  2018-03-11 12:00                                             ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 16:05 UTC (permalink / raw)
  To: phillip.wood, Johannes Schindelin
  Cc: Jacob Keller, Sergey Organov, Git Mailing List, Johannes Sixt,
	Junio C Hamano

On 08/03/2018 13:16, Phillip Wood wrote:
> 
> > Side note: I wonder whether we really need to perform the additional check
> > that ensures that the <merge-head> refers to the rewritten version of the
> > original merge commit's parent.
> > 
> > [...]
> 
> Oops that was referring to the first side note. I think fast forwarding
> is a good idea. I'm not so sure about checking that <merge-head> refers
> to the rewritten version of the original merge commit's parent any more
> though. Having thought some more, I think we would want to allow the
> user to rearrange a topic branch that is the parent of a merge and that
> would require allowing a different parent as the old parent could be
> dropped or swapped with another commit in the branch. I can't think of a
> way to mechanically check that the new parent is 'somehow derived from'
> the old one.

Exactly, we must not depend on exact parent commits, but on parent 
"branches" (so to say).

And that is why I think explicit mapping would be pretty helpful (if 
not the only approach).

> > I did wonder about using 'pick <original-merge>' for rebasing merges and
> > keeping 'merge ...' for recreating them but I'm not sure if that is a
> > good idea. It has the advantage that the user cannot specify the wrong
> > parents for the merge to be rebased as 'git rebase' would work out if
> > the parents have been rebased, but maybe it's a bit magical to use pick
> > for merge commits. Also there isn't such a simple way for the user to go
> > from 'rabase this merge' to 'recreate this merge' as they'd have to
> > write the whole merge line themselves (though I guess something like
> > emacs' git-rebase.el would be able to help with that)
> 
> Scrub that, it is too magical and I don't think it would work with
> rearranged commits - it's making the --preserve-merges mistake all over
> again. It's a shame to have 'merge' mean 'recreate the merge' and
> 'rebase the merge' but I don't think there is an easy way round that.

I actually like `pick` for _rebasing_ merge commits, as `pick` is 
already used for rebasing non-merge commits, too, so it feels natural.

Then `merge` is left to do what it is meant for - merging (or 
"recreate the merge", in the given context).

I tried to outline a possible user interface in that other reply[1], 
elaborating it a bit, too,

Regards, Buga

[1] https://public-inbox.org/git/f3872fb9-01bc-b2f1-aee9-cfc0e4db77d6@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 11:20                                       ` Phillip Wood
  2018-03-08 12:16                                         ` Phillip Wood
  2018-03-08 15:56                                         ` Igor Djordjevic
@ 2018-03-08 16:07                                         ` Jacob Keller
  2018-03-11 12:11                                           ` Johannes Schindelin
  2 siblings, 1 reply; 173+ messages in thread
From: Jacob Keller @ 2018-03-08 16:07 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Johannes Schindelin, Igor Djordjevic, Sergey Organov,
	Git Mailing List, Johannes Sixt, Junio C Hamano

On Thu, Mar 8, 2018 at 3:20 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:
> On 07/03/18 07:26, Johannes Schindelin wrote:
>> Hi Buga,
>>
>> On Tue, 6 Mar 2018, Igor Djordjevic wrote:
>>
>>> On 06/03/2018 19:12, Johannes Schindelin wrote:
>>>>
>>>>>> And I guess being consistent is pretty important, too - if you add new
>>>>>> content during merge rebase, it should always show up in the merge,
>>>>>> period.
>>>>>
>>>>> Yes, that should make it easy for the user to know what to expect from
>>>>> rebase.
>>>>
>>>> [...]
>>>>
>>>> It will be slightly inconsistent. But in a defendable way, I think.
>>>
>>> I like where this discussion is heading, and here`s what I thought
>>> about it :)
>>>
>>> [...]
>>>
>>> Here`s a twist - not letting `merge` trying to be too smart by
>>> figuring out whether passed arguments correspond to rewritten
>>> versions of the original merge parents (which would be too
>>> restrictive, too, I`m afraid), but just be explicit about it, instead!
>>
>> That's the missing piece, I think.
>>
>>> So, it could be something like:
>>>
>>>      merge -C deadbee 123abc:cafecafe 234bcd:bedbedbed
>>
>> I like where this is heading, too, but I do not think that we can do this
>> on a per-MERGE_HEAD basis. The vast majority of merge commits, in
>> practice, have two parents. So the `merge` command would actually only
>> have one revision to merge (because HEAD is the implicit first parent). So
>> that is easy.
>>
>> But as soon as you go octopus, you can either perform an octopus merge, or
>> rebase the original merge commit. You cannot really mix and match here.
>>
>> Unless we reimplement the octopus merge (which works quite a bit
>> differently from the "rebase merge commit" strategy, even if it is
>> incremental, too), which has its own challenges: if there are merge
>> conflicts before merging the last MERGE_HEAD, the octopus merge will exit
>> with status 2, telling you "Should not be doing an octopus.". While we
>> will want to keep merge conflict markers and continue with the "rebase the
>> original merge commit" strategy.
>>
>> And it would slam the door shut for adding support for *other* merge
>> strategies to perform a more-than-two-parents merge.
>>
>> Also, I do not think that it makes a whole lot of sense in practice to let
>> users edit what will be used for "original parent". If the user wants to
>> do complicated stuff, they can already do that, via `exec`. The `merge`
>> command really should be about facilitating common workflows, guiding the
>> user to what is sane.
>>
>> Currently my favorite idea is to introduce a new flag: -R (for "rebase the
>> original merge commit"). It would look like this:
>>
>>       merge -R -C <original-merge> <merge-head> # <oneline>
>>
>> This flag would of course trigger the consistency check (does the number
>> of parents of the original merge commit agree with the parameter list? Was
>> an original merge commit specified to begin with?), and it would not fall
>> back to the recursive merge, but error out if that check failed.
>>
>> Side note: I wonder whether we really need to perform the additional check
>> that ensures that the <merge-head> refers to the rewritten version of the
>> original merge commit's parent.
>>
>> Second side note: if we can fast-forward, currently we prefer that, and I
>> think we should keep that behavior with -R, too.
>
> I think that would be a good idea to avoid unpleasant surprises.
>
>> If the user wants to force a new merge, they simply remove that -R flag.
>>
>> What do you think?
>
> I did wonder about using 'pick <original-merge>' for rebasing merges and
> keeping 'merge ...' for recreating them but I'm not sure if that is a
> good idea. It has the advantage that the user cannot specify the wrong
> parents for the merge to be rebased as 'git rebase' would work out if
> the parents have been rebased, but maybe it's a bit magical to use pick
> for merge commits. Also there isn't such a simple way for the user to go
> from 'rabase this merge' to 'recreate this merge' as they'd have to
> write the whole merge line themselves (though I guess something like
> emacs' git-rebase.el would be able to help with that)
>
> Best Wishes
>
> Phillip
>

Since the ultimate commit hashes of newly rebased commits would be
unknown at the time of writing the todo file, I'm not sure how this
would work to specify the parents?

>
>> Ciao,
>> Dscho
>>
>

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 15:16                                       ` Igor Djordjevic
@ 2018-03-08 16:21                                         ` Igor Djordjevic
  2018-03-11 12:22                                           ` Johannes Schindelin
  2018-03-14 14:24                                         ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 16:21 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

On 08/03/2018 16:16, Igor Djordjevic wrote:
> 
> > Unless we reimplement the octopus merge (which works quite a bit
> > differently from the "rebase merge commit" strategy, even if it is
> > incremental, too), which has its own challenges: if there are merge
> > conflicts before merging the last MERGE_HEAD, the octopus merge will exit
> > with status 2, telling you "Should not be doing an octopus.". While we
> > will want to keep merge conflict markers and continue with the "rebase the
> > original merge commit" strategy.
> >
> > [...]
> 
> The thing is, in my opinion, as long as we are _rebasing_, you can`t 
> pick any merge strategy, as it doesn`t really make much sense. If you 
> do want a specific strategy, than that`s _recreating_ a merge, and it 
> goes fine with what you already have for `--recreate-merges`.
> 
> On merge rebasing, the underlying strategy we decide to use is just an 
> implementation detail, picking the one that works best (or the only 
> one that works, even), user should have nothing to do with it.

Just to add, if not already assumable, that I think we should stop 
and let user react on conflicts on each of the "rebase the original 
commit" strategy steps (rebase first parent, rebase second parent... 
merge parents).

I guess this stresses not using real "octopus merge" strategy even in 
case where we`re rebasing octopus merge commit even more (and aligns 
nicely with what you seem to expect already).

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-06 11:45                           ` Sergey Organov
@ 2018-03-08 16:30                             ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 16:30 UTC (permalink / raw)
  To: Sergey Organov, Phillip Wood
  Cc: phillip.wood, Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

On 06/03/2018 12:45, Sergey Organov wrote:
> 
> > > 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.
> 
> Yes, hopefully it's predictable, but is it the intended one? We don't
> know, so there is still some level of uncertainty.
> 
> When in doubt, I try to find similar cases. There are two I'm aware of:
> 
> 1. "git merge" just commits the result when there are no conflicts.
> However, it supposedly has been run by the user just now, and thus user
> can amend what he gets. That's effectively a stop for amendment from our
> POV.
> 
> 2. When rebasing, "rerere", when fires, stages the changes, and rebasing
> stops for amendment. For me "rerere" behavior is rather annoying (I've
> never in fact amended what it prepared), but I always assumed there are
> good reasons it behaves this way.
> 
> Overall, to be consistent, it seems we do need to stop at U1' != U2', at
> least by default. Additional options could be supported then to specify
> user intentions, both on the command level and in the todo list,
> provided it proves to be useful.

Just to say I agree with this, `if U1' == U2' then proceed else stop` 
seems as a good sanity check.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-07 14:08       ` Johannes Schindelin
  2018-03-07 15:16         ` Sergey Organov
@ 2018-03-08 19:58         ` Igor Djordjevic
  2018-03-08 20:27           ` Igor Djordjevic
  2018-03-11 15:40           ` Johannes Schindelin
  1 sibling, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 19:58 UTC (permalink / raw)
  To: Johannes Schindelin, Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Phillip Wood

Hi Johannes,

On 07/03/2018 15:08, Johannes Schindelin wrote:
> 
> > > Didn't we settle on Phillip's "perform successive three-way merges
> > > between the original merge commit and the new tips with the old tips
> > > as base" strategy?
> >
> > It seems you did, dunno exactly why.
> 
> That is not true. You make it sound like I was the only one who liked
> this, and not Phillip and Buga, too.

For myself, I do actually favor Sergey`s approach in general, but 
_implemented_ through what Phillip described (or a mixture of both, to 
be precise). But, let me explain... :)

> > The main problem with this decision is that we still don't see how and
> > when to stop for user amendment using this method. OTOH, the original
> > has this issue carefully discussed.
> 
> Why would we want to stop, unless there are merge conflicts?

Because we can reliably know that something "unusual" happened - and 
by that I don`t necessarily mean "wrong", but just might be worth 
user inspection.

For example, situation like this (M is made on A3 with `-s ours`, 
obsoleting Bx commits):

(1) ---X8--X9 (master)
       |\
       | A1---A2---A3
       |             \
       |              M (topic)
       |             /
       \-B1---B2---B3

... where we want to rebase M onto X9 is what I would call "usual 
stuff", but this situation (M is still made on A3 with `-s ours`, 
obsoleting Bx commits, but note cherry-picked B2'):

(2) ---X8--B2'--X9 (master)
       |\
       | A1---A2---A3
       |             \
       |              M (topic)
       |             /
       \-B1---B2---B3

... where we still want to rebase M onto X9 is what we might consider 
"unusual", because we noticed that something that shouldn`t be part 
of the rebased merge commit (due to previous `-s ours`) actually got 
in there (due to later cherry-pick), and just wanting the user to 
check and confirm.

This is the major reason why I would prefer Sergey`s approach in 
general... and might be I also have a good explanation on *why* it 
works, but let`s get there ;)

(p.s. This is only one, but certainly not the only case)

> > "rebase sides of the merge commit and then three-way merge them back
> > using original merge commit as base"
> 
> And that is also wrong, as I had proved already! Only Buga's addition made
> it robust against dropping/modifying commits, and that addition also makes
> it more complicated.

No, this is actually right, that sentence nicely describing _how_ it 
works. That addition of mine was just including the correct merge 
base (being the original merge commit that we are "rebasing"), and 
that`s what Sergey is talking about.

> And it still has no satisfactory simple explanation why it works.

Getting there... :)

> > > - it is *very easy* to reason about, once it is pointed out that
> > > rebases and merges result in the same trees.
> >
> > The original is as easy to reason about, if not easier, especially as
> > recursive merge strategy is not being used there in new ways.
> 
> So do it. I still have to hear a single-sentence, clear and obvious
> explanation why it works.
> 
> And please do not describe why your original version works, because it
> does not work. Describe why the one amended with Buga's hack works.
> 
> > I honestly don't see any advantages of Phillip's method over the
> > original, except personal preferences. At the same time, I have no
> > objection of using it either, provided consistency check problem is
> > solved there as well.
> 
> Okay, let me reiterate then, because I do not want this point to be
> missed:
> 
> Phillip's method is essentially merging the new tips into the original
> merge, pretending that the new tips were not rebased but merged into
> upstream.
> 
> So it exploits the duality of the rebase and merge operation, which both
> result in identical trees (potentially after resolving merge conflicts).
> 
> I cannot think of any such interpretation for your proposal augmented by
> Buga's fix-ups. And I haven't heard any such interpretation from your
> side, either.

Ok here it goes (I might be still wrong, but please bare with me).

What Sergey originally described also uses the "duality of the rebase 
and merge operation", too ;) Just in a bit different way (and might 
be a more straightforward one?).

Here`s a starting point, two commits A and B, merged into M:

(3) ---A
        \
         M
        /
    ---B


According the "patch theory"[1] (which might not be too popular 
around here, but should serve the purpose for what I`m trying to 
explain), each merge commit can be "transformed" to two non-merge 
commits, one on top of each of the merge parents, where new commit 
brings its original merge parent commit tree to the state of the 
merge commit tree:

(4) ---A---U1



    ---B---U2


Now, we have two new commits, U1 and U2, each having the same tree as 
previous merge commit M, but representing changes in regards to 
specific parents - and this is essentially what Sergey`s original 
approach was using (whether he knew it, or not).

When it comes to rebasing, it`s pretty simple, too. As this:

(5) ---X1---o---o---o---o---o---X2 (master)
       |\
       | A1---A2---A3
       |             \
       |              M
       |             /
       \-B1---B2---B3

... actually equals this:

(6) ---X1---o---o---o---o---o---X2 (master)
       |\
       | A1---A2---A3---U1
       |
       |
       |
       \-B1---B2---B3---U2

... where trees of M, U1 and U2 are same, and we can use the regular 
rebase semantics and rebase it to this:

(7) ---X1---o---o---o---o---o---X2 (master)
                                |\
                                | A1'--A2'--A3'--U1'
                                |
                                |
                                |
                                \-B1'--B2'--B3'--U2'

... which is essentially this again:

(8) ---X1---o---o---o---o---o---X2 (master)
                                |\
                                | A1'--A2'--A3'
                                |            \
                                |             M'
                                |            /
                                \-B1'--B2'--B3'

... where it is still true that trees of U1', U2' and M' are still 
the same. So we managed to rebase a merge commit without ever doing a 
merge :) (note that, practically, we _can_ finally even merge U1' and 
U2' to produce M', it shouldn`t really matter as all the trees are 
the same, so merge will be a no-op)

But, as we saw (what you`ve explained), this doesn`t really work in 
case one of the sides of the merge gets "disbalanced" (so to say), 
like dropping a commit (which could also happen non-interactively, 
where a commit has been cherry-picked to a different branch, but
previously obsoleted by `-s ours` merge).

As observed, dropped commit could still wrongly get into final merge 
commit tree (or cherry-picked commit wrongly not get there), due to 
the nature of those rebased U1 and U2 temporary commits to hold all 
the differences in regards to their specific merge commit parent.

A natural improvement to original idea which Sergey eventually came 
up with after my findings (which you now ended up calling a hack, even, 
but I would respectfully disagree), is to use original merge commit M 
as a merge base for final merge of U1' and U2' - and here is why it 
works, too, and why I wouldn`t consider it a hack, but a proper (and 
a solid) solution.

Merge commit M is what we are rebasing, so we should have a way to 
somehow learn from it, alongside U1 and U2 temporary commits - and 
that is what using original merge commit M as a base does for us. It 
helps us spot any "disbalances" that happened in comparison to original 
merge parent trees, which is exactly what we`re interested in.

In ideal case (as per "patch theory"[1]), final merge of rebased U1' 
and U2' with original merge commit M as a base would be rather simple, 
too (not to say pointless again), as both U1' and U2' would have same 
changes in comparison to M, namely all the changes from commit we are 
rebasing from (X1 above) to commit we are rebasing to (X2 above).

But in a more complex case like this:

(9) ---X1---B2'---o---o---o---o---X2 (master)
                                  |\
                                  | A12--A2'---B3'
                                  |             \
                                  |              M'
                                  |             /
                                  \-B1'--B3'---B4

..., being what we are ultimately interested in, it helps us notice 
all kind of changes on our rebased merge parent branches, and act 
accordingly (as shown by my previously posted test script[2]).

All this said (and hoping anyone is still reading this), to shed some 
light on what I meant by "favoring Sergey`s approach in general, but 
_implemented_ through what Phillip described".

I think we should use temporary commits U1 and U2, being a sound 
approach backed up by the "patch theory"[1], as it helps us notice 
any "disbalances" of the rebased merge commit parents trees (so we 
can stop for user inspection), finally merging them with original 
merge commit as a base, in order to spot additional changes on trees 
of the merge commit parents we are rebasing.

But, the merging steps themselves, backed up by a need to stop for 
conflict resolution as soon as one happens, should be performed 
_iteratively_, like Phillip showed us... I would think :)

And, for example, that would do wonders when we introduce completely 
new branch during an interactive rebase, too, for example:

(10) ---X1---B2'---o---o---o---o---X2 (master)
        |\                        /|\
        | A1---A2---A3---U1       || A12--A2'---B3'---U1'
        |             \           ||             \
        |              M          ||              M'
        |             /           ||             /|
        \-B1---B2---B3---U2       |\---B3'---B4-/-|---U2'
                                  |               |
                                  \-----B1'-------/


In this situation, we would merge all temporary Ux commits using 
original merge M as a merge base, and then merge _that_ resulting 
tree with B1' (being a newly introduced merge commit parent), using 
whatever merge base is most appropriate between the two (in this 
case, X2).

So, for diagram (10) above, I guess something like this:

	git merge-recursive U1' -- M U2'
	tree="$(git write-tree)"
	# in case of original merge being octopus, we would continue like:
	# git merge-recursive $tree -- M U3'
	# tree="$(git write-tree)"
	# git merge-recursive $tree -- M U4'
	# ... and so on, then finally:
	git merge-recursive $tree -- "$(git merge-base U1' U2' B1')" B1'
	# in more general case, it would be:
	# git merge-recursive $tree -- "$(git merge-base <all-parents-of-new-merge-commit>)" B1'
	tree="$(git write-tree)"
	git tag M' "$(git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1')"

... where we would stop for conflict resolution after each failing 
merge-recursive attempt (which is also true for producing rebased U1' 
and U2' in the first place).

There, I would still need to make a test script doing this, but I 
hope the concept is clear, and I`m not missing something crucial here.

Comments welcome :)

Regards, Buga

[1] https://en.wikibooks.org/wiki/Understanding_Darcs/Patch_theory
[2] https://public-inbox.org/git/f1a960dc-cc5c-e7b0-10b6-39e5516655b3@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08 19:58         ` Igor Djordjevic
@ 2018-03-08 20:27           ` Igor Djordjevic
  2018-03-08 22:05             ` Igor Djordjevic
  2018-03-11 15:40           ` Johannes Schindelin
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 20:27 UTC (permalink / raw)
  To: Johannes Schindelin, Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Phillip Wood

On 08/03/2018 20:58, Igor Djordjevic wrote:
> 
> 	git merge-recursive U1' -- M U2'
> 	tree="$(git write-tree)"
> 	# in case of original merge being octopus, we would continue like:
> 	# git merge-recursive $tree -- M U3'
> 	# tree="$(git write-tree)"
> 	# git merge-recursive $tree -- M U4'
> 	# ... and so on, then finally:
> 	git merge-recursive $tree -- "$(git merge-base U1' U2' B1')" B1'
> 	# in more general case, it would be:
> 	# git merge-recursive $tree -- "$(git merge-base <all-parents-of-new-merge-commit>)" B1'
> 	tree="$(git write-tree)"
> 	git tag M' "$(git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1')"

That last line should obviously read just:

	git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1'

..., above mentioned `git tag M'` part being a leftover from my other test script.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08 20:27           ` Igor Djordjevic
@ 2018-03-08 22:05             ` Igor Djordjevic
  2018-03-08 23:31               ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 22:05 UTC (permalink / raw)
  To: Johannes Schindelin, Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Phillip Wood

On 08/03/2018 21:27, Igor Djordjevic wrote:
> 
> > 	git merge-recursive U1' -- M U2'
> > 	tree="$(git write-tree)"
> > 	# in case of original merge being octopus, we would continue like:
> > 	# git merge-recursive $tree -- M U3'
> > 	# tree="$(git write-tree)"
> > 	# git merge-recursive $tree -- M U4'
> > 	# ... and so on, then finally:
> > 	git merge-recursive $tree -- "$(git merge-base U1' U2' B1')" B1'
> > 	# in more general case, it would be:
> > 	# git merge-recursive $tree -- "$(git merge-base <all-parents-of-new-merge-commit>)" B1'
> > 	tree="$(git write-tree)"
> > 	git tag M' "$(git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1')"
> 
> That last line should obviously read just:
> 
> 	git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1'
> 
> ..., above mentioned `git tag M'` part being a leftover from my other test script.

Eh, pardon me, I managed to mess up all the merge-recursive lines, 
too, in regards to where the merge-base commit goes... Here`s a 
complete (and corrected) sample:

	git merge-recursive M -- U1' U2'
	tree="$(git write-tree)"
	# in case of original merge being octopus, we would continue like:
	# git merge-recursive M -- $tree U3'
	# tree="$(git write-tree)"
	# git merge-recursive M -- $tree U4'
	# ... and so on, then finally:
	git merge-recursive "$(git merge-base U1' U2' B1')" -- $tree B1'
	# ... or even:
	# git merge-recursive "$(git merge-base B3' B4 B1')" -- $tree B1'
	# as in more general case, it would be:
	# git merge-recursive "$(git merge-base <all-parents-of-new-merge-commit>)" -- $tree B1'
	tree="$(git write-tree)"
	git log --pretty=%B -1 M | git commit-tree $tree -p B3' -p B4 -p B1'

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08 22:05             ` Igor Djordjevic
@ 2018-03-08 23:31               ` Igor Djordjevic
  2018-03-11 15:47                 ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-08 23:31 UTC (permalink / raw)
  To: Johannes Schindelin, Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Phillip Wood

On 08/03/2018 20:58, Igor Djordjevic wrote:
> 
> > Phillip's method is essentially merging the new tips into the original
> > merge, pretending that the new tips were not rebased but merged into
> > upstream.
> 
> [...]
> 
> Here`s a starting point, two commits A and B, merged into M:
> 
> (3) ---A
>         \
>          M
>         /
>     ---B
> 
> 
> According the "patch theory"[1] (which might not be too popular 
> around here, but should serve the purpose for what I`m trying to 
> explain), each merge commit can be "transformed" to two non-merge 
> commits, one on top of each of the merge parents, where new commit 
> brings its original merge parent commit tree to the state of the 
> merge commit tree:
> 
> (4) ---A---U1
> 
> 
> 
>     ---B---U2
> 
> 
> Now, we have two new commits, U1 and U2, each having the same tree as 
> previous merge commit M, but representing changes in regards to 
> specific parents - and this is essentially what Sergey`s original 
> approach was using (whether he knew it, or not).
> 
> When it comes to rebasing, it`s pretty simple, too. As this:
> 
> (5) ---X1---o---o---o---o---o---X2 (master)
>        |\
>        | A1---A2---A3
>        |             \
>        |              M
>        |             /
>        \-B1---B2---B3
> 
> ... actually equals this:
> 
> (6) ---X1---o---o---o---o---o---X2 (master)
>        |\
>        | A1---A2---A3---U1
>        |
>        |
>        |
>        \-B1---B2---B3---U2
> 
> ... where trees of M, U1 and U2 are same, and we can use the regular 
> rebase semantics and rebase it to this:
> 
> (7) ---X1---o---o---o---o---o---X2 (master)
>                                 |\
>                                 | A1'--A2'--A3'--U1'
>                                 |
>                                 |
>                                 |
>                                 \-B1'--B2'--B3'--U2'
> 
> ... which is essentially this again:
> 
> (8) ---X1---o---o---o---o---o---X2 (master)
>                                 |\
>                                 | A1'--A2'--A3'
>                                 |            \
>                                 |             M'
>                                 |            /
>                                 \-B1'--B2'--B3'
> 

Having explained all this, I realized this is the same "essentially 
merging the new tips into the original pretending that the new tips 
were not rebased but merged into upstream" as Phillip`s one, just 
that we have additional temporary commits U1 and U2 (as per mentioned 
"patch theory") :)

Merging U1' and U2' with M as a base can initially be presented like 
this as well (what Phillip`s approach would yield):

	git merge-recursive U1 -- M U1'
	tree="$(git write-tree)"
	git merge-recursive U2 -- $tree U2'
	tree="$(git write-tree)"
	
..., where we know U1 = U2 = M (in regards to trees), so this is the 
same as:

	git merge-recursive M -- M U1'
	tree="$(git write-tree)"
	git merge-recursive M -- $tree U2'
	tree="$(git write-tree)"

..., which can be further simplified, being the same as:

	git merge-recursive M -- U1' U2'
	tree="$(git write-tree)"

... which is exactly what Sergey`s (updated) approach suggests, 
merging U1' and U2' with M as merge-base (and shown inside that 
sample implementation script I provided[1]) :)

With all this said, I think it`s safe to say these two approaches are 
exactly the same, just Sergey`s being simplified (thus harder to 
initially understand), and the only actual question is whether we 
value U1' and U2' enough, as possible "something suspicious happened" 
indicators, to use them, or not.

I would think yes, but I would be open for more samples of where do 
they become useful for reporting "suspicious activity", too.

Regards, Buga

[1] https://public-inbox.org/git/b11785bd-5c96-43c1-95d8-b28eccfd13c8@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08  8:11                                           ` Junio C Hamano
@ 2018-03-09 17:09                                             ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-09 17:09 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Igor Djordjevic, Jacob Keller, Sergey Organov,
	Git Mailing List, Johannes Sixt

Hi Junio,

On Thu, 8 Mar 2018, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> If we are talking about a drastic change, a few more days may not be
> >> sufficient, but we are not in a hurry, as this already sounds like a
> >> 2.18 material anyway.
> >
> > It is not at all a drastic change. It will actually make the current patch
> > series better (simplifying the "can we fast-forward?" check).
> >
> > I just want to make sure that I already have Phillip's strategy working,
> > but it will be yet another topic branch on top of the topic branch that
> > will add support for octopus merges *after* the current --recreate-merges
> > topic branch ;-)
> 
> Oh, if the "not redoing the merge afresh, but attempt to reuse the
> previous merge" that was discussed is going to be done as an
> update/addition to the "redo the merge afresh" you already had in
> production forever (and I had in 'pu' for quite a while in various
> polished-ness during iteration), then I do prefer merging down what
> has already proven to be 'next' worthy without waiting for the
> discussion and your local verification of Phillip's new thing,
> especially given that you'll be giving an explicit control to the
> users which variant of "merge" insn will be used and the addition
> of the Phillip's thing won't be a backward-compatibility issue when
> it comes later.

I would like to stress that the `--recreate-merges` functionality has
*not* beed in production forever. It is a reimplementation in pure C of
the Unix shell script that has been in production for five years
(unchanged only for the last half year, the last critical fix happened in
January last year).

So I do see the value here to use `next` as test bed, and once the patch
series will hit `next`, I will also merge it into Git for Windows (as an
EXPERIMENTAL feature).

As such, I am quite comfortable with refactoring a bit here and there, for
example how to handle the case where a `merge` can be fast-forwarded.

I think the changes I made in the last few days were an actual improvement
to readability, even if the only reason for those changes was that I
wanted to accommodate the "rebase merge commits" thing.

FWIW I am fine with bumping this down to 2.18. I got a little bit of
valuable feedback at GitMerge, e.g. that --recreate-merges does not (yet)
have a mode where it can update refs corresponding to the rebased commits.
(This could actually turn out to be independent of --recreate-merges,
even).

I already pushed out the updated branch, and it can be inspected at

	https://github.com/git/git/pull/447

The reason I did not send this out yet is that I want to give it a final
look-over myself, so that I do not waste people's time again (as I did
with that monster interdiff that was pretty bogus, too).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08  8:18                                 ` Junio C Hamano
@ 2018-03-11 11:56                                   ` Johannes Schindelin
  2018-03-13 18:24                                     ` Junio C Hamano
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 11:56 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Sergey Organov, Igor Djordjevic, phillip.wood,
	Git mailing list, Jacob Keller, Johannes Sixt

Hi Junio,

On Thu, 8 Mar 2018, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> Non-textual semantic conflicts are made (in the best case just once)
> >> as a separate commit on top of mechanical auto-merge whose focus is
> >> predictability (rather than cleverness) done by Git, and then that
> >> separate commit is kept outside the history.  When replaying these
> >> merges to rebuild the 'pu' branch, after resetting the tip to
> >> 'master', each topic is merged mechanically, and if such a fix-up
> >> commit is present, "cherry-pick --no-commit" applies it and then
> >> "commit --amend --no-edit" to adjust the merge.  I find it quite
> >> valuable to have a separate record of what "evil" non-mechanical
> >> adjustment was done, which I know won't be lost in the noise when
> >> these merges need to be redone daily or more often.
> >
> > So essentially, you have something that `git rerere` would have learned,
> > but as a commit?
> 
> You probably wouldn't be asking that if you read what you cut out
> when you quoted above ;-) 

Sorry to be so stupid, and no, the part I cut did not clarify it for me,
hence my question.

> There are a collection of cherry-pickable commits in hierarchy under
> refs/merge-fix.  They are indexed by the branch that will cause
> semantic conflicts that do not involve textual conflicts at all (the
> important implication of which is that 'rerere' fundamentally will
> not trigger to help resolving them) [*1*], and are used to create
> evil merge when a corresponding branch is merged to 'pu' (and down).

I see. My question was unclear, I agree. Please let me re-try:

So essentially, what your cherry-pick'able commits are is a way to store
what rerere would have stored (i.e. the set of merge conflicts together
with their resolution)?

If so, what tooling do you have to identify quickly what to cherry-pick,
given merge conflicts?

(I know I could spend some half hour to scour your `todo` branch and the
documentation you wrote about how you maintain Git, but you already know
the answer to this question, and it would probably be interesting to
others who are as eager to have better tools for handling merge conflicts
as I am, too, so it would save time overall to discuss this single
question here.)

> *1* One topic adds an extra parameter to read_index_from() that has
> been and still is defined in a file and merged to 'pu' first, while
> another topic adds a new callsite for the same function in a file
> that the former topic does not touch, hence a merge of the latter
> topic has no textual conflict to the file with a new callsite, but
> still needs adjusting.  That sort of think.

Okay, so it is even more intricate than `rerere`: it does not only store
the merge conflicts (by grace of using the "patch ID" of the merge
conflicts) together with their resolution, but instead it has some sort of
idea of what context needs to be met to require the resolution?

Color me intrigued.

If you really found a way to automate describing, say, that a function
signature changed in one branch, and a caller was introduced in another,
and that merging those requires adjusting the caller in a specific way, I
now *really* think that this should be made available more broadly.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 16:05                                           ` Igor Djordjevic
@ 2018-03-11 12:00                                             ` Johannes Schindelin
  2018-03-11 16:33                                               ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 12:00 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Thu, 8 Mar 2018, Igor Djordjevic wrote:

> On 08/03/2018 13:16, Phillip Wood wrote:
> > 
> > > I did wonder about using 'pick <original-merge>' for rebasing merges
> > > and keeping 'merge ...' for recreating them but I'm not sure if that
> > > is a good idea. It has the advantage that the user cannot specify
> > > the wrong parents for the merge to be rebased as 'git rebase' would
> > > work out if the parents have been rebased, but maybe it's a bit
> > > magical to use pick for merge commits. Also there isn't such a
> > > simple way for the user to go from 'rabase this merge' to 'recreate
> > > this merge' as they'd have to write the whole merge line themselves
> > > (though I guess something like emacs' git-rebase.el would be able to
> > > help with that)
> > 
> > Scrub that, it is too magical and I don't think it would work with
> > rearranged commits - it's making the --preserve-merges mistake all
> > over again. It's a shame to have 'merge' mean 'recreate the merge' and
> > 'rebase the merge' but I don't think there is an easy way round that.
> 
> I actually like `pick` for _rebasing_ merge commits, as `pick` is 
> already used for rebasing non-merge commits, too, so it feels natural.

Phillip is right, though: this would repeat the design mistake of
--preserve-merges.

We must not forget that the interactive mode is the target here, and that
the syntax (as well as the generated todo list) must allow for easy
modification. The `pick <merge>` approach does not allow that, so we
cannot use it.

The `merge -R -C <original-commit> <merge-head>` approach is a lot better:
it offers the flexibility, without sacrificing the ease when not modifying
the todo list.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 15:56                                         ` Igor Djordjevic
@ 2018-03-11 12:08                                           ` Johannes Schindelin
  2018-03-11 17:34                                             ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 12:08 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Thu, 8 Mar 2018, Igor Djordjevic wrote:

> On 08/03/2018 12:20, Phillip Wood wrote:
> > 
> > I did wonder about using 'pick <original-merge>' for rebasing merges
> > and keeping 'merge ...' for recreating them but I'm not sure if that
> > is a good idea. It has the advantage that the user cannot specify the
> > wrong parents for the merge to be rebased as 'git rebase' would work
> > out if the parents have been rebased, but maybe it's a bit magical to
> > use pick for merge commits. Also there isn't such a simple way for the
> > user to go from 'rabase this merge' to 'recreate this merge' as they'd
> > have to write the whole merge line themselves (though I guess
> > something like emacs' git-rebase.el would be able to help with that)
> 
> Hmm, funny enough, `pick <original merge>` was something I though about
> originally, too, feeling that it might make more sense in terms on
> what`s really going on, but I guess I wanted it incorporated into
> `--recreate-merges` too much that I tried really hard to fit it in,
> without changing it much :/

The `pick <original-merge>` syntax is too limited to allow reordering, let
alone changing the parents.

> And now that I said this in a previous reply:
> 
> > The thing is, in my opinion, as long as we are _rebasing_, you can`t 
> > pick any merge strategy, as it doesn`t really make much sense. If you 
> > do want a specific strategy, than that`s _recreating_ a merge, and it 
> > goes fine with what you already have for `--recreate-merges`.
> > 
> > On merge rebasing, the underline strategy we decide to use is just an 
> > implementation detail, picking the one that works best (or the only 
> > one that works, even), user should have nothing to do with it.
> 
> The difference between "rebase merge commit" and "recreate merge 
> commit" might starting to be more evident.

True.

> So... I might actually go for this one now. And (trying to stick with 
> explicit mappings, still :P), now that we`re not married to `merge` 
> expectations a user may already have, maybe a format like this:
> 
>   pick <original-merge> <original-parent1>:HEAD <original-parent2>:<new-parent2>

I do not really like it, as it makes things a ton less intuitive. If you
did not know about this here discussion, and you did not read the manual
(and let's face it: a UI that does not require users to read the manual is
vastly superior to a UI that does), and you encountered this command:

	merge deadbeef cafecafe:download-button

what would you think those parameters would mean?

Granted, encountering

	merge -R -C deadbeef download-button # Merge branch 'download-button'

is still not *quite* as intuitive as I would wish. Although, to be honest,
if I encountered this, I would think that I should probably leave the -R
and the -C deadbeef alone, and that I could change what is getting merged
by changing the `download-button` parameter.

> p.s. Are we moving towards `--rebase-merges` I mentioned in that 
> other topic[1], as an add-on series after `--recreate-merges` hits 
> the mainstream (as-is)...? :P

That's an interesting question. One that I do not want to answer alone,
but I would be in favor of `--rebase-merges` as it is IMHO a much better
name for what this option is all about.

Other opinions?

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 16:07                                         ` Jacob Keller
@ 2018-03-11 12:11                                           ` Johannes Schindelin
  2018-03-11 17:46                                             ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 12:11 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Phillip Wood, Igor Djordjevic, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Jake,

On Thu, 8 Mar 2018, Jacob Keller wrote:

> On Thu, Mar 8, 2018 at 3:20 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:
> > I did wonder about using 'pick <original-merge>' for rebasing merges
> > and keeping 'merge ...' for recreating them but I'm not sure if that
> > is a good idea. It has the advantage that the user cannot specify the
> > wrong parents for the merge to be rebased as 'git rebase' would work
> > out if the parents have been rebased, but maybe it's a bit magical to
> > use pick for merge commits. Also there isn't such a simple way for the
> > user to go from 'rabase this merge' to 'recreate this merge' as they'd
> > have to write the whole merge line themselves (though I guess
> > something like emacs' git-rebase.el would be able to help with that)
> 
> Since the ultimate commit hashes of newly rebased commits would be
> unknown at the time of writing the todo file, I'm not sure how this
> would work to specify the parents?

I agree with Phillip's follow-up that the `pick <original-merge>` syntax
would pose a problem, but for different reasons: We already tried it, with
--preserve-merges, and it is just a really stupid syntax that does not
allow the user even to reorder commits. Or drop commits (except at the
very end of the todo list).

As to your concern: The ultimate commit hashes do not have to be known
until the merge commit is rebased. The current approach is to use labels
for them (`label <name>`), which Simply Works.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 16:21                                         ` Igor Djordjevic
@ 2018-03-11 12:22                                           ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 12:22 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Thu, 8 Mar 2018, Igor Djordjevic wrote:

> On 08/03/2018 16:16, Igor Djordjevic wrote:
> > 
> > > Unless we reimplement the octopus merge (which works quite a bit
> > > differently from the "rebase merge commit" strategy, even if it is
> > > incremental, too), which has its own challenges: if there are merge
> > > conflicts before merging the last MERGE_HEAD, the octopus merge will exit
> > > with status 2, telling you "Should not be doing an octopus.". While we
> > > will want to keep merge conflict markers and continue with the "rebase the
> > > original merge commit" strategy.
> > >
> > > [...]
> > 
> > The thing is, in my opinion, as long as we are _rebasing_, you can`t 
> > pick any merge strategy, as it doesn`t really make much sense. If you 
> > do want a specific strategy, than that`s _recreating_ a merge, and it 
> > goes fine with what you already have for `--recreate-merges`.
> > 
> > On merge rebasing, the underlying strategy we decide to use is just an 
> > implementation detail, picking the one that works best (or the only 
> > one that works, even), user should have nothing to do with it.
> 
> Just to add, if not already assumable, that I think we should stop 
> and let user react on conflicts on each of the "rebase the original 
> commit" strategy steps (rebase first parent, rebase second parent... 
> merge parents).

I am not sure about that. Actually, I am pretty certain that we should
imitate the recursive merge, which "accumulates" merge conflicts and only
presents the final result, possibly with nested merge conflicts.

In practice, we see that rarely, and more often than not, in those cases
the user wants to re-do the merge differently, anyway.

In the scenario we discuss here, I would wager a bet that the user
encountering nested merge conflicts would most likely see that the rebase
tried to rebase a merge, then `git reset --hard` and re-do the merge
manually (i.e. *recreate* rather than *rebase*, most likely with
(non-nested) merge conflicts).

> I guess this stresses not using real "octopus merge" strategy even in 
> case where we`re rebasing octopus merge commit even more (and aligns 
> nicely with what you seem to expect already).

My mistake, I should have pointed you my implementation:

https://github.com/git/git/commit/d41a29ceb61ff445efd0f97a836f381de57c5a41

The full thicket of branches can be found here:

https://github.com/git/git/compare/master...dscho:sequencer-shears

I chose to make this an add-on patch after adding support for octopus
merges because it actually makes it easier to think about "rebase merge
commits" independently of the number of merge parents.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08 19:58         ` Igor Djordjevic
  2018-03-08 20:27           ` Igor Djordjevic
@ 2018-03-11 15:40           ` Johannes Schindelin
  2018-03-11 22:04             ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 15:40 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Buga,

On Thu, 8 Mar 2018, Igor Djordjevic wrote:

> On 07/03/2018 15:08, Johannes Schindelin wrote:
> > 
> > > > Didn't we settle on Phillip's "perform successive three-way merges
> > > > between the original merge commit and the new tips with the old tips
> > > > as base" strategy?
> > >
> > > It seems you did, dunno exactly why.
> > 
> > That is not true. You make it sound like I was the only one who liked
> > this, and not Phillip and Buga, too.
> 
> For myself, I do actually favor Sergey`s approach in general, but 
> _implemented_ through what Phillip described (or a mixture of both, to 
> be precise). But, let me explain... :)

So as you explained later in this sub-thread, Sergey's approach is
essentially the same as Phillip's.

I still do not understand Sergey's approach on a fundamental level. I
mean, I can follow his instructions how to implement his algorithm, but it
is as if I had a blindfold on and somebody guided me through a maze: I
understand *what* I am supposed to do, but I have no clue *why*.

And admittedly, I got very frustrated when a document was thrown my way
that is too long to read in one sitting, and all of my attempts at getting
clear and comprehensible answers to specific questions were met with "go
and read that document, I am sure you will understand then".

For something as fundamental to my daily workflow as an interactive rebase
(*especially* when trying to maintain the branch topology), this is no
good at all.

Since you already confirmed that there is essentially no difference
between the two approaches, I will simply go with the one I understand, in
particular I understand *why* it works.

But let's read on, maybe I will change my mind based on your explanations
(which do answer my questions, thank you so much for that)...

> > > The main problem with this decision is that we still don't see how
> > > and when to stop for user amendment using this method. OTOH, the
> > > original has this issue carefully discussed.
> > 
> > Why would we want to stop, unless there are merge conflicts?
> 
> Because we can reliably know that something "unusual" happened - and by
> that I don`t necessarily mean "wrong", but just might be worth user
> inspection.

We have a similar conundrum in recursive merges. Remember how multiple
merge bases are merged recursively? There can be merge conflicts, too, in
*any* of the individual merges involved, and indeed, there are (under
relatively rare circumstances).

Since we already faced that problem, and we already answered it by
presenting possibly nested merge conflicts, I am in strong favor of
keeping our new scenario consistent: present possibly-nested merge
conflicts.

As far as I understand, one of the arguments in favor of the current
approach was: there is no good way to tell the user where they are, and
how to continue from there. So better just to continue and present the
user with the entire set of conflicts, and have an obvious way out.

> For example, situation like this (M is made on A3 with `-s ours`, 
> obsoleting Bx commits):
> 
> (1) ---X8--X9 (master)
>        |\
>        | A1---A2---A3
>        |             \
>        |              M (topic)
>        |             /
>        \-B1---B2---B3
> 
> ... where we want to rebase M onto X9 is what I would call "usual 
> stuff", but this situation (M is still made on A3 with `-s ours`, 
> obsoleting Bx commits, but note cherry-picked B2'):
> 
> (2) ---X8--B2'--X9 (master)
>        |\
>        | A1---A2---A3
>        |             \
>        |              M (topic)
>        |             /
>        \-B1---B2---B3
> 
> ... where we still want to rebase M onto X9 is what we might consider 
> "unusual", because we noticed that something that shouldn`t be part 
> of the rebased merge commit (due to previous `-s ours`) actually got 
> in there (due to later cherry-pick), and just wanting the user to 
> check and confirm.

We already have those scenarios when performing a regular interactive
rebase, where a patch was already applied upstream. In the normal case,
the user is not even shown B2, thanks to the --cherry-pick option used in
generating the todo list.

Granted, in some cases --cherry-pick does not detect that, and then we
generate a todo list including B2, and when that patch is applied, the
interactive rebase stops, saying that there are no changes to be
committed.

And this behavior is exactly the same with --recreate-merges!

So I do not think that it would make sense to bother the user *again* when
rebasing the merge commit.

If there are merge conflicts, yes, we will have to. If there are none
(even if your U1' != U2'), it would be outright annoying to stop.

> > > "rebase sides of the merge commit and then three-way merge them back
> > > using original merge commit as base"
> > 
> > And that is also wrong, as I had proved already! Only Buga's addition
> > made it robust against dropping/modifying commits, and that addition
> > also makes it more complicated.
> 
> No, this is actually right, that sentence nicely describing _how_ it 
> works.

Does it? Because that's exactly backwards from how Phillip's approach
works: it certainly does not use the original merge commit as base. It
uses the non-rebased merge parent as base, one at a time.

Now I am curious...

> That addition of mine was just including the correct merge base (being
> the original merge commit that we are "rebasing"), and that`s what
> Sergey is talking about.

Okay, render me still unconvinced that Sergey's approach is actually
correct.

> > > > - it is *very easy* to reason about, once it is pointed out that
> > > > rebases and merges result in the same trees.
> > >
> > > The original is as easy to reason about, if not easier, especially as
> > > recursive merge strategy is not being used there in new ways.
> > 
> > So do it. I still have to hear a single-sentence, clear and obvious
> > explanation why it works.

So the closest I got was the description with the *original merge commit*
as merge base, which I disagree with. It does not make sense to me.

Consecutive three-way merges between the original merge commit and the
merge parents (with the *original merge parents* as merge base,
respectively), still makes the most sense to me: it is a merge between the
amendmends to the merge commit and the changes introduced by rebasing the
merge parents.

I am now getting the sense as if Sergey's approach (with your, let's say,
"fix") is trying to apply too much, and by using the original merge commit
as merge base then tries to undo part of that.

> > > I honestly don't see any advantages of Phillip's method over the
> > > original, except personal preferences. At the same time, I have no
> > > objection of using it either, provided consistency check problem is
> > > solved there as well.
> > 
> > Okay, let me reiterate then, because I do not want this point to be
> > missed:
> > 
> > Phillip's method is essentially merging the new tips into the original
> > merge, pretending that the new tips were not rebased but merged into
> > upstream.
> > 
> > So it exploits the duality of the rebase and merge operation, which both
> > result in identical trees (potentially after resolving merge conflicts).
> > 
> > I cannot think of any such interpretation for your proposal augmented by
> > Buga's fix-ups. And I haven't heard any such interpretation from your
> > side, either.
> 
> Ok here it goes (I might be still wrong, but please bare with me).
> 
> What Sergey originally described also uses the "duality of the rebase 
> and merge operation", too ;) Just in a bit different way (and might 
> be a more straightforward one?).

I will be the judge whether it looks more straight-forward to me. :-)

> Here`s a starting point, two commits A and B, merged into M:
> 
> (3) ---A
>         \
>          M
>         /
>     ---B
> 
> 
> According the "patch theory"[1] (which might not be too popular 
> around here, but should serve the purpose for what I`m trying to 
> explain),

I think that the patch theory is not usually quoted here because

1) originally, Darcs had this snake oil way of (over-)selling its theory
   as having "roots in quantum mechanics" [*1*] at the time Git took off,
   and

2) it does not really have a good concept of rebasing, even if it should
   be theoretically possible to integrate that into the "theory of
   patches".

> each merge commit can be "transformed" to two non-merge commits, one on
> top of each of the merge parents, where new commit brings its original
> merge parent commit tree to the state of the merge commit tree:
> 
> (4) ---A---U1
> 
> 
> 
>     ---B---U2

You get the same result if you stick to regular graph theory, of course.
All you need to do is to interpret the directed vertices between nodes as
a patch & parent relationship. Simple.

> Now, we have two new commits, U1 and U2, each having the same tree as 
> previous merge commit M, but representing changes in regards to 
> specific parents - and this is essentially what Sergey`s original 
> approach was using (whether he knew it, or not).
> 
> When it comes to rebasing, it`s pretty simple, too. As this:
> 
> (5) ---X1---o---o---o---o---o---X2 (master)
>        |\
>        | A1---A2---A3
>        |             \
>        |              M
>        |             /
>        \-B1---B2---B3
> 
> ... actually equals this:
> 
> (6) ---X1---o---o---o---o---o---X2 (master)
>        |\
>        | A1---A2---A3---U1
>        |
>        |
>        |
>        \-B1---B2---B3---U2
> 
> ... where trees of M, U1 and U2 are same,

Okay, so you basically duplicated the merge commit and dropped its
semantics as a merge commit. That is a very big difference to Phillip's
approach already.

It opens the door to ambiguities, as we will see later.

> and we can use the regular rebase semantics and rebase it to this:
> 
> (7) ---X1---o---o---o---o---o---X2 (master)
>                                 |\
>                                 | A1'--A2'--A3'--U1'
>                                 |
>                                 |
>                                 |
>                                 \-B1'--B2'--B3'--U2'
> 
> ... which is essentially this again:
> 
> (8) ---X1---o---o---o---o---o---X2 (master)
>                                 |\
>                                 | A1'--A2'--A3'
>                                 |            \
>                                 |             M'
>                                 |            /
>                                 \-B1'--B2'--B3'
> 
> ... where it is still true that trees of U1', U2' and M' are still 
> the same. So we managed to rebase a merge commit without ever doing a 
> merge :) (note that, practically, we _can_ finally even merge U1' and 
> U2' to produce M', it shouldn`t really matter as all the trees are 
> the same, so merge will be a no-op)

U1' and U2' do not have the same tree, though *especially* when the user
did not edit the todo list to insert/modify/drop/reorder commits.

This violation of expectations is of course caused by "duplicating the
merge commit" into U1 and U2.

But let's continue.

> But, as we saw (what you`ve explained), this doesn`t really work in 
> case one of the sides of the merge gets "disbalanced" (so to say), 
> like dropping a commit (which could also happen non-interactively, 
> where a commit has been cherry-picked to a different branch, but
> previously obsoleted by `-s ours` merge).

Precisely. A *very* important counter-argument to this approach so far.

> As observed, dropped commit could still wrongly get into final merge 
> commit tree (or cherry-picked commit wrongly not get there), due to 
> the nature of those rebased U1 and U2 temporary commits to hold all 
> the differences in regards to their specific merge commit parent.

The reason for this, of course, is that either U1's or U2's diff will show
those differences, *and we still try to rebase them even if the user
already dropped them*.

But let's continue.

> A natural improvement to original idea which Sergey eventually came 
> up with after my findings (which you now ended up calling a hack, even, 
> but I would respectfully disagree), is to use original merge commit M 
> as a merge base for final merge of U1' and U2' - and here is why it 
> works, too, and why I wouldn`t consider it a hack, but a proper (and 
> a solid) solution.
> 
> Merge commit M is what we are rebasing, so we should have a way to 
> somehow learn from it, alongside U1 and U2 temporary commits - and 
> that is what using original merge commit M as a base does for us. It 
> helps us spot any "disbalances" that happened in comparison to original 
> merge parent trees, which is exactly what we`re interested in.

... except that it gets the direction wrong. Rather than trying to *avoid*
rebasing possibly dropped changes, it tries to kind of "undo" them by
using a merge base that does not really make sense (unless you think of it
as a "revert").

It would make sense if M could interpreted as a branch point. But it
cannot, as we specifically did *not* continue to develop the merge parents
from that merge commit.

Instead, what we did was to branch off of the original branch point X1.

Reframing the rebase of a sub-branch (X1..A3) as merge with upstream,
however, we can interpret M and A3' as revisions we want to merge, with A3
as the merge base.

(You can think of it in terms of the "theory of patches" thusly: if X1..A3
is represented by patch K, X1..X2 by patch L, and X2..A3' by K', then what
we want to merge into M is K^(-1).L.K', which is precisely what A3..A3'
translates to.)

You can also think of it as diverging changes going from A3: one direction
was to merge (resulting in the commit M), the other direction was to
rebase onto X2 (resulting in A3'), and a 3-way merge M <- A3 -> A3' will
reconcile those changes (and you will want to repeat with B3/B3', too).

Let's put that into the context of your example: instead of introducing U1
and U2, we introduce V1 and V2 right away, as temporary *merge* commits,
where the tree of V1 is identical to the one of A3', and the tree of V2
to that of B3'.

 ---X1---o---o---o---o---o---X2 (master)
    |                        |\
    |                        | A1'--A2'--A3'--V1
    |\                       |               /
    | -A1---A2---A3----------+---------------
    |              \         |
    |               M        \-B1'--B2'--B3'--V2
    |              /                         /
     \-B1---B2---B3--------------------------

Note: the *really* important difference is that these temporary commits
are based on the *rebased* history rather than the *unrebased* history.

Second note: this is very related to Git for Windows' original strategy of
coping with the non-fast-forwarding nature of rebases: after rebasing the
Windows-specific patches on top of core Git, we merged (with `-s ours`)
the unrebased history. This 1) makes it fast-forwardable, and 2) still
rebases the patches [*2*].

Third note: my favorite mental model is still the duality of rebasing and
merging, in which case V1 and V2 would not have A3' and B3' as first
parents, but X2.

Phillip's strategy is to merge M with V1 and V2.

This translates to "merge the amendments of the merge commit M with the
changes introduced by rebasing its merge parents".

The really cute part about this is that (in contrast to using U1' and
U2'), we do not merge the amendments of the merge commit multiple times,
but exactly once. And therefore, we do not need to "undo" them by using
the original merge commit as merge base, either (which would introduce
many more opportunities for merge conflicts to creep in, oftentimes
unnecessary conflicts to begin with).

> In ideal case (as per "patch theory"[1]), final merge of rebased U1' 
> and U2' with original merge commit M as a base would be rather simple, 
> too (not to say pointless again), as both U1' and U2' would have same 
> changes in comparison to M, namely all the changes from commit we are 
> rebasing from (X1 above) to commit we are rebasing to (X2 above).
> 
> But in a more complex case like this:
> 
> (9) ---X1---B2'---o---o---o---o---X2 (master)
>                                   |\
>                                   | A12--A2'---B3'
>                                   |             \
>                                   |              M'
>                                   |             /
>                                   \-B1'--B3'---B4
> 
> ..., being what we are ultimately interested in, it helps us notice 
> all kind of changes on our rebased merge parent branches, and act 
> accordingly (as shown by my previously posted test script[2]).

To use the "theory of patches" to explain why Phillip's approach is so
much more appealing: in Sergey's approach, we will rebase U1 (which is
"sort of" B1.B2.B3 in the "theory of patches"). If the cherry-pick of B2'
caused merge conflicts that had to be resolved, then these merge conflicts
will have to be resolved *again* when rebasing U1 (because B2 is *part of*
U1). And of course they will have to be resolved *again* when merging U1'
with U2'.

In short: Phillip's approach is a short-cut that avoids unnecessary merge
conflicts because it avoids rebasing changes that would need to be undone
right away anyway.

> All this said (and hoping anyone is still reading this), to shed some 
> light on what I meant by "favoring Sergey`s approach in general, but 
> _implemented_ through what Phillip described".
> 
> I think we should use temporary commits U1 and U2, being a sound 
> approach backed up by the "patch theory"[1], as it helps us notice 
> any "disbalances" of the rebased merge commit parents trees (so we 
> can stop for user inspection), finally merging them with original 
> merge commit as a base, in order to spot additional changes on trees 
> of the merge commit parents we are rebasing.

In Phillip's approach, we do not need to rebase the amendments of the
merge commit M twice (or even more times, for octopus merges). Therefore,
there is no opportunity for these imbalances.

> But, the merging steps themselves, backed up by a need to stop for 
> conflict resolution as soon as one happens, should be performed 
> _iteratively_, like Phillip showed us... I would think :)

From a user interface perspective, this is a bad idea: you would now have
to communicate to the user *where* in the process they are.

And for that you would have to explain that process to them. Including
"theory of patches" and all.

Taking me as a prime example, you now know how tedious and impractical
that would be.

> And, for example, that would do wonders when we introduce completely 
> new branch during an interactive rebase, too, for example:
> 
> (10) ---X1---B2'---o---o---o---o---X2 (master)
>         |\                        /|\
>         | A1---A2---A3---U1       || A12--A2'---B3'---U1'
>         |             \           ||             \
>         |              M          ||              M'
>         |             /           ||             /|
>         \-B1---B2---B3---U2       |\---B3'---B4-/-|---U2'
>                                   |               |
>                                   \-----B1'-------/
> 
> 
> In this situation, we would merge all temporary Ux commits using 
> original merge M as a merge base, and then merge _that_ resulting 
> tree with B1' (being a newly introduced merge commit parent), using 
> whatever merge base is most appropriate between the two (in this 
> case, X2).

The semantics of octopus merges are very different from regular recursive
merges. I am not sure that we want to go there in this discussion...

If, on the other hand, you do not try to turn M from a regular 2-parent
merge commit into an octopus merge during an interactive rebase, but
instead split the B branch into two branches *before* merging the result
into the rebased merge commit, we are in the same square as
reordering/dropping/inserting/modifying patches: to neither of the
presented strategies would it matter what kind of branch topology the
merge parents have.

For the record: I am still not convinced that Phillip's and Sergey's
approach are equivalent, even in terms of the "theory of patches". But if
they are, then Phillip's version is a shorter version that avoids applying
changes just to revert them right away. And in a setting where each patch
can cause merge conflicts (as the interactive rebase is), the less changes
you have to apply, the better.

Ciao,
Dscho

> [1] https://en.wikibooks.org/wiki/Understanding_Darcs/Patch_theory

Footnote *1*: https://web.archive.org/web/20050511033324/http://darcs.net/
And it must be said: the "theory of patches" are not rooted in quantum
mechanics. They might be inspired by Darc's inventor being more familiar
with it than with fundamental algebra, for sure. The "theory of patches"
is nothing else than an algebra on graph vertices, though.

Footnote *2*: We called this strategy the rebasing merge. We replaced it
by the opposite strategy ("merging rebase", i.e. starting with the `git
merge -s ours <unrebased>` and *then* applying the rebased patches)
because the latter makes it really obvious where the Windows-specific
patches *start*.

Of course, you could draw the diagram very easily also using the merging
rebase, the conclusion would still be the same.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08 23:31               ` Igor Djordjevic
@ 2018-03-11 15:47                 ` Johannes Schindelin
  2018-03-11 20:53                   ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-11 15:47 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Buga,

On Fri, 9 Mar 2018, Igor Djordjevic wrote:

> On 08/03/2018 20:58, Igor Djordjevic wrote:
> > 
> > > Phillip's method is essentially merging the new tips into the original
> > > merge, pretending that the new tips were not rebased but merged into
> > > upstream.
> > 
> > [...]
> > 
> > Here`s a starting point, two commits A and B, merged into M:
> > 
> > (3) ---A
> >         \
> >          M
> >         /
> >     ---B
> > 
> > 
> > According the "patch theory"[1] (which might not be too popular 
> > around here, but should serve the purpose for what I`m trying to 
> > explain), each merge commit can be "transformed" to two non-merge 
> > commits, one on top of each of the merge parents, where new commit 
> > brings its original merge parent commit tree to the state of the 
> > merge commit tree:
> > 
> > (4) ---A---U1
> > 
> > 
> > 
> >     ---B---U2
> > 
> > 
> > Now, we have two new commits, U1 and U2, each having the same tree as 
> > previous merge commit M, but representing changes in regards to 
> > specific parents - and this is essentially what Sergey`s original 
> > approach was using (whether he knew it, or not).
> > 
> > When it comes to rebasing, it`s pretty simple, too. As this:
> > 
> > (5) ---X1---o---o---o---o---o---X2 (master)
> >        |\
> >        | A1---A2---A3
> >        |             \
> >        |              M
> >        |             /
> >        \-B1---B2---B3
> > 
> > ... actually equals this:
> > 
> > (6) ---X1---o---o---o---o---o---X2 (master)
> >        |\
> >        | A1---A2---A3---U1
> >        |
> >        |
> >        |
> >        \-B1---B2---B3---U2
> > 
> > ... where trees of M, U1 and U2 are same, and we can use the regular 
> > rebase semantics and rebase it to this:
> > 
> > (7) ---X1---o---o---o---o---o---X2 (master)
> >                                 |\
> >                                 | A1'--A2'--A3'--U1'
> >                                 |
> >                                 |
> >                                 |
> >                                 \-B1'--B2'--B3'--U2'
> > 
> > ... which is essentially this again:
> > 
> > (8) ---X1---o---o---o---o---o---X2 (master)
> >                                 |\
> >                                 | A1'--A2'--A3'
> >                                 |            \
> >                                 |             M'
> >                                 |            /
> >                                 \-B1'--B2'--B3'
> > 
> 
> Having explained all this, I realized this is the same "essentially 
> merging the new tips into the original pretending that the new tips 
> were not rebased but merged into upstream" as Phillip`s one, just 
> that we have additional temporary commits U1 and U2 (as per mentioned 
> "patch theory") :)

But if the old tips had been merged into upstream (resulting in the new
tips), then the merge bases would be *the old tips*.

I am still not sure for what scenarios Phillip's strategy is the same as
Sergey's (updated) one, as the former strategy can do completely without
temporary commits, and cannot introduce ambiguities when rebasing the
changes introduced by M (i.e. the "amendmendts" we talked about).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-11 12:00                                             ` Johannes Schindelin
@ 2018-03-11 16:33                                               ` Igor Djordjevic
  2018-03-12 10:37                                                 ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-11 16:33 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Dscho,

On 11/03/2018 13:00, Johannes Schindelin wrote:
> 
> > I actually like `pick` for _rebasing_ merge commits, as `pick` is 
> > already used for rebasing non-merge commits, too, so it feels natural.
> 
> Phillip is right, though: this would repeat the design mistake of
> --preserve-merges.
> 
> We must not forget that the interactive mode is the target here, and that
> the syntax (as well as the generated todo list) must allow for easy
> modification. The `pick <merge>` approach does not allow that, so we
> cannot use it.
> 
> The `merge -R -C <original-commit> <merge-head>` approach is a lot better:
> it offers the flexibility, without sacrificing the ease when not modifying
> the todo list.

Eh, I`m afraid the quote you took is missing the rest of its 
(important) context, where I mentioned already proposed format for 
`pick` in that other subthread[1], including other parameters beside 
merge commit to pick, as that parent mapping.

I agree with both of you that `pick <merge-commit>` is inflexible 
(not to say just plain wrong), but I never thought about it like that.

If we are to extract further mentioned explicit old:new merge 
parameter mapping to a separate discussion point, what we`re 
eventually left with is just replacing this:

	merge -R -C <original--merge-commit> <merge-head>

... with this:

	pick <original--merge-commit> <merge-head>


That is what I had in mind, seeming possibly more straightforward and 
beautifully aligned with previously existing (and well known) 
`rebase` terminology.

Not to say this would make it possible to use other `rebase -i` todo 
list commands, too, like if you want to amend/edit merge commit after 
it was rebased, you would write:

	edit <original--merge-commit> <merge-head>

..., where in case you would simply like to reword its commit 
message, it would be just:

	reword <original--merge-commit> <merge-head>


Even `squash` and `fixup` could have their place in combination with 
a (to be rebased) merge commit, albeit in a pretty exotic rebases, 
thus these could probably be just disallowed - for the time being, at 
least.

The real power would be buried in implementation, learning to rebase 
merge commits, so user is left with a very familiar interface, slightly 
adapted do accommodate a bit different nature of merge commit in 
comparison to an ordinary one, also to allow a bit more of interactive 
rebase functionality, but it would pretty much stay the same, without 
even a need to learn about new `merge`, `-R`, `-C`, and so on.

Yes, those would have its purpose, but for real merging then 
(creating new merges, or recreating old ones), not necessarily for 
merge rebasing.

With state of `merge -R -C ...` (that `-R` being the culprit), it 
kind of feels like we`re now trying to bolt "rebase merges" 
functionality onto a totally different one (recreate merges, serving 
a different purpose), making them both needlessly dependent on each 
other, further complicating user interface, making it more confusing 
and less tunable as per each separate functionality needs (rebase vs. 
recreate).

I guess I`m the one to pretty much blame here, too, as I really 
wanted `--recreate-merges` to handle "rebase merges" better, only to 
later realize it might not be the best tool for the job, and that a 
more separate approach would be better (at least not through the same 
`merge` todo list command)...

Regards, Buga

[1] https://public-inbox.org/git/f3872fb9-01bc-b2f1-aee9-cfc0e4db77d6@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-11 12:08                                           ` Johannes Schindelin
@ 2018-03-11 17:34                                             ` Igor Djordjevic
  2018-03-12 10:46                                               ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-11 17:34 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Dscho,

On 11/03/2018 13:08, Johannes Schindelin wrote:
> 
> > Hmm, funny enough, `pick <original merge>` was something I though about
> > originally, too, feeling that it might make more sense in terms on
> > what`s really going on, but I guess I wanted it incorporated into
> > `--recreate-merges` too much that I tried really hard to fit it in,
> > without changing it much :/
> 
> The `pick <original-merge>` syntax is too limited to allow reordering, let
> alone changing the parents.

I agree, `pick <original-merge>` syntax alone is never what I had in 
mind, so it`s missing further context here, touched in that other 
subthread[1]. My fault, sorry for confusion.

> >   pick <original-merge> <original-parent1>:HEAD <original-parent2>:<new-parent2>
> 
> I do not really like it, as it makes things a ton less intuitive. If you
> did not know about this here discussion, and you did not read the manual
> (and let's face it: a UI that does not require users to read the manual is
> vastly superior to a UI that does), and you encountered this command:
> 
> 	merge deadbeef cafecafe:download-button
> 
> what would you think those parameters would mean?
> 
> Granted, encountering
> 
> 	merge -R -C deadbeef download-button # Merge branch 'download-button'
> 
> is still not *quite* as intuitive as I would wish. Although, to be honest,
> if I encountered this, I would think that I should probably leave the -R
> and the -C deadbeef alone, and that I could change what is getting merged
> by changing the `download-button` parameter.

Agreed, encountering mapping is slightly more complicated, but I 
would argue it`s much more powerful at the same time, too, thus 
pretty much worth it.

Without it, actually, it seems like we`re repeating the mistake of 
`--preserve-merges`, where we`re assuming too much (order of new and 
old parents being the same, and I guess number of them, too).

Oh, and as we`re still discussing in terms of `merge` command, using 
(elsewhere mentioned[1]) `pick` instead, it might be even less 
non-intuitive, as we`re not married to `merge` semantics any more:

	pick deadbeef cafecafe:download-button


And might be calling it "non-intuitive" is unfair, I guess it would 
rather be "not familiar yet", being case with any new functionality, 
let alone a very powerful one, where getting a clue on what it does 
at the beginning could do wonders later.

Sacrificing that power for a bit of perceived simplicity, where it 
actually assumes stuff on its own (trying to stay simple for the 
user), doesn`t seem as a good way to go in the long run.

Sometimes one just needs to read the manual, and I don`t really think 
this is a ton complicated, but just something we didn`t really have 
before (real merge rebasing), so it requires a moment to grasp the 
concept.

But I`m still not sure if there isn`t a better way to present 
explicit mapping, just that <old>:<new> seemed as the most straightforward 
one to pass on the point for the purpose of discussing it.

And I`m not reluctant to simplifying user interface, or for dropping 
the explicit mapping altogether, even, just for carefully measuring 
what we could lose without explicit mapping - think flexibility, but 
ease and correctness of implementation, too, as we need to guess the 
old merge parents and which new one they should correspond to.

> > p.s. Are we moving towards `--rebase-merges` I mentioned in that 
> > other topic[1], as an add-on series after `--recreate-merges` hits 
> > the mainstream (as-is)...? :P
> 
> That's an interesting question. One that I do not want to answer alone,
> but I would be in favor of `--rebase-merges` as it is IMHO a much better
> name for what this option is all about.

Saying in favor of `--rebase-merges`, you mean as a separate option, 
alongside `--recreate-merges` (once that series lands)?

That does seem as the most clean, intuitive and straightforward 
solution. Depending on the option you provide (recreate vs rebase), 
todo list would be populated accordingly by default - but important 
thing is "todo list parser" would know to parse both, so one can 
still adapt todo list to both recreate (some) and rebase (some other) 
merges at the same time.

Of course, this only once `--rebase-merges` gets implemented, too, 
but as we had waited for it for so long, it won`t hurt to wait a bit 
more and possibly do it more properly, than rush it now and make a 
confusing user interface, needlessly welding two functionalities 
together (rebase vs. recreate).

But I guess you already knew my thoughts, so let`s see what other`s 
think, too ;)

Regards, Buga

[1] https://public-inbox.org/git/f4e6237a-84dc-1aa8-150d-041806e2416e@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-11 12:11                                           ` Johannes Schindelin
@ 2018-03-11 17:46                                             ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-11 17:46 UTC (permalink / raw)
  To: Johannes Schindelin, Jacob Keller
  Cc: Phillip Wood, Sergey Organov, Git Mailing List, Johannes Sixt,
	Junio C Hamano

Hi Dscho,

On 11/03/2018 13:11, Johannes Schindelin wrote:
> 
> > > I did wonder about using 'pick <original-merge>' for rebasing merges
> > > and keeping 'merge ...' for recreating them but I'm not sure if that
> > > is a good idea. It has the advantage that the user cannot specify the
> > > wrong parents for the merge to be rebased as 'git rebase' would work
> > > out if the parents have been rebased, but maybe it's a bit magical to
> > > use pick for merge commits. Also there isn't such a simple way for the
> > > user to go from 'rabase this merge' to 'recreate this merge' as they'd
> > > have to write the whole merge line themselves (though I guess
> > > something like emacs' git-rebase.el would be able to help with that)
> >
> > Since the ultimate commit hashes of newly rebased commits would be
> > unknown at the time of writing the todo file, I'm not sure how this
> > would work to specify the parents?
> 
> I agree with Phillip's follow-up that the `pick <original-merge>` syntax
> would pose a problem, but for different reasons: We already tried it, with
> --preserve-merges, and it is just a really stupid syntax that does not
> allow the user even to reorder commits. Or drop commits (except at the
> very end of the todo list).

Hehe, please excuse me, but in the light of that other explicit (or 
not) parent mapping discussion[1], I would take a chance to be really 
sneaky here and say that being non-explicit "is just a really stupid 
syntax that does not allow the user even to reorder rebased merge 
parents. Or drop parents (except at the very end of the parent list)." ;)

[1] https://public-inbox.org/git/b329bb98-f9d6-3d51-2513-465aad2fa37a@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 15:47                 ` Johannes Schindelin
@ 2018-03-11 20:53                   ` Igor Djordjevic
  2018-03-12 10:20                     ` Johannes Schindelin
  2018-03-12 13:07                     ` Sergey Organov
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-11 20:53 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Dscho,

On 11/03/2018 16:47, Johannes Schindelin wrote:
> 
> > > > Phillip's method is essentially merging the new tips into the original
> > > > merge, pretending that the new tips were not rebased but merged into
> > > > upstream.
> > >
> > > [...]
> > >
> > > Here`s a starting point, two commits A and B, merged into M:
> > >
> > > (3) ---A
> > >         \
> > >          M
> > >         /
> > >     ---B
> > >
> > >
> > > According the "patch theory"[1] (which might not be too popular 
> > > around here, but should serve the purpose for what I`m trying to 
> > > explain), each merge commit can be "transformed" to two non-merge 
> > > commits, one on top of each of the merge parents, where new commit 
> > > brings its original merge parent commit tree to the state of the 
> > > merge commit tree:
> > >
> > > (4) ---A---U1
> > >
> > >
> > >
> > >     ---B---U2
> > >
> > >
> > > Now, we have two new commits, U1 and U2, each having the same tree as 
> > > previous merge commit M, but representing changes in regards to 
> > > specific parents - and this is essentially what Sergey`s original 
> > > approach was using (whether he knew it, or not).
> > >
> > > When it comes to rebasing, it`s pretty simple, too. As this:
> > >
> > > (5) ---X1---o---o---o---o---o---X2 (master)
> > >        |\
> > >        | A1---A2---A3
> > >        |             \
> > >        |              M
> > >        |             /
> > >        \-B1---B2---B3
> > >
> > > ... actually equals this:
> > >
> > > (6) ---X1---o---o---o---o---o---X2 (master)
> > >        |\
> > >        | A1---A2---A3---U1
> > >        |
> > >        |
> > >        |
> > >        \-B1---B2---B3---U2
> > >
> > > ... where trees of M, U1 and U2 are same, and we can use the regular 
> > > rebase semantics and rebase it to this:
> > >
> > > (7) ---X1---o---o---o---o---o---X2 (master)
> > >                                 |\
> > >                                 | A1'--A2'--A3'--U1'
> > >                                 |
> > >                                 |
> > >                                 |
> > >                                 \-B1'--B2'--B3'--U2'
> > >
> > > ... which is essentially this again:
> > >
> > > (8) ---X1---o---o---o---o---o---X2 (master)
> > >                                 |\
> > >                                 | A1'--A2'--A3'
> > >                                 |            \
> > >                                 |             M'
> > >                                 |            /
> > >                                 \-B1'--B2'--B3'
> > >
> >
> > Having explained all this, I realized this is the same "essentially 
> > merging the new tips into the original pretending that the new tips 
> > were not rebased but merged into upstream" as Phillip`s one, just 
> > that we have additional temporary commits U1 and U2 (as per mentioned 
> > "patch theory") :)
> 
> But if the old tips had been merged into upstream (resulting in the new
> tips), then the merge bases would be *the old tips*.

Exactly, and that is what part you`ve cut out of the quote was 
showing :) By Phillip`s implementation, we would start with *old tips* 
as merge bases, indeed (old tips being U1 and U2 in this case), where 
it further gets transformed as previously written:

> 	git merge-recursive U1 -- M U1'
> 	tree="$(git write-tree)"
> 	git merge-recursive U2 -- $tree U2'
> 	tree="$(git write-tree)"
> 
> ..., where we know U1 = U2 = M (in regards to trees), so this is the 
> same as:
> 
> 	git merge-recursive M -- M U1'
> 	tree="$(git write-tree)"
> 	git merge-recursive M -- $tree U2'
> 	tree="$(git write-tree)"

Here, `git merge-recursive M -- M U1'` simply equals to U1' tree 
(being a fast-forward merge), so we can write the two merges above as
a single merge, too:

> 	git merge-recursive M -- U1' U2'
> 	tree="$(git write-tree)"
> 
> ... which is exactly what Sergey`s (updated) approach suggests, 
> merging U1' and U2' with M as merge-base (and shown inside that 
> sample implementation script I provided[1]) :)

So from *old tips* being the rebased merge base (Phillip), we got to 
*old merge commit* being the rebased merge base (Sergey), or vice 
versa. Does this shed a bit more light on it now? Or you wanted to 
point out something else in the first place...?

> I am still not sure for what scenarios Phillip's strategy is the same as
> Sergey's (updated) one, as the former strategy can do completely without
> temporary commits [...]

I think the root of misunderstanding might be coming from the fact 
that Sergey was mainly describing a general concept (without a 
strictly defined implementation strategy, not being restricted to a 
specific one), where Phillip came up with a solution that eventually 
seems to use the same concept (as those transformations above should 
show), but simplifying it further inside a concrete implementation.

By saying that Phillip "simplified it", even though transformations 
shown above might show different, I mean he managed to further 
decompose what Sergey was aiming for, abstracting temporary commits U1 
and U2 out of the equation, thus making them optional, but not 
required.

So, if easier to implement and reason about, I think what Phillip 
described is a way to go to produce the rebased merged commit - but 
in case we want to have that "clean rebased merge" check that U1' == 
U2' comparison does provide, as they should really be the same (tree 
wise) in simple (and the most used?) merge rebasing, we can do that 
after the fact, even.

Might be this check could be the most useful in non-interactive 
rebases, where rebased merge parents` trees could be expected to stay 
"balanced" more often (U1' == U2'), without interactive fiddling to 
"disbalance" them. I don`t know, just thinking out loud.

> [...] and cannot introduce ambiguities when rebasing the
> changes introduced by M (i.e. the "amendmendts" we talked about).

Hmm, not following here, which ambiguities are we talking about?

Thanks, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 15:40           ` Johannes Schindelin
@ 2018-03-11 22:04             ` Igor Djordjevic
  2018-03-12 12:05               ` Sergey Organov
  2018-03-12 22:41               ` Igor Djordjevic
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-11 22:04 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Dscho,

I`m yet to read (and reason about) your whole (very informative) 
reply, but I just wanted to address this part first, as it might be a 
clear end-game situation already, due to a mutual agreement, all the 
rest being purely academic, interesting, but not any more (that) 
important to discuss.

On 11/03/2018 16:40, Johannes Schindelin wrote:
> 
> > For myself, I do actually favor Sergey`s approach in general, but 
> > _implemented_ through what Phillip described (or a mixture of both, to 
> > be precise). But, let me explain... :)
> 
> So as you explained later in this sub-thread, Sergey's approach is
> essentially the same as Phillip's.
> 
> I still do not understand Sergey's approach on a fundamental level. I
> mean, I can follow his instructions how to implement his algorithm, but it
> is as if I had a blindfold on and somebody guided me through a maze: I
> understand *what* I am supposed to do, but I have no clue *why*.
> 
> And admittedly, I got very frustrated when a document was thrown my way
> that is too long to read in one sitting, and all of my attempts at getting
> clear and comprehensible answers to specific questions were met with "go
> and read that document, I am sure you will understand then".
> 
> For something as fundamental to my daily workflow as an interactive rebase
> (*especially* when trying to maintain the branch topology), this is no
> good at all.
> 
> Since you already confirmed that there is essentially no difference
> between the two approaches, I will simply go with the one I understand, in
> particular I understand *why* it works.
> 
> But let's read on, maybe I will change my mind based on your explanations
> (which do answer my questions, thank you so much for that)...

No problem, I learned much myself trying to write those explanations 
in the first place, and I still need to read on yet myself, seeing 
how well my explanations actually fared :) Thank you for still 
holding on, though.

But I just wanted to point out that you can really just go with what 
Phillip described if you find that easier to reason about (and/or 
implement), there`s even no need for mind changing, as essentially, 
and in my opinion, it seems to be just a bit different implementation 
of the same concept (but not requiring temporary commits).

That said, *if* we decide we like temporary commit U1' == U2' consistency 
check (especially for non-interactive rebase, maybe), we can produce 
these after the fact for the sake of the check only.

I will come with a follow-up, but all the rest might be less important.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 20:53                   ` Igor Djordjevic
@ 2018-03-12 10:20                     ` Johannes Schindelin
  2018-03-12 13:49                       ` Sergey Organov
  2018-03-13  0:29                       ` Igor Djordjevic
  2018-03-12 13:07                     ` Sergey Organov
  1 sibling, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-12 10:20 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Buga,

On Sun, 11 Mar 2018, Igor Djordjevic wrote:

> On 11/03/2018 16:47, Johannes Schindelin wrote:
> > 
> > > Having explained all this, I realized this is the same "essentially
> > > merging the new tips into the original pretending that the new tips
> > > were not rebased but merged into upstream" as Phillip`s one, just
> > > that we have additional temporary commits U1 and U2 (as per
> > > mentioned "patch theory") :)
> > 
> > But if the old tips had been merged into upstream (resulting in the
> > new tips), then the merge bases would be *the old tips*.
> 
> Exactly, and that is what part you`ve cut out of the quote was 
> showing :) By Phillip`s implementation, we would start with *old tips* 
> as merge bases, indeed (old tips being U1 and U2 in this case),

I really do not see how it would make sense to take the original merge
commit as merge base in this scenario. It makes no *logical* sense: in
which interpretation did you develop changes in two divergent directions
from that merge commit?

Whereas if you use the old tips as merge bases, you can say very easily
what those two directions were: one merged with other merge parents, the
other direction rebased on top of upstream. There. Two divergent sets of
changes that we want to reconcile ("merge"). Easy as apple pie.

> where it further gets transformed as previously written:
> 
> > 	git merge-recursive U1 -- M U1'
> > 	tree="$(git write-tree)"
> > 	git merge-recursive U2 -- $tree U2'
> > 	tree="$(git write-tree)"
> > 
> > ..., where we know U1 = U2 = M (in regards to trees), so this is the 
> > same as:
> > 
> > 	git merge-recursive M -- M U1'
> > 	tree="$(git write-tree)"
> > 	git merge-recursive M -- $tree U2'
> > 	tree="$(git write-tree)"
> 
> Here, `git merge-recursive M -- M U1'` simply equals to U1' tree 
> (being a fast-forward merge), so we can write the two merges above as
> a single merge, too:
> 
> > 	git merge-recursive M -- U1' U2'
> > 	tree="$(git write-tree)"
> > 
> > ... which is exactly what Sergey`s (updated) approach suggests, 
> > merging U1' and U2' with M as merge-base (and shown inside that 
> > sample implementation script I provided[1]) :)
> 
> So from *old tips* being the rebased merge base (Phillip), we got to 
> *old merge commit* being the rebased merge base (Sergey), or vice 
> versa. Does this shed a bit more light on it now? Or you wanted to 
> point out something else in the first place...?

Okay, I'll trust you that these stunts show that the two strategies are
equivalent as to what their results are.

The biggest difference is that it is easy for me to see the motivation
behind Phillip's strategy, whereas I am still puzzled why one would come
up with a complicated strategy that splits merge commits and re-merges
them later, and why it should work in general (I still suspect that this
is not the case).

Where "easy" meant that I had to spend 1h still to figure out why using
the unrebased merge parents as merge bases. The same amount of time did
not allow me to wrap my head around Sergey's verbose explanations.

But I'll take your word for it that the strategies are equivalent, and go
with the one that has both a simpler explanation (in my mind, at least),
and an more robust implementation.

> > I am still not sure for what scenarios Phillip's strategy is the same as
> > Sergey's (updated) one, as the former strategy can do completely without
> > temporary commits [...]
> 
> I think the root of misunderstanding might be coming from the fact 
> that Sergey was mainly describing a general concept (without a 
> strictly defined implementation strategy, not being restricted to a 
> specific one), where Phillip came up with a solution that eventually 
> seems to use the same concept (as those transformations above should 
> show), but simplifying it further inside a concrete implementation.

Well, Sergey started off by suggesting the "rebase the patch relatively to
the first parent always" strategy, then came up with a long-ish email
describing a different approach (which I slowly realize is related to the
first strategy, and it would have been *much* appreciated if it was not
left to the reader to figure that one out), then incorporated what I
called your hack (again, no clear and concise description what changed,
just throwing a bunch of big bones to the dogs with the next long-ish
document).

So I will not apologize for stopping to pay so much attention to that
sub-thread at some point.

> By saying that Phillip "simplified it", even though transformations
> shown above might show different, I mean he managed to further decompose
> what Sergey was aiming for, abstracting temporary commits U1 and U2 out
> of the equation, thus making them optional, but not required.

That is not how I read Phillip's mail. It was more like "how about this
instead". And it was simple enough, with clear example how to implement
it, that I thought about that single mail for an hour, until I was
satisfied that the motivation behind this strategy is sound.

Then you confirmed that it worked on your examples, and that's what
settled the score here.

> So, if easier to implement and reason about, I think what Phillip 
> described is a way to go to produce the rebased merged commit - but 
> in case we want to have that "clean rebased merge" check that U1' == 
> U2' comparison does provide, as they should really be the same (tree 
> wise) in simple (and the most used?) merge rebasing, we can do that 
> after the fact, even.

I do not know what that U1' == U2' check would buy us, as Phillip's
strategy does not require rebasing the amendments *twice*.

> Might be this check could be the most useful in non-interactive 
> rebases, where rebased merge parents` trees could be expected to stay 
> "balanced" more often (U1' == U2'), without interactive fiddling to 
> "disbalance" them. I don`t know, just thinking out loud.

Again, Phillip's strategy does not leave things to be "disbalanced".

> > [...] and cannot introduce ambiguities when rebasing the
> > changes introduced by M (i.e. the "amendmendts" we talked about).
> 
> Hmm, not following here, which ambiguities are we talking about?

U1' vs U2' of course. Those are two things that can be different, even if
they ideally would have identical trees.

Phillip's strategy does not leave that room for ambiguity.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-11 16:33                                               ` Igor Djordjevic
@ 2018-03-12 10:37                                                 ` Johannes Schindelin
  2018-03-12 12:56                                                   ` Sergey Organov
  2018-03-12 23:54                                                   ` Igor Djordjevic
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-12 10:37 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Sun, 11 Mar 2018, Igor Djordjevic wrote:

> I agree with both of you that `pick <merge-commit>` is inflexible 
> (not to say just plain wrong), but I never thought about it like that.
> 
> If we are to extract further mentioned explicit old:new merge 
> parameter mapping to a separate discussion point, what we`re 
> eventually left with is just replacing this:
> 
> 	merge -R -C <original--merge-commit> <merge-head>
> 
> ... with this:
> 
> 	pick <original--merge-commit> <merge-head>

I see where you are coming from.

I also see where users will be coming from. Reading a todo list in the
editor is as much documentation as it is a "program to execute". And I am
afraid that reading a command without even mentioning the term "merge"
once is pretty misleading in this setting.

And even from the theoretical point of view: cherry-picking non-merge
commits is *so much different* from "rebasing merge commits" as discussed
here, so much so that using the same command would be even more
misleading.

> That is what I had in mind, seeming possibly more straightforward and 
> beautifully aligned with previously existing (and well known) 
> `rebase` terminology.
> 
> Not to say this would make it possible to use other `rebase -i` todo 
> list commands, too, like if you want to amend/edit merge commit after 
> it was rebased, you would write:
> 
> 	edit <original--merge-commit> <merge-head>
> 
> ..., where in case you would simply like to reword its commit 
> message, it would be just:
> 
> 	reword <original--merge-commit> <merge-head>
> 
> 
> Even `squash` and `fixup` could have their place in combination with 
> a (to be rebased) merge commit, albeit in a pretty exotic rebases, 
> thus these could probably be just disallowed - for the time being, at 
> least.

Sure, for someone who read the manual, that would be easy to use. Of
course, that's the minority.

Also: the `edit` command is poorly named to begin with. A much cleaner
design would be to introduce the `break` command as suggested by Stephan.

> The real power would be buried in implementation, learning to rebase 
> merge commits, so user is left with a very familiar interface, slightly 
> adapted do accommodate a bit different nature of merge commit in 
> comparison to an ordinary one, also to allow a bit more of interactive 
> rebase functionality, but it would pretty much stay the same, without 
> even a need to learn about new `merge`, `-R`, `-C`, and so on.
> 
> Yes, those would have its purpose, but for real merging then 
> (creating new merges, or recreating old ones), not necessarily for 
> merge rebasing.
> 
> With state of `merge -R -C ...` (that `-R` being the culprit), it 
> kind of feels like we`re now trying to bolt "rebase merges" 
> functionality onto a totally different one (recreate merges, serving 
> a different purpose), making them both needlessly dependent on each 
> other, further complicating user interface, making it more confusing 
> and less tunable as per each separate functionality needs (rebase vs. 
> recreate).
> 
> I guess I`m the one to pretty much blame here, too, as I really 
> wanted `--recreate-merges` to handle "rebase merges" better, only to 
> later realize it might not be the best tool for the job, and that a 
> more separate approach would be better (at least not through the same 
> `merge` todo list command)...
> 
> [1] https://public-inbox.org/git/f3872fb9-01bc-b2f1-aee9-cfc0e4db77d6@gmail.com/

Well, the `-R` option is no worse than `git merge`'s `-s <strategy>`
option (which *also* changes the strategies rather drastically).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-11 17:34                                             ` Igor Djordjevic
@ 2018-03-12 10:46                                               ` Johannes Schindelin
  2018-03-13  0:16                                                 ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-12 10:46 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

I just have two thoughts to contribute as answer, so please excuse the
heavily cut quoted text:

On Sun, 11 Mar 2018, Igor Djordjevic wrote:

> Sometimes one just needs to read the manual, and I don`t really think
> this is a ton complicated, but just something we didn`t really have
> before (real merge rebasing), so it requires a moment to grasp the
> concept.

If that were the case, we would not keep getting bug reports about
--preserve-merges failing to reorder patches.

> Saying in favor of `--rebase-merges`, you mean as a separate option,
> alongside `--recreate-merges` (once that series lands)?

No. I am against yet another option. The only reason I pollute the option
name space further with --recreate-merges is that it would be confusing to
users if the new mode was called --preserve-merges=v2 (but work *totally
differently*).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 22:04             ` Igor Djordjevic
@ 2018-03-12 12:05               ` Sergey Organov
  2018-03-26 11:33                 ` Johannes Schindelin
  2018-03-12 22:41               ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 12:05 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, git, Johannes Sixt, Junio C Hamano,
	Jacob Keller, Phillip Wood

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

[...]

> That said, *if* we decide we like temporary commit U1' == U2' consistency 
> check (especially for non-interactive rebase, maybe), we can produce 
> these after the fact for the sake of the check only.

I don't believe interactive vs. non-interactive split is actually
helpful. I'd consider non-interactive just a special case of interactive
when user didn't edit the todo list, nothing more. No special treatment
should be required.

For one, consistency checks in both modes has similar importance, even
if only because there could be parts of history being interactively
rebased which the user didn't intend to edit, nor actually edited during
given session.

Now let me get back to pros and cons of the two approaches to rebasing
merges we have. Below I still advocate my approach by further discussing
the differences, but simultaneously I'd like to emphasize that whatever
particular way of rebasing merges will finally be used, it will be a
huge step forward and I'm glad I've raised the issue in the first place.

First, please consider the fact that my "rebase sides" method has yet
another nice property: it reduces back to original "rebase the commit"
operation when you apply it to a non-merge commit. In other words, it's
true generalization on top of rebasing of simple commit.

OTOH, Phillip's approach, when reduced to non-merge commit, still does a
version of rebase, but very specific one, and in inverse manner. I.e.,
rather than merging changes of the commit to be rebased into the new
base, it merges changes introduced by the new base into the commit being
rebased.

One consequence is that when conflict occurs, Phillip's approach will
give surprising order of ours vs theirs changes, inverted with respect
to those of the usual rebase of non-merge commit, while my approach will
give exact non-merge commit semantics. It could likely be fixed by
slightly modifying Phillip's approach, but it will make its
implementation more complex.

Another consequence is that, provided my version is used, all options
that tune "simple commit rebase" behavior will automagically work for
rebasing merge commits, in exactly the same manner. OTOH, Phillip's
approach, without special attention in implementation, will do the same
thing no matter what -m -s, or -X options say.

Yet another consequence is that my approach will likely result in better
code reuse. Even though mine seems to be harder to implement stand-alone
than Phillip's one, it should be actually easier to implement inside the
"git rebase", as it will use exactly the same machinery that "git
rebase" already uses to rebase simple commits, adding only final "git
merge-recursive" (or "git merge-resolve", or "git merge-octopus", -- any
of them  will do the job), which current implementation already performs
as well, for re-creating merges from scratch.

Second thought, unrelated to the above. To me it seems that my "rebasing
sides" approach, being entirely symmetric, is cleaner than incremental
merging suggested by Phillip, as with my approach one will still deal
with branches independently, in the same way as for simple commits,
until the single final merge operation. This comes with drawback of 1
additional step in my approach when compared to the Phillip's one
though, but then mine has definitely no issues with the exact order of
merges.

Overall, to me it seems that unmodified Phillip's approach will bring
unnecessarily wide set of new user experiences, and fixing it will
require some tricks in implementation, for no apparent reason.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08  6:45                                   ` Johannes Schindelin
@ 2018-03-12 12:31                                     ` Sergey Organov
  2018-03-26 11:37                                       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 12:31 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Sergey,
>
> On Wed, 7 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > 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?
>
> Imagine a todo list that contains this line
>
> 	merge -C abcdef 123456
>
> and now the user edits it (this is an interactive rebase, after all),
> adding another merge head:
>
> 	merge -C abcdef 987654 123456
>
> Now your strategy would have a serious problem: to find the original
> version of 987654. If there was one.

We are talking about different checks then. My method has a built-in
check that Pillip's one doesn't. All the external checks, if any, will
have to be the same.

>
>> > 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.
>
> But I already pointed out how that would undo a commit having been
> dropped.

No. Not for this version. You did it for originally flawed version of
the method, that has been already fixed by addition of "using original
merge commit as merge base" in the above sentence, and that was the
exact reason for [RFC v2], that in turn is explicitly stated at the
beginning of [RFC v2].

>
>> > 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.
>
> That is assuming a lot. That is exactly what this consistency check is
> for, that I mentioned earlier, and which you listed as a downside of
> Phillip's strategy (forgetting that your strategy has the same downside,
> so...).

Again, we are talking about different checks. My method has a built-in
check that Pillip's doesn't. All the external checks, if any, will have
to be the same.

> But I guess that you are still talking about the non-interactive version
> of the rebase, and missed that our conversation proceeded to the point
> where we want that same strategy to work *also* in the interactive version
> (and not have a completely different functionality depending whether you
> use --interactive or not)?

For me, non-interactive is an interactive one with unmodified todo list.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-08  7:01           ` Johannes Schindelin
@ 2018-03-12 12:42             ` Sergey Organov
  2018-03-26 11:50               ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 12:42 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Sergey,

[...]

> That is misrepresenting what happened.

No, it's you who are spreading misinformation, probably unintentional,
but still.

> First, you came up with a strategy. I pointed out shortcomings that
> implied that we cannot use it unchanged. Then, Buga fixed your strategy by
> using additional steps (making the process more complicated than before,
> still without a simple-enough explanation for my liking, fixing the
> shortcomings). Then, Phillip presented a super-simple strategy and Buga
> confirmed that it also fixes the shortcomings I pointed out.

Except that you've missed very essential thing: even before Phillip
presented his method, the original has been fixed and simultaneously
became even simpler. It's now entirely described in [RFC v2] that you
apparently still refuse to read.

> I am very excited that we finally found something that works *and* is easy
> to reason about.

You have chances to be even more exited as we in fact have 2 of them
that both work and are both easy to reason about.

> Let's focus on that strategy rather than going back to the strategy which
> has known flaws and only an unsatisfyingly complex explanation.

Not that fast, as it now has no known flaws and still has surprisingly
simple explanation. It also has its own niceties that are currently
being discussed elsewhere in the thread.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-12 10:37                                                 ` Johannes Schindelin
@ 2018-03-12 12:56                                                   ` Sergey Organov
  2018-03-13  0:01                                                     ` Igor Djordjevic
  2018-03-12 23:54                                                   ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 12:56 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Buga,
>
> On Sun, 11 Mar 2018, Igor Djordjevic wrote:
>
>> I agree with both of you that `pick <merge-commit>` is inflexible 
>> (not to say just plain wrong), but I never thought about it like that.
>> 
>> If we are to extract further mentioned explicit old:new merge 
>> parameter mapping to a separate discussion point, what we`re 
>> eventually left with is just replacing this:
>> 
>> 	merge -R -C <original--merge-commit> <merge-head>
>> 
>> ... with this:
>> 
>> 	pick <original--merge-commit> <merge-head>
>
> I see where you are coming from.
>
> I also see where users will be coming from. Reading a todo list in the
> editor is as much documentation as it is a "program to execute". And I am
> afraid that reading a command without even mentioning the term "merge"
> once is pretty misleading in this setting.
>
> And even from the theoretical point of view: cherry-picking non-merge
> commits is *so much different* from "rebasing merge commits" as discussed
> here, so much so that using the same command would be even more
> misleading.

This last statement is plain wrong when applied to the method in the
[RFC] you are replying to. Using the method in [RFC], "cherry-pick
non-merge" is nothing more or less than reduced version of generic
"cherry-pick merge", exactly as it should be.

Or, in other words, "cherry-pick merge" is generalization of
"cherry-pick non-merge" to multiple parents.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 20:53                   ` Igor Djordjevic
  2018-03-12 10:20                     ` Johannes Schindelin
@ 2018-03-12 13:07                     ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 13:07 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, git, Johannes Sixt, Junio C Hamano,
	Jacob Keller, Phillip Wood

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Dscho,

[...]

> I think the root of misunderstanding might be coming from the fact
> that Sergey was mainly describing a general concept (without a
> strictly defined implementation strategy, not being restricted to a
> specific one), where Phillip came up with a solution that eventually
> seems to use the same concept (as those transformations above should
> show), but simplifying it further inside a concrete implementation.

As a side-note, starting from sound general concept leaves a hope to
end-up with something like Git, while starting from an implementation,
however nice it is, gives a danger of ending-up with something like Bzr.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 10:20                     ` Johannes Schindelin
@ 2018-03-12 13:49                       ` Sergey Organov
  2018-03-26 12:44                         ` Johannes Schindelin
  2018-03-13  0:29                       ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-12 13:49 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

[...]

> The biggest difference is that it is easy for me to see the motivation
> behind Phillip's strategy, whereas I am still puzzled why one would come
> up with a complicated strategy that splits merge commits and re-merges
> them later, and why it should work in general (I still suspect that this
> is not the case).

Because I believe that rebasing simple commit (1 parent) should be
nothing else but reduced version of rebasing any commit (N parents) at
N=1. The [RFC v2] being discussed provides exactly such a method.

OTOH, check what Phillip's version does at N=1. Is it the same as
"rebase simple commit" strategy you already happily use? If not, please
explain why it must be different.

> Where "easy" meant that I had to spend 1h still to figure out why using
> the unrebased merge parents as merge bases.

That's because you try to figure out something that is not there in the
[RFC v2]. I suggest to forget everything you've already imagined and
just read the [RFC v2] proposal afresh. It should take about 10 minutes
or less to get it. Really.

> The same amount of time did not allow me to wrap my head around
> Sergey's verbose explanations.

Honestly, I don't believe it, sorry, but I'm willing to explain anything
you wish to be explained in _[RFC v2]_.

> But I'll take your word for it that the strategies are equivalent, and go
> with the one that has both a simpler explanation (in my mind, at least),
> and an more robust implementation.

It's up to you, and it'd still be much better than what we have now, but
you will need to face the (I think unfortunate) consequences I just
summarized elsewhere in the thread.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 22:04             ` Igor Djordjevic
  2018-03-12 12:05               ` Sergey Organov
@ 2018-03-12 22:41               ` Igor Djordjevic
  1 sibling, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-12 22:41 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Dscho,

On 11/03/2018 23:04, Igor Djordjevic wrote:
> 
> I`m yet to read (and reason about) your whole (very informative) 
> reply, but I just wanted to address this part first, as it might be a 
> clear end-game situation already, due to a mutual agreement, all the 
> rest being purely academic, interesting, but not any more (that) 
> important to discuss.

Ok, here`s the follow-up.

It`s "for discussion sake only", nothing really groundbreaking in 
here, I would think.

On 11/03/2018 16:40, Johannes Schindelin wrote:
> 
> > > > The main problem with this decision is that we still don't see how
> > > > and when to stop for user amendment using this method. OTOH, the
> > > > original has this issue carefully discussed.
> > >
> > > Why would we want to stop, unless there are merge conflicts?
> >
> > Because we can reliably know that something "unusual" happened - and by
> > that I don`t necessarily mean "wrong", but just might be worth user
> > inspection.
> 
> We have a similar conundrum in recursive merges. Remember how multiple
> merge bases are merged recursively? There can be merge conflicts, too, in
> *any* of the individual merges involved, and indeed, there are (under
> relatively rare circumstances).
> 
> Since we already faced that problem, and we already answered it by
> presenting possibly nested merge conflicts, I am in strong favor of
> keeping our new scenario consistent: present possibly-nested merge
> conflicts.

This is something I didn`t really know (possibly-nested merge 
conflicts already being a regular part of Git user experience), 
thanks for explaining it.

In the light of this, I can only agree, let`s keep it consistent.

If anyone ever decides / finds out there`s a better approach in 
regards to user experience, this might get revised, but it`s a 
different beast altogether, yes.

> As far as I understand, one of the arguments in favor of the current
> approach was: there is no good way to tell the user where they are, and
> how to continue from there. So better just to continue and present the
> user with the entire set of conflicts, and have an obvious way out.

Yes, I see this as the main concern, too. I would have expected that 
being in a kind of a "limbo" for a while shouldn`t be too bad, but I 
guess that`s too academic (and inexperienced) thought, and from a 
practical point of view one may not really know how to approach the 
(iterative) conflicts in the first place, not knowing his exact 
position (nor what`s to come)...?

Or, might be we _can_ provide enough clues on where we currently are 
(even if still inside some intermediate state)...? But, this still 
might be a topic for the future, indeed, and unrelated to rebasing 
merges alone (as you pointed out already).

> > For example, situation like this (M is made on A3 with `-s ours`, 
> > obsoleting Bx commits):
> >
> > (1) ---X8--X9 (master)
> >        |\
> >        | A1---A2---A3
> >        |             \
> >        |              M (topic)
> >        |             /
> >        \-B1---B2---B3
> >
> > ... where we want to rebase M onto X9 is what I would call "usual 
> > stuff", but this situation (M is still made on A3 with `-s ours`, 
> > obsoleting Bx commits, but note cherry-picked B2'):
> >
> > (2) ---X8--B2'--X9 (master)
> >        |\
> >        | A1---A2---A3
> >        |             \
> >        |              M (topic)
> >        |             /
> >        \-B1---B2---B3
> >
> > ... where we still want to rebase M onto X9 is what we might consider 
> > "unusual", because we noticed that something that shouldn`t be part 
> > of the rebased merge commit (due to previous `-s ours`) actually got 
> > in there (due to later cherry-pick), and just wanting the user to 
> > check and confirm.
> 
> We already have those scenarios when performing a regular interactive
> rebase, where a patch was already applied upstream. In the normal case,
> the user is not even shown B2, thanks to the --cherry-pick option used in
> generating the todo list.
> 
> Granted, in some cases --cherry-pick does not detect that, and then we
> generate a todo list including B2, and when that patch is applied, the
> interactive rebase stops, saying that there are no changes to be
> committed.
> 
> And this behavior is exactly the same with --recreate-merges!
> 
> So I do not think that it would make sense to bother the user *again* when
> rebasing the merge commit.

This seems fair enough. Phillip also pointed out it might be more 
annoyance then help, but as no one was really sure of the possibilities 
we`re discussing here, I thought being better to play it a bit on the 
safe side, for the first time, at least.

I would still like to see more examples of where this U1' == U2' 
check actually helps, and counter ones, where it only serves to annoy. 
Might be we only discover them in the future, though, once the new 
functionality is in use.

> If there are merge conflicts, yes, we will have to. If there are none
> (even if your U1' != U2'), it would be outright annoying to stop.

I hope you`re right :)

> > > > "rebase sides of the merge commit and then three-way merge them back
> > > > using original merge commit as base"
> > >
> > > And that is also wrong, as I had proved already! Only Buga's addition
> > > made it robust against dropping/modifying commits, and that addition
> > > also makes it more complicated.
> >
> > No, this is actually right, that sentence nicely describing _how_ it 
> > works.
> 
> Does it? Because that's exactly backwards from how Phillip's approach
> works: it certainly does not use the original merge commit as base. It
> uses the non-rebased merge parent as base, one at a time.

Hmm, might we are all misunderstanding each other here - what I meant 
is that what Sergey wrote about his approach is exactly how _his_ 
approach works (quoted above): "rebase sides of the merge commit and 
then three-way merge them back using original merge commit as base".

> So the closest I got was the description with the *original merge commit*
> as merge base, which I disagree with. It does not make sense to me.
> 
> Consecutive three-way merges between the original merge commit and the
> merge parents (with the *original merge parents* as merge base,
> respectively), still makes the most sense to me: it is a merge between the
> amendmends to the merge commit and the changes introduced by rebasing the
> merge parents.

This is what I hopefully managed to explain as essentially being the 
same concept, using those "merge-recursive transformations", where we 
can get from using *original merge parents* as a merge base, to using 
*original merge commit* as a merge base[1] (when using U1 and U2).

> I am now getting the sense as if Sergey's approach (with your, let's say,
> "fix") is trying to apply too much, and by using the original merge commit
> as merge base then tries to undo part of that.

This is an interesting way to look at it. If you observe rebased 
temporary commits U1' and U2' alone, then I guess the answer would be 
*yes*, even.

But I think they should only be observed as an intermediate step to 
reach a final (rebased) merge commit, and the missing (and inseparable) 
link is that original merge commit used as a base for merging them.

They _did_ become from that original merge commit (tree) in the first 
place, merge commit alone transformed to more separate (but dependent 
commits), thus they are still somewhat related/dependent to it after 
their rebasing and prior the final merge (more on it in a graph later).

> > > > I honestly don't see any advantages of Phillip's method over the
> > > > original, except personal preferences. At the same time, I have no
> > > > objection of using it either, provided consistency check problem is
> > > > solved there as well.
> > >
> > > Okay, let me reiterate then, because I do not want this point to be
> > > missed:
> > >
> > > Phillip's method is essentially merging the new tips into the original
> > > merge, pretending that the new tips were not rebased but merged into
> > > upstream.
> > >
> > > So it exploits the duality of the rebase and merge operation, which both
> > > result in identical trees (potentially after resolving merge conflicts).
> > >
> > > I cannot think of any such interpretation for your proposal augmented by
> > > Buga's fix-ups. And I haven't heard any such interpretation from your
> > > side, either.
> >
> > Ok here it goes (I might be still wrong, but please bare with me).
> >
> > What Sergey originally described also uses the "duality of the rebase 
> > and merge operation", too ;) Just in a bit different way (and might 
> > be a more straightforward one?).
> 
> I will be the judge whether it looks more straight-forward to me. :-)

Hehe, fair enough ;) I was speaking mainly for myself (at the time) :)

> > Here`s a starting point, two commits A and B, merged into M:
> >
> > (3) ---A
> >         \
> >          M
> >         /
> >     ---B
> >
> >
> > According the "patch theory"[1] (which might not be too popular 
> > around here, but should serve the purpose for what I`m trying to 
> > explain),
> 
> I think that the patch theory is not usually quoted here because
> 
> 1) originally, Darcs had this snake oil way of (over-)selling its theory
>    as having "roots in quantum mechanics" [*1*] at the time Git took off,
>    and
> 
> 2) it does not really have a good concept of rebasing, even if it should
>    be theoretically possible to integrate that into the "theory of
>    patches".

Yeah, I actually remembered some Linus` comment about "theory of 
patches" being nice... well, in theory (like for scientist), but not 
that much useful in practice - I`m paraphrasing now, here`s a proper 
reference[2].

Basically, it did provide me with a hint to take "patch theory" with 
a grain of salt, just that I did seem to find a logical explanation 
in there for what Sergey was originally describing, and what otherwise 
did "feel" as a good thing, but I wanted some more support on _why_ 
it feels so (on why it works, as you asked yourself, while it did seem 
to work, nonetheless).

> > each merge commit can be "transformed" to two non-merge commits, one on
> > top of each of the merge parents, where new commit brings its original
> > merge parent commit tree to the state of the merge commit tree:
> >
> > (4) ---A---U1
> >
> >
> >
> >     ---B---U2
> 
> You get the same result if you stick to regular graph theory, of course.
> All you need to do is to interpret the directed vertices between nodes as
> a patch & parent relationship. Simple.

Not knowing much about graph theory myself (other than what I grasped 
through Git so far), I`ll take your word here - and might be educate 
myself more about it, too :)

> > Now, we have two new commits, U1 and U2, each having the same tree as 
> > previous merge commit M, but representing changes in regards to 
> > specific parents - and this is essentially what Sergey`s original 
> > approach was using (whether he knew it, or not).
> >
> > When it comes to rebasing, it`s pretty simple, too. As this:
> >
> > (5) ---X1---o---o---o---o---o---X2 (master)
> >        |\
> >        | A1---A2---A3
> >        |             \
> >        |              M
> >        |             /
> >        \-B1---B2---B3
> >
> > ... actually equals this:
> >
> > (6) ---X1---o---o---o---o---o---X2 (master)
> >        |\
> >        | A1---A2---A3---U1
> >        |
> >        |
> >        |
> >        \-B1---B2---B3---U2
> >
> > ... where trees of M, U1 and U2 are same,
> 
> Okay, so you basically duplicated the merge commit and dropped its
> semantics as a merge commit. That is a very big difference to Phillip's
> approach already.

Yes, the purpose here was to explain why Sergey`s approach (still) 
works, not to compare it with Phillip`s (which also works).

From here on, we can use simple (and existing) rebase functionality / 
semantics to really rebase that original merge. There are no new 
concepts needed, it just works (TM) :)

> It opens the door to ambiguities, as we will see later.
> 
> > and we can use the regular rebase semantics and rebase it to this:
> >
> > (7) ---X1---o---o---o---o---o---X2 (master)
> >                                 |\
> >                                 | A1'--A2'--A3'--U1'
> >                                 |
> >                                 |
> >                                 |
> >                                 \-B1'--B2'--B3'--U2'
> >
> > ... which is essentially this again:
> >
> > (8) ---X1---o---o---o---o---o---X2 (master)
> >                                 |\
> >                                 | A1'--A2'--A3'
> >                                 |            \
> >                                 |             M'
> >                                 |            /
> >                                 \-B1'--B2'--B3'
> >
> > ... where it is still true that trees of U1', U2' and M' are still 
> > the same. So we managed to rebase a merge commit without ever doing a 
> > merge :) (note that, practically, we _can_ finally even merge U1' and 
> > U2' to produce M', it shouldn`t really matter as all the trees are 
> > the same, so merge will be a no-op)
> 
> U1' and U2' do not have the same tree, though *especially* when the user
> did not edit the todo list to insert/modify/drop/reorder commits.

I lost you here - how come they don`t? The only difference between U1 
and U1' will be what`s between X1 and X2, and the same is true for U2 
and U2'. And as we know U1 = U2, it means U1' = U2' still holds.

I`m following further...

> This violation of expectations is of course caused by "duplicating the
> merge commit" into U1 and U2.

It`s just a change of semantics, a natural transformation (I wouldn`t 
know a more "proper, scientific" term, sorry), all still being 
correct. But I guess we can call it "duplicating" as well.

> But let's continue.
> 
> > But, as we saw (what you`ve explained), this doesn`t really work in 
> > case one of the sides of the merge gets "disbalanced" (so to say), 
> > like dropping a commit (which could also happen non-interactively, 
> > where a commit has been cherry-picked to a different branch, but
> > previously obsoleted by `-s ours` merge).
> 
> Precisely. A *very* important counter-argument to this approach so far.
> 
> > As observed, dropped commit could still wrongly get into final merge 
> > commit tree (or cherry-picked commit wrongly not get there), due to 
> > the nature of those rebased U1 and U2 temporary commits to hold all 
> > the differences in regards to their specific merge commit parent.
> 
> The reason for this, of course, is that either U1's or U2's diff will show
> those differences, *and we still try to rebase them even if the user
> already dropped them*.
> 
> But let's continue.
> 
> > A natural improvement to original idea which Sergey eventually came 
> > up with after my findings (which you now ended up calling a hack, even, 
> > but I would respectfully disagree), is to use original merge commit M 
> > as a merge base for final merge of U1' and U2' - and here is why it 
> > works, too, and why I wouldn`t consider it a hack, but a proper (and 
> > a solid) solution.
> >
> > Merge commit M is what we are rebasing, so we should have a way to 
> > somehow learn from it, alongside U1 and U2 temporary commits - and 
> > that is what using original merge commit M as a base does for us. It 
> > helps us spot any "disbalances" that happened in comparison to original 
> > merge parent trees, which is exactly what we`re interested in.
> 
> ... except that it gets the direction wrong. Rather than trying to *avoid*
> rebasing possibly dropped changes, it tries to kind of "undo" them by
> using a merge base that does not really make sense (unless you think of it
> as a "revert").

I disagree, but I`ll explain more later. The point is that we are 
really rebasing original merge commit, where rebased merge commit 
might have missed some stuff (due to possibly dropped changes) - so 
once we successfully rebased the original merge commit, we are to 
acknowledge additional changes we didn`t catch so far (as we would 
catch additions), and act accordingly.

Using original merge commit for the purpose feels natural to me - 
it`s where we are starting from, using both parent branches evolution 
to get where we`re heading to, being a rebased merge commit.

> It would make sense if M could interpreted as a branch point. But it
> cannot, as we specifically did *not* continue to develop the merge parents
> from that merge commit.

But we did (for the sake of rebasing), transforming the very merge 
commit into those commits developed on top of its merge parents :)

> Instead, what we did was to branch off of the original branch point X1.
> 
> Reframing the rebase of a sub-branch (X1..A3) as merge with upstream,
> however, we can interpret M and A3' as revisions we want to merge, with A3
> as the merge base.
> 
> (You can think of it in terms of the "theory of patches" thusly: if X1..A3
> is represented by patch K, X1..X2 by patch L, and X2..A3' by K', then what
> we want to merge into M is K^(-1).L.K', which is precisely what A3..A3'
> translates to.)
> 
> You can also think of it as diverging changes going from A3: one direction
> was to merge (resulting in the commit M), the other direction was to
> rebase onto X2 (resulting in A3'), and a 3-way merge M <- A3 -> A3' will
> reconcile those changes (and you will want to repeat with B3/B3', too).
> 
> Let's put that into the context of your example: instead of introducing U1
> and U2, we introduce V1 and V2 right away, as temporary *merge* commits,
> where the tree of V1 is identical to the one of A3', and the tree of V2
> to that of B3'.
> 
>  ---X1---o---o---o---o---o---X2 (master)
>     |                        |\
>     |                        | A1'--A2'--A3'--V1
>     |\                       |               /
>     | -A1---A2---A3----------+---------------
>     |              \         |
>     |               M        \-B1'--B2'--B3'--V2
>     |              /                         /
>      \-B1---B2---B3--------------------------
> 
> Note: the *really* important difference is that these temporary commits
> are based on the *rebased* history rather than the *unrebased* history.

This might be an important advantage of Phillip`s "simplification", 
indeed.

> Third note: my favorite mental model is still the duality of rebasing and
> merging, in which case V1 and V2 would not have A3' and B3' as first
> parents, but X2.
> 
> Phillip's strategy is to merge M with V1 and V2.
> 
> This translates to "merge the amendments of the merge commit M with the
> changes introduced by rebasing its merge parents".

All this I agree with. And all this can be used to support what 
Sergey described, too (being a bit different, though) :) Let`s see...

 ---X1---o---o---o---o---o---X2 (master)
    |                        |\
    |                        | A1'--A2'--A3'--U1'
    |\                       |               /
    | -A1---A2---A3---U1-----+---------------
    |              \ /       |
    |               M        \-B1'--B2'--B3'--U2'
    |              / \                       /
     \-B1---B2---B3---U2---------------------


To get the original merge commit M, you can just merge U1 and U2 
again back to single commit - original commit M is a natural merge-base. 
It`s the same with rebased commits U1' and U2' - to get rebased merge 
commit, you merge them. Again, original commit M is a natural 
merge-base.

This is why I disagreed above with statement that merging U1' and U2' 
with base M doesn`t make sense. I think this shows it does.

> The really cute part about this is that (in contrast to using U1' and
> U2'), we do not merge the amendments of the merge commit multiple times,
> but exactly once. And therefore, we do not need to "undo" them by using
> the original merge commit as merge base, either (which would introduce
> many more opportunities for merge conflicts to creep in, oftentimes
> unnecessary conflicts to begin with).

This might be true, which is why I might find Phillip`s approach to 
be a further optimization of what Sergey came up with. But I would 
still like to do some tests concerning manually amended conflicts, 
will report once I do.

Though, we do not merge the amendments of the merge commit multiple 
times here either, but we do rebase them multiple times, true (being 
held in each rebased Ux' commit again).

> > In ideal case (as per "patch theory"[1]), final merge of rebased U1' 
> > and U2' with original merge commit M as a base would be rather simple, 
> > too (not to say pointless again), as both U1' and U2' would have same 
> > changes in comparison to M, namely all the changes from commit we are 
> > rebasing from (X1 above) to commit we are rebasing to (X2 above).
> >
> > But in a more complex case like this:
> >
> > (9) ---X1---B2'---o---o---o---o---X2 (master)
> >                                   |\
> >                                   | A12--A2'---B3'
> >                                   |             \
> >                                   |              M'
> >                                   |             /
> >                                   \-B1'--B3'---B4
> >
> > ..., being what we are ultimately interested in, it helps us notice 
> > all kind of changes on our rebased merge parent branches, and act 
> > accordingly (as shown by my previously posted test script).
> 
> To use the "theory of patches" to explain why Phillip's approach is so
> much more appealing: in Sergey's approach, we will rebase U1 (which is
> "sort of" B1.B2.B3 in the "theory of patches"). If the cherry-pick of B2'
> caused merge conflicts that had to be resolved, then these merge conflicts
> will have to be resolved *again* when rebasing U1 (because B2 is *part of*
> U1). And of course they will have to be resolved *again* when merging U1'
> with U2'.

I think I disagree here - U1 doesn`t purely hold B1.B2.B3, but their 
_application onto A3 tree_ - with all conflicts resolutions that might 
have happened. Thus rebasing U1 on top of A3' should be as safe as it 
gets.

Am I missing something?

> In short: Phillip's approach is a short-cut that avoids unnecessary merge
> conflicts because it avoids rebasing changes that would need to be undone
> right away anyway.

I don`t agree it avoids conflicts (as resolutions of those are already 
part of U1, I`ll need to test), but I do agree on Phillip`s approach 
seeming to be a shortcut, though.

> > All this said (and hoping anyone is still reading this), to shed some 
> > light on what I meant by "favoring Sergey`s approach in general, but 
> > _implemented_ through what Phillip described".
> >
> > I think we should use temporary commits U1 and U2, being a sound 
> > approach backed up by the "patch theory"[1], as it helps us notice 
> > any "disbalances" of the rebased merge commit parents trees (so we 
> > can stop for user inspection), finally merging them with original 
> > merge commit as a base, in order to spot additional changes on trees 
> > of the merge commit parents we are rebasing.
> 
> In Phillip's approach, we do not need to rebase the amendments of the
> merge commit M twice (or even more times, for octopus merges). Therefore,
> there is no opportunity for these imbalances.

First sentence is true, conclusion not (necessarily) - it`s not 
rebasing amendments that introduces "disbalances", but changes in 
rebased merge parents` trees other than what found inside X1.X2.

> > But, the merging steps themselves, backed up by a need to stop for 
> > conflict resolution as soon as one happens, should be performed 
> > _iteratively_, like Phillip showed us... I would think :)
> 
> From a user interface perspective, this is a bad idea: you would now have
> to communicate to the user *where* in the process they are.
> 
> And for that you would have to explain that process to them. Including
> "theory of patches" and all.
> 
> Taking me as a prime example, you now know how tedious and impractical
> that would be.

I don`t (or didn`t, at least) find this as terrible as you`re 
describing it - yes, we`d need a way to explain people that they`re 
in an intermediate state, on the way to rebase a merge commit, but 
that`s all they would really need to know.

Might be it would be enough to say "currently rebasing merge parent 
X" (or "commit X"), then later "currently rebasing merge parent Y", 
or something along the lines of what rebase already does, just to 
provide a hint so one can act accordingly (resolve conflicts), and 
--continue.

But, as you previously mentioned this already being a known issue 
(possibly nested merge conflicts), which I wasn`t really aware of, I 
would refrain from further discussing it now and just agree to do 
what we do in the other case, too, as you suggested.

> > And, for example, that would do wonders when we introduce completely 
> > new branch during an interactive rebase, too, for example:
> >
> > (10) ---X1---B2'---o---o---o---o---X2 (master)
> >         |\                        /|\
> >         | A1---A2---A3---U1       || A12--A2'---B3'---U1'
> >         |             \           ||             \
> >         |              M          ||              M'
> >         |             /           ||             /|
> >         \-B1---B2---B3---U2       |\---B3'---B4-/-|---U2'
> >                                   |               |
> >                                   \-----B1'-------/
> >
> >
> > In this situation, we would merge all temporary Ux commits using 
> > original merge M as a merge base, and then merge _that_ resulting 
> > tree with B1' (being a newly introduced merge commit parent), using 
> > whatever merge base is most appropriate between the two (in this 
> > case, X2).
> 
> The semantics of octopus merges are very different from regular recursive
> merges. I am not sure that we want to go there in this discussion...
> 
> If, on the other hand, you do not try to turn M from a regular 2-parent
> merge commit into an octopus merge during an interactive rebase, but
> instead split the B branch into two branches *before* merging the result
> into the rebased merge commit, we are in the same square as
> reordering/dropping/inserting/modifying patches: to neither of the
> presented strategies would it matter what kind of branch topology the
> merge parents have.
> 
> For the record: I am still not convinced that Phillip's and Sergey's
> approach are equivalent, even in terms of the "theory of patches". [...]

And you shouldn`t be, nor it was my wish to conclude so, but only to 
(try to) explain _why_ Sergey`s approach works _as well_, without anything 
magical about it (nor wrong), thus no need to be afraid of it, either.

> [...] But if
> they are, then Phillip's version is a shorter version that avoids applying
> changes just to revert them right away. And in a setting where each patch
> can cause merge conflicts (as the interactive rebase is), the less changes
> you have to apply, the better.

I would still like to see some comparisons (for myself), but this is 
fair enough for now, and understandable.

Thank you for a very interesting (and educative!) read :)

Regards, Buga

[1] https://public-inbox.org/git/6362804d-e204-a9e0-9ff0-51d8497ce921@gmail.com/
[2] https://public-inbox.org/git/Pine.LNX.4.64.0604030727250.3781@g5.osdl.org/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-12 10:37                                                 ` Johannes Schindelin
  2018-03-12 12:56                                                   ` Sergey Organov
@ 2018-03-12 23:54                                                   ` Igor Djordjevic
  2018-03-13  6:25                                                     ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-12 23:54 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Dscho,

On 12/03/2018 11:37, Johannes Schindelin wrote:
> 
> > If we are to extract further mentioned explicit old:new merge 
> > parameter mapping to a separate discussion point, what we`re 
> > eventually left with is just replacing this:
> >
> > 	merge -R -C <original--merge-commit> <merge-head>
> >
> > ... with this:
> >
> > 	pick <original--merge-commit> <merge-head>
> 
> I see where you are coming from.
> 
> I also see where users will be coming from. Reading a todo list in the
> editor is as much documentation as it is a "program to execute". And I am
> afraid that reading a command without even mentioning the term "merge"
> once is pretty misleading in this setting.
> 
> And even from the theoretical point of view: cherry-picking non-merge
> commits is *so much different* from "rebasing merge commits" as discussed
> here, so much so that using the same command would be even more
> misleading.

I would disagree here, as it seems you`re going too much into 
implementation and theory here, where it shouldn`t really matter from 
the user`s point of view - the point is to rebase a commit, `pick` it 
from one place and plant it elsewhere.

Yes, some commits might have a bit different semantics then others 
(merge vs non-merge), but it should just be an implementation detail, 
in my opinion, no need to leak it in user`s face (more than necessary).

I feel that "merge" is a command that works really well in the 
mindset of (re)creating merges. But if we are "only" rebasing an 
existing merge, `pick` seems much more appropriate (to me, at least), 
and it aligns with what I`m already expecting `pick` to be doing.

Down below, if we are (re)creating the merge, or doing magic to 
somehow just port it over, should be irrelevant. So "rebase" equals 
"pick and plant" (port), not "merge".

> > That is what I had in mind, seeming possibly more straightforward and 
> > beautifully aligned with previously existing (and well known) 
> > `rebase` terminology.
> >
> > Not to say this would make it possible to use other `rebase -i` todo 
> > list commands, too, like if you want to amend/edit merge commit after 
> > it was rebased, you would write:
> >
> > 	edit <original--merge-commit> <merge-head>
> >
> > ..., where in case you would simply like to reword its commit 
> > message, it would be just:
> >
> > 	reword <original--merge-commit> <merge-head>
> >
> >
> > Even `squash` and `fixup` could have their place in combination with 
> > a (to be rebased) merge commit, albeit in a pretty exotic rebases, 
> > thus these could probably be just disallowed - for the time being, at 
> > least.
> 
> Sure, for someone who read the manual, that would be easy to use. Of
> course, that's the minority.

I`m not following you here - the point is these are already existing 
commands, which would still fit in just nicely, so nothing new to 
learn nor read.

Now, if we are to discuss use cases where people don`t even know what 
they`re doing, I would think that misses the point. Besides, it`s 
always easier to make more mistakes when you introduce yet more 
commands/semantics to think about/learn, and I think it can be avoided 
here, for the better.

> Also: the `edit` command is poorly named to begin with. A much cleaner
> design would be to introduce the `break` command as suggested by Stephan.

This is orthogonal to what we`re discussing. Existing commands might 
not be perfect, but that`s what we have now, so let`s be consistent, 
not putting additional burden on the user there, at least.

But for the record - I tend to agree, I often find myself wondering 
if `edit`-ed commit means `rebase` stops after applying the changes 
and _before_ making the commit itself (so we just edit and 
--continue), or _after_ it (so we edit, `commit --amend` and 
--continue).

> > The real power would be buried in implementation, learning to rebase 
> > merge commits, so user is left with a very familiar interface, slightly 
> > adapted do accommodate a bit different nature of merge commit in 
> > comparison to an ordinary one, also to allow a bit more of interactive 
> > rebase functionality, but it would pretty much stay the same, without 
> > even a need to learn about new `merge`, `-R`, `-C`, and so on.
> >
> > Yes, those would have its purpose, but for real merging then 
> > (creating new merges, or recreating old ones), not necessarily for 
> > merge rebasing.
> >
> > With state of `merge -R -C ...` (that `-R` being the culprit), it 
> > kind of feels like we`re now trying to bolt "rebase merges" 
> > functionality onto a totally different one (recreate merges, serving 
> > a different purpose), making them both needlessly dependent on each 
> > other, further complicating user interface, making it more confusing 
> > and less tunable as per each separate functionality needs (rebase vs. 
> > recreate).
> >
> > I guess I`m the one to pretty much blame here, too, as I really 
> > wanted `--recreate-merges` to handle "rebase merges" better, only to 
> > later realize it might not be the best tool for the job, and that a 
> > more separate approach would be better (at least not through the same 
> > `merge` todo list command)...
> >
> > [1] https://public-inbox.org/git/f3872fb9-01bc-b2f1-aee9-cfc0e4db77d6@gmail.com/
> 
> Well, the `-R` option is no worse than `git merge`'s `-s <strategy>`
> option (which *also* changes the strategies rather drastically).

What I wrote above, -R seems unfitting for `merge`, as we are not 
really merging, but rebasing (conceptually, not implementation wise).

And it makes even less sense in the light of that previous comment 
where "rebasing a merge" shouldn`t even allow selecting a specific 
strategy, as that would imply (re)creating the merge instead.

So using `merge` for rebasing existing (merge) commit seems as rather 
misusing the concept of `merging`.

I don`t know, I`m thinking if we are looking at todo list from 
different perspectives - to you, it seems to be a sequence of 
commands to create something new (yes, from something that already 
exists, but that`s implementation detail). In that context, using 
`merge` might have sense (even if still being a very special merge).

But to me, as we already have `pick` and not `commit` to rebase 
commits, it means we are not creating but rather reusing what we have 
(being an important concept to reason about), thus `pick` would still 
fit in for picking a merge commit just nicely, and more naturally, too.

*Maybe if* `-R` would be turned into a full fledged "rebase" (or 
"rebasing") merge strategy, written as:

	merge -s rebase -C <original--merge-commit> <merge-head>

..., might be it would make more sense to me (aligned with what 
`merge` is expected to do). But even then, I might prefer having 
`pick` as a syntactic sugar over `merge -s rebase`, at least.

In the light of this, I _could_ even see `-R` as short for `-s 
rebase`, but it still seems a bit awkward and forced.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-12 12:56                                                   ` Sergey Organov
@ 2018-03-13  0:01                                                     ` Igor Djordjevic
  2018-03-26 12:03                                                       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-13  0:01 UTC (permalink / raw)
  To: Sergey Organov, Johannes Schindelin
  Cc: phillip.wood, Jacob Keller, Git Mailing List, Johannes Sixt,
	Junio C Hamano

On 12/03/2018 13:56, Sergey Organov wrote:
> 
> > > I agree with both of you that `pick <merge-commit>` is inflexible 
> > > (not to say just plain wrong), but I never thought about it like that.
> > >
> > > If we are to extract further mentioned explicit old:new merge 
> > > parameter mapping to a separate discussion point, what we`re 
> > > eventually left with is just replacing this:
> > >
> > > 	merge -R -C <original--merge-commit> <merge-head>
> > >
> > > ... with this:
> > >
> > > 	pick <original--merge-commit> <merge-head>
> >
> > I see where you are coming from.
> >
> > I also see where users will be coming from. Reading a todo list in the
> > editor is as much documentation as it is a "program to execute". And I am
> > afraid that reading a command without even mentioning the term "merge"
> > once is pretty misleading in this setting.
> >
> > And even from the theoretical point of view: cherry-picking non-merge
> > commits is *so much different* from "rebasing merge commits" as discussed
> > here, so much so that using the same command would be even more
> > misleading.
> 
> This last statement is plain wrong when applied to the method in the
> [RFC] you are replying to. Using the method in [RFC], "cherry-pick
> non-merge" is nothing more or less than reduced version of generic
> "cherry-pick merge", exactly as it should be.
> 
> Or, in other words, "cherry-pick merge" is generalization of
> "cherry-pick non-merge" to multiple parents.

I think Sergey does have a point here, his approach showing it.

Phillip`s simplification might be further from it, though, but we`re 
talking implementation again - important mental model should just be 
"rebasing a commit" (merge or non-merge), how we`re doing it is 
irrelevant for the user, the point (goal) is the same.

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-12 10:46                                               ` Johannes Schindelin
@ 2018-03-13  0:16                                                 ` Igor Djordjevic
  2018-03-26 13:07                                                   ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-13  0:16 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Dscho,

On 12/03/2018 11:46, Johannes Schindelin wrote:
> 
> > Sometimes one just needs to read the manual, and I don`t really think
> > this is a ton complicated, but just something we didn`t really have
> > before (real merge rebasing), so it requires a moment to grasp the
> > concept.
> 
> If that were the case, we would not keep getting bug reports about
> --preserve-merges failing to reorder patches.

Not sure where that is heading to, but what I`m arguing about is that 
introducing new commands and concepts (`merge`, and with `-R`) just 
makes the situation even worse (more stuff to grasp).

Reusing existing concepts where possible doesn`t have this problem.

> > Saying in favor of `--rebase-merges`, you mean as a separate option,
> > alongside `--recreate-merges` (once that series lands)?
> 
> No. I am against yet another option. The only reason I pollute the option
> name space further with --recreate-merges is that it would be confusing to
> users if the new mode was called --preserve-merges=v2 (but work *totally
> differently*).

I see. So I take you`re thinking about renaming `--recreate-merges` 
to `--rebase-merges` instead?

That would seem sensible, too, I think, being the default usage mode 
in the first place. Being able to actually (re)create merges, too, 
once user goes interactive, would be "just" an additional (nice and 
powerful) feature on top of it.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 10:20                     ` Johannes Schindelin
  2018-03-12 13:49                       ` Sergey Organov
@ 2018-03-13  0:29                       ` Igor Djordjevic
  2018-03-26 13:58                         ` Johannes Schindelin
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-13  0:29 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Dscho,

On 12/03/2018 11:20, Johannes Schindelin wrote:
> 
> > > [...] and cannot introduce ambiguities when rebasing the
> > > changes introduced by M (i.e. the "amendmendts" we talked about).
> >
> > Hmm, not following here, which ambiguities are we talking about?
> 
> U1' vs U2' of course. Those are two things that can be different, even if
> they ideally would have identical trees.
> 
> Phillip's strategy does not leave that room for ambiguity.

Ehm, in Sergey`s approach, this is not an issue, but a feature :)

If U1' != U2', it just means a more complex rebase happened, but it 
doesn`t compromise the result (rebased merge) in any way.

On the other hand, if U1' == U2', we can be pretty sure that merge 
rebasing went as clean as possible.

That`s the idea, at least.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-12 23:54                                                   ` Igor Djordjevic
@ 2018-03-13  6:25                                                     ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-13  6:25 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

[...]

> I don`t know, I`m thinking if we are looking at todo list from
> different perspectives - to you, it seems to be a sequence of
> commands to create something new (yes, from something that already
> exists, but that`s implementation detail). In that context, using
> `merge` might have sense (even if still being a very special merge).
>
> But to me, as we already have `pick` and not `commit` to rebase
> commits, it means we are not creating but rather reusing what we have
> (being an important concept to reason about), thus `pick` would still
> fit in for picking a merge commit just nicely, and more naturally,
> too.

Exactly.

Fundamentally, a good editing tool should first be able to just safely
reproduce back what it got, only then everything else goes.

From this follows that git history-editing tool should start from sound
history representation, i.e., some text representation of the DAG that
allows to re-create the DAG back.

The simplest way is to just list all the nodes in some topological
order, along with references to the parents. Then, to simplify the list,
first parent, unless explicitly specified, could be assumed to be the
preceding item in the list.

Next, we actually need _to do_ something with this, so we convert this
to a _todo_ list by prepending action to each element of the list (isn't
it Lisp once again?). Let the action be called 'pick'. Then, e.g., the
piece of history:

           B1-------B2
          /           \
 S--M0---M1---- M2 ----M3 -- M4

from M0 to M4 will be represented like this:

skip S          # Just to have a handy reference
pick M0         # Implicit first parent (S)
pick M1         # Implicit first parent (M0)
pick M2         # Implicit first parent (M1)
pick B1 M1      # Explicit first parent
pick B2         # Implicit first parent (B1)
pick M3 M2 B2   # Explicit first and second parents
pick M4

Which basically gets us back to what you are advocating.

Here is another variant, using command options to specify parents (I
also exchanged order of branch and mainline):

skip S          # To have the base reference handy
pick M0         # Implicit first parent (S)
pick B1         # Implicit first parent (M0)
pick B2         # Implicit first parent (B1)
pick M1 -1 M0   # Explicit first parent M0
pick M2         # Implicit first parent (M1)
pick M3 -2 B2   # Implicit first parent (M2) and
                # explicit second parent B2
pick M4         # Implicit first parent (M3)

I like this one even better.

IMHO, this is indeed a good starting point. No special treatment for
merges is needed so far.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
  2018-03-03  0:29                       ` Igor Djordjevic
  2018-03-05 17:52                       ` Johannes Schindelin
@ 2018-03-13 16:10                       ` Sergey Organov
  2018-03-14  1:12                         ` Igor Djordjevic
  2 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-13 16:10 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Igor Djordjevic, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Phillip,

Phillip Wood <phillip.wood@talktalk.net> writes:

[...]

> 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')

I wonder if it's OK to exchange the order of heads in the first merge
(also dropped C for brevity):

git checkout --detach A'
git merge-recursive A -- A' M
tree=$(git write-tree)
git merge-recursive B -- $tree B'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')

If so, don't the first 2 lines now read: "rebase (first parent of) M on
top of A'"?

If so, then it could be implemented so that it reduces back to regular
rebase of non-merges when applied to a single-parent commit, similar to
the method in the RFC, striking out one of advantages of the RFC.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-11 11:56                                   ` Johannes Schindelin
@ 2018-03-13 18:24                                     ` Junio C Hamano
  2018-03-26 13:17                                       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Junio C Hamano @ 2018-03-13 18:24 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Phillip Wood, Sergey Organov, Igor Djordjevic, phillip.wood,
	Git mailing list, Jacob Keller, Johannes Sixt

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> So essentially, what your cherry-pick'able commits are is a way to store
> what rerere would have stored (i.e. the set of merge conflicts together
> with their resolution)?

If rerere would have stored, I wouldn't have separate band-aid
system on top.  These fix-up commits are usually on parts that do
not get involved in textual conflicts; "rerere" which relies on
having textual conflicts (the "shape" of the text in the conflicted
region is what lets "rerere" index into its database to find the
recorded resolution) wouldn't have stored them and that is
fundamental.

> If so, what tooling do you have to identify quickly what to cherry-pick,
> given merge conflicts?

It exactly is the issue I've been trying to find ideal solution for
quite a while and not successfully.  Here is a sample thread

  https://public-inbox.org/git/xmqqeft3u0u5.fsf@gitster.mtv.corp.google.com/#t

and every message I mention "merge-fix" is relevant.

The current band-aid system punts and indexes the merge-fix changes
by merely a branch name.  When refs/merge-fix/X exists, what it
means is "When branch X is merged to an integration branch, it is
likely that the integration branch _already_ has merged an unnamed
topic that causes semantic conflicts and requires this fix-up".
This needs occasional manual adjustment---e.g. when the topic X
turns out to be a lot more stable than the other topic Y that was
causing us trouble with semantic conflicts, I may at some point
reorder the topics and have topic X advance to 'next' before topic Y
does.  And when that happens, when I merge X to 'next', because Y is
not yet in 'next', I shouldn't apply refs/merge-fix/X (often, an
attempt to cherry-pick it on top of a merge of X into 'next' would
fail, which would be a bit of safety, but not always).  What I
should do instead is to rename refs/merge-fix/X to refs/merge-fix/Y
immediately before merging X to 'next', so that the cherry-pick is
not applied.  When rebuilding 'master'->'jch'->'pu' chain, X (now in
'next') will be merged before Y (not in 'next') gets merged, and
when it is Y's turn to be merged, the merge-fix I used to apply when
merging topic X will be applied.

In the ideal world (I think I'm repeating the ideas raised in the
thread quoted), the merge-fix database should be indexed with a pair
of commit object names (e.g. a step in branch X that adds a new
callsite for function frotz() and a step in branch Y that changes
the function signature of frotz()), and teach the system to
cherry-pick refs/merge-fix/A-B to resolve semantic conflicts, when
both commits A and B appears in the integration branch for the first
time.  And make sure these are kept up-to-date across rebasing of
commits A and B.  After rebasing the topics X and Y that contained
the commits A and B, if they became C and D, the system somehow
needs to be able to locate the previous merge-fix that was valid for
A-B pair when C-D pair gets merged.


^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-13 16:10                       ` Sergey Organov
@ 2018-03-14  1:12                         ` Igor Djordjevic
  2018-03-14  7:21                           ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-14  1:12 UTC (permalink / raw)
  To: Sergey Organov, Phillip Wood
  Cc: phillip.wood, Git mailing list, Jacob Keller, Johannes Schindelin,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 13/03/2018 17:10, Sergey Organov wrote:
> 
> > 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')
> 
> I wonder if it's OK to exchange the order of heads in the first merge
> (also dropped C for brevity):

It should be, being "left" or "right" hand side ("theirs" or "ours") 
of the three-way merge shouldn`t matter, they`re still both equally 
compared to the merge-base.

> git checkout --detach A'
> git merge-recursive A -- A' M
> tree=$(git write-tree)
> git merge-recursive B -- $tree B'
> tree=$(git write-tree)
> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
> 
> If so, don't the first 2 lines now read: "rebase (first parent of) M on
> top of A'"?

Hmm, lol, yes...? :) So basically, this:

(1)	git checkout --detach M
	git merge-recursive A -- M A'
	tree=$(git write-tree)
	...

... is equivalent to this:

(2)	git checkout --detach A'
	git merge-recursive A -- A' M
	tree=$(git write-tree)
	...

..., being equivalent to this:

(3)	git checkout --detach A'
	git cherry-pick -m 1 M
	tree=$(git write-tree)
	...

..., where in all three cases that `$tree` is equivalent to U1' we 
discussed about so much already :)

I tested it like this as well, slightly modifying previously sent out 
script (like this one[1]), and it still seems to be working ;) Nice!

> If so, then it could be implemented so that it reduces back to regular
> rebase of non-merges when applied to a single-parent commit, similar to
> the method in the RFC, striking out one of advantages of the RFC.

I guess so, but I think it now boils down only to what one finds 
easier to reason about even more.

I`m just glad we got to U1' from this perspective as well, hopefully 
adding even more faith in the overall concept, being beaten from both 
ends and dropping out to be the same (minus minor implementation details).

Regards, Buga

[1] https://public-inbox.org/git/872944c4-ca97-9f55-a424-86d1e3299a22@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-14  1:12                         ` Igor Djordjevic
@ 2018-03-14  7:21                           ` Sergey Organov
  2018-03-15  0:09                             ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-14  7:21 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 13/03/2018 17:10, Sergey Organov wrote:
>> 
>> > 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')
>> 
>> I wonder if it's OK to exchange the order of heads in the first merge
>> (also dropped C for brevity):
>
> It should be, being "left" or "right" hand side ("theirs" or "ours") 
> of the three-way merge shouldn`t matter, they`re still both equally 
> compared to the merge-base.
>
>> git checkout --detach A'
>> git merge-recursive A -- A' M
>> tree=$(git write-tree)
>> git merge-recursive B -- $tree B'
>> tree=$(git write-tree)
>> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
>> 
>> If so, don't the first 2 lines now read: "rebase (first parent of) M on
>> top of A'"?
>
> Hmm, lol, yes...? :) So basically, this:
>
> (1)	git checkout --detach M
> 	git merge-recursive A -- M A'
> 	tree=$(git write-tree)
> 	...
>
> ... is equivalent to this:
>
> (2)	git checkout --detach A'
> 	git merge-recursive A -- A' M
> 	tree=$(git write-tree)
> 	...
>
> ..., being equivalent to this:
>
> (3)	git checkout --detach A'
> 	git cherry-pick -m 1 M
> 	tree=$(git write-tree)
> 	...
>
> ..., where in all three cases that `$tree` is equivalent to U1' we 
> discussed about so much already :)

Exactly, and thanks for noticing that it's actually U1', that happens to
soon become rather handy, see below.

> I tested it like this as well, slightly modifying previously sent out 
> script (like this one[1]), and it still seems to be working ;) Nice!

Very nice of you, thanks!

Yet another outcome of this transformation is that the fist step is now
free to (and probably should) utilize all the options (-s, -X, etc.)
that usual rebase has:

git-rebase-first-parent --onto A' M
tree=$(git write-tree)

where 'git-rebase-first-parent' is whatever machinery is currently being
used to rebase simple non-merge commit.

[

Moreover, Phillip's method could further be transformed to what is in
RFC, not that I think it should, see below. Just for the sake of
completeness though, here is the essential missing transformation that
makes Phillip's method symmetric, after which it becomes true special
case of the RFC with particular rebase-first-parent implementation:

git checkout --detach A'
git merge-recursive A -- A' M
tree_U1'=$(git write-tree)
git checkout --detach B'
git merge-recursive B -- B' M
tree_U2'=$(git write-tree)
git merge-recursive M -- $tree_U1' $tree_U2'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')

]

>
>> If so, then it could be implemented so that it reduces back to regular
>> rebase of non-merges when applied to a single-parent commit, similar to
>> the method in the RFC, striking out one of advantages of the RFC.
>
> I guess so, but I think it now boils down only to what one finds 
> easier to reason about even more.

I actually think there is more to it. It's incremental asymmetric nature
of the Phillip's approach that I now find rather appealing and worth to
be used in practice.

While the RFC approach, being entirely symmetric, is nice from the POV
of theory and reasoning, yet simple to implement, the actual user
interface of Git is inherently asymmetric with respect to merges (one
merges side-branch(es) to mainline), so asymmetric approach of the
Phillip's method should give smoother user experience, even if only
because of 1 less merge.

There are still 2 issues about the implementation that need to be
discussed though:

1. Still inverted order of the second merge compared to RFC.

It'd be simple to "fix" again, except I'm not sure it'd be better, and
as there is no existing experiences with this step to follow, it
probably should be left as in the original, where it means "merge the
changes made in B' (w.r.t B) into our intermediate version of the
resulting merge".

The original Phillip's version seems to better fit the asymmetry between
mainline and side-branch handling.

The actual difference will be only in the order of ours vs theirs in
conflicts though, and thus it's not that critical.

2. The U1' == U2' consistency check in RFC that I still think is worth
to be implemented.

In application to the method being discussed, we only need the check if
the final merge went without conflicts, so the user was not already
involved, and the check itself is then pretty simple:

 "proceed without stop only if $tree = $tree_U1'"

Its equivalence to the U1' == U2' test in the RFC follows from the fact
that if M' is non-conflicting merge of U1' and U2', then M' == U1' if
and only if U2' == U1'.

Finally, here is a sketch of the implementation that I'd suggest to
use:

git-rebase-first-parent --onto A' M
tree_U1'=$(git write-tree)
git merge-recursive B -- $tree_U1' B'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
[ $conflicted_last_merge = "yes" ] ||
  trees-match $tree_U1' $tree || 
  stop-for-user-amendment

where 'git-rebase-first-parent' denotes whatever machinery is currently
being used to rebase simple non-merge commit. Handy approximation of
which for stand-alone scripting is:

git checkout --detach A' && git cherry-pick -m 1 M

[As an interesting note, observe how, after all, that original Johannes
Sixt's idea of rebasing of merge commit by cherry-picking its first
parent is back there.]

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-08 15:16                                       ` Igor Djordjevic
  2018-03-08 16:21                                         ` Igor Djordjevic
@ 2018-03-14 14:24                                         ` Sergey Organov
  2018-03-14 23:11                                           ` Igor Djordjevic
  2018-03-26 13:47                                           ` Johannes Schindelin
  1 sibling, 2 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-14 14:24 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Dscho,
>
> On 07/03/2018 08:26, Johannes Schindelin wrote:

[...]

>> Second side note: if we can fast-forward, currently we prefer that, and I
>> think we should keep that behavior with -R, too.
>
> I agree.

I'm admittedly somewhat lost in the discussion, but are you talking
fast-forward on _rebasing_ existing merge? Where would it go in any of
the suggested algorithms of rebasing and why?

I readily see how it can break merges. E.g., any "git merge --ff-only
--no-ff" merge will magically disappear. So, even if somehow supported,
fast-forward should not be performed by default during _rebasing_ of a
merge.

>> If the user wants to force a new merge, they simply remove that -R
>> flag.

Alternatively, they'd replace 'pick' with 'merge', as they already do
for other actions. "A plurality is not to be posited without necessity".

Please, _please_, don't use 'merge' command to 'pick' merge commits!
It's utterly confusing!

Thinking about it I've got an idea that what we actually need is
--no-flatten flag that, when used alone, will just tell "git rebase" to
stop flattening history, and which will be implicitly imposed by
--recreate-merges (and --preserve-merges).

Then the only thing the --recreate-merges will tune is to put 'merge'
directives into the todo list for merge commits, exactly according to
what its name suggests, while the default behavior will be to put 'pick'
with suitable syntax into the todo. And arguments to the
--recreate-merge will specify additional options for the 'merge'
directive, obviously.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-14 14:24                                         ` Sergey Organov
@ 2018-03-14 23:11                                           ` Igor Djordjevic
  2018-03-15  6:00                                             ` Sergey Organov
  2018-03-17  2:08                                             ` Igor Djordjevic
  2018-03-26 13:47                                           ` Johannes Schindelin
  1 sibling, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-14 23:11 UTC (permalink / raw)
  To: Sergey Organov, Johannes Schindelin
  Cc: Phillip Wood, Jacob Keller, Git Mailing List, Johannes Sixt,
	Junio C Hamano

On 14/03/2018 15:24, Sergey Organov wrote:
> 
> > > Second side note: if we can fast-forward, currently we prefer that, and I
> > > think we should keep that behavior with -R, too.
> >
> > I agree.
> 
> I'm admittedly somewhat lost in the discussion, but are you talking
> fast-forward on _rebasing_ existing merge? Where would it go in any of
> the suggested algorithms of rebasing and why?
> 
> I readily see how it can break merges. E.g., any "git merge --ff-only
> --no-ff" merge will magically disappear. So, even if somehow supported,
> fast-forward should not be performed by default during _rebasing_ of a
> merge.

Hmm, now that you brought this up, I can only agree, of course.

What I had in my mind was more similar to "no-rebase-cousins", like 
if we can get away without actually rebasing the merge but still 
using the original one, do it. But I guess that`s not what Johannes 
originally asked about.

This is another definitive difference between rebasing (`pick`?) and 
recreating (`merge`) a merge commit - in the case where we`re rebasing, 
of course it doesn`t make sense to drop commit this time (due to 
fast-forward). This does make sense in recreating the merge (only).

> > > If the user wants to force a new merge, they simply remove that -R
> > > flag.

And this sounds wrong now, too, because we actually have _three_
possible behaviors here - (1) rebase merge commit, which should 
always do what its told (so no fast-forwarding, otherwise the whole 
concept of rebasing a merge commit doesn`t make sense), and recreate 
merge commit, which should (2) by default use fast-forward where 
possible (or whatever the settings say), but (3) also be possible to 
force a new merge as well (through standard `--no-ff`, I guess, or 
something).

> Alternatively, they'd replace 'pick' with 'merge', as they already do
> for other actions. "A plurality is not to be posited without necessity".
> 
> Please, _please_, don't use 'merge' command to 'pick' merge commits!
> It's utterly confusing!

I agree here, as previously discussed[1], but let`s hear Johannes.

> Thinking about it I've got an idea that what we actually need is
> --no-flatten flag that, when used alone, will just tell "git rebase" to
> stop flattening history, and which will be implicitly imposed by
> --recreate-merges (and --preserve-merges).
> 
> Then the only thing the --recreate-merges will tune is to put 'merge'
> directives into the todo list for merge commits, exactly according to
> what its name suggests, while the default behavior will be to put 'pick'
> with suitable syntax into the todo. And arguments to the
> --recreate-merge will specify additional options for the 'merge'
> directive, obviously.

This seem to basically boil down to what I mentioned previously[2] 
through use of new `--rebase-merges` alongside `--recreate-merges`, just 
that you named it `--no-flatten` here, but the point is the same - and 
not something Johannes liked, "polluting" rebase option space further.

I would agree with him, and settling onto `--rebase-merges` _instead_ of 
`--recreate-merges` seems as a more appropriate name, indeed, now that 
default behavior is actually merge commit rebasing and not recreating 
(recreating still being possible through user editing the todo list).

Now, the only thing left seems to be agreeing on actual command to 
use to rebase the merge commit, to `pick` it, so to say... ;)

Regards, Buga

[1] https://public-inbox.org/git/77b695d0-7564-80d7-d9e6-70a531e66eda@gmail.com/
[2] https://public-inbox.org/git/b329bb98-f9d6-3d51-2513-465aad2fa37a@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-14  7:21                           ` Sergey Organov
@ 2018-03-15  0:09                             ` Igor Djordjevic
  2018-03-15  7:52                               ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-15  0:09 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Sergey,

On 14/03/2018 08:21, Sergey Organov wrote:
> 
> There are still 2 issues about the implementation that need to be
> discussed though:
> 
> 1. Still inverted order of the second merge compared to RFC.
> 
> It'd be simple to "fix" again, except I'm not sure it'd be better, and
> as there is no existing experiences with this step to follow, it
> probably should be left as in the original, where it means "merge the
> changes made in B' (w.r.t B) into our intermediate version of the
> resulting merge".
> 
> The original Phillip's version seems to better fit the asymmetry between
> mainline and side-branch handling.
> 
> The actual difference will be only in the order of ours vs theirs in
> conflicts though, and thus it's not that critical.

Shouldn`t this be easy to solve just by changing the order of <head> 
and <remote>, on passing to `git merge-recursive`, if needed? (or 
that`s what you meant by "simple to fix"?)

> 2. The U1' == U2' consistency check in RFC that I still think is worth
> to be implemented.

At the moment, I think we`d appreciate test cases where it actually 
proves useful, as the general consensus seems to be leaning towards 
it possibly being annoying (over-paranoid).

> In application to the method being discussed, we only need the check if
> the final merge went without conflicts, so the user was not already
> involved, and the check itself is then pretty simple:
> 
>  "proceed without stop only if $tree = $tree_U1'"
> 
> Its equivalence to the U1' == U2' test in the RFC follows from the fact
> that if M' is non-conflicting merge of U1' and U2', then M' == U1' if
> and only if U2' == U1'.

Nicely spot! I`m glad there`s still (kind of) former U1' == U2' check 
in this approach, too, in case it proves useful :)

> Finally, here is a sketch of the implementation that I'd suggest to
> use:
> 
> git-rebase-first-parent --onto A' M
> tree_U1'=$(git write-tree)
> git merge-recursive B -- $tree_U1' B'
> tree=$(git write-tree)
> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
> [ $conflicted_last_merge = "yes" ] ||
>   trees-match $tree_U1' $tree || 
>   stop-for-user-amendment

Yes, in case where we would want the "no-op merge" check (equivalent 
to U1' == U2' with original approach), this aligns with something I 
would expect.

Note that all the "rebase merge commit" steps leading to the check 
will/should probably be observed as a single one from user`s perspective 
(in worst case ending with nested conflicts we discussed), thus 
`$conflicted_last_merge` is not related to `merge-recursive` step(s) 
only, but `rebase-first-parent`, too (just in case this isn`t implied).

Might be easier to reason about simply as `[ $conflicts = "yes" ] || `

> where 'git-rebase-first-parent' denotes whatever machinery is currently
> being used to rebase simple non-merge commit. Handy approximation of
> which for stand-alone scripting is:
> 
> git checkout --detach A' && git cherry-pick -m 1 M
> 
> [As an interesting note, observe how, after all, that original Johannes
> Sixt's idea of rebasing of merge commit by cherry-picking its first
> parent is back there.]

Heh ;) It`s always a bit enlightening when people start from different 
positions, opposing ones, even (or so it may seem, at least), but 
eventually end up in the same place, through means of open (minded) 
discussion.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-14 23:11                                           ` Igor Djordjevic
@ 2018-03-15  6:00                                             ` Sergey Organov
  2018-03-15 21:51                                               ` Igor Djordjevic
  2018-03-17  2:08                                             ` Igor Djordjevic
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-15  6:00 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 14/03/2018 15:24, Sergey Organov wrote:
[...]
>> Thinking about it I've got an idea that what we actually need is
>> --no-flatten flag that, when used alone, will just tell "git rebase" to
>> stop flattening history, and which will be implicitly imposed by
>> --recreate-merges (and --preserve-merges).
>> 
>> Then the only thing the --recreate-merges will tune is to put 'merge'
>> directives into the todo list for merge commits, exactly according to
>> what its name suggests, while the default behavior will be to put 'pick'
>> with suitable syntax into the todo. And arguments to the
>> --recreate-merge will specify additional options for the 'merge'
>> directive, obviously.
>
> This seem to basically boil down to what I mentioned previously[2] 
> through use of new `--rebase-merges` alongside `--recreate-merges`, just 
> that you named it `--no-flatten` here, but the point is the same - and 
> not something Johannes liked, "polluting" rebase option space further.

Not quite so. The problem with --XXX-merges flags is that they do two
things at once: they say _what_ to do and _how_ to do it. Clean UI
designs usually have these things separate, and that's what I propose.

The --[no-]flatten says _what_ (not) to do, and --recreate-merges says
_how_ exactly it will be performed. In this model --no-flatten could
have been called, say --preserve-shape, but not --rebase-merges.

To minimize pollution, the _how_ part could rather be made option value:

--no-flatten[=<strategy>]

where <strategy> is 'rebase', 'remerge', etc.

In this case we will need separate option to specify strategy options,
if required, that will lead us to something similar to the set of merge
strategies options.

> I would agree with him, and settling onto `--rebase-merges` _instead_ of 
> `--recreate-merges` seems as a more appropriate name, indeed, now that 
> default behavior is actually merge commit rebasing and not recreating 
> (recreating still being possible through user editing the todo list).

I hope he'd be pleased to be able to say --no-flatten=remerge and get
back his current mode of operation, that he obviously has a good use
for.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-15  0:09                             ` Igor Djordjevic
@ 2018-03-15  7:52                               ` Sergey Organov
  2018-03-15 23:08                                 ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-15  7:52 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 14/03/2018 08:21, Sergey Organov wrote:
>> 
>> There are still 2 issues about the implementation that need to be
>> discussed though:
>> 
>> 1. Still inverted order of the second merge compared to RFC.
>> 
>> It'd be simple to "fix" again, except I'm not sure it'd be better, and
>> as there is no existing experiences with this step to follow, it
>> probably should be left as in the original, where it means "merge the
>> changes made in B' (w.r.t B) into our intermediate version of the
>> resulting merge".
>> 
>> The original Phillip's version seems to better fit the asymmetry between
>> mainline and side-branch handling.
>> 
>> The actual difference will be only in the order of ours vs theirs in
>> conflicts though, and thus it's not that critical.
>
> Shouldn`t this be easy to solve just by changing the order of <head> 
> and <remote>, on passing to `git merge-recursive`, if needed? (or 
> that`s what you meant by "simple to fix"?)

Yes, that's exactly what I meant, except it looks cleaner as is, so I
don't think the exchange is called for.

>> 2. The U1' == U2' consistency check in RFC that I still think is worth
>> to be implemented.
>
> At the moment, I think we`d appreciate test cases where it actually 
> proves useful, as the general consensus seems to be leaning towards 
> it possibly being annoying (over-paranoid).

As we now have a simple way to actually check it even in this algorithm,
I'd suggest command-line option to either relax or enforce the check,
whatever the default is. For the default, I'd still opt for safety, as
without it we will gather little experience with this new matter.

Honestly, without this check available, I'd likely vote for at least an
option for stopping on every rebased merge, on the ground that if
rebasing a non-merge could be a trouble, rebasing a merge is at least
double-trouble, and it's not that frequent anyway. So the check we
discuss is actually a way to make all the process much less paranoid,
not more.

By the way, nobody yet commented about "rerere" behavior that basically
stops rebasing every time it fires. Do you consider it over-paranoid?

As for test cases, I have none myself, but "-s ours" merge may be an
example of an actual trouble.

If we don't treat it specially, then changes to side branch will be
silently propagated over the merge, that's obviously not what is needed,
provided user keeps his intention to leave the merge "-s ours".

If we do treat it specially, it could be the case that the merge in
question only looks like "-s ours" by pure accident, and thus changes to
the side branch should be propagated.

I don't see how we can safely proceed without stop for user assistance.
Had we already achieved some consensus on this issue?

>
>> In application to the method being discussed, we only need the check if
>> the final merge went without conflicts, so the user was not already
>> involved, and the check itself is then pretty simple:
>> 
>>  "proceed without stop only if $tree = $tree_U1'"
>> 
>> Its equivalence to the U1' == U2' test in the RFC follows from the fact
>> that if M' is non-conflicting merge of U1' and U2', then M' == U1' if
>> and only if U2' == U1'.
>
> Nicely spot! I`m glad there`s still (kind of) former U1' == U2' check 
> in this approach, too, in case it proves useful :)
>
>> Finally, here is a sketch of the implementation that I'd suggest to
>> use:
>> 
>> git-rebase-first-parent --onto A' M
>> tree_U1'=$(git write-tree)
>> git merge-recursive B -- $tree_U1' B'
>> tree=$(git write-tree)
>> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
>> [ $conflicted_last_merge = "yes" ] ||
>>   trees-match $tree_U1' $tree || 
>>   stop-for-user-amendment
>
> Yes, in case where we would want the "no-op merge" check (equivalent 
> to U1' == U2' with original approach), this aligns with something I 
> would expect.
>
> Note that all the "rebase merge commit" steps leading to the check 
> will/should probably be observed as a single one from user`s perspective 
> (in worst case ending with nested conflicts we discussed), thus 
> `$conflicted_last_merge` is not related to `merge-recursive` step(s) 
> only, but `rebase-first-parent`, too (just in case this isn`t implied).
>
> Might be easier to reason about simply as `[ $conflicts = "yes" ] || `

No. For this check it's essential to ensure that no tweaking of the
content has been performed under the hood after the user has resolved
conflicts, i.e., after he has been involved last time.

If all this is done in one "huge merge" step from user point of view,
then the check belongs to this merge, as this is the last (and the only)
one. If it's done in steps (and I vote for it), only the last merge
status is essential for the check, preceding merges don't matter.

As I said, putting myself on the user side, I'd prefer entirely separate
first step of the algorithm, exactly as written, with its own conflict
resolution, all running entirely the same way as it does with non-merge
commits. I'm used to it and don't want to learn something new without
necessity. I.e., I'd prefer to actually see it in two separate stages,
like this:

Rebasing mainline of the merge...
[.. possible conflicts resolution ..]
Merging in changes to side branch(es)...
[.. possible conflicts resolution ..]

And if the second stage gives non-trivial conflicts, I'd like to have a
simple way to just do "merge -s ours <heads>" on top of already rebased
mainline of the merge and go with it. Note that the latter is
significantly different than re-merging everything from scratch, that
would be the only choice with "all-in-one" approach, and it essentially
gives me back those simple "rebase first parent and just record other
parents" semantics when needed.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-15  6:00                                             ` Sergey Organov
@ 2018-03-15 21:51                                               ` Igor Djordjevic
  0 siblings, 0 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-15 21:51 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 15/03/2018 07:00, Sergey Organov wrote:
> 
> > > Thinking about it I've got an idea that what we actually need is
> > > --no-flatten flag that, when used alone, will just tell "git rebase" to
> > > stop flattening history, and which will be implicitly imposed by
> > > --recreate-merges (and --preserve-merges).
> > >
> > > Then the only thing the --recreate-merges will tune is to put 'merge'
> > > directives into the todo list for merge commits, exactly according to
> > > what its name suggests, while the default behavior will be to put 'pick'
> > > with suitable syntax into the todo. And arguments to the
> > > --recreate-merge will specify additional options for the 'merge'
> > > directive, obviously.
> >
> > This seem to basically boil down to what I mentioned previously[2] 
> > through use of new `--rebase-merges` alongside `--recreate-merges`, just 
> > that you named it `--no-flatten` here, but the point is the same - and 
> > not something Johannes liked, "polluting" rebase option space further.
> 
> Not quite so. The problem with --XXX-merges flags is that they do two
> things at once: they say _what_ to do and _how_ to do it. Clean UI
> designs usually have these things separate, and that's what I propose.
> 
> The --[no-]flatten says _what_ (not) to do, and --recreate-merges says
> _how_ exactly it will be performed. In this model --no-flatten could
> have been called, say --preserve-shape, but not --rebase-merges.
> 
> To minimize pollution, the _how_ part could rather be made option value:
> 
> --no-flatten[=<strategy>]
> 
> where <strategy> is 'rebase', 'remerge', etc.
> 
> In this case we will need separate option to specify strategy options,
> if required, that will lead us to something similar to the set of merge
> strategies options.
> 
> > I would agree with him, and settling onto `--rebase-merges` _instead_ of 
> > `--recreate-merges` seems as a more appropriate name, indeed, now that 
> > default behavior is actually merge commit rebasing and not recreating 
> > (recreating still being possible through user editing the todo list).
> 
> I hope he'd be pleased to be able to say --no-flatten=remerge and get
> back his current mode of operation, that he obviously has a good use
> for.

Makes sense, I like it, thanks for elaborating. [ Especially that you 
used "(no) flatten" phrasing, where original `--preserve-merges` 
documentation says it`s used "not to flatten the history", nice touch ;) ]

Not sure if I would prefer original `recreate` instead of `remerge` 
as that particular strategy name, though, but might be I`m just more 
used to the former one at the moment.

But if I think about it more, "recreate" seems straightforward enough - 
create something (again), kind of making it more obvious that it is a 
new thing (that we are now creating, again, based on some old thing).

On the other hand, "remerge" communicates "merge something again", 
which doesn`t necessarily mean creating a new thing (based on, but 
not attached to old thing), and could also be interpreted as "merge 
existing thing again" (and leaves me wondering if it would better 
suite some other strategy possible in the future).

Not sure if that explanation suffices, but it comes to "recreate a 
merge" having more clear meaning than "remerge a merge", being 
somewhat ambiguous, thus confusing (to me, at least).

I don`t know, thinking too much?

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-15  7:52                               ` Sergey Organov
@ 2018-03-15 23:08                                 ` Igor Djordjevic
  2018-03-16  7:31                                   ` Sergey Organov
  2018-03-26 14:11                                   ` Johannes Schindelin
  0 siblings, 2 replies; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-15 23:08 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Sergey,

On 15/03/2018 08:52, Sergey Organov wrote:
> 
> > > 2. The U1' == U2' consistency check in RFC that I still think is worth
> > > to be implemented.
> >
> > At the moment, I think we`d appreciate test cases where it actually 
> > proves useful, as the general consensus seems to be leaning towards 
> > it possibly being annoying (over-paranoid).
> 
> As we now have a simple way to actually check it even in this algorithm,
> I'd suggest command-line option to either relax or enforce the check,
> whatever the default is. For the default, I'd still opt for safety, as
> without it we will gather little experience with this new matter.
> 
> Honestly, without this check available, I'd likely vote for at least an
> option for stopping on every rebased merge, on the ground that if
> rebasing a non-merge could be a trouble, rebasing a merge is at least
> double-trouble, and it's not that frequent anyway. So the check we
> discuss is actually a way to make all the process much less paranoid,
> not more.
> 
> By the way, nobody yet commented about "rerere" behavior that basically
> stops rebasing every time it fires. Do you consider it over-paranoid?

I wouldn`t really know, my workflows are usually/still rather simple, I 
don`t think I`ve ever used it on purpose, and I don`t really remember 
I`ve triggered it by accident, not having it stop for amendment, at least.

You did say you find it annoying yourself, though ;) But also accepting 
it as something that probably has a good reason, though (thus not 
considering it over-paranoid, even if annoying).

> As for test cases, I have none myself, but "-s ours" merge may be an
> example of an actual trouble.
> 
> If we don't treat it specially, then changes to side branch will be
> silently propagated over the merge, that's obviously not what is needed,
> provided user keeps his intention to leave the merge "-s ours".
> 
> If we do treat it specially, it could be the case that the merge in
> question only looks like "-s ours" by pure accident, and thus changes to
> the side branch should be propagated.
> 
> I don't see how we can safely proceed without stop for user assistance.
> Had we already achieved some consensus on this issue?

I don`t know, from what Johannes said in the past, I got an 
impression that this is to be expected ("by design"), and not worth 
bothering to stop for. And he is one of the heaviest users of (merge) 
rebasing I know.

Personally, I still feel it would make sense to stop in case like 
this, indeed, but it`s just my humble (and not necessarily much 
educated) opinion.

> > > Finally, here is a sketch of the implementation that I'd suggest to
> > > use:
> > >
> > > git-rebase-first-parent --onto A' M
> > > tree_U1'=$(git write-tree)
> > > git merge-recursive B -- $tree_U1' B'
> > > tree=$(git write-tree)
> > > M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
> > > [ $conflicted_last_merge = "yes" ] ||
> > >   trees-match $tree_U1' $tree || 
> > >   stop-for-user-amendment
> >
> > Yes, in case where we would want the "no-op merge" check (equivalent 
> > to U1' == U2' with original approach), this aligns with something I 
> > would expect.
> >
> > Note that all the "rebase merge commit" steps leading to the check 
> > will/should probably be observed as a single one from user`s perspective 
> > (in worst case ending with nested conflicts we discussed), thus 
> > `$conflicted_last_merge` is not related to `merge-recursive` step(s) 
> > only, but `rebase-first-parent`, too (just in case this isn`t implied).
> >
> > Might be easier to reason about simply as `[ $conflicts = "yes" ] || `
> 
> No. For this check it's essential to ensure that no tweaking of the
> content has been performed under the hood after the user has resolved
> conflicts, i.e., after he has been involved last time.
> 
> If all this is done in one "huge merge" step from user point of view,
> then the check belongs to this merge, as this is the last (and the only)
> one. If it's done in steps (and I vote for it), only the last merge
> status is essential for the check, preceding merges don't matter.

"Huge merge" step (from user point of view) is exactly how I perceived 
Johannes` opinion on it, describing it`s already part of Git user 
experience (with possible nested conflicts), while otherwise possibly 
hard to explain where we are precisely at in the moment of stopping for 
(intermediate) conflict resolution.

Thus only `$conflicts`, meaning anything in the "huge merge", as no user 
action/tweaking/involvement can happen until the "huge merge" is done.

> As I said, putting myself on the user side, I'd prefer entirely separate
> first step of the algorithm, exactly as written, with its own conflict
> resolution, all running entirely the same way as it does with non-merge
> commits. I'm used to it and don't want to learn something new without
> necessity. I.e., I'd prefer to actually see it in two separate stages,
> like this:
> 
> Rebasing mainline of the merge...
> [.. possible conflicts resolution ..]
> Merging in changes to side branch(es)...
> [.. possible conflicts resolution ..]
> 
> And if the second stage gives non-trivial conflicts, I'd like to have a
> simple way to just do "merge -s ours <heads>" on top of already rebased
> mainline of the merge and go with it. Note that the latter is
> significantly different than re-merging everything from scratch, that
> would be the only choice with "all-in-one" approach, and it essentially
> gives me back those simple "rebase first parent and just record other
> parents" semantics when needed.

I`m undecided here, and while I do see a point in what you`re saying, 
this being new to general public I dont`t think you being accustomed 
to it is a very strong argument :)

Yes, having more steps would mean more power/options to the user, but 
more complexity to explain to and guide him through as well, not really 
sure where the line should be drawn - for the first time, at least.

Also note that, for example, in case side branch(es) dropped some 
commits (interactively or otherwise), first step alone would still 
reintroduce those dropped changes, thus later possible `merge -s ours 
<heads>` would be a pretty bad "evil merge" case and a wrong thing to 
do in general.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-15 23:08                                 ` Igor Djordjevic
@ 2018-03-16  7:31                                   ` Sergey Organov
  2018-03-17  3:04                                     ` Igor Djordjevic
  2018-03-26 14:11                                   ` Johannes Schindelin
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-16  7:31 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,

[...]

>> As I said, putting myself on the user side, I'd prefer entirely separate
>> first step of the algorithm, exactly as written, with its own conflict
>> resolution, all running entirely the same way as it does with non-merge
>> commits. I'm used to it and don't want to learn something new without
>> necessity. I.e., I'd prefer to actually see it in two separate stages,
>> like this:
>> 
>> Rebasing mainline of the merge...
>> [.. possible conflicts resolution ..]
>> Merging in changes to side branch(es)...
>> [.. possible conflicts resolution ..]
>> 
>> And if the second stage gives non-trivial conflicts, I'd like to have a
>> simple way to just do "merge -s ours <heads>" on top of already rebased
>> mainline of the merge and go with it. Note that the latter is
>> significantly different than re-merging everything from scratch, that
>> would be the only choice with "all-in-one" approach, and it essentially
>> gives me back those simple "rebase first parent and just record other
>> parents" semantics when needed.
>
> I`m undecided here, and while I do see a point in what you`re saying, 
> this being new to general public I dont`t think you being accustomed 
> to it is a very strong argument :)

Sure. It's mostly that having already familiar step separate seems to be
a good idea, as well as resulting isolation of the new stuff, where I
readily agree not to granulate it further. As if the latter actually
makes any difference... Octopus merges? I mean, really?

> Yes, having more steps would mean more power/options to the user, but 
> more complexity to explain to and guide him through as well, not really 
> sure where the line should be drawn - for the first time, at least.

A good thing is that while it runs smoothly it still runs smoothly both
ways.

> Also note that, for example, in case side branch(es) dropped some 
> commits (interactively or otherwise), first step alone would still 
> reintroduce those dropped changes, thus later possible `merge -s ours 
> <heads>` would be a pretty bad "evil merge" case and a wrong thing to 
> do in general.

Except that my presumption is that the second step has been run already
and has stopped due to conflicts, so I see the conflicting result of
dropping those commits on side branch(es), check the previous state of
the right side of the conflicting merge, and decide those state, being
the result of the fist step after possibly demanding conflicts
resolution, is fine after all. Thus I just re-merge -x ours the
branch(es), instead of re-merging everythig from scratch only to finally
get back to the same result, be it evil or not, the hard way.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-14 23:11                                           ` Igor Djordjevic
  2018-03-15  6:00                                             ` Sergey Organov
@ 2018-03-17  2:08                                             ` Igor Djordjevic
  2018-03-19  5:44                                               ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-17  2:08 UTC (permalink / raw)
  To: Sergey Organov, Johannes Schindelin
  Cc: Phillip Wood, Jacob Keller, Git Mailing List, Johannes Sixt,
	Junio C Hamano

On 15/03/2018 00:11, Igor Djordjevic wrote:
> 
> > > > Second side note: if we can fast-forward, currently we prefer
> > > > that, and I think we should keep that behavior with -R, too.
> > >
> > > I agree.
> > 
> > I'm admittedly somewhat lost in the discussion, but are you
> > talking fast-forward on _rebasing_ existing merge? Where would it
> > go in any of the suggested algorithms of rebasing and why?
> > 
> > I readily see how it can break merges. E.g., any "git merge
> > --ff-only --no-ff" merge will magically disappear. So, even if
> > somehow supported, fast-forward should not be performed by default
> > during _rebasing_ of a merge.
> 
> Hmm, now that you brought this up, I can only agree, of course.
> 
> What I had in my mind was more similar to "no-rebase-cousins", like 
> if we can get away without actually rebasing the merge but still 
> using the original one, do it. But I guess that`s not what Johannes 
> originally asked about.
> 
> This is another definitive difference between rebasing (`pick`?) and 
> recreating (`merge`) a merge commit - in the case where we`re rebasing, 
> of course it doesn`t make sense to drop commit this time (due to 
> fast-forward). This does make sense in recreating the merge (only).

Eh, I might take this back. I think my original interpretation (and 
agreement) to fast-forwarding is correct.

But the confusion here comes from `--no-ff` as used for merging, as 
opposed to `--no-ff` as used for rebasing. I _think_ Johannes meant 
the latter one.

In rebasing, `--no-ff` means that even if a commit inside todo list 
isn`t to be changed, do not reuse it but create a new one. Here`s 
excerpt from the docs[1]:

  --no-ff
    With --interactive, cherry-pick all rebased commits instead of 
    fast-forwarding over the unchanged ones. This ensures that the 
    entire history of the rebased branch is composed of new commits.

    Without --interactive, this is a synonym for --force-rebase.


So fast-forwarding in case of rebasing (merge commits as well) is 
something you would want by default, as it wouldn`t drop/lose 
anything, but merely reuse existing commit (if unchanged), instead of 
cherry-picking (rebasing) it into a new (merge) commit anyway.

The same goes for this part:

> > > > If the user wants to force a new merge, they simply remove that
> > > > -R flag.

This means that using `-R` flag is sensitive to `--no-ff` rebase 
option, original merge commit _reused_ (fast-forwarded) when possible 
(unchanged, and `--no-ff` not provided), or original merge commit 
_rebased_ (when changed, or `--no-ff` provided).

If `-R` flag is removed, then merge commit is always _recreated_, no 
matter if `--no-ff` option is used or not.

p.s. I`m still a bit opposed to `-R` flag in the first place, as 
discussed elsewhere[2][3], but that`s unrelated to this fast-forward 
discussion.

Related to it, though, if `pick` command would be used instead of 
`merge -R` to signal merge commit _rebasing_, it would fit into 
existing logic nicely, where `pick` is already sensitive to `--no-ff` 
option (for rebasing regular commits). Then `merge` alone could be 
naturally (and only) used for _recreating_ merge commits, as 
originally intended (and intuitively expected).

Regards, Buga

[1] https://git-scm.com/docs/git-rebase#git-rebase---no-ff
[2] https://public-inbox.org/git/77b695d0-7564-80d7-d9e6-70a531e66eda@gmail.com/
[3] https://public-inbox.org/git/a3d40dca-f508-5853-89bc-1f9ab393416b@gmail.com/

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-16  7:31                                   ` Sergey Organov
@ 2018-03-17  3:04                                     ` Igor Djordjevic
  2018-03-19  6:01                                       ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-17  3:04 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Sergey,

On 16/03/2018 08:31, Sergey Organov wrote:
> 
> > > As I said, putting myself on the user side, I'd prefer entirely
> > > separate first step of the algorithm, exactly as written, with
> > > its own conflict resolution, all running entirely the same way as
> > > it does with non-merge commits. I'm used to it and don't want to
> > > learn something new without necessity. I.e., I'd prefer to
> > > actually see it in two separate stages, like this:
> > >
> > > Rebasing mainline of the merge...
> > > [.. possible conflicts resolution ..]
> > > Merging in changes to side branch(es)...
> > > [.. possible conflicts resolution ..]
> > >
> > > And if the second stage gives non-trivial conflicts, I'd like to
> > > have a simple way to just do "merge -s ours <heads>" on top of
> > > already rebased mainline of the merge and go with it. Note that
> > > the latter is significantly different than re-merging everything
> > > from scratch, that would be the only choice with "all-in-one"
> > > approach, and it essentially gives me back those simple "rebase
> > > first parent and just record other parents" semantics when
> > > needed.
> > 
> > [...]
> > 
> > Also note that, for example, in case side branch(es) dropped some 
> > commits (interactively or otherwise), first step alone would still 
> > reintroduce those dropped changes, thus later possible `merge -s ours 
> > <heads>` would be a pretty bad "evil merge" case and a wrong thing to 
> > do in general.
> 
> Except that my presumption is that the second step has been run already
> and has stopped due to conflicts, so I see the conflicting result of
> dropping those commits on side branch(es), check the previous state of
> the right side of the conflicting merge, and decide those state, being
> the result of the fist step after possibly demanding conflicts
> resolution, is fine after all. Thus I just re-merge -x ours the
> branch(es), instead of re-merging everythig from scratch only to finally
> get back to the same result, be it evil or not, the hard way.

Might be my comment missed the point here, it should have been more 
about what you said regarding "first step having its own conflict 
resolution" - in case of dropped commits on side branch(es), you would 
be trying to resolve conflicts using one tree that doesn`t/shouldn`t 
even exist anymore (rebased merge commit first parent changes), which 
might be pretty confusing, only to find the "second stage" later 
removing changes that you might have actually picked as "first stage" 
conflict resolution, making it all even worse.

Only once "huge merge" is done completely (meaning all steps involved 
in merge commit rebasing), user can have a more realistic overview of 
(possibly nested, even) conflicts to resolve (and knowing his resolution 
will actually stick).

Regarding `merge -s ours <heads>` you mention, as you say it would 
happen only after "huge merge" is complete (with possible conflicts), 
I guess it`s unrelated to having "merge commit rebasing" happen in 
one go ("huge merge"), or iteratively, in stages (from user`s 
perspective, unrelated to underlying implementation)...?

Thus I`m questioning use-case for step-by-step merge commit rebasing 
where each stage has its own conflict resolution, in the face of it 
possibly being more confusing than helpful.

Otherwise, I see the point in what you would like to accomplish with 
that `merge -s ours <heads>` (not from scratch), but I`m not sure 
what would be the most sane way to allow it, and if it would be worth 
it in the first place, seeming to be a pretty exotic use case.

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-17  2:08                                             ` Igor Djordjevic
@ 2018-03-19  5:44                                               ` Sergey Organov
  2018-03-19 21:35                                                 ` Igor Djordjevic
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-19  5:44 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> On 15/03/2018 00:11, Igor Djordjevic wrote:
>> 
>> > > > Second side note: if we can fast-forward, currently we prefer
>> > > > that, and I think we should keep that behavior with -R, too.
>> > >
>> > > I agree.
>> > 
>> > I'm admittedly somewhat lost in the discussion, but are you
>> > talking fast-forward on _rebasing_ existing merge? Where would it
>> > go in any of the suggested algorithms of rebasing and why?
>> > 
>> > I readily see how it can break merges. E.g., any "git merge
>> > --ff-only --no-ff" merge will magically disappear. So, even if
>> > somehow supported, fast-forward should not be performed by default
>> > during _rebasing_ of a merge.
>> 
>> Hmm, now that you brought this up, I can only agree, of course.
>> 
>> What I had in my mind was more similar to "no-rebase-cousins", like 
>> if we can get away without actually rebasing the merge but still 
>> using the original one, do it. But I guess that`s not what Johannes 
>> originally asked about.
>> 
>> This is another definitive difference between rebasing (`pick`?) and 
>> recreating (`merge`) a merge commit - in the case where we`re rebasing, 
>> of course it doesn`t make sense to drop commit this time (due to 
>> fast-forward). This does make sense in recreating the merge (only).
>
> Eh, I might take this back. I think my original interpretation (and 
> agreement) to fast-forwarding is correct.
>
> But the confusion here comes from `--no-ff` as used for merging, as 
> opposed to `--no-ff` as used for rebasing. I _think_ Johannes meant 
> the latter one.
>
> In rebasing, `--no-ff` means that even if a commit inside todo list 
> isn`t to be changed, do not reuse it but create a new one. Here`s 
> excerpt from the docs[1]:
>
>   --no-ff
>     With --interactive, cherry-pick all rebased commits instead of 
>     fast-forwarding over the unchanged ones. This ensures that the 
>     entire history of the rebased branch is composed of new commits.
>
>     Without --interactive, this is a synonym for --force-rebase.
>
>
> So fast-forwarding in case of rebasing (merge commits as well) is 
> something you would want by default, as it wouldn`t drop/lose 
> anything, but merely reuse existing commit (if unchanged), instead of 
> cherry-picking (rebasing) it into a new (merge) commit anyway.

This sounds like breakage. E.g., it seems to be breaking every "-x ours"
merge out there.

Fast-forwarding existing merge, one way or another, still seems to be
wrong idea to me, as merge commit is not only about content change, but
also about joint point at particular place in the DAG.

As for fast-forwarding re-merge, explicitly requested, I'm not sure. On
one hand, it's inline with the default "git merge" behavior, on the
other hand, it still feels wrong, somehow.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-17  3:04                                     ` Igor Djordjevic
@ 2018-03-19  6:01                                       ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-19  6:01 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Phillip Wood, phillip.wood, Git mailing list, Jacob Keller,
	Johannes Schindelin, Johannes Sixt, Junio C Hamano

Hi Buga,

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 16/03/2018 08:31, Sergey Organov wrote:
>> 
>> > > As I said, putting myself on the user side, I'd prefer entirely
>> > > separate first step of the algorithm, exactly as written, with
>> > > its own conflict resolution, all running entirely the same way as
>> > > it does with non-merge commits. I'm used to it and don't want to
>> > > learn something new without necessity. I.e., I'd prefer to
>> > > actually see it in two separate stages, like this:
>> > >
>> > > Rebasing mainline of the merge...
>> > > [.. possible conflicts resolution ..]
>> > > Merging in changes to side branch(es)...
>> > > [.. possible conflicts resolution ..]
>> > >
>> > > And if the second stage gives non-trivial conflicts, I'd like to
>> > > have a simple way to just do "merge -s ours <heads>" on top of
>> > > already rebased mainline of the merge and go with it. Note that
>> > > the latter is significantly different than re-merging everything
>> > > from scratch, that would be the only choice with "all-in-one"
>> > > approach, and it essentially gives me back those simple "rebase
>> > > first parent and just record other parents" semantics when
>> > > needed.
>> > 
>> > [...]
>> > 
>> > Also note that, for example, in case side branch(es) dropped some 
>> > commits (interactively or otherwise), first step alone would still 
>> > reintroduce those dropped changes, thus later possible `merge -s ours 
>> > <heads>` would be a pretty bad "evil merge" case and a wrong thing to 
>> > do in general.
>> 
>> Except that my presumption is that the second step has been run already
>> and has stopped due to conflicts, so I see the conflicting result of
>> dropping those commits on side branch(es), check the previous state of
>> the right side of the conflicting merge, and decide those state, being
>> the result of the fist step after possibly demanding conflicts
>> resolution, is fine after all. Thus I just re-merge -x ours the
>> branch(es), instead of re-merging everythig from scratch only to finally
>> get back to the same result, be it evil or not, the hard way.
>
> Might be my comment missed the point here, it should have been more 
> about what you said regarding "first step having its own conflict 
> resolution" - in case of dropped commits on side branch(es), you would 
> be trying to resolve conflicts using one tree that doesn`t/shouldn`t 
> even exist anymore (rebased merge commit first parent changes), which 
> might be pretty confusing, only to find the "second stage" later 
> removing changes that you might have actually picked as "first stage" 
> conflict resolution, making it all even worse.
>
> Only once "huge merge" is done completely (meaning all steps involved 
> in merge commit rebasing), user can have a more realistic overview of 
> (possibly nested, even) conflicts to resolve (and knowing his resolution 
> will actually stick).
>
> Regarding `merge -s ours <heads>` you mention, as you say it would 
> happen only after "huge merge" is complete (with possible conflicts), 
> I guess it`s unrelated to having "merge commit rebasing" happen in 
> one go ("huge merge"), or iteratively, in stages (from user`s 
> perspective, unrelated to underlying implementation)...?
>
> Thus I`m questioning use-case for step-by-step merge commit rebasing 
> where each stage has its own conflict resolution, in the face of it 
> possibly being more confusing than helpful.
>
> Otherwise, I see the point in what you would like to accomplish with 
> that `merge -s ours <heads>` (not from scratch), but I`m not sure 
> what would be the most sane way to allow it, and if it would be worth 
> it in the first place, seeming to be a pretty exotic use case.

I find your arguments sound, except that I somehow feel that "exotic use
case" will solve 90% of conflicting merge rebases, as merges are about
the mainline in the first place. It's likely nothing more than personal
feeling though, so I'd simply agree to disagree at this point, at least
until some actual experience is gained.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-19  5:44                                               ` Sergey Organov
@ 2018-03-19 21:35                                                 ` Igor Djordjevic
  2018-03-20 14:43                                                   ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Igor Djordjevic @ 2018-03-19 21:35 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On 19/03/2018 06:44, Sergey Organov wrote:
> 
> > > > > > Second side note: if we can fast-forward, currently we prefer
> > > > > > that, and I think we should keep that behavior with -R, too.
> > > > >
> > > > > I agree.
> > > >
> > > > I'm admittedly somewhat lost in the discussion, but are you
> > > > talking fast-forward on _rebasing_ existing merge? Where would it
> > > > go in any of the suggested algorithms of rebasing and why?
> > > >
> > > > I readily see how it can break merges. E.g., any "git merge
> > > > --ff-only --no-ff" merge will magically disappear. So, even if
> > > > somehow supported, fast-forward should not be performed by default
> > > > during _rebasing_ of a merge.
> > >
> > > Hmm, now that you brought this up, I can only agree, of course.
> > >
> > > What I had in my mind was more similar to "no-rebase-cousins", like 
> > > if we can get away without actually rebasing the merge but still 
> > > using the original one, do it. But I guess that`s not what Johannes 
> > > originally asked about.
> > >
> > > This is another definitive difference between rebasing (`pick`?) and 
> > > recreating (`merge`) a merge commit - in the case where we`re rebasing, 
> > > of course it doesn`t make sense to drop commit this time (due to 
> > > fast-forward). This does make sense in recreating the merge (only).
> >
> > Eh, I might take this back. I think my original interpretation (and 
> > agreement) to fast-forwarding is correct.
> >
> > But the confusion here comes from `--no-ff` as used for merging, as 
> > opposed to `--no-ff` as used for rebasing. I _think_ Johannes meant 
> > the latter one.
> >
> > In rebasing, `--no-ff` means that even if a commit inside todo list 
> > isn`t to be changed, do not reuse it but create a new one. Here`s 
> > excerpt from the docs[1]:
> >
> >   --no-ff
> >     With --interactive, cherry-pick all rebased commits instead of 
> >     fast-forwarding over the unchanged ones. This ensures that the 
> >     entire history of the rebased branch is composed of new commits.
> >
> >     Without --interactive, this is a synonym for --force-rebase.
> >
> >
> > So fast-forwarding in case of rebasing (merge commits as well) is 
> > something you would want by default, as it wouldn`t drop/lose 
> > anything, but merely reuse existing commit (if unchanged), instead of 
> > cherry-picking (rebasing) it into a new (merge) commit anyway.
> 
> This sounds like breakage. E.g., it seems to be breaking every "-x ours"
> merge out there.

Either you are not understanding how rebase fast-forward works, or 
I`m missing what you are pointing to... Mind explaining how can 
something that`s left unchanged suddenly become a breakage?

> Fast-forwarding existing merge, one way or another, still seems to be
> wrong idea to me, as merge commit is not only about content change, but
> also about joint point at particular place in the DAG.

Not sure what this has to do with rebase fast-forwarding, either - 
nothing changes for fast-forwarded (merge or non-merge) commit in 
question, both content, joint point and everything else stays exactly 
the same. If anything changed, then it can`t/won`t be fast-forwarded, 
being unchanged is a prerequisite.

Let me elaborate a bit. Here`s a starting diagram:

(1) ---X1---X2---X3 (master)
                 |\
                 | A1---A2---A3
                 |            \
                 |             M---C1---C2 (topic)
                 |            /
                 \-B1---B2---B3


With "topic" being active branch, we start interactive rebase with 
`git rebase -i master`. Generated todo list will hold commits A1 to 
A3, B1 to B3, M and C1 to C2.

Now, if we decide to `edit` commit C1, leaving everything else the 
same, fast-forward logic will make the new situation look like this:

(2) ---X1---X2---X3 (master)
                 |\
                 | A1---A2---A3
                 |            \
                 |             M---C1'--C2' (topic)
                 |            /
                 \-B1---B2---B3


Notice how only C1 and C2 changed to C1' and C2'? That`s rebase 
fast-forwarding, noticing earlier commits left unchanged, thus 
reusing original ones.

No matter what, no breakage can happen to M in this case, as it`s 
left (reused) exactly as it was - it`s fast-forward rebased.

If we `edit`-ed commit A2, we would have ended in a situation like this:

(3) ---X1---X2---X3 (master)
                 |\
                 | A1---A2'--A3'
                 |            \
                 |             M'--C1'--C2' (topic)
                 |            /
                 \-B1---B2---B3


This time we have new commits A2', A3', M', C1' and C2' - so 
everything influenced by the change that happened will be changed 
(merge commit as well), where all the rest can still be reused 
(fast-forwarded).

If we had started rebasing with `git rebase -i --no-ff master`, no 
matter which commits we `edit` (or none, even), we would end up with 
this instead:

(4) ---X1---X2---X3 (master)
                 |\
                 | A1'--A2'--A3'
                 |            \
                 |             M'--C1'--C2' (topic)
                 |            /
                 \-B1'--B2'--B3'


So even in case where nothing is changed, no rebase fast-forwarding 
is performed (as requested), and each and every commit is changed (old 
commit replaced with its rebased version, commit hash changed).

Now, if our starting position looked like this instead:

(5) ---X1---X2---X3---X4---X5 (master)
                 |\
                 | A1---A2---A3
                 |            \
                 |             M---C1---C2 (topic)
                 |            /
                 \-B1---B2---B3

... and we simply do `git rebase -i master` again, causing all side 
commits to be rebased onto X5, then no fast forwarding can be done, as 
all commits _are_ changed (by having their parent changed, no matter if 
we additionally edit them or not), so even without `--no-ff` option we 
end up with this:

(6) ---X1---X2---X3---X4---X5 (master)
                           |\
                           | A1'--A2'--A3'
                           |            \
                           |             M'--C1'--C2' (topic)
                           |            /
                           \-B1'--B2'--B3'


Does this settle your concerns, or I`m missing something?

> As for fast-forwarding re-merge, explicitly requested, I'm not sure. On
> one hand, it's inline with the default "git merge" behavior, on the
> other hand, it still feels wrong, somehow.

Regarding fast-forwarding in context of merging, in case where we are 
recreating merges (not rebasing them), following existing `git merge` 
logic might make sense, where I would expect rebasing todo list `merge` 
command to pick-up tricks from `git merge` as needed, like learning 
to accept `--no-ff` option, for example, thus not fast-forwarding 
merges (on request) even when possible.

Though, I do agree that in case you want to recreate an existing merge 
(instead of just rebasing it), `merge` command fast-forwarding might 
probably not be what you want for the most of the time, but I`m afraid 
having rebase todo list `merge` command default behavior different than 
`git merge` default one (in regards to fast-forwarding) would be 
confusing... or not?

From what I could grasp so far, usually Git commands` default 
behavior is (explained to be) chosen per "most common use case", so 
might be non fast-forwarding would be fine as default for rebase todo 
list `merge` command, even though different than `git merge` itself...?

Regards, Buga

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-19 21:35                                                 ` Igor Djordjevic
@ 2018-03-20 14:43                                                   ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-20 14:43 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Johannes Schindelin, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:

> Hi Sergey,
>
> On 19/03/2018 06:44, Sergey Organov wrote:
>> 
>> > > > > > Second side note: if we can fast-forward, currently we prefer
>> > > > > > that, and I think we should keep that behavior with -R, too.
>> > > > >
>> > > > > I agree.
>> > > >
>> > > > I'm admittedly somewhat lost in the discussion, but are you
>> > > > talking fast-forward on _rebasing_ existing merge? Where would it
>> > > > go in any of the suggested algorithms of rebasing and why?
>> > > >
>> > > > I readily see how it can break merges. E.g., any "git merge
>> > > > --ff-only --no-ff" merge will magically disappear. So, even if
>> > > > somehow supported, fast-forward should not be performed by default
>> > > > during _rebasing_ of a merge.
>> > >
>> > > Hmm, now that you brought this up, I can only agree, of course.
>> > >
>> > > What I had in my mind was more similar to "no-rebase-cousins", like 
>> > > if we can get away without actually rebasing the merge but still 
>> > > using the original one, do it. But I guess that`s not what Johannes 
>> > > originally asked about.
>> > >
>> > > This is another definitive difference between rebasing (`pick`?) and 
>> > > recreating (`merge`) a merge commit - in the case where we`re rebasing, 
>> > > of course it doesn`t make sense to drop commit this time (due to 
>> > > fast-forward). This does make sense in recreating the merge (only).
>> >
>> > Eh, I might take this back. I think my original interpretation (and 
>> > agreement) to fast-forwarding is correct.
>> >
>> > But the confusion here comes from `--no-ff` as used for merging, as 
>> > opposed to `--no-ff` as used for rebasing. I _think_ Johannes meant 
>> > the latter one.
>> >
>> > In rebasing, `--no-ff` means that even if a commit inside todo list 
>> > isn`t to be changed, do not reuse it but create a new one. Here`s 
>> > excerpt from the docs[1]:
>> >
>> >   --no-ff
>> >     With --interactive, cherry-pick all rebased commits instead of 
>> >     fast-forwarding over the unchanged ones. This ensures that the 
>> >     entire history of the rebased branch is composed of new commits.
>> >
>> >     Without --interactive, this is a synonym for --force-rebase.
>> >
>> >
>> > So fast-forwarding in case of rebasing (merge commits as well) is 
>> > something you would want by default, as it wouldn`t drop/lose 
>> > anything, but merely reuse existing commit (if unchanged), instead of 
>> > cherry-picking (rebasing) it into a new (merge) commit anyway.
>> 
>> This sounds like breakage. E.g., it seems to be breaking every "-x ours"
>> merge out there.
>
> Either you are not understanding how rebase fast-forward works, or 
> I`m missing what you are pointing to... Mind explaining how can 
> something that`s left unchanged suddenly become a breakage?

It was misunderstanding on my side indeed, sorry.

>
>> Fast-forwarding existing merge, one way or another, still seems to be
>> wrong idea to me, as merge commit is not only about content change, but
>> also about joint point at particular place in the DAG.
>
> Not sure what this has to do with rebase fast-forwarding, either - 
> nothing changes for fast-forwarded (merge or non-merge) commit in 
> question, both content, joint point and everything else stays exactly 
> the same. If anything changed, then it can`t/won`t be fast-forwarded, 
> being unchanged is a prerequisite.
>
> Let me elaborate a bit. Here`s a starting diagram:

[... detailed explanation skipped for brevity ...]

> Does this settle your concerns, or I`m missing something?

Yes, it does, thank you! Leaving as many leading commits as possible
unchanged during rebase is what fast-forward mean in this case then, and
it's pretty OK with me.

>> As for fast-forwarding re-merge, explicitly requested, I'm not sure. On
>> one hand, it's inline with the default "git merge" behavior, on the
>> other hand, it still feels wrong, somehow.
>
> Regarding fast-forwarding in context of merging, in case where we are 
> recreating merges (not rebasing them), following existing `git merge` 
> logic might make sense, where I would expect rebasing todo list `merge` 
> command to pick-up tricks from `git merge` as needed, like learning 
> to accept `--no-ff` option, for example, thus not fast-forwarding 
> merges (on request) even when possible.
>
> Though, I do agree that in case you want to recreate an existing merge 
> (instead of just rebasing it), `merge` command fast-forwarding might 
> probably not be what you want for the most of the time, but I`m afraid 
> having rebase todo list `merge` command default behavior different than 
> `git merge` default one (in regards to fast-forwarding) would be 
> confusing... or not?
>
> From what I could grasp so far, usually Git commands` default 
> behavior is (explained to be) chosen per "most common use case", so 
> might be non fast-forwarding would be fine as default for rebase todo 
> list `merge` command, even though different than `git merge`
> itself...?

As far as I can tell, fast-forward as default for "git merge" has been
chosen to avoid excessive unintended merges in typical workflows, and
therefore this decision is not actually applicable to rebasing, I think.
I'm inclined to disable merges to fast-forward during history rebasing,
at least by default.

As far as I can tell, current "--preserve-merges" does fast-forward and
breaks "git merge --ff-only --no-ff" merges, among other things it
breaks, and that was my primary concern here. Didn't check
what "--recreate-merges" does though.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 12:05               ` Sergey Organov
@ 2018-03-26 11:33                 ` Johannes Schindelin
  2018-03-27  5:34                   ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 11:33 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Sergey,

On Mon, 12 Mar 2018, Sergey Organov wrote:

> [...]
> 
> Yet another consequence is that my approach will likely result in better
> code reuse.

This is a purely academic speculation. At least until somebody implements
Phillip's method. Oh wait, I already started to implement it, and it was
not exactly hard to implement:

https://github.com/dscho/git/commit/26d2858800a4e0d3cc6313ddb54dd4d2ce516f31

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 12:31                                     ` Sergey Organov
@ 2018-03-26 11:37                                       ` Johannes Schindelin
  2018-03-27  5:11                                         ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 11:37 UTC (permalink / raw)
  To: Sergey Organov
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Mon, 12 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >
> > On Wed, 7 Mar 2018, Sergey Organov wrote:
> >
> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >> 
> >> > 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?
> >
> > Imagine a todo list that contains this line
> >
> > 	merge -C abcdef 123456
> >
> > and now the user edits it (this is an interactive rebase, after all),
> > adding another merge head:
> >
> > 	merge -C abcdef 987654 123456
> >
> > Now your strategy would have a serious problem: to find the original
> > version of 987654. If there was one.
> 
> We are talking about different checks then. My method has a built-in
> check that Pillip's one doesn't.

Since you did not bother to elaborate, I have to assume that your
"built-in check" is that thing where intermediate merges can give you
conflicts?

If so, there is a possibility in Phillip's method for such conflicts, too:
we have to perform as many 3-way merges as there are parent commits.

It does make me uncomfortable to have to speculate what you meant, though.

> >> > 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.
> >
> > But I already pointed out how that would undo a commit having been
> > dropped.
> 
> No. Not for this version. You did it for originally flawed version of
> the method, that has been already fixed by addition of "using original
> merge commit as merge base" in the above sentence, and that was the
> exact reason for [RFC v2], that in turn is explicitly stated at the
> beginning of [RFC v2].

Since there is no "interdiff" between your versions, it is unreasonably
hard to deduce this, and since you seem to be unwilling to explain... I'll
just leave it at that.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 12:42             ` Sergey Organov
@ 2018-03-26 11:50               ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 11:50 UTC (permalink / raw)
  To: Sergey Organov
  Cc: git, Johannes Sixt, Junio C Hamano, Jacob Keller, Igor Djordjevic,
	Phillip Wood

Hi Sergey,

On Mon, 12 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> > Hi Sergey,
> 
> [...]
> 
> > That is misrepresenting what happened.
> 
> No, it's you who are spreading misinformation, probably unintentional,
> but still.

Way to go, Sergey. Way to go.

> [... more of the same...]
>
> > Let's focus on that strategy rather than going back to the strategy
> > which has known flaws and only an unsatisfyingly complex explanation.
> 
> Not that fast, as it now has no known flaws and still has surprisingly
> simple explanation. It also has its own niceties that are currently
> being discussed elsewhere in the thread.

I find it surprisingly complicated. Of course, that may be just me, but I
am not exactly a noob when it comes to interactive rebases.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-13  0:01                                                     ` Igor Djordjevic
@ 2018-03-26 12:03                                                       ` Johannes Schindelin
  2018-03-27  5:08                                                         ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 12:03 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Tue, 13 Mar 2018, Igor Djordjevic wrote:

> On 12/03/2018 13:56, Sergey Organov wrote:
> > 
> > > > I agree with both of you that `pick <merge-commit>` is inflexible
> > > > (not to say just plain wrong), but I never thought about it like
> > > > that.
> > > >
> > > > If we are to extract further mentioned explicit old:new merge
> > > > parameter mapping to a separate discussion point, what we`re
> > > > eventually left with is just replacing this:
> > > >
> > > > 	merge -R -C <original--merge-commit> <merge-head>
> > > >
> > > > ... with this:
> > > >
> > > > 	pick <original--merge-commit> <merge-head>
> > >
> > > I see where you are coming from.
> > >
> > > I also see where users will be coming from. Reading a todo list in
> > > the editor is as much documentation as it is a "program to execute".
> > > And I am afraid that reading a command without even mentioning the
> > > term "merge" once is pretty misleading in this setting.
> > >
> > > And even from the theoretical point of view: cherry-picking
> > > non-merge commits is *so much different* from "rebasing merge
> > > commits" as discussed here, so much so that using the same command
> > > would be even more misleading.
> > 
> > This last statement is plain wrong when applied to the method in the
> > [RFC] you are replying to.

That is only because the RFC seems to go out of its way to break down a
single merge commit into as many commits as there are merge commit
parents.

This is a pretty convoluted way to think about it: if you have three
parent commits, for example, that way of thinking would introduce three
intermediate commits, one with the changes of parent 2 & 3 combined, one
with the changes of parent 1 & 3 combined, and one with the changes of
parent 1 & 2 combined.

To rebase those commits, you essentially have to rebase *every parent's
changes twice*.

It gets worse with merge commits that have 4 parents. In that case, you
have to rebase every parent's changes *three times*.

And so on.

> > Using the method in [RFC], "cherry-pick non-merge" is nothing more or
> > less than reduced version of generic "cherry-pick merge", exactly as
> > it should be.

I really get the impression that you reject Phillip's proposal on the
ground of not being yours. In other words, the purpose of this here
argument is to praise one proposal because of its heritage, rather than
trying to come up with the best solution.

On that basis, I will go with the proposal that is clearly the simplest
and does the job and gets away with avoiding unnecessary work.

> > Or, in other words, "cherry-pick merge" is generalization of
> > "cherry-pick non-merge" to multiple parents.
> 
> I think Sergey does have a point here, his approach showing it.

His approach is showing that he wants to shoehorn the "rebase a merge
commit" idea into a form where you can cherry-pick *something*.

It does not have to make sense. And to me, it really does not.

> Phillip`s simplification might be further from it, though, but we`re 
> talking implementation again - important mental model should just be 
> "rebasing a commit" (merge or non-merge), how we`re doing it is 
> irrelevant for the user, the point (goal) is the same.

Except that Phillip's simplification is not a simplification. It comes
from a different point of view: trying to reconcile the diverging changes.

Phillip's is a true generalization of the "rebase vs merge" story: it is
no longer about merging, or about rebasing, but about reconciling
divergent commit histories, with whatever tool is appropriate.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-12 13:49                       ` Sergey Organov
@ 2018-03-26 12:44                         ` Johannes Schindelin
  2018-03-27  5:32                           ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 12:44 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Sergey,

On Mon, 12 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > [...]
> >
> > Where "easy" meant that I had to spend 1h still to figure out why
> > using the unrebased merge parents as merge bases.
> 
> That's because you try to figure out something that is not there in the
> [RFC v2]. I suggest to forget everything you've already imagined and
> just read the [RFC v2] proposal afresh. It should take about 10 minutes
> or less to get it. Really.
> 
> > The same amount of time did not allow me to wrap my head around
> > Sergey's verbose explanations.
> 
> Honestly, I don't believe it, sorry, but I'm willing to explain anything
> you wish to be explained in _[RFC v2]_.

No, really. If you cannot bring yourself to believe my words, then I hate
to break it to you: I am not lying.

As to "I'm willing to explain anything you wish to be explained in RFC
v2": I was asking, and asking, and asking again, for a simple summary of
the idea behind your proposal. Nothing. That was the answer.

I had to figure it out myself: the idea is to *create* fake commits,
non-merge ones, for every single merge commit parent. Those fake commits
combine the changes of *all* merge commit parents *but one*. And then
those commits are rebased, individually, with tons of opportunities for
merge conflicts. Repeated ones. And then that result is merged.

Except that there is something more convoluted going on because of that
dumb, annoying requirement that this also has to work interactively. Where
somebody like myself might have done something really annoying such as
dropping commits, or even amending them with changes that had not been in
the previous version of the merge commit parents.

So then, after doing a ton of work to rebase the original merge commit's
changes, we perform three-way merges with the already-rebased parents (or
actually, the new tips, because as I pointed out, the parent commit may
have been dropped or reordered) to *undo* those painfully rebased changes.

No matter how much you are married to RFC v2: it *does* do unnecessary
work, it *does* result in a *lot* more opportunity for merge conflicts,
and as a bonus: it even introduces the opportunity to come up with
two versions of the rebased merge commit that disagree with one another.

> > But I'll take your word for it that the strategies are equivalent, and
> > go with the one that has both a simpler explanation (in my mind, at
> > least), and an more robust implementation.
> 
> It's up to you, and it'd still be much better than what we have now, but
> you will need to face the (I think unfortunate) consequences I just
> summarized elsewhere in the thread.

No, it was not up to me. It was up to you to convince me (or for that
matter, anybody else on the Git mailing list), that your approaches are
essentially the same.

But they are not, as your RFC v2 includes a detour of unnecessary work.
Even if the end result is theoretically the same, *practically* your
approach forces a lot of work on the user, work that is often just thrown
away!

Let's take a concrete example. Like, an example that really came up. On
the Git mailing list. A tangible example that shapes my experience, and
more importantly: other Git users' experience as well.

I introduced a change to the funny construct `void *data = data;` that was
meant to fool GCC versions that could not figure out that data would not
be used uninitialized. While that shut up GCC, it upset other compilers
that now said that this new construct does not make sense. My approach was
to introduce the macro `FAKE_INIT(type, name, value)` which would be
expanded to `type name = name;` for GCC, and to `type name = (type)value`
for all other compilers.

This was a change I wanted to cook in Git for Windows for a couple of
iterations until I am sure it works as expected, also on non-Windows
platforms, and with other compilers than MSVC, GCC and Clang.

Recently Ramsay Jones spent the time to research this issue a lot deeper
than it was done before, and found out *which* GCC versions are affected,
and introduced a patch series that fixes this problem for real, *undoing*
the `void *data = data;` mess.

Obviously, this fix conflicts with my work-around.

Okay, good, so what would happen, hypothetically, if the Git garden shears
I use (and which you probably still haven't studied, even if I pointed you
to it several times, but expecting me to read RFC v2 at the same time
instead of answering my questions about it) were adjusted to use RFC v2 to
rebase merges?

To answer that, I first have to tell you that Git for Windows' branch
thicket consists of roughly 70 topic branches that are partially
criss-cross-merged. There are roughly 40 merge commits (44 if I counted
correctly) on the commit graph between HEAD and that work-around that
conflicts with Ramsay's fix.

So the first thing that would happen when rebasing the branch thicket is
this: I would encounter the merge conflict when my workaround is
cherry-picked, realize that my work-around is no longer necessary, and
call `git rebase --skip` and that's that.

The Git garden shears currently do not even try to rebase merge commits,
so that really would be that. I might encounter unrelated conflicts, but
nothing about the `void *data = data;` issue again.

With Phillip's approach, the same is true, as the 40+ merge commits would
be rebased with my work-around being undone by the first three-way merge,
the second three-way merge being unaffected (and no further 3-way merge
necessary because I do not do octopus merges in Git for Windows branch
thicket) and that's that.

(There is a chance, of course, that I misunderstood, or that I missed
something. The proof lies in the pudding. Somebody will have to try this,
and this somebody is probably me.)

With your RFC v2 approach, however, the original tips of all of those 40+
merges would be "cherry-picked" (via those intermediate fake commits).
Conflicting every single time. And of course the subsequent three-way
merge to undo the changes (because I dropped the change via `git rebase
--skip`) would conflict *again*.

That's an awful lot of merge conflicts. One might be tempted to suggest
complexifying the entire procedure by requiring `rerere`, and my initial
answer would be that this still conflicts when the context lines of the
merge conflict changed, but the truth is: this level of complexity, this
amount of merge conflicts, is not even necessary, as seen by looking at
Phillip's strategy.

This is what I referred to as "unecessary work". And to me, as a power
(read: frequent) user of the closest thing we have to --recreate-merges in
the wild, it is not funny. Not funny at all.

BTW this is the level of detail I would have wished your answers to my
repeated questions for clarification of your RFC v2 to be. And that is
what I expect in response to my valid questions in the future.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-13  0:16                                                 ` Igor Djordjevic
@ 2018-03-26 13:07                                                   ` Johannes Schindelin
  2018-03-27  5:51                                                     ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 13:07 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: phillip.wood, Jacob Keller, Sergey Organov, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Buga,

On Tue, 13 Mar 2018, Igor Djordjevic wrote:

> On 12/03/2018 11:46, Johannes Schindelin wrote:
> > 
> > > Sometimes one just needs to read the manual, and I don`t really
> > > think this is a ton complicated, but just something we didn`t really
> > > have before (real merge rebasing), so it requires a moment to grasp
> > > the concept.
> > 
> > If that were the case, we would not keep getting bug reports about
> > --preserve-merges failing to reorder patches.
> 
> Not sure where that is heading to, but what I`m arguing about is that 
> introducing new commands and concepts (`merge`, and with `-R`) just 
> makes the situation even worse (more stuff to grasp).

The problem with re-using `pick` is that its concept does not apply to
merges. The cherry-pick of a non-merge commit is well-defined: the current
HEAD is implicitly chosen as the cherry-picked commit's (single) parent
commit. There is no ambiguity here.

But for merge commits, we need to specify the parent commits (apart from
the first one) *explicitly*. There was no need for that in the `pick`
command, nor in the concept of a cherry-pick.

> Reusing existing concepts where possible doesn`t have this problem.

Existing concepts are great. As long as they fit the requirements of the
new scenarios. In this case, `pick` does *not* fit the requirement of
"rebase a merge commit".

If you really want to force the `pick` concept onto the use case where you
need to "reapply" merges, then the closest you get really is Sergey's
idea, which I came to reject when considering its practical implications.

Even so, you would have to make the `pick` command more complicated to
support merge commits. And whatever you would do to extend the `pick`
command would *not make any sense* to the current use case of the `pick`
command.

The real problem, of course, is that a non-merge commit, when viewed from
the perspective of the changes it introduced, is a very different beast
than a merge commit: it does not need to reconcile changes, ever, because
there is really only one "patch" to one revision. That is very different
from a merge commit, whose changes can even disagree with one another (and
in fact be resolved with changes disagreeing *yet again*)!

> > > Saying in favor of `--rebase-merges`, you mean as a separate option,
> > > alongside `--recreate-merges` (once that series lands)?
> > 
> > No. I am against yet another option. The only reason I pollute the
> > option name space further with --recreate-merges is that it would be
> > confusing to users if the new mode was called --preserve-merges=v2
> > (but work *totally differently*).
> 
> I see. So I take you`re thinking about renaming `--recreate-merges` to
> `--rebase-merges` instead?

Thinking about it. Nothing will happen before v2.17.0 on that front,
though, because -- unlike you gentle people -- I have to focus on
stabilizing Git's code base now.

> That would seem sensible, too, I think, being the default usage mode in
> the first place. Being able to actually (re)create merges, too, once
> user goes interactive, would be "just" an additional (nice and powerful)
> feature on top of it.

The implementation detail is, of course, that I will introduce this with
the technically-simpler strategy: always recreating merge commits with the
recursive strategy. A follow-up patch series will add support for rebasing
merge commits, and then use it by default.

This latter part will need a lot of experimentation, though. That's why I
want the --recreate-merges patch series cooking in `next` first.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-13 18:24                                     ` Junio C Hamano
@ 2018-03-26 13:17                                       ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 13:17 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Sergey Organov, Igor Djordjevic, phillip.wood,
	Git mailing list, Jacob Keller, Johannes Sixt

Hi Junio,

On Tue, 13 Mar 2018, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > If so, what tooling do you have to identify quickly what to
> > cherry-pick, given merge conflicts?
> 
> It exactly is the issue I've been trying to find ideal solution for
> quite a while and not successfully.  Here is a sample thread
> 
>   https://public-inbox.org/git/xmqqeft3u0u5.fsf@gitster.mtv.corp.google.com/#t
> 
> and every message I mention "merge-fix" is relevant.
>
> [... detailed explanation of the current "band-aid" system...]

Thank you for the very thorough account. I have been struggling with this,
too, even chatting to Michael Haggerty about this at the Contributors'
Summit (where I missed you a lot). He pointed me to a blog post of his
about the very interesting concept of "obsolete markers" in Hg:

http://softwareswirl.blogspot.de/2013/05/obsolete-markers-in-mercurial.html

Granted, that concept really makes most sense for rebase, but I wonder
whether the concept could be extended to help your (and my) use case of
frequently-changing targets (for me, it is more rebase targets, for you it
is merge targets). It would probably be implemented using commit notes, to
allow for bidirectional mappings.

Ciao,
Dscho
> 
> The current band-aid system punts and indexes the merge-fix changes
> by merely a branch name.  When refs/merge-fix/X exists, what it
> means is "When branch X is merged to an integration branch, it is
> likely that the integration branch _already_ has merged an unnamed
> topic that causes semantic conflicts and requires this fix-up".
> This needs occasional manual adjustment---e.g. when the topic X
> turns out to be a lot more stable than the other topic Y that was
> causing us trouble with semantic conflicts, I may at some point
> reorder the topics and have topic X advance to 'next' before topic Y
> does.  And when that happens, when I merge X to 'next', because Y is
> not yet in 'next', I shouldn't apply refs/merge-fix/X (often, an
> attempt to cherry-pick it on top of a merge of X into 'next' would
> fail, which would be a bit of safety, but not always).  What I
> should do instead is to rename refs/merge-fix/X to refs/merge-fix/Y
> immediately before merging X to 'next', so that the cherry-pick is
> not applied.  When rebuilding 'master'->'jch'->'pu' chain, X (now in
> 'next') will be merged before Y (not in 'next') gets merged, and
> when it is Y's turn to be merged, the merge-fix I used to apply when
> merging topic X will be applied.
> 
> In the ideal world (I think I'm repeating the ideas raised in the
> thread quoted), the merge-fix database should be indexed with a pair
> of commit object names (e.g. a step in branch X that adds a new
> callsite for function frotz() and a step in branch Y that changes
> the function signature of frotz()), and teach the system to
> cherry-pick refs/merge-fix/A-B to resolve semantic conflicts, when
> both commits A and B appears in the integration branch for the first
> time.  And make sure these are kept up-to-date across rebasing of
> commits A and B.  After rebasing the topics X and Y that contained
> the commits A and B, if they became C and D, the system somehow
> needs to be able to locate the previous merge-fix that was valid for
> A-B pair when C-D pair gets merged.
> 
> 

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-14 14:24                                         ` Sergey Organov
  2018-03-14 23:11                                           ` Igor Djordjevic
@ 2018-03-26 13:47                                           ` Johannes Schindelin
  1 sibling, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 13:47 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, Phillip Wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Wed, 14 Mar 2018, Sergey Organov wrote:

> Igor Djordjevic <igor.d.djordjevic@gmail.com> writes:
> 
> > On 07/03/2018 08:26, Johannes Schindelin wrote:
> 
> [...]
> 
> >> Second side note: if we can fast-forward, currently we prefer that,
> >> and I think we should keep that behavior with -R, too.
> >
> > I agree.
> 
> I'm admittedly somewhat lost in the discussion,

... and cutting so much context does not help...

The "fast-forward" here refers to the thing `git rebase` does unless you
call it with `--force-rebase`: if we are about to `pick` a commit, and
HEAD already points to its parent commit, we do not evey cherry-pick: we
just fast-forward to the original commit.

Likewise, `merge -C <commit>` will simply fast-forward to the specified
commit if HEAD is pointing to its first parent and the specified merge
heads are also identical to the respective original parent commits.

What I said with my comment was that `merge -R -C <commit>` will behave in
exactly the same way.

> but are you talking fast-forward on _rebasing_ existing merge? Where
> would it go in any of the suggested algorithms of rebasing and why?

And this is something I did not talk about yet, but it does come up in
practice: the main use case of rebasing branches is to follow an
"upstream", of course. And guess what? From time to time, some of the
branches get merged upstream (or, in Git's own source code, applied). In
this case, the Git garden shears *do* skip the merge (as the new merge
head is already an ancestor of HEAD). In --recreate-merges, it *will*
create a new merge commit, though, which is a bit annoying.

The best way to handle this would *probably* look similar to how "empty"
commits (i.e. commits whose patch is empty) are handled by rebase. But it
is not yet clear to me how that would look in practice, as `pick <commit>`
is a single operation that can be easily commented out in the todo list
depending on `--allow-empty`, while the `merge -C <commit>` command is not
the entire operation, as there may be `pick` commands in the merge head
that could potentially be skipped due to merge conflicts with
already-applied versions of the same patches.

If this sounds unclear to you, please do ask for clarification. Although
this is currently not my highest priority in the `sequencer-shears` branch
thicket (where `--recreate-merges` is the first part).

> I readily see how it can break merges. E.g., any "git merge --ff-only
> --no-ff" merge will magically disappear.

Did you mean `git merge --no-ff`? Combining `--ff-only` with `--no-ff`
does not make sense.

> So, even if somehow supported, fast-forward should not be performed by
> default during _rebasing_ of a merge.

That statement is too general to be correct.

*If* we detect that the original merge could have been fast-forwarded
instead (and it is very easy to detect that in --make-list), we would have
to handle that similar to afore-mentioned empty commits in conjunction
with `--allow-empty`.

If the original merge could not have been fast-forwarded, but during the
rebase it *can*, we should skip it, as the reason for the merge commit
is clearly no longer there.

This, by the way, is an insight you can really only win by using
--recreate-merges (or the Git garden shears). And using it a *lot*.
Because otherwise, you will not even guess correctly what will, and what
won't, come up in practice.

> >> If the user wants to force a new merge, they simply remove that -R
> >> flag.
> 
> Alternatively, they'd replace 'pick' with 'merge', as they already do
> for other actions. "A plurality is not to be posited without necessity".

No, no, no and no again!

We learned how *wrong* the `pick` approach was with --preserve-merges!
Have you *ever* used --preserve-merges in any real way? If so, you *have*
encountered the problems, and probably directed several lengthy curses my
way for that lousy design.

A pick is a pick is a pick. Of a single patch with metadata such as the
author, commit message and the date.

A merge is not a patch. While a pick introduces a specific change on top
of a single revision, the changes introduced by a merge are ideally
already there. It is conceptually *very* different.

Sure, a merge commit sometimes needs to introduce extra changes on top of
the merged changes, sure, that happens (and is called "evil merge" in Git
parlance). Those *additional* changes should be kept as minimal as
possible, in particular there should only be changes *necessitated* by the
reconciled changes.

So no, a `pick` is not a `merge`. Not at all.

> Please, _please_, don't use 'merge' command to 'pick' merge commits!
> It's utterly confusing!

Please, please, please *work* with branch thickets for a while. And you
will see that it makes a *huge* difference!

For example, within a block of `pick` lines, it is relatively safe to
reorder. You see more or less what changes interact with one another.

Be *very* careful when reordering `merge` lines, in particular when you
have a real-world branch thicket, not just a toy example.

Do *not* use `pick` for merges. Not. Ever.

By the way, let this here discussion serve as Yet Another Example why it
is important to not only consider new features theoretically. Only in
practice do you find out where your theory sounded too good to be actually
true.

> Thinking about it I've got an idea that what we actually need is
> --no-flatten flag that, when used alone, will just tell "git rebase" to
> stop flattening history, and which will be implicitly imposed by
> --recreate-merges (and --preserve-merges).

... and this flag would only make sense if every `git rebase` involved a
todo list.

Which it does not.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-13  0:29                       ` Igor Djordjevic
@ 2018-03-26 13:58                         ` Johannes Schindelin
  0 siblings, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 13:58 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Buga,

On Tue, 13 Mar 2018, Igor Djordjevic wrote:

> On 12/03/2018 11:20, Johannes Schindelin wrote:
> > 
> > > > [...] and cannot introduce ambiguities when rebasing the
> > > > changes introduced by M (i.e. the "amendmendts" we talked about).
> > >
> > > Hmm, not following here, which ambiguities are we talking about?
> > 
> > U1' vs U2' of course. Those are two things that can be different, even if
> > they ideally would have identical trees.
> > 
> > Phillip's strategy does not leave that room for ambiguity.
> 
> Ehm, in Sergey`s approach, this is not an issue, but a feature :)

Well, in my use cases, this would not be a good feature. It would be
highly annoying, confusing, and cost me tons of time.

> If U1' != U2', it just means a more complex rebase happened, but it 
> doesn`t compromise the result (rebased merge) in any way.

No, it just means that your strategy failed to give a consistent answer to
the question "what would the rebased merge commit's tree look like".

> On the other hand, if U1' == U2', we can be pretty sure that merge
> rebasing went as clean as possible.

With the backsplanation I gave for Phillip's strategy, I can be as sure
that rebasing went as clean as possible if it does not produce merge
conflicts: it reconciles the changes introduced by 1) rebasing the merge
tips with the changes introduced by 2) the original merge commit relative
to its parents.

And even if it produces merge conflicts, I know at least that those are
conflicts between those two sets of changes.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-15 23:08                                 ` Igor Djordjevic
  2018-03-16  7:31                                   ` Sergey Organov
@ 2018-03-26 14:11                                   ` Johannes Schindelin
  1 sibling, 0 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-26 14:11 UTC (permalink / raw)
  To: Igor Djordjevic
  Cc: Sergey Organov, Phillip Wood, phillip.wood, Git mailing list,
	Jacob Keller, Johannes Sixt, Junio C Hamano

Hi Buga,

On Fri, 16 Mar 2018, Igor Djordjevic wrote:

> [...]
>
> Yes, having more steps would mean more power/options to the user, but
> more complexity to explain to and guide him through as well, not really
> sure where the line should be drawn - for the first time, at least.

If you want to avoid having a huge discussion with me about bias, male
privilege and how unaware most men are of it, and how it excludes half the
potential usership/talented developers, and how representation matters --
and believe me, you do want to avoid this discussion -- you will want to
avoid referring to the user as a "he".

It might be true in your case. But it is also true in your case that you
are Russian. Yet you write English here, probably to avoid excluding
people from the discussion. And you should demonstrate the same courtesy
to people who do not happen to identify with the same gender as you do.

In short: every time you think of a user or a developer as "he", take a
step back and reflect how excluded *you* would feel if someone forced you
to change that to "she". That is exactly how much you exclude non-males if
you think of them as "he". Just don't.

The remedy is easy: use the gender-neutral "they". Which is, by the way,
not a modern invention as of late (in contrast to the "he" you use):
https://en.wikipedia.org/wiki/Singular_they#Older_usage

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-26 12:03                                                       ` Johannes Schindelin
@ 2018-03-27  5:08                                                         ` Sergey Organov
  2018-03-27 13:35                                                           ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-27  5:08 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Buga,
>
> On Tue, 13 Mar 2018, Igor Djordjevic wrote:
>
>> On 12/03/2018 13:56, Sergey Organov wrote:
>> > 
>> > > > I agree with both of you that `pick <merge-commit>` is inflexible
>> > > > (not to say just plain wrong), but I never thought about it like
>> > > > that.
>> > > >
>> > > > If we are to extract further mentioned explicit old:new merge
>> > > > parameter mapping to a separate discussion point, what we`re
>> > > > eventually left with is just replacing this:
>> > > >
>> > > > 	merge -R -C <original--merge-commit> <merge-head>
>> > > >
>> > > > ... with this:
>> > > >
>> > > > 	pick <original--merge-commit> <merge-head>
>> > >
>> > > I see where you are coming from.
>> > >
>> > > I also see where users will be coming from. Reading a todo list in
>> > > the editor is as much documentation as it is a "program to execute".
>> > > And I am afraid that reading a command without even mentioning the
>> > > term "merge" once is pretty misleading in this setting.
>> > >
>> > > And even from the theoretical point of view: cherry-picking
>> > > non-merge commits is *so much different* from "rebasing merge
>> > > commits" as discussed here, so much so that using the same command
>> > > would be even more misleading.
>> > 
>> > This last statement is plain wrong when applied to the method in the
>> > [RFC] you are replying to.
>
> That is only because the RFC seems to go out of its way to break down a
> single merge commit into as many commits as there are merge commit
> parents.

Complex entity is being split for ease of reasoning. People tend to use
this often.

> This is a pretty convoluted way to think about it: if you have three
> parent commits, for example, that way of thinking would introduce three
> intermediate commits, one with the changes of parent 2 & 3 combined, one
> with the changes of parent 1 & 3 combined, and one with the changes of
> parent 1 & 2 combined.

No.

> To rebase those commits, you essentially have to rebase *every parent's
> changes twice*.

No.

> It gets worse with merge commits that have 4 parents. In that case, you
> have to rebase every parent's changes *three times*.

Sorry, the [RFC] has nothing of the above. Once again, it's still just
as simple is: rebase every side of the merge then merge the results
using the original merge commit as a merge base.

And if you can't or don't want to grok the explanation in the RFC, just
forget the explanation, no problem.

> And so on.
>
>> > Using the method in [RFC], "cherry-pick non-merge" is nothing more or
>> > less than reduced version of generic "cherry-pick merge", exactly as
>> > it should be.
>
> I really get the impression that you reject Phillip's proposal on the
> ground of not being yours. In other words, the purpose of this here
> argument is to praise one proposal because of its heritage, rather than
> trying to come up with the best solution.

No. As the discussion evolved, I inclined to conclusion that modified
Phillip's algorithm is actually better suited for the implementation
[1].

> On that basis, I will go with the proposal that is clearly the simplest
> and does the job and gets away with avoiding unnecessary work.

These algorithms are actually the same one, as has already been shown
elsewhere in the discussion. Asymmetric incremental nature of the
Phillip's one is apparently better suited for naturally asymmetrical way
Git already handles merging. FYI, here is the latest proposal that came
out of discussion [1]:

git-rebase-first-parent --onto A' M
tree_U1'=$(git write-tree)
git merge-recursive B -- $tree_U1' B'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
[ $conflicted_last_merge = "yes" ] ||
  trees-match $tree_U1' $tree || 
  stop-for-user-amendment

where 'git-rebase-first-parent' denotes whatever machinery is currently
being used to rebase simple non-merge commit.

>
>> > Or, in other words, "cherry-pick merge" is generalization of
>> > "cherry-pick non-merge" to multiple parents.
>> 
>> I think Sergey does have a point here, his approach showing it.
>
> His approach is showing that he wants to shoehorn the "rebase a merge
> commit" idea into a form where you can cherry-pick *something*.
>
> It does not have to make sense. And to me, it really does not.

Except that Phillip's one does exactly this as well, only in incremental
manner, as shown in [1].

>
>> Phillip`s simplification might be further from it, though, but we`re 
>> talking implementation again - important mental model should just be 
>> "rebasing a commit" (merge or non-merge), how we`re doing it is 
>> irrelevant for the user, the point (goal) is the same.
>
> Except that Phillip's simplification is not a simplification. It comes
> from a different point of view: trying to reconcile the diverging
> changes.

They are essentially the same as one easily converts to another and back
[1]. They will only bring different user experience in case of
conflicts.

> Phillip's is a true generalization of the "rebase vs merge" story: it is
> no longer about merging, or about rebasing, but about reconciling
> divergent commit histories, with whatever tool is appropriate.

Whatever. They are essentially the same thing. The only difference is
incremental vs parallel [1].

References:

[1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com/

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-26 11:37                                       ` Johannes Schindelin
@ 2018-03-27  5:11                                         ` Sergey Organov
  2018-03-27 12:55                                           ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-27  5:11 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Mon, 12 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> >
>> > On Wed, 7 Mar 2018, Sergey Organov wrote:
>> >
>> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> >> 
>> >> > 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?
>> >
>> > Imagine a todo list that contains this line
>> >
>> > 	merge -C abcdef 123456
>> >
>> > and now the user edits it (this is an interactive rebase, after all),
>> > adding another merge head:
>> >
>> > 	merge -C abcdef 987654 123456
>> >
>> > Now your strategy would have a serious problem: to find the original
>> > version of 987654. If there was one.
>> 
>> We are talking about different checks then. My method has a built-in
>> check that Pillip's one doesn't.
>
> Since you did not bother to elaborate, I have to assume that your
> "built-in check" is that thing where intermediate merges can give you
> conflicts?
>
> If so, there is a possibility in Phillip's method for such conflicts, too:
> we have to perform as many 3-way merges as there are parent commits.
>
> It does make me uncomfortable to have to speculate what you meant,
> though.

It doesn't matter anymore as this check could easily be added to
Phillip's algorithm as well, see [1].

[1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-26 12:44                         ` Johannes Schindelin
@ 2018-03-27  5:32                           ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-27  5:32 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Dear Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Mon, 12 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > [...]
>> >
>> > Where "easy" meant that I had to spend 1h still to figure out why
>> > using the unrebased merge parents as merge bases.
>> 
>> That's because you try to figure out something that is not there in the
>> [RFC v2]. I suggest to forget everything you've already imagined and
>> just read the [RFC v2] proposal afresh. It should take about 10 minutes
>> or less to get it. Really.
>> 
>> > The same amount of time did not allow me to wrap my head around
>> > Sergey's verbose explanations.
>> 
>> Honestly, I don't believe it, sorry, but I'm willing to explain anything
>> you wish to be explained in _[RFC v2]_.
>
> No, really. If you cannot bring yourself to believe my words, then I hate
> to break it to you: I am not lying.
>
> As to "I'm willing to explain anything you wish to be explained in RFC
> v2": I was asking, and asking, and asking again, for a simple summary of
> the idea behind your proposal. Nothing. That was the answer.

No. The answer rather was this simple explanation that I gave you
multiple times already "rebase each side of the merge, then merge the
results back using original merge commit as the merge base". Yet you say
there was none. I'm confused.

Well, as it seems you grok Phillip's notation just fine, here is RFC
algorithm in this notation [1]:

git checkout --detach A'
git merge-recursive A -- A' M
tree_U1'=$(git write-tree)
git checkout --detach B'
git merge-recursive B -- B' M
tree_U2'=$(git write-tree)
git merge-recursive M -- $tree_U1' $tree_U2'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')

> I had to figure it out myself: the idea is to *create* fake commits,
> non-merge ones, for every single merge commit parent. Those fake commits
> combine the changes of *all* merge commit parents *but one*. And then
> those commits are rebased, individually, with tons of opportunities for
> merge conflicts. Repeated ones. And then that result is merged.

Wrong. See above.

Anyway, it doesn't matter anymore, see [1].

References:

[1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC v2] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-26 11:33                 ` Johannes Schindelin
@ 2018-03-27  5:34                   ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-27  5:34 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, git, Johannes Sixt, Junio C Hamano, Jacob Keller,
	Phillip Wood

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Mon, 12 Mar 2018, Sergey Organov wrote:
>
>> [...]
>> 
>> Yet another consequence is that my approach will likely result in better
>> code reuse.
>
> This is a purely academic speculation. At least until somebody implements
> Phillip's method. Oh wait, I already started to implement it, and it was
> not exactly hard to implement:
>
> https://github.com/dscho/git/commit/26d2858800a4e0d3cc6313ddb54dd4d2ce516f31

Nice! Please see [1] for some recent relevant discussion.

[1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com/

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-26 13:07                                                   ` Johannes Schindelin
@ 2018-03-27  5:51                                                     ` Sergey Organov
  2018-03-27 13:49                                                       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-27  5:51 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Buga,
>
> On Tue, 13 Mar 2018, Igor Djordjevic wrote:
>
>> On 12/03/2018 11:46, Johannes Schindelin wrote:
>> > 
>> > > Sometimes one just needs to read the manual, and I don`t really
>> > > think this is a ton complicated, but just something we didn`t really
>> > > have before (real merge rebasing), so it requires a moment to grasp
>> > > the concept.
>> > 
>> > If that were the case, we would not keep getting bug reports about
>> > --preserve-merges failing to reorder patches.
>> 
>> Not sure where that is heading to, but what I`m arguing about is that 
>> introducing new commands and concepts (`merge`, and with `-R`) just 
>> makes the situation even worse (more stuff to grasp).
>
> The problem with re-using `pick` is that its concept does not apply to
> merges. The cherry-pick of a non-merge commit is well-defined: the current
> HEAD is implicitly chosen as the cherry-picked commit's (single) parent
> commit. There is no ambiguity here.
>
> But for merge commits, we need to specify the parent commits (apart from
> the first one) *explicitly*. There was no need for that in the `pick`
> command, nor in the concept of a cherry-pick.
>
>> Reusing existing concepts where possible doesn`t have this problem.
>
> Existing concepts are great. As long as they fit the requirements of the
> new scenarios. In this case, `pick` does *not* fit the requirement of
> "rebase a merge commit".

It does, provided you use suitable syntax.

> If you really want to force the `pick` concept onto the use case where
> you need to "reapply" merges, then the closest you get really is
> Sergey's idea, which I came to reject when considering its practical
> implications.

Which one, and what are the implications that are bad, I wonder?

> Even so, you would have to make the `pick` command more complicated to
> support merge commits. And whatever you would do to extend the `pick`
> command would *not make any sense* to the current use case of the `pick`
> command.

It would rather make a lot of sense. Please don't use 'merge' to pick
commits, merge ones or not!

> The real problem, of course, is that a non-merge commit, when viewed from
> the perspective of the changes it introduced, is a very different beast
> than a merge commit: it does not need to reconcile changes, ever, because
> there is really only one "patch" to one revision. That is very different
> from a merge commit, whose changes can even disagree with one another (and
> in fact be resolved with changes disagreeing *yet again*)!

You'd still 'pick' it though, not 'merge'. You don't merge "merge
commit", it makes no sense. It only makes perfect sense when you get rid
of original "merge commit" and re-merge from scratch, as you were doing
till now.

>> > > Saying in favor of `--rebase-merges`, you mean as a separate option,
>> > > alongside `--recreate-merges` (once that series lands)?
>> > 
>> > No. I am against yet another option. The only reason I pollute the
>> > option name space further with --recreate-merges is that it would be
>> > confusing to users if the new mode was called --preserve-merges=v2
>> > (but work *totally differently*).
>> 
>> I see. So I take you`re thinking about renaming `--recreate-merges` to
>> `--rebase-merges` instead?
>
> Thinking about it. Nothing will happen before v2.17.0 on that front,
> though, because -- unlike you gentle people -- I have to focus on
> stabilizing Git's code base now.
>
>> That would seem sensible, too, I think, being the default usage mode in
>> the first place. Being able to actually (re)create merges, too, once
>> user goes interactive, would be "just" an additional (nice and powerful)
>> feature on top of it.
>
> The implementation detail is, of course, that I will introduce this with
> the technically-simpler strategy: always recreating merge commits with the
> recursive strategy. A follow-up patch series will add support for rebasing
> merge commits, and then use it by default.

Switching to use it by default would be backward incompatible again? Yet
another option to obsolete? Sigh. 

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-27  5:11                                         ` Sergey Organov
@ 2018-03-27 12:55                                           ` Johannes Schindelin
  2018-03-28  4:32                                             ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-27 12:55 UTC (permalink / raw)
  To: Sergey Organov
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Tue, 27 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > On Mon, 12 Mar 2018, Sergey Organov wrote:
> >
> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >> >
> >> > On Wed, 7 Mar 2018, Sergey Organov wrote:
> >> >
> >> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >> >> 
> >> >> > 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?
> >> >
> >> > Imagine a todo list that contains this line
> >> >
> >> > 	merge -C abcdef 123456
> >> >
> >> > and now the user edits it (this is an interactive rebase, after
> >> > all), adding another merge head:
> >> >
> >> > 	merge -C abcdef 987654 123456
> >> >
> >> > Now your strategy would have a serious problem: to find the
> >> > original version of 987654. If there was one.
> >> 
> >> We are talking about different checks then. My method has a built-in
> >> check that Pillip's one doesn't.
> >
> > Since you did not bother to elaborate, I have to assume that your
> > "built-in check" is that thing where intermediate merges can give you
> > conflicts?
> >
> > If so, there is a possibility in Phillip's method for such conflicts,
> > too: we have to perform as many 3-way merges as there are parent
> > commits.
> >
> > It does make me uncomfortable to have to speculate what you meant,
> > though.
> 
> It doesn't matter anymore as this check could easily be added to
> Phillip's algorithm as well, see [1].
> 
> [1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com

Ah, and there I was, thinking that finally you would answer my questions
directly, instead you keep directing me elsewhere ("read that! Somewhere
in there you will find the answer you are looking for").

My time is a bit too valuable, and I will not continue a discussion where
my questions are constantly deflected that way.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-27  5:08                                                         ` Sergey Organov
@ 2018-03-27 13:35                                                           ` Johannes Schindelin
  2018-04-02  6:07                                                             ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-27 13:35 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Tue, 27 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >
> > On Tue, 13 Mar 2018, Igor Djordjevic wrote:
> >
> >> On 12/03/2018 13:56, Sergey Organov wrote:
> >> > 
> >> > > > I agree with both of you that `pick <merge-commit>` is inflexible
> >> > > > (not to say just plain wrong), but I never thought about it like
> >> > > > that.
> >> > > >
> >> > > > If we are to extract further mentioned explicit old:new merge
> >> > > > parameter mapping to a separate discussion point, what we`re
> >> > > > eventually left with is just replacing this:
> >> > > >
> >> > > > 	merge -R -C <original--merge-commit> <merge-head>
> >> > > >
> >> > > > ... with this:
> >> > > >
> >> > > > 	pick <original--merge-commit> <merge-head>
> >> > >
> >> > > I see where you are coming from.
> >> > >
> >> > > I also see where users will be coming from. Reading a todo list in
> >> > > the editor is as much documentation as it is a "program to execute".
> >> > > And I am afraid that reading a command without even mentioning the
> >> > > term "merge" once is pretty misleading in this setting.
> >> > >
> >> > > And even from the theoretical point of view: cherry-picking
> >> > > non-merge commits is *so much different* from "rebasing merge
> >> > > commits" as discussed here, so much so that using the same command
> >> > > would be even more misleading.
> >> > 
> >> > This last statement is plain wrong when applied to the method in the
> >> > [RFC] you are replying to.
> >
> > That is only because the RFC seems to go out of its way to break down a
> > single merge commit into as many commits as there are merge commit
> > parents.
> 
> Complex entity is being split for ease of reasoning. People tend to use
> this often.

Sure. Divide and conquer. Not duplicate and complicate, though.

> > This is a pretty convoluted way to think about it: if you have three
> > parent commits, for example, that way of thinking would introduce three
> > intermediate commits, one with the changes of parent 2 & 3 combined, one
> > with the changes of parent 1 & 3 combined, and one with the changes of
> > parent 1 & 2 combined.
> 
> No.

Sorry. This is unacceptable. If you disagree, sure, you are free to do
that. If you want to contribute to a fruitful discussion, just saying "No"
without explaining why you *think* that my statement is wrong is just...
unconstructive.

> > To rebase those commits, you essentially have to rebase *every
> > parent's changes twice*.
> 
> No.

Same here.

> > It gets worse with merge commits that have 4 parents. In that case, you
> > have to rebase every parent's changes *three times*.
> 
> Sorry, the [RFC] has nothing of the above. Once again, it's still just
> as simple is: rebase every side of the merge then merge the results
> using the original merge commit as a merge base.
> 
> And if you can't or don't want to grok the explanation in the RFC, just
> forget the explanation, no problem.

Your RFC talks about U1 and U2, for the two merge parents.

Obviously this strategy can be generalized to n parents. I thought you had
thought of that and simply did not bother to talk about it.

Sorry, my mistake. I should not assume so much.

> > And so on.
> >
> >> > Using the method in [RFC], "cherry-pick non-merge" is nothing more or
> >> > less than reduced version of generic "cherry-pick merge", exactly as
> >> > it should be.
> >
> > I really get the impression that you reject Phillip's proposal on the
> > ground of not being yours. In other words, the purpose of this here
> > argument is to praise one proposal because of its heritage, rather than
> > trying to come up with the best solution.
> 
> No. As the discussion evolved, I inclined to conclusion that modified
> Phillip's algorithm is actually better suited for the implementation
> [1].

Again a link.

If that's what you are looking for, I will throw a hundred links your way
and see how constructive a discussion you find that.

> > On that basis, I will go with the proposal that is clearly the simplest
> > and does the job and gets away with avoiding unnecessary work.
> 
> These algorithms are actually the same one, as has already been shown
> elsewhere in the discussion.

I disproved that already. My example showed that instead of reconciling
the diverging changes starting from the original merge parents, RFC v2
tries to rebase those parents first, and then use the original merge
commit as base of "diverging changes" that never started from that
original merge commit.

Essentially, where Phillip's strategy imitates a cherry-pick's 3-way
merge, your strategy tries to rebase the merge tips independently from the
user (who already rebased them, thank you very much), and then runs a
*revert*: while a cherry-pick uses the picked commit's parent as merge
base, a revert uses the to-be-reverted commit itself as merge base.

In short: Phillip's strategy is only equivalent to yours if you ignore the
fact that you perform unnecessary work only to undo it in the end.

> Asymmetric incremental nature of the Phillip's one is apparently better
> suited for naturally asymmetrical way Git already handles merging. FYI,
> here is the latest proposal that came out of discussion [1]:

And another link.

> git-rebase-first-parent --onto A' M
> tree_U1'=$(git write-tree)
> git merge-recursive B -- $tree_U1' B'
> tree=$(git write-tree)
> M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
> [ $conflicted_last_merge = "yes" ] ||
>   trees-match $tree_U1' $tree || 
>   stop-for-user-amendment

And a bunch of commands from which the reader is expected to deduce the
idea.

> >> > Or, in other words, "cherry-pick merge" is generalization of
> >> > "cherry-pick non-merge" to multiple parents.
> >> 
> >> I think Sergey does have a point here, his approach showing it.
> >
> > His approach is showing that he wants to shoehorn the "rebase a merge
> > commit" idea into a form where you can cherry-pick *something*.
> >
> > It does not have to make sense. And to me, it really does not.
> 
> Except that Phillip's one does exactly this as well, only in incremental
> manner, as shown in [1].

And yet another link.

> >> Phillip`s simplification might be further from it, though, but we`re 
> >> talking implementation again - important mental model should just be 
> >> "rebasing a commit" (merge or non-merge), how we`re doing it is 
> >> irrelevant for the user, the point (goal) is the same.
> >
> > Except that Phillip's simplification is not a simplification. It comes
> > from a different point of view: trying to reconcile the diverging
> > changes.
> 
> They are essentially the same as one easily converts to another and back
> [1].

Repeating this does not make it more true.

With your method, this branch structure:

- A - B
    \   \
      C - D

would be rebased by first cherry-picking B, then C, then doing it *again*
because you need to construct U1 (which is kind of C) and U2 (which is
kind of B) and rebase those, and if the user resolved merge conflicts
while rebasing B, those merge conflicts will have to be resolved *again*
when rebasing U2, and if the user dropped part of B, U2 will still have to
rebase them, and the final merge with the original D as merge base will
have to undo those changes.

Your strategy involves *a lot* more work, and *a lot* more opportunities
for merge conflicts, and it also allows for giving incongruent answers
to the question "what should the tree of the rebased merge commit look
like?".

> They will only bring different user experience in case of conflicts.

Oh yes, they do. The amount, to begin with.

> > Phillip's is a true generalization of the "rebase vs merge" story: it is
> > no longer about merging, or about rebasing, but about reconciling
> > divergent commit histories, with whatever tool is appropriate.
> 
> Whatever. They are essentially the same thing. The only difference is
> incremental vs parallel [1].

You know, if you promise to answer whatever questions I have, just simply
stop throwing around links. Answer my questions. To the point. Not
deflecting. This is getting ridiculous.

> [1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com/

I will allow myself the joke and answer the concerns you had in this mail
thusly:

https://public-inbox.org/git/nycvar.QRO.7.76.6.1803261405170.77@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz/

So that you cannot miss it: there is a really good, real-world example
showing why Phillip's strategy is so much more practical than yours,
proving that they are not equivalent (except in a very narrow, purely
theoretical sense that ignores the possibility of merge conflicts). Look
for "concrete example" in that mail.

Another most important thing from that mail is really, really important.
So I will stress it instead of expecting you to pick up on it, by
repeating it here:

	BTW this is the level of detail I would have wished your answers
	to my repeated questions for clarification of your RFC v2 to be.
	And that is what I expect in response to my valid questions in the
	future.

Instead, you chose to fling a link in my direction again.

And yes, I read your mail, and no, it does not clarify anything, or even
addresses my objections.

If you contest my understanding of your strategy (where I say that U1 is
essentially the changes of the *other* merged branch), you will have to do
a much better job at explaining yourself. "No" is definitely not adequate.

And don't promise to answer my questions if you plan only on throwing a
link at me. That is no good.

So to repeat my point (that you contested without any argument, by a rude
and totally unmeritedly terse "No"):

> > To rebase those commits, you essentially have to rebase *every
> > parent's changes twice*.
> 
> No.

In the parlance of your RFC v2, where you start with this history (which I
translated into the left-to-right notation that is used in pretty much all
of Git's own documentation about interactive rebases, which you apparently
either did not read, or chose *not* to imitate, creating yet another
unnecessary diversion):

- B1
     \
- B2 - M

You now insert U1 and U2 with trees identical to M:

- B1 - U1
          \
- B2 - U2 - M

So U1 is essentially B2 cherry-picked on top of B1, and U2 is essentially
B1 cherry-picked on top of B2.

These U1/U2 commits are now to be cherry-picked on top of the rebased B1'
and B2'. I spare you more diagrams, you get the idea.

Now, the changes in U1/U2 *are* the changes of the merge parents, that's
how they were constructed.

Since they repeat what B1 and B2 are about, and since B1'/B2' means they
are rebased, and since U1'/U2' are *also* rebased, but independently...

	...  you essentially have to rebase *every parent's changes twice*.

The answer "No" to this is... astonishing.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-27  5:51                                                     ` Sergey Organov
@ 2018-03-27 13:49                                                       ` Johannes Schindelin
  2018-03-28  5:57                                                         ` Sergey Organov
  2018-03-28  5:57                                                         ` Sergey Organov
  0 siblings, 2 replies; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-27 13:49 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Tue, 27 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > On Tue, 13 Mar 2018, Igor Djordjevic wrote:
> >
> >> On 12/03/2018 11:46, Johannes Schindelin wrote:
> >> > 
> >> > > Sometimes one just needs to read the manual, and I don`t really
> >> > > think this is a ton complicated, but just something we didn`t
> >> > > really have before (real merge rebasing), so it requires a moment
> >> > > to grasp the concept.
> >> > 
> >> > If that were the case, we would not keep getting bug reports about
> >> > --preserve-merges failing to reorder patches.
> >> 
> >> Not sure where that is heading to, but what I`m arguing about is that
> >> introducing new commands and concepts (`merge`, and with `-R`) just
> >> makes the situation even worse (more stuff to grasp).
> >
> > The problem with re-using `pick` is that its concept does not apply to
> > merges. The cherry-pick of a non-merge commit is well-defined: the
> > current HEAD is implicitly chosen as the cherry-picked commit's
> > (single) parent commit. There is no ambiguity here.
> >
> > But for merge commits, we need to specify the parent commits (apart
> > from the first one) *explicitly*. There was no need for that in the
> > `pick` command, nor in the concept of a cherry-pick.
> >
> >> Reusing existing concepts where possible doesn`t have this problem.
> >
> > Existing concepts are great. As long as they fit the requirements of
> > the new scenarios. In this case, `pick` does *not* fit the requirement
> > of "rebase a merge commit".
> 
> It does, provided you use suitable syntax.

You know what `pick` would also do, provided you use suitable syntax? Pick
your nose.

Don't blame me for this ridiculous turn the discussion took.

Of course, using the suitable syntax you can do anything. Unless there is
*already* a syntax and you cannot break it for backwards-compatibility
reasons, as is the case here.

But I'll stop here. Even my account how there are conceptual differences
between the changes in merge vs non-merge commits (the non-merge commit
*introduces* changes, the merge commit *reconciles existing* changes)
seems to fly by without convincing you.

I use rebase every day. I use the Git garden shears every week. If you do
not trust my experience with these things, nothing will convince you. You
are just stuck with your pre-existing opinion.

> > If you really want to force the `pick` concept onto the use case where
> > you need to "reapply" merges, then the closest you get really is
> > Sergey's idea, which I came to reject when considering its practical
> > implications.
> 
> Which one, and what are the implications that are bad, I wonder?

The strategy described in RFC v2, which does too much work, forces the
user to potentially address the same merge conflicts multiple times, and
worst of all: risks merge conflicts with changes the user *already*
dropped.

> > Even so, you would have to make the `pick` command more complicated to
> > support merge commits. And whatever you would do to extend the `pick`
> > command would *not make any sense* to the current use case of the `pick`
> > command.
> 
> It would rather make a lot of sense. Please don't use 'merge' to pick
> commits, merge ones or not!

It would rather make a lot of sense. If you completely ignored everything
I said about preserve-merges. If you ignored what I said about problems
moving regular `pick` lines across merge commits. If you ignored all the
experience I have with Git garden shears and that I tried really patiently
for an impatient man to impart on you.

> > The real problem, of course, is that a non-merge commit, when viewed
> > from the perspective of the changes it introduced, is a very different
> > beast than a merge commit: it does not need to reconcile changes,
> > ever, because there is really only one "patch" to one revision. That
> > is very different from a merge commit, whose changes can even disagree
> > with one another (and in fact be resolved with changes disagreeing
> > *yet again*)!
> 
> You'd still 'pick' it though, not 'merge'. You don't merge "merge
> commit", it makes no sense. It only makes perfect sense when you get rid
> of original "merge commit" and re-merge from scratch, as you were doing
> till now.

No, you merge "merge head". And you use "merge commit"'s commit message.
*That* makes sense.

Picking a merge commit? Not so. What do you merge? The original merge
commit's second parent? Or a rebased version thereof? What if that commit
has been `pick`ed *twice*?

No, you can repeat it all you want, it still does not make sense. Now that
I think of the possiblity of picking the original parents multiple times,
it does not even make theoretical sense.

> > The implementation detail is, of course, that I will introduce this with
> > the technically-simpler strategy: always recreating merge commits with the
> > recursive strategy. A follow-up patch series will add support for rebasing
> > merge commits, and then use it by default.
> 
> Switching to use it by default would be backward incompatible again? Yet
> another option to obsolete? Sigh. 

Oh wow.

Backwards compatibility of a feature that existed only as a topic branch
in `next` before being worked on more? Any other splendid ideas?

And what's that about another option to obsolete? Who said that I would
obsolete any newly-introduced option?

I would introduce either --recreate-merges or --rebase-merges, and then
just stick with it.

I guess it is my turn to sigh.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear)
  2018-03-27 12:55                                           ` Johannes Schindelin
@ 2018-03-28  4:32                                             ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-28  4:32 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: phillip.wood, Igor Djordjevic, Git mailing list, Jacob Keller,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Sergey,
>
> On Tue, 27 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > On Mon, 12 Mar 2018, Sergey Organov wrote:
>> >
>> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> >> >
>> >> > On Wed, 7 Mar 2018, Sergey Organov wrote:
>> >> >
>> >> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> >> >> 
>> >> >> > 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?
>> >> >
>> >> > Imagine a todo list that contains this line
>> >> >
>> >> > 	merge -C abcdef 123456
>> >> >
>> >> > and now the user edits it (this is an interactive rebase, after
>> >> > all), adding another merge head:
>> >> >
>> >> > 	merge -C abcdef 987654 123456
>> >> >
>> >> > Now your strategy would have a serious problem: to find the
>> >> > original version of 987654. If there was one.
>> >> 
>> >> We are talking about different checks then. My method has a built-in
>> >> check that Pillip's one doesn't.
>> >
>> > Since you did not bother to elaborate, I have to assume that your
>> > "built-in check" is that thing where intermediate merges can give you
>> > conflicts?
>> >
>> > If so, there is a possibility in Phillip's method for such conflicts,
>> > too: we have to perform as many 3-way merges as there are parent
>> > commits.
>> >
>> > It does make me uncomfortable to have to speculate what you meant,
>> > though.
>> 
>> It doesn't matter anymore as this check could easily be added to
>> Phillip's algorithm as well, see [1].
>> 
>> [1] https://public-inbox.org/git/87efkn6s1h.fsf@javad.com
>
> Ah, and there I was, thinking that finally you would answer my questions
> directly, instead you keep directing me elsewhere ("read that! Somewhere
> in there you will find the answer you are looking for").

Except I've copy-pasted it for /you/ from that reference in another
answer to /you/, and /you/ denied it there as being unexplained. As it
actually happens to be discussed and explained in the referenced
material, should I rather copy-paste the entire reference to fulfill
your requirements?

Here I repeat, directly again, that essential quote from that reference,
in case you forgot it:

<QUOTE>
git-rebase-first-parent --onto A' M
tree_U1'=$(git write-tree)
git merge-recursive B -- $tree_U1' B'
tree=$(git write-tree)
M'=$(git log --pretty=%B -1 M | git commit-tree -pA' -pB')
[ $conflicted_last_merge = "yes" ] ||
  trees-match $tree_U1' $tree || 
  stop-for-user-amendment
  
where 'git-rebase-first-parent' denotes whatever machinery is currently
being used to rebase simple non-merge commit.
</QUOTE>

> My time is a bit too valuable, and I will not continue a discussion where
> my questions are constantly deflected that way.

No deflection on my side was ever intended. The referenced discussion
actually has explanations. Maybe one whole page of reading, and it is to
be read in context, and then a few follow-ups in that discussion could
also be of interest, provided you are interested. I'm sorry should you
have no time for that.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-27 13:49                                                       ` Johannes Schindelin
@ 2018-03-28  5:57                                                         ` Sergey Organov
  2018-03-30 13:41                                                           ` Johannes Schindelin
  2018-03-28  5:57                                                         ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-28  5:57 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,

[...]

> But I'll stop here. Even my account how there are conceptual differences
> between the changes in merge vs non-merge commits (the non-merge commit
> *introduces* changes, the merge commit *reconciles existing* changes)
> seems to fly by without convincing you.

Good for you, but Git should keep caring about content, it should care
not about meaning. Please leave it to the user to assign meaning to
their content.

If you rather want a SCM that focuses on meaning, I'd suggest to look at
Bzr and see how it goes.

> I use rebase every day. I use the Git garden shears every week. If you do
> not trust my experience with these things, nothing will convince you. 

Unfortunately you have exactly zero experience with rebasing merges as
you've never actually rebased them till now, and it's rebasing merges
that matters in this particular discussion.

> You are just stuck with your pre-existing opinion.

I'm afraid that it's rather your huge experience with re-creating merges
that makes you stuck to your pre-existing opinion and carefully shields
you from experiencing actual paradigm shift.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-27 13:49                                                       ` Johannes Schindelin
  2018-03-28  5:57                                                         ` Sergey Organov
@ 2018-03-28  5:57                                                         ` Sergey Organov
       [not found]                                                           ` <CA+P7+xoDQ2mzhxeZPFhaY+TaSoKkQm=5AtoduHH06-VggOJ2jg@mail.gmail.com>
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-28  5:57 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Sergey,
>

[...]

>> >> Reusing existing concepts where possible doesn`t have this problem.
>> >
>> > Existing concepts are great. As long as they fit the requirements of
>> > the new scenarios. In this case, `pick` does *not* fit the requirement
>> > of "rebase a merge commit".
>> 
>> It does, provided you use suitable syntax.
>
> You know what `pick` would also do, provided you use suitable syntax? Pick
> your nose.
>
> Don't blame me for this ridiculous turn the discussion took.
>
> Of course, using the suitable syntax you can do anything. Unless there is
> *already* a syntax and you cannot break it for backwards-compatibility
> reasons, as is the case here.

Backward compatibility to what? To a broken '--preserve-merges'? I had a
feel you've invented '--recreate-merges' exactly to break that
compatibility. No?

Or is it "Backwards compatibility of a feature that existed only as a
topic branch in `next` before being worked on more?", as you say
yourself below?

[...]

>> > The implementation detail is, of course, that I will introduce this with
>> > the technically-simpler strategy: always recreating merge commits with the
>> > recursive strategy. A follow-up patch series will add support for rebasing
>> > merge commits, and then use it by default.
>> 
>> Switching to use it by default would be backward incompatible again? Yet
>> another option to obsolete? Sigh. 
>
> Oh wow.
>
> Backwards compatibility of a feature that existed only as a topic branch
> in `next` before being worked on more? Any other splendid ideas?

Either you care about compatibility or not. You can't have it both ways,
sorry.

And "technically-simpler strategy: always recreating merge commits with
the recursive strategy" vs. "rebasing merge commits" is not just a minor
strategy change, it's entire paradigm shift in handling merge commits
while rebasing. I'm afraid you will still come up with a wrong design
unless you finally accept this fact.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
       [not found]                                                           ` <CA+P7+xoDQ2mzhxeZPFhaY+TaSoKkQm=5AtoduHH06-VggOJ2jg@mail.gmail.com>
@ 2018-03-28 11:29                                                             ` Sergey Organov
       [not found]                                                               ` <CA+P7+xo19mHrWz9Fy-ifgCcVJM2xwzcLj7F2NvFe2LwGbaJiDQ@mail.gmail.com>
  2018-03-28 12:10                                                             ` Sergey Organov
  1 sibling, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-28 11:29 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Johannes Schindelin, Igor Djordjevic, Phillip Wood,
	Git Mailing List, Johannes Sixt, Junio C Hamano

Jacob Keller <jacob.keller@gmail.com> writes:

> On Tue, Mar 27, 2018 at 10:57 PM, Sergey Organov <sorganov@gmail.com> wrote:
>>
>> Hi Johannes,
>>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> > Hi Sergey,
>> >
>>
>> [...]
>>
>> >> >> Reusing existing concepts where possible doesn`t have this problem.
>> >> >
>> >> > Existing concepts are great. As long as they fit the requirements of
>> >> > the new scenarios. In this case, `pick` does *not* fit the
> requirement
>> >> > of "rebase a merge commit".
>> >>
>> >> It does, provided you use suitable syntax.
>> >
>> > You know what `pick` would also do, provided you use suitable syntax?
> Pick
>> > your nose.
>> >
>> > Don't blame me for this ridiculous turn the discussion took.
>> >
>> > Of course, using the suitable syntax you can do anything. Unless there
> is
>> > *already* a syntax and you cannot break it for backwards-compatibility
>> > reasons, as is the case here.
>>
>> Backward compatibility to what? To a broken '--preserve-merges'? I had a
>> feel you've invented '--recreate-merges' exactly to break that
>> compatibility. No?
>>
>> Or is it "Backwards compatibility of a feature that existed only as a
>> topic branch in `next` before being worked on more?", as you say
>> yourself below?
>>
>
> I'm pretty sure he meant that changing the meaning and behavior of "pick"
> is incompatible, as people use scripts which check the edit lists, and
> these scripts would expect pick to behave in a certain way.

Are we still speaking about that new --recreate-merges feature? You
already care for compatibility for it? You expect there are already
scripts that use it?

Once again, it seems like you care and don't care about backward
compatibility at the same time, here is your phrase below:

"He absolutely cares about compatibility, but in this case, the feature
has not yet been merged into an official release."

Are we still speaking about that new --recreate-merges feature?

Do you guys care for compatibility for this particular --recreate-merges
feature or not? I'm lost. "Yes" or "No" answer, if you please!

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
       [not found]                                                           ` <CA+P7+xoDQ2mzhxeZPFhaY+TaSoKkQm=5AtoduHH06-VggOJ2jg@mail.gmail.com>
  2018-03-28 11:29                                                             ` Sergey Organov
@ 2018-03-28 12:10                                                             ` Sergey Organov
  1 sibling, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-28 12:10 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Johannes Schindelin, Igor Djordjevic, Phillip Wood,
	Git Mailing List, Johannes Sixt, Junio C Hamano

Jacob Keller <jacob.keller@gmail.com> writes:

> On Tue, Mar 27, 2018 at 10:57 PM, Sergey Organov <sorganov@gmail.com> wrote:
>>
>> Hi Johannes,
>>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

[...]

> I'm pretty sure the fact has already been accepted, as he did indeed
> implement and develop a strategy for rebasing the merges (Phillip's
> strategy). He hasn't chosen to re-write all the code such that it was
> "always" this method, but rather kept it as an incremental patch on top as
> it makes it easier to review the changes since we've already spent time
> looking at and reviewing the --recreate-merges patches.

That's perfectly OK with me, except that he apparently still can't
accept the fact that rebasing a non-merge is not fundamentally different
from rebasing a merge.

"Rebase non-merge" is just a special case of generic "rebase commit",
provided we do have generic method that is capable to rebase any commit,
and we do have it, Phillip's or not.

> Having watched from the sidelines, I've been unable to completely
> understand and parse the strategies completely, but I've also found
> Phillip's method to be easier to understand.

It doesn't matter at all for this particular discussion. Let's call the
method "rebase a commit", a black-box, that is capable to rebase any
commit. I don't care what implementation is inside. Rebasing a commit is
still rebasing a commit, and it should not be called "merge" in the todo
list.

> As someone who's read the discussion on the sidelines, it certainly
> does feel like there is some misunderstanding on both sides. Neither
> of you have been able to get the other to see what you clearly both
> believe strongly.

Calling "rebase" operation "merge" is wrong no matter what method is
used to rebase a commit. Isn't it obvious? It's currently called "pick"
in the todo and it seems natural to continue to use that name for
picking a commit, whatever number of parents it happens to have.

> Unfortunately I do not have any suggestion as to how to resolve the
> misunderstanding.

This sub-thread is not about method at all, so no resolution on that
matter is required here. This sub-thread is about todo format only.

> Sergey's method appears to me to be more complex, and I agree that the
> extra steps could cause more merge conflicts, at least in how it was
> originally conceptualized and implemented. It is possible that we are
> mis-understanding the terminology for U1 and U2? It sure seems like it
> introduces more changes for merge conflicts than the strategy proposed by
> Phillip. However, the latest editions also sound a lot closer to Phillip's
> strategy in general, so maybe I have mis-understood how it works and what
> is fundamentally different about the two strategies.

There is nothing fundamentally different between them and thus I don't
care in this discussion what exact method is being used.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
       [not found]                                                               ` <CA+P7+xo19mHrWz9Fy-ifgCcVJM2xwzcLj7F2NvFe2LwGbaJiDQ@mail.gmail.com>
@ 2018-03-29  5:53                                                                 ` Sergey Organov
  2018-03-30 10:38                                                                   ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-29  5:53 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Johannes Schindelin, Igor Djordjevic, Phillip Wood,
	Git Mailing List, Johannes Sixt, Junio C Hamano

Jacob Keller <jacob.keller@gmail.com> writes:

> On Wed, Mar 28, 2018 at 4:29 AM, Sergey Organov <sorganov@gmail.com> wrote:
>
>> Jacob Keller <jacob.keller@gmail.com> writes:
>>
>> > On Tue, Mar 27, 2018 at 10:57 PM, Sergey Organov <sorganov@gmail.com>
>> wrote:
>> >>
>> >> Hi Johannes,
>> >>
>> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> >> > Hi Sergey,
>> >> >
>> >>
>> >> [...]
>> >>
>> >> >> >> Reusing existing concepts where possible doesn`t have this
>> problem.
>> >> >> >
>> >> >> > Existing concepts are great. As long as they fit the requirements
>> of
>> >> >> > the new scenarios. In this case, `pick` does *not* fit the
>> > requirement
>> >> >> > of "rebase a merge commit".
>> >> >>
>> >> >> It does, provided you use suitable syntax.
>> >> >
>> >> > You know what `pick` would also do, provided you use suitable syntax?
>> > Pick
>> >> > your nose.
>> >> >
>> >> > Don't blame me for this ridiculous turn the discussion took.
>> >> >
>> >> > Of course, using the suitable syntax you can do anything. Unless there
>> > is
>> >> > *already* a syntax and you cannot break it for backwards-compatibility
>> >> > reasons, as is the case here.
>> >>
>> >> Backward compatibility to what? To a broken '--preserve-merges'? I had a
>> >> feel you've invented '--recreate-merges' exactly to break that
>> >> compatibility. No?
>> >>
>> >> Or is it "Backwards compatibility of a feature that existed only as a
>> >> topic branch in `next` before being worked on more?", as you say
>> >> yourself below?
>> >>
>> >
>> > I'm pretty sure he meant that changing the meaning and behavior of "pick"
>> > is incompatible, as people use scripts which check the edit lists, and
>> > these scripts would expect pick to behave in a certain way.
>>
>> Are we still speaking about that new --recreate-merges feature? You
>> already care for compatibility for it? You expect there are already
>> scripts that use it?
>>
>> Once again, it seems like you care and don't care about backward
>> compatibility at the same time, here is your phrase below:
>>
>> "He absolutely cares about compatibility, but in this case, the feature
>> has not yet been merged into an official release."
>>
>> Are we still speaking about that new --recreate-merges feature?
>>
>> Do you guys care for compatibility for this particular --recreate-merges
>> feature or not? I'm lost. "Yes" or "No" answer, if you please!
>>
>> -- Sergey
>>
>
> I care about the general compatibility of the rebase todo list regardless
> of which options you enabled on the command line to generate it.

It's a good thing in general, yes. However, I recall I was told by the
author that --recreate-merges was introduced exactly to break backward
compatibility of the todo list. If so, could we please agree to stop
using backward compatibility as an objection in the discussion of this
particular feature?

> Yes this has a bit of problem because *any* new todo command will
> break the todo list, but it's better to only add new commands rather
> than change semantics of existing ones.

I'm not against new commands in general. I'm against inventing new
entities without necessity, so, provided we did agree not to care about
compatibility, there should be some other necessity to invent yet
another command. I don't see such a necessity. Do you?

The main principle I stand for in this discussion though is that all the
commits should be treated equally as much as possible, new command or no
new command. Doing otherwise will lead to all kinds of troubles and
confusion, both in implementation and in user experience.

Overall, what I think is needed is extending the syntax of existing todo
commands to handle merge commits. This will give a provision to get it
right this time. Otherwise it will likely end up being yet another
subject of deprecation in the future.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-29  5:53                                                                 ` Sergey Organov
@ 2018-03-30 10:38                                                                   ` Johannes Schindelin
  2018-03-30 12:36                                                                     ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-30 10:38 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Jacob Keller, Igor Djordjevic, Phillip Wood, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi,

On Thu, 29 Mar 2018, Sergey Organov wrote:

> Jacob Keller <jacob.keller@gmail.com> writes:
> 
> > I care about the general compatibility of the rebase todo list
> > regardless of which options you enabled on the command line to
> > generate it.
> 
> It's a good thing in general, yes. However, I recall I was told by the
> author that --recreate-merges was introduced exactly to break backward
> compatibility of the todo list. If so, could we please agree to stop
> using backward compatibility as an objection in the discussion of this
> particular feature?

That is a serious misrepresentation of what I said.

If I had changed --preserve-merges to the new format, *that* would have
broken backwards-compatibility.

So the entire reason of introducing --recreate-merges was to *not have to
break backwards-compatibility*.

I definitely did not say the *exact opposite*.

Hopefully this clarifies your confusion,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-30 10:38                                                                   ` Johannes Schindelin
@ 2018-03-30 12:36                                                                     ` Sergey Organov
  2018-03-30 13:33                                                                       ` Johannes Schindelin
  0 siblings, 1 reply; 173+ messages in thread
From: Sergey Organov @ 2018-03-30 12:36 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Jacob Keller, Igor Djordjevic, Phillip Wood, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi,
>
> On Thu, 29 Mar 2018, Sergey Organov wrote:
>
>> Jacob Keller <jacob.keller@gmail.com> writes:
>> 
>> > I care about the general compatibility of the rebase todo list
>> > regardless of which options you enabled on the command line to
>> > generate it.
>> 
>> It's a good thing in general, yes. However, I recall I was told by the
>> author that --recreate-merges was introduced exactly to break backward
>> compatibility of the todo list. If so, could we please agree to stop
>> using backward compatibility as an objection in the discussion of this
>> particular feature?
>
> That is a serious misrepresentation of what I said.
>
> If I had changed --preserve-merges to the new format, *that* would have
> broken backwards-compatibility.
>
> So the entire reason of introducing --recreate-merges was to *not have to
> break backwards-compatibility*.
>
> I definitely did not say the *exact opposite*.

I'm sorry I committed ambiguity in my wording that allowed it to be
misinterpreted. I actually intended to say roughly the same thing you
are saying, as what matters for the discussion is that new todo list
format does not need to be (backward-)compatible to that of
--preserve-merges. 

> Hopefully this clarifies your confusion,

There was actually no confusion on my side, and I like your wording
better.

Except that you've managed to clarify your intentions without actually
addressing the primary concern:

Could we please agree to stop using backward compatibility as an
objection in the discussion of the  --recreate-merges feature?

Could we?

I understand you are still resistant to change 'pick' syntax, but it's
not because of backward-compatibility, right?

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-30 12:36                                                                     ` Sergey Organov
@ 2018-03-30 13:33                                                                       ` Johannes Schindelin
  2018-03-30 15:13                                                                         ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-30 13:33 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Jacob Keller, Igor Djordjevic, Phillip Wood, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Fri, 30 Mar 2018, Sergey Organov wrote:

> Could we please agree to stop using backward compatibility as an
> objection in the discussion of the  --recreate-merges feature?

No.

The expectation of users as to what a `pick` is has not changed just
because you wish it would.

That is a matter of backwards-compatibility.

You see, if you are driving a car for a hundred years already, and then
switch to a different car, and it has a lever in the same place as your
previous car's windshield wiper, but in the new car it has a button that
activates the emergency driver seat ejection OMG *it has a seat ejection
like in the James Bond movies! Where can I get that car?* Sorry for
disgressing.

I am really concerned about that willingness to put an innocuous button,
so to speak, onto something users got really used to, over the course of a
decade or so, when that button should really be made red and blinking and
OMG where can I get that car?

So to reiterate, I am really interested in a practical solution that won't
cause nasty surprises. Meaning: `pick` != merge. That was a mistake in
preserve-merges, as I have only mentioned like a hundred times, and we
won't repeat it.

Now back to that important question: where can I get such a James Bond
car? Ideally also with Turbo Boost. Oh wait, that was somebody else's car.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-28  5:57                                                         ` Sergey Organov
@ 2018-03-30 13:41                                                           ` Johannes Schindelin
  2018-03-30 16:36                                                             ` Sergey Organov
  0 siblings, 1 reply; 173+ messages in thread
From: Johannes Schindelin @ 2018-03-30 13:41 UTC (permalink / raw)
  To: Sergey Organov
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Sergey,

On Wed, 28 Mar 2018, Sergey Organov wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > I use rebase every day. I use the Git garden shears every week. If you
> > do not trust my experience with these things, nothing will convince
> > you. 
> 
> Unfortunately you have exactly zero experience with rebasing merges as
> you've never actually rebased them till now, and it's rebasing merges
> that matters in this particular discussion.

Who says that I have 0 experience with that? Oh yes, you do. Like, as if
you know.

Guess what I do with those Git garden shears' merges? Can you guess? Of
course you can. But you'll never know until I tell you. It is a little
silly to try to tell me that I do not have any experience with rebasing
merges when you have no idea what strategies I tried in the past.

Now, Phillip's strategy is clearly the best strategy I ever heard about,
and I am in the process of doing Actual Work to Put It To The Test.

> > You are just stuck with your pre-existing opinion.
> 
> I'm afraid that it's rather your huge experience with re-creating merges
> that makes you stuck to your pre-existing opinion and carefully shields
> you from experiencing actual paradigm shift.

You know what? Whatevs.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-30 13:33                                                                       ` Johannes Schindelin
@ 2018-03-30 15:13                                                                         ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-30 15:13 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Jacob Keller, Igor Djordjevic, Phillip Wood, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Fri, 30 Mar 2018, Sergey Organov wrote:
>
>> Could we please agree to stop using backward compatibility as an
>> objection in the discussion of the  --recreate-merges feature?
>
> No.
>
> The expectation of users as to what a `pick` is has not changed just
> because you wish it would.

As if I ever suggested to change user expectations. Could you please
stop putting words into my mouth?

I _am_ a user, and I expect 'pick' to pick commits, no matter how many
parents they might have.

And no, --preserve-merges did not ever pick commits with number of
parents more than one, it rather threw them away and re-merged the
heads. Calling it 'pick' was a huge mistake indeed! Fixing that mistake
is what I expect, as a user.

Just teach the 'pick' to correctly pick any commit, please!

>
> That is a matter of backwards-compatibility.

OK, fine, at least its only about user expectations and not about some
scripting incompatibility.

> You see, if you are driving a car for a hundred years already, and then
> switch to a different car, and it has a lever in the same place as your
> previous car's windshield wiper, but in the new car it has a button that
> activates the emergency driver seat ejection OMG *it has a seat ejection
> like in the James Bond movies! Where can I get that car?* Sorry for
> disgressing.

Except it's irrelevant as the 'pick' will still pick commits.

> I am really concerned about that willingness to put an innocuous button,
> so to speak, onto something users got really used to, over the course of a
> decade or so, when that button should really be made red and blinking and
> OMG where can I get that car?

It's irrelevant as the 'pick' will still pick commits.

> So to reiterate, I am really interested in a practical solution that won't
> cause nasty surprises.

I rather don't see how it possibly could cause any surprises, especially
compared to using 'merge' to pick commits.

> Meaning: `pick` != merge.

Exactly! Use 'merge' when you merge, as you are already doing. Use 'pick'
when you are picking. You don't merge "merge commit" when you are
picking it!

> That was a mistake in preserve-merges, as I have only mentioned like a
> hundred times, and we won't repeat it.

The mistake was that it used 'pick' to denote re-merge. You already
fixed that mistake by introducing 'merge' to re-merge, thanks God.

Please don't commit yet another mistake by now using 'merge' to pick!

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-30 13:41                                                           ` Johannes Schindelin
@ 2018-03-30 16:36                                                             ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-03-30 16:36 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>
> On Wed, 28 Mar 2018, Sergey Organov wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > I use rebase every day. I use the Git garden shears every week. If you
>> > do not trust my experience with these things, nothing will convince
>> > you. 
>> 
>> Unfortunately you have exactly zero experience with rebasing merges as
>> you've never actually rebased them till now, and it's rebasing merges
>> that matters in this particular discussion.
>
> Who says that I have 0 experience with that? Oh yes, you do. Like, as if
> you know.

I just didn't see even single symptom of it in the discussion, still I
said nothing about it until you started to use your presumed experience
in place of true arguments.

> Guess what I do with those Git garden shears' merges? Can you guess? Of
> course you can. But you'll never know until I tell you. It is a little
> silly to try to tell me that I do not have any experience with rebasing
> merges when you have no idea what strategies I tried in the past.

Please notice that I never even started to discuss your 'merge'
directive, exactly because I believe you have huge experience both
implementing and using it that could be relied upon. Just don't mix-in
rebasing merge commits into it, as that is fundamentally different
operation.

And the other unspoken strategies you tried are irrelevant here, as you
declined the whole idea of replaying merge-the-commit instead of
replaying merge-the-operation until recently, and it seems you still do,
at least to some level, by attempting to use 'merge' operation to replay
"the (merge) _commit_". I'm afraid that whatever you've tried in the
past likely suffered from the same conceptual confusion, and thus did
not work indeed.

That said, negative experience is still an experience, often helpful,
but what is relevant here is that it likely was not about rebasing merge
commits at all, so trying to use this irrelevant experience in the
discussion to support your arguments, or rather lack of them, seems even
more unfair to me then using relevant experience for that purpose.

> Now, Phillip's strategy is clearly the best strategy I ever heard
> about,

For 'pick' vs 'merge', it's not about strategy, it's about concept. It
looks like you still believe that you somehow "merge" a merge commit
when you actually rebase it (with Phillip's strategy if you wish). You
don't merge it anywhere, period.

> and I am in the process of doing Actual Work to Put It To The Test.

That's the best outcome of the discussion I ever hoped for, seriously,
and I'll be all ears to listen to the outcomes of the experience you
will gain with it.

BTW, when you have it working, use the strategy you've implemented for
non-merge commits as well, as a good one should still work fine.
Moreover, it should better bring exactly the same results as the default
existing strategy being used for non-merge commits, even in conflicting
situations.

Hopefully /that/ experience will finally push you strong enough to get
the concept right, and you will finally understand that what you've 
implemented is nothing else but a _cherry-pick_ of a _merge commit_,
that reads simply _pick_, for brevity.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

* Re: [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear)
  2018-03-27 13:35                                                           ` Johannes Schindelin
@ 2018-04-02  6:07                                                             ` Sergey Organov
  0 siblings, 0 replies; 173+ messages in thread
From: Sergey Organov @ 2018-04-02  6:07 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Igor Djordjevic, phillip.wood, Jacob Keller, Git Mailing List,
	Johannes Sixt, Junio C Hamano

Hi Johannes,

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Sergey,
>

[...]

> In the parlance of your RFC v2, where you start with this history (which I
> translated into the left-to-right notation that is used in pretty much all
> of Git's own documentation about interactive rebases, which you apparently
> either did not read, or chose *not* to imitate, creating yet another
> unnecessary diversion):
>
>
> - B1
>      \
> - B2 - M

First, it should rather be:

- B1
    \
     M
    / 
- B2

as RFC presents essentially symmetric approach and I'd like it to be
explicit. Representation in RFC simply saves some vertical
space: 

  M
 / \
B1  B2

Another reason to use it is that I liked to somehow indicate that this
is about abstract DAG representation of Git history, not to be confused
with some actual practical Git history. So that, for example, the reader
can't be tempted to even try to assume that M has been necessarily
created by "git merge" operation in the first place.

That said, I'm sorry if it upsets you. I'll stick to your preferred
notation below.

>
> You now insert U1 and U2 with trees identical to M:
>
> - B1 - U1
>           \
> - B2 - U2 - M

 - B1 - U1 
          \
           UM
          / 
 - B2 - U2

_YES_. You've slightly screwed RFC as UM is not M anymore, having
different parents, but otherwise it's still right.

> So U1 is essentially B2 cherry-picked on top of B1, and U2 is essentially
> B1 cherry-picked on top of B2.

_NO_. No any cherry-picking has been involved, and I see absolutely no
reason to pretend there has, except to intentionally make otherwise
simple thing look tricky.

U1 tree is still M tree, and U2 tree is still M tree, and UM tree is
still M tree. That's what actually matters from RFC POV.

> These U1/U2 commits are now to be cherry-picked on top of the rebased B1'
> and B2'. I spare you more diagrams, you get the idea.

_YES_. Exactly 2 cherry-picks.

> Now, the changes in U1/U2 *are* the changes of the merge parents, that's
> how they were constructed.

Either _YES_, or _NO_, depending on the exact meaning of the term "the
changes of the merge parents" you've used, but I suspect it's _NO_,
taking into account your further inferences.

The U1/U2 are constructed by simply duplicating the tree of the original
merge commit M and thus they represent the changes _to_ the merge
parents B1/B2 introduced by M, and not the changes "_of_ the merge
parents" B1/B2, provided the latter meant to have some relation to the
changes introduced by the merge parents B1/B2 themselves.

>
> Since they repeat what B1 and B2 are about,

_NO_, they do not repeat what B1 and B2 are about at all. They rather
represent what M is about. In other words, whatever B1 and B2 are about,
the RFC method doesn't care.

And as this is fundamental misinterpretation of the RFC on your side, it
starts to be big _NO_ from now on...

> and since B1'/B2' means they are rebased, and since U1'/U2' are *also*
> rebased, but independently...
>
> 	...  you essentially have to rebase *every parent's changes
> 	twice*.

_NO_. U1' is rebase of U1 (on top of B1'), and U2' is rebase of U2 (on
top of B2'). Each of U1/U2 is rebased only once.

> The answer "No" to this is... astonishing.

It's still _NO_, sorry.

In fact, I could have said _NO_ the first time you started to assign
some arbitrary "meaning" to the commits, as RFC is about somewhat formal
proof of the method, using already well-known operations on the DAG, and
to criticize the RFC, you need to either find and show a _formal_
mistake somewhere in the proof logic, or to show a use-case where it
fails, as you did for RFC v1. Assigning arbitrary "meaning" to the DAG
nodes and operations on them won't do the trick, sorry.

I'd like to reach some agreement on formal correctness of the RFC first,
and then discuss the meanings, the implementations, and other
consequences based on well-established formal base.

-- Sergey

^ permalink raw reply	[flat|nested] 173+ messages in thread

end of thread, other threads:[~2018-04-02  6:07 UTC | newest]

Thread overview: 173+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-16 13:08 [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Sergey Organov
2018-02-18  4:16 ` Jacob Keller
2018-02-19  5:28   ` Sergey Organov
2018-02-19 23:44 ` Igor Djordjevic
2018-02-20 12:42   ` Sergey Organov
2018-02-27  0:07   ` Johannes Schindelin
2018-02-27  5:01     ` Sergey Organov
2018-02-27  5:30     ` Jacob Keller
2018-02-27 16:21       ` Johannes Schindelin
2018-02-27 18:55         ` Igor Djordjevic
2018-02-27 19:59           ` Igor Djordjevic
2018-02-27 23:27             ` Johannes Schindelin
2018-02-28  2:12               ` Igor Djordjevic
2018-02-28  4:35                 ` Igor Djordjevic
2018-02-28  6:14                   ` Sergey Organov
2018-02-28 20:53                     ` Igor Djordjevic
2018-02-28  5:44               ` Sergey Organov
2018-02-28 19:42                 ` Igor Djordjevic
2018-02-27 23:40             ` Igor Djordjevic
2018-02-28  0:10               ` Junio C Hamano
2018-02-28  2:35                 ` Igor Djordjevic
2018-02-28  5:27                 ` Sergey Organov
2018-02-28  0:36               ` Jacob Keller
2018-02-28  1:33                 ` Igor Djordjevic
2018-02-28  1:43                   ` Igor Djordjevic
2018-02-28  5:21                   ` Sergey Organov
2018-02-28 19:09                     ` Igor Djordjevic
2018-03-01  5:27                       ` Sergey Organov
2018-02-28  5:19               ` Sergey Organov
2018-02-28 20:25                 ` Igor Djordjevic
2018-02-28 22:17                   ` Igor Djordjevic
2018-03-01  5:19                     ` Sergey Organov
2018-03-01  5:39                   ` Sergey Organov
2018-03-02  1:16                     ` Igor Djordjevic
2018-03-02  5:40                       ` Sergey Organov
2018-03-02 17:45                         ` Igor Djordjevic
2018-03-02 11:17                       ` [RFC] Rebasing merges: a jorney to the ultimate solution(RoadClear) Phillip Wood
2018-03-02 12:36                         ` Phillip Wood
2018-03-02 16:02                           ` Jacob Keller
2018-03-02 23:33                             ` Igor Djordjevic
2018-03-06 10:36                               ` Phillip Wood
2018-03-06 18:12                                 ` Johannes Schindelin
2018-03-06 19:43                                   ` Igor Djordjevic
2018-03-07  7:26                                     ` Johannes Schindelin
2018-03-08 11:20                                       ` Phillip Wood
2018-03-08 12:16                                         ` Phillip Wood
2018-03-08 16:05                                           ` Igor Djordjevic
2018-03-11 12:00                                             ` Johannes Schindelin
2018-03-11 16:33                                               ` Igor Djordjevic
2018-03-12 10:37                                                 ` Johannes Schindelin
2018-03-12 12:56                                                   ` Sergey Organov
2018-03-13  0:01                                                     ` Igor Djordjevic
2018-03-26 12:03                                                       ` Johannes Schindelin
2018-03-27  5:08                                                         ` Sergey Organov
2018-03-27 13:35                                                           ` Johannes Schindelin
2018-04-02  6:07                                                             ` Sergey Organov
2018-03-12 23:54                                                   ` Igor Djordjevic
2018-03-13  6:25                                                     ` Sergey Organov
2018-03-08 15:56                                         ` Igor Djordjevic
2018-03-11 12:08                                           ` Johannes Schindelin
2018-03-11 17:34                                             ` Igor Djordjevic
2018-03-12 10:46                                               ` Johannes Schindelin
2018-03-13  0:16                                                 ` Igor Djordjevic
2018-03-26 13:07                                                   ` Johannes Schindelin
2018-03-27  5:51                                                     ` Sergey Organov
2018-03-27 13:49                                                       ` Johannes Schindelin
2018-03-28  5:57                                                         ` Sergey Organov
2018-03-30 13:41                                                           ` Johannes Schindelin
2018-03-30 16:36                                                             ` Sergey Organov
2018-03-28  5:57                                                         ` Sergey Organov
     [not found]                                                           ` <CA+P7+xoDQ2mzhxeZPFhaY+TaSoKkQm=5AtoduHH06-VggOJ2jg@mail.gmail.com>
2018-03-28 11:29                                                             ` Sergey Organov
     [not found]                                                               ` <CA+P7+xo19mHrWz9Fy-ifgCcVJM2xwzcLj7F2NvFe2LwGbaJiDQ@mail.gmail.com>
2018-03-29  5:53                                                                 ` Sergey Organov
2018-03-30 10:38                                                                   ` Johannes Schindelin
2018-03-30 12:36                                                                     ` Sergey Organov
2018-03-30 13:33                                                                       ` Johannes Schindelin
2018-03-30 15:13                                                                         ` Sergey Organov
2018-03-28 12:10                                                             ` Sergey Organov
2018-03-08 16:07                                         ` Jacob Keller
2018-03-11 12:11                                           ` Johannes Schindelin
2018-03-11 17:46                                             ` Igor Djordjevic
2018-03-08 15:16                                       ` Igor Djordjevic
2018-03-08 16:21                                         ` Igor Djordjevic
2018-03-11 12:22                                           ` Johannes Schindelin
2018-03-14 14:24                                         ` Sergey Organov
2018-03-14 23:11                                           ` Igor Djordjevic
2018-03-15  6:00                                             ` Sergey Organov
2018-03-15 21:51                                               ` Igor Djordjevic
2018-03-17  2:08                                             ` Igor Djordjevic
2018-03-19  5:44                                               ` Sergey Organov
2018-03-19 21:35                                                 ` Igor Djordjevic
2018-03-20 14:43                                                   ` Sergey Organov
2018-03-26 13:47                                           ` Johannes Schindelin
2018-03-06 23:24                                   ` Junio C Hamano
2018-03-07  7:09                                     ` Johannes Schindelin
2018-03-07 18:20                                       ` Junio C Hamano
2018-03-08  7:03                                         ` Johannes Schindelin
2018-03-08  8:11                                           ` Junio C Hamano
2018-03-09 17:09                                             ` Johannes Schindelin
2018-03-02 16:00                         ` Jacob Keller
2018-03-02 18:14                           ` Igor Djordjevic
2018-03-03 17:29                             ` Igor Djordjevic
2018-03-05  5:35                               ` Sergey Organov
2018-03-02 11:31                     ` [RFC] Rebasing merges: a jorney to the ultimate solution (Road Clear) Phillip Wood
2018-03-03  0:29                       ` Igor Djordjevic
2018-03-05  5:00                         ` Sergey Organov
2018-03-06 10:52                           ` Phillip Wood
2018-03-06 16:56                             ` Junio C Hamano
2018-03-08  7:05                               ` Johannes Schindelin
2018-03-08  8:18                                 ` Junio C Hamano
2018-03-11 11:56                                   ` Johannes Schindelin
2018-03-13 18:24                                     ` Junio C Hamano
2018-03-26 13:17                                       ` Johannes Schindelin
2018-03-05 17:29                         ` Johannes Schindelin
2018-03-06 23:21                           ` Igor Djordjevic
2018-03-07  7:04                             ` Johannes Schindelin
2018-03-06 10:45                         ` Phillip Wood
2018-03-06 11:45                           ` Sergey Organov
2018-03-08 16:30                             ` Igor Djordjevic
2018-03-06 18:12                           ` Johannes Schindelin
2018-03-07  5:08                             ` Sergey Organov
2018-03-07  6:58                               ` Johannes Schindelin
2018-03-07 14:34                                 ` Sergey Organov
2018-03-08  6:45                                   ` Johannes Schindelin
2018-03-12 12:31                                     ` Sergey Organov
2018-03-26 11:37                                       ` Johannes Schindelin
2018-03-27  5:11                                         ` Sergey Organov
2018-03-27 12:55                                           ` Johannes Schindelin
2018-03-28  4:32                                             ` Sergey Organov
2018-03-08 11:08                             ` Phillip Wood
2018-03-05 17:52                       ` Johannes Schindelin
2018-03-13 16:10                       ` Sergey Organov
2018-03-14  1:12                         ` Igor Djordjevic
2018-03-14  7:21                           ` Sergey Organov
2018-03-15  0:09                             ` Igor Djordjevic
2018-03-15  7:52                               ` Sergey Organov
2018-03-15 23:08                                 ` Igor Djordjevic
2018-03-16  7:31                                   ` Sergey Organov
2018-03-17  3:04                                     ` Igor Djordjevic
2018-03-19  6:01                                       ` Sergey Organov
2018-03-26 14:11                                   ` Johannes Schindelin
2018-02-28  0:29         ` Jacob Keller
2018-02-27 11:57     ` Sergey Organov
2018-02-27 18:14       ` Junio C Hamano
2018-02-28  0:30         ` Jacob Keller
2018-02-28  5:54           ` Sergey Organov
2018-02-28  4:53         ` Sergey Organov
2018-03-06 13:26 ` [RFC v2] " Sergey Organov
2018-03-07  6:46   ` Johannes Schindelin
2018-03-07 13:27     ` Sergey Organov
2018-03-07 14:08       ` Johannes Schindelin
2018-03-07 15:16         ` Sergey Organov
2018-03-08  7:01           ` Johannes Schindelin
2018-03-12 12:42             ` Sergey Organov
2018-03-26 11:50               ` Johannes Schindelin
2018-03-08 19:58         ` Igor Djordjevic
2018-03-08 20:27           ` Igor Djordjevic
2018-03-08 22:05             ` Igor Djordjevic
2018-03-08 23:31               ` Igor Djordjevic
2018-03-11 15:47                 ` Johannes Schindelin
2018-03-11 20:53                   ` Igor Djordjevic
2018-03-12 10:20                     ` Johannes Schindelin
2018-03-12 13:49                       ` Sergey Organov
2018-03-26 12:44                         ` Johannes Schindelin
2018-03-27  5:32                           ` Sergey Organov
2018-03-13  0:29                       ` Igor Djordjevic
2018-03-26 13:58                         ` Johannes Schindelin
2018-03-12 13:07                     ` Sergey Organov
2018-03-11 15:40           ` Johannes Schindelin
2018-03-11 22:04             ` Igor Djordjevic
2018-03-12 12:05               ` Sergey Organov
2018-03-26 11:33                 ` Johannes Schindelin
2018-03-27  5:34                   ` Sergey Organov
2018-03-12 22:41               ` Igor Djordjevic

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).