Hello, I think that rebase preserve-merges algorithm needs further improvements. Probably, you already know it. I was playing with rebasing linux kernel tree and found that rebase preserve-merges works in counterintuitive way while handling merge-commits. I don't want to discuss arising merge conflicts which can be illuminated with help of rerere trained at original branch. An major issue here with silently auto-merging, some merge commits were silently rebased with different content and it breaks the build :) I crafted toy example to demonstrate an issue: https://github.com/matwey/example-git-rebase-pm Here I have commit range v0.1..v0.2, then I return to v0.1 and introduce new commit abc-0.1 with new file on v0.1. Then I want to rebase v0.1..v0.2 onto new abc-0.1. It is obvious that as soon as I introduced single new file the difference between old v0.2 and new v0.2 should have only this new file. However it actually contains other changes. Reproduce as the following: git checkout v0.2 git branch abc-0.2 git checkout v0.1 git branch abc-0.1 git checkout abc-0.1 vim LICENSE git add LICENSE git commit -a git rebase --preserve-merges --onto abc-0.1 v0.1 abc-0.2 Actual result: diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 297edb3..0000000 --- a/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -Hello, world! - diff --git a/mod.c b/mod.c index e6fa107..e339597 100644 --- a/mod.c +++ b/mod.c @@ -2,5 +2,5 @@ #include "mod.h" int mod_fun(char* buf, int len) { - return -calc_fun(buf, len); + return -calc_fun(buf, len, 0); } Expected result: diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 297edb3..0000000 --- a/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -Hello, world! - As far as I understand the root cause of this that when new merge commit is created by rebase it is done simply by git merge $new_parents without taking into account any actual state of the initial merge commit. This is why all manually introduced changes in merge-commits are lost at rebase stage. I think that the following improvement of merge-commit recreating stage would be possible: 1) Let we have merge commit in the source tree -- A --\ M-- -- B --/ And we want to rebase M to the new tree where new parents A' and B' already existing. -- A' -- B' 2) Then I suggest to perform it in three steps. First, lets take all possible differences between A and M, B and M and so on... and then try to cherry-pick the diffs on A' and B' respectively. Here AM' and BM' are temporary commits which is very close in their states (in ideal case, equal). -- A' -- (AM)' -- B' -- (BM)' 3) Then we do git merge (AM)' (BM)' and ask user to resolve the conflicts if any. Here we obtain the following tree. Where M" is produced by git merge. -- A' -- (AM)' --\ M" -- B' -- (BM)' --/ 4) Then we do merge between A' and B' but reset the state to be the same as in M" before new merge commit is created: -- A' -- (AM)' --\ \ \ M' (==M") M" / / -- B' -- (BM)' --/ 5) Then (AM)' (BM)' and M" are discarded from the tree. Probably, I missed something important or incorrectly read git-rebase--interactive.sh -- With best regards, Matwey V. Kornilov