Reverting a pull request completely

LearningJrDev picture LearningJrDev · Sep 30, 2016 · Viewed 11.8k times · Source

Current master has been worked on for the past month. Also a branch called contract has been worked on simultaneously.

contract was merged into master by mistake. 100 commits, with dates that overlap master commits that already existed.

How can I completely remove the pull request that merged all 100 commits from contract into master?

We did a GitHub revert on the pull request, and that removed the code but not the history. Now when we attempt to do a mock pull request of merging contract into master it shows there are no changes. (I assume because those commits already went in but were reverted?)

There have been no commits on master after the merge aside from the revert.

How can I completely remove any commits added by the pull request, and still allow them to go in successfully when we eventually do merge contract into master?

Answer

Scott Weldon picture Scott Weldon · Sep 30, 2016

Your history might look something like this:

*--*--*--C---A--B [master]
 \          /
  *--*--*--*--* [contract]

Where A is the bad merge, B is the revert, and C is the last good commit before the merge. (To determine what the hashes are for your case, do e.g. git log --oneline --decorate.)

When you revert a merge, Git takes that to mean that you never want those changes to be re-merged into the repository. If you were just working from the command line, the solution is to revert the revert, see: Re-doing a reverted merge in Git. However, that would prevent you from using GitHub's PR UI.

Instead, you can rewrite history. Obligatory WARNING: rewriting history can be dangerous and disruptive for other developers using your repository. Be sure to communicate clearly that you are doing so.

If you haven't already, switch to master (git checkout master).

If you don't have any commits after C aside from A, B, and any commits from contract, then do:

git reset --hard HEAD~2

Or:

git reset --hard C

Then your history will look like this:

*--*--*--C [master]
 \          
  *--*--*--*--* [contract]

If there have been more commits in the meantime, then you will have to rebase instead. Do:

git rebase -i X

In the rebase-todo file, delete any lines that correspond to the contract branch, as well as the revert commit B. (In my testing, the merge commit A wasn't shown, but remove that if it is.) If there are any conflicts, fix them and continue with git rebase --continue.

After you're done rewriting history, do:

git push --force-with-lease origin master

Your master branch will now have no evidence that the merge ever took place, and a PR from contract should show all the expected commits.