0

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.

  • How about do a `merge` again but keep all files same with feature? `git checkout feature && git merge -s ours dev` – banyudu Apr 21 '23 at 03:40
  • @banyudu There are new merge conflicts from the new changes, so this wouldn't work. I already posted a solution for how I ended up doing it. Took me a few hours to figure out. – Dmitri Ranfft Apr 21 '23 at 03:50
  • 1
    There won't be merge conflicts if you specify `-s ours` option – banyudu Apr 21 '23 at 04:06
  • @banyudu They will still be there, it's just that git will use `-s ours` to resolve them. But this will sometimes result in incorrect resolution. – Dmitri Ranfft Apr 21 '23 at 07:16
  • @DmitriRanfft maybe you're thinking of `-X ours`? With `-s ours` it's not possible to have conflicts. Of course, you won't get any of the changes either, you only get the commit IDs. ;) – TTT Apr 21 '23 at 15:58
  • @TTT Indeed I was. I apologize, @banyudu . From what I understand of `-s ours` after reading the docs, it wouldn't merge the new changes. But if I rolled back the dev branch, did another merge at the same state as the first merge, and then restored the dev branch to merge again with the new changes, it could work. – Dmitri Ranfft Apr 22 '23 at 03:25

1 Answers1

0

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.