First of all, don’t panic. This can be fixed easily or relatively easily!

However, let’s get this out of the way:

ಠ_ಠ

Why you push -f master?

Okay, now that we have that out of the way, let’s go over the ways to fix this.

Local Commit Reference

If you have the commits locally, you can simply repush the correct commit SHA back to master. Your accidental force push should print out the following:

To https://github.com/#{YOUR_ORG}/#{YOUR_REPO}.git
+ #{PREVIOUS_MASTER_SHA}...#{NEW_MASTER_SHA} master -> master (forced update)

You can tell Git to update the reference of a remote branch with a colon by pushing again to master with

git push origin #{PREVIOUS_MASTER_SHA}:master -f

Easy, right?

Remote Reference

What’s not so easy is if you accidentally force push master to GitHub and you haven't fetched recently. Git will not let you push a reference you don’t have locally, and it won’t let you fetch commits that cannot be reached from some named reference like a branch.

Fortunately, GitHub has an API that you can easily hit through everyone’s favorite command line data transfer tool, curl.

You’ll need the same information as in the previous section regarding the SHAs, but you’ll need to jump through one more hoop. PREVIOUS_MASTER_SHA is actually only the first 7 characters of the commit’s SHA so to get the full thing, we’re going to have to go to GitHub. Just pop over to https://github.com/#{YOUR_ORG}/#{YOUR_REPO}/commit/#{PREVIOUS_MASTER_SHA} and you’ll see the full SHA. Grab that, head over to your terminal, and put in the following:

curl -u #{YOUR_GITHUB_USERNAME} --request PATCH https://api.github.com/repos/#{YOUR_ORG}/#{YOUR_REPO}/git/refs/heads/master --data '{"sha": "#{FULL_PREVIOUS_MASTER_SHA}", "force": true}'

Enter your password, and everything should be good.

An ounce of prevention is worth a pound of cure

Find out why you accidentally force pushed to master, and learn from it. What is your Git config’s push value is set to? The default is matching which can get you into trouble if you just do a blank git push -f. This is changing to simple in Git 2.0 but current is another good choice. More options can be found in Git's man pages or at http://git-scm.com/docs/git-config.

Also be careful with aliases that make it too easy to use flags you might not want. I personally use gush for git push which requires me to explicitly type -f, but many have set up an alias like gpf=‘git push -f’. It can save a little time but can easily get you in a bind.

As Uncle Ben said:

“With great power comes great responsibility.”

Use -f carefully.

— Aaron Rosenberg (@agrberg)

mwahahaha