Here is the situation I ran into this morning. I was happily writing code, periodically pulling changes from GitHub, like a good developer that I am. When I was finally ready to push, I received a merge from GitHub that deleted most of my Django project files. Turns out that one of the other developer on the team accidentally deleted those files and committed it locally. He didn’t push all the commits up to GitHub until just before I tried to push mine (hence I didn’t get any merges from him all morning).
So now I’m in this state where my local repo has a number of commits that I need to push up. But I can’t because I don’t want to merge in all the accidental deletes. I need to remove/fix the deletion first before I can push. How do I do that? After a few minutes on Google, I got the general approach of using git rebase but nothing that points me to actual steps that I think will fix my problem. Then I turned to Twitter, posted a question, and waited while I did some more googling. Low and behold, I received tips from @XTZGZoReX on a possible approach and after I tried it out, it works perfectly!
Here is how:
- Leave the existing local repo alone. You may want to reset it back to the pre-merge state by doing
git reset --hard
- Make a fresh clone of the repo from GitHub. Let’s call it repo2.
- Perform an interactive rebase on repo2 and set the starting point to one commit before the one you want to nuke:
git rebase -i
- The interactive rebase will open up a text editor (vi if you haven’t changed the default configuration) with a list of all the commits starting from the commit you want to nuke to the latest.
- Remove the line with the commit you want to nuke (‘dd’ keys in vi) and save the file (‘:wq’ in vi)
- Git will now try to apply all the subsequent commits and roll forward to the latest state. In my case, I needed to manually add and remove some files on some of the commits. After each git add/remove, you need to tell git to continue the rebase:
git rebase --continue
- When all the commits are roll-forwarded by git, push it up to GitHub by doing a force push:
git push -f
- Now, switch back to your original repo and pull the latest from GitHub. Good chance that you will get merge conflicts. Resolve, commit, and push. And you are done!