Search code examples
gitgit-rebasegit-rewrite-history

Git rebase with --preserve-merges fails to reapply merge commits due to conflicts


Assume the following git history

 M (master) <- merge that required resolving a conflict, e.g. coming from F and G
 | \
 |  |
 F  G
 |  | 
 C  D  <- commit to edit (D), unrelated to the merge conflict (e.g. adding a new file)
 |  |
 | /
 B
 |
 A  <- initial commit

What I'm trying to achieve is to edit the commit D (with rewriting the history). I'm using the command git rebase --interactive --preserve-merges A master, marking the commit D to be edited, edit it when rebasing stops, amend the commit and git rebase --continue.

What I'm receiving on the point before the M commit is a request to resolve conflicts. The same conflicts were already resolved when merging branches to M, thus I expected the rebase to go smoothly.

$ git rebase --continue
Auto-merging yet-another-test.txt
CONFLICT (content): Merge conflict in yet-another-test.txt
Automatic merge failed; fix conflicts and then commit the result.
Error redoing merge d01ffb203aae9ef450ae7a863de5fce2ed5184bb

You can find the example here, you can try e.g. git rebase -i -p 8b58 master and edit the 30c7 commit

The reason I'm asking is because the git history of my real repo is quite complicated with a lot of merges along the way (around 300 commits in total) and the commit I need to edit is close to the initial commit, so I'd like to preserve the history (avoid squashing) but I'd rather not resolve all the conflicts again.

Is there a better way of doing this?

Tried on git for windows 2.17.1 (recent).


Solution

  • Git doesn't record previous merge resolutions by default, which is why you are presented with the exact same conflict when rebase has to recreate a merge commit:

    Error redoing merge d01ffb203aae9ef450ae7a863de5fce2ed5184bb
    

    This is where git-rerere (reuse recorded resolution) comes in:

    This command assists the developer in this process by recording conflicted automerge results and corresponding hand resolve results on the initial manual merge, and applying previously recorded hand resolutions to their corresponding automerge results.

    For Git to start recording merge resolutions, you first to need to enable rerere in your repository (or globally) by saying:

    git config rerere.enabled true
    

    or

    git config --global rerere.enabled true
    

    Unfortunately, it won't help you save time in this particular instance—since you've already resolved the conflicts in the various merge commits while it was off—but it will make a difference going forward.

    Edit: actually, there seems to be a way to record previous resolutions from existing merge commits by running a shell script called rerere-train. I haven't tried this myself though, so YMMV.