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
>