Avoid re-resolving conflicts in a git rebase including merge commits

Viewed 1703

Trying to move history from one repo to another (no common root) by adding the source remote and rebasing to a branch on the local destination repo.

Source:

A-B-C
 \ /
  E

Wanted on destination:

F-A'-B'-C'
   \   /
     E'

Documentation for git rebase --rebase-merges says it will preserve topology and re-create merge commits, but:

Any resolved merge conflicts or manual amendments in these merge commits will have to be resolved/re-applied manually.

Now, I want to avoid re-resolving conflicts and I am puzzled why this is needed at all. The content of the merge commit is there, I can detach HEAD to to.

So, I tried

$ git rebase -i --onto destnewbranch --root --rebase-merges srcbranch
Could not apply C... master # Merge branch 'master'

(detached HEAD|REBASE x/y)
$ git status
interactive rebase in progress; onto F
Last commands done (n commands done):
   pick B
   merge -C C master # Merge branch 'master' 

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

To solve conflicts, I am taking the content of C:

$ git reset --hard C

Now content is good but history is not, so:

$ git reset --soft HEAD@{1}

All is there is left to do is to move on with the rebase:

$ git commit
$ git rebase --continue

However, history is wrong since I am left with:

F-A'-B'-C'

So C' is not a merge commit, although it has the resolved changes of both B and E. Merge commits that don't cause conflict are successfully rebased with the topology intact.

I think it is me who is messing up the the MERGING state of the rebase by resetting and the final git commit will NOT create a merge commit, but a regular one. If I don't do the resets and resolve the conflicts manually, git commit will create a merge commit.

git version 2.26.0.windows.1

1 Answers

To resolve a conflict using the contents of an existing commit, use git restore. Do not use git reset --hard which modifies HEAD.

However even that doesn't work in most cases. What you are probably looking for is called rerere (short for "reuse recorded resolutions"). Enable it in the Git configuration:

git config rerere.enabled true

When it is enabled, it will record all resolved conflicts, allowing to replay them later. To automatically stage these recorded conflicts, you can also set rerere.autoupdate:

git config rerere.autoupdate true

Additionally, you can teach rerere about past merges and resolved conflict using the rerere-train.sh script which is often distributed with Git (on Ubuntu it is available at /usr/share/doc/git/contrib/rerere-train.sh). Call it before rebasing to avoid having to redo any previously resolved conflicts:

rerere-train.sh destnewbranch..srcbranch
git rebase destnewbranch srcbranch --onto destnewbranch --rebase-merges
Related