I have a commit graph that looks something like the one below. Commits marked with *
represent lots of commits.
A*
|
B---------
| |
C* D* <- old feature branch
| |
E---------
|
F*
|
G <- master
The merge commit E was done incorrectly and some changes (not all) in C* have been lost. How can I redo that merge to reintroduce the changes into the current master?
Everything has already been pushed (open source project), so changing history is not an option.
I tried creating a patch from the commits C* and apply that to the master, but because some of the changes from C* have been merged correctly and because the project evolved since that commit, about 80% of the patch fails.
Ideally, we would take all the changes in C*, apply them to master and resolve all conflicts. But because the branches have already been merged, git doesn't detect any changes and won't allow to merge again.
$ git checkout 5bc5295 # C
HEAD is now at 5bc5295... cleanup
$ git checkout -b "missing-commits"
Switched to a new branch 'missing-commits'
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge missing-commits
Already up-to-date.
Ideally, we would take all the changes in C*, apply them to master and resolve all conflicts.
No, ideally you would rewind time, do the merge as it was back then (but correctly, this time), then reapply the F*
changes and end up with a correct master
.
You can do it like this:
git checkout missing-commits
git checkout -b correct-merge
git merge D # do it right, this time around!
git checkout master
git checkout -b correct-master
git rebase --onto correct-merge wrong-merge correct-master # have fun with the mother of all rebases!
If you manage to handle all the conflicts during the rebase, you will end up with exactly what you wanted, originally, in the branch correct-master
.
Since you do not want to change history, you could then create a big patch and apply that to your current master
, but I like this approach more (assuming you did all your work in your working directory yourrepos
):
cd yourrepos ; git checkout correct-master ; cd ..
cp -a yourrepos newrepos
rm -rf newrepos/.git
cd yourrepos ; git checkout master ; cd ..
cp -a yourrepos/.git newrepos/
Now, when you enter newrepos
and do a git status
, you will be on branch master
and will see precisely all changes between master
and correct-master
, as if you had applied a patch. It will catch deleted files, new files, changed files, changed permissions, changed symlinks and so on.
Ideally, if everything went right, it will show exactly the missing commits from C
. Use your favourite variant of git add
and finally a nice git commit
(or several, if you prefer), and you're done. No history has been rewritten.
You can try by a cherry-pick:
git checkout master
git cherry-pick B..[last_commit_of_C*]
Git may ask you to confirm the cherry-pick since the commits are in the ancestors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With