6

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

Mart
  • 135
  • 1
  • 10
  • this usually happens when 2 branches' history diverges (usually due to a rebase) and ends up with the same changes in different diffs. I just did this myself, pushed it, and facepalmed, a few days ago. Then had to rebase again and fix the conflicts on every subsequent commit. I don't know a better solution, but looking forward to seeing what the group comes up with. – erik258 Aug 05 '20 at 15:49

1 Answers1

3

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
Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31
  • Tried git restore instead of git reset. It unfortunately gets rid of changes in F at every conflicted merge. Since I only had one file added in F, I restored that from F, just so I can continue... – Mart Aug 06 '20 at 07:36
  • ... I hit a roadblock with submodules however. Command git restore --source C -- . does not bring changes to submodule HEAD, no matter --recurse-submodules. I noticed it picking the next the commit immediately after C (I have more than the simplified example here) and rebase conflicted on the submodule. Tried git checkout --theirs submodule and ended up with $ git submodule status U0000000000000000000000000000000000000000. Fail. – Mart Aug 06 '20 at 07:36
  • Tried rerere too... During rerere-train.sh I am having issues with binary files: warning: Cannot merge binary files: system.hdf (ours vs. theirs) error: could not parse conflict hunks in 'system.hdf'. No surprise that these binary files do not get automatically resolved during the rebase merge. All in all, this is a giant fail. – Mart Aug 06 '20 at 18:10
  • 1
    `rerere`, like most other git features, doesn't support binary files. I believe there no easy way to automatically re-resolve conflicts in binary files. Perhaps you can avoid rebasing and use `git merge --allow-unrelated-histories` instead? – Etienne Laurin Aug 06 '20 at 18:30
  • Was trying to avoid git merge --allow-unrelated-histories, but it seems to be the most accesible solution. – Mart Aug 07 '20 at 07:31