Search code examples
gitmerge-conflict-resolution

Git merge commit lost a parent, now I have to resolve the old conflicts again


I have 2 branches, say "dev" and "feature". A while ago I merged dev into feature resolving a ton of merge conflicts. I don't remember how it happened, but the merge commit lost the reference to one of the parent commits (probably some cherry-pick or rebase shenenigans). Now the feature branch has all the old code from dev, but when I try to merge dev into feature again to add the new changes that were added since, I'm asked to resolve the same conflicts that I resolved during the first merge.

So the question is, is there a way to do the 2nd merge without having to manually resolve all the conflicts from the first merge again? Ideally I would also like to fix the history in the process.

I tried doing

git replace --graft <merge commit with missing parent> $(git show --pretty="format:%P" --no-abbrev-commit --no-patch <merge commit with missing parent>) <missing parent>

as was suggested here, but doing git push -f after this does not seem to do anything, and the change remains local only. After this I could merge the new changes without resolving old conflicts, but if this replacement isn't actually pushed, I don't know if anyone would even have the same code after checking out the branch, so I'm afraid of pushing this now.

I also tried using rerere after running the rerere-train.sh script, but this does not actually help with any of the conflicts from the first merge.


Solution

  • I think I found a solution.

    1. Do the git replace that I already tried originally:
    git replace --graft <merge commit with missing parent> $(git show --pretty="format:%P" --no-abbrev-commit --no-patch <merge commit with missing parent>) <missing parent>
    
    1. Create a temporary branch from the one with the faulty merge so that you can view the git log later:
    git branch -c <faulty branch> temp
    
    1. Reset to a commit just before the broken merge:
    git reset --hard <last commit before the merge>
    
    1. Go to .git/refs/replace in a different window. There will be a file with the hash of the faulty merge commit as the name. The content of that file is the hash of the fixed commit generated in step 1 which you will need next.
    2. Merge the fixed commit from the previous step into the branch:
    git merge --ff-only <fixed commit>
    
    1. Cherry-pick the remaining (non-merge) commits to the new branch. You can use the temp branch from step 2 to get the hashes git log temp:
    git cherry-pick <first missing commit>^..<last missing commit>
    
    1. Delete the replacement file you accessed in step 4.

    At this point the branch should be fixed locally and can be force-pushed if needed, and the temp branch can be deleted.