git push --force and how to deal with it
Have you ever found yourself in a situation where the wrong git command has wreaked havoc on your project’s repo? People make mistakes, and sometimes those mistakes can cost hours of your team’s time. In this tutorial, we’ll show you how to quickly recover from an unfortunate
git push --force.
If you’re thinking this doesn’t sound like something you’d do, well, let’s not get overly confident. Sooner or later, this is going to happen. While working with several remotes in the same git repository, you will eventually
git push --force into
master (or another important branch that should’ve never been messed with).
That could happen, for instance, when deploying with Heroku, which use separate git remotes to build and deploy an application. After a long day of work, it could be incredibly easy to execute
git push --force instead of the usual
git push --force deis master.
Oops! In the blink of an eye, your teammates have lost all their latest work. Time to face their rage.
However, as one excellent guide tells us, DON’T PANIC! The upside is, you’re using git, and that means everything can be fixed.
Best case scenario: someone else working on the same code pulled a recent version of the
masterbranch just before you broke it. Here, all you have to do is to go into your team chat and ask this person to force push their recent changes.
If you’re lucky, their local repository will have the full history of commits, your mistake will be overwritten with fresh code, and nothing will be lost. But what if you’re not so lucky? Then, read on!
Case 1: You were the last person to push to
master before the mistake
Good news! You have everything you need to undo your mistake before your very eyes. Just do not close or clear your terminal. First, go into your team’s chat and confess your sins. Ask people not to mess with the repo for the next minute or so while you’re fixing things.
Inside your shell, look at the output of
git push --force and try to locate a line that resembles this one:
+ deadbeef...f00f00ba master -> master (forced update)
The first group of symbols (which looks like a commit’s SHA prefix) is the key to pulling off this rescue operation. In this case,
deadbeef is your last good commit to the
master just before you inflicted this unfortunate damage.
So all you need is to… force push (fighting fire with fire!) this commit back to the
master branch, on top of the bad one.
$ git push --force origin deadbeef:master
Congratulations! You’ve saved the day. Now, it’s time to learn from your mistakes.
master was changed by someone else before you messed up
So, let’s say that just before you performed
git push --force, someone had closed a bunch of pull requests, and so
master now looks nothing like your local copy. You can no longer do
git push --force sha1:master because you do not have the recent commits locally (and you can’t get them with
git fetch because they no longer belong to any branch). Nevertheless, keep calm and ask your teammates to stay off the remote for a while.
We will benefit from the fact that GitHub does not immediately remove unreachable commits. Of course, you also can’t fetch them, but there is a workaround.
But, note that this will only work if you are “watching” the repository (meaning that everything happening in it appears in a feed displayed on your GitHub’s front page). Open this feed and look for something like this:
an hour ago Username pushed to master at org/repo - deadbeef Implement foo - deadf00d Fix bar
Now you can:
- Compose a URL
deadbeefis the hash of the last good commit to the damaged branch
- Open the branches/tags switcher in GitHub’s web UI
- Create a name for a new temporary branch (e.g.,
- Click “Create branch”
Now you can fetch all missing commits:
$ git fetch From github.com:org/repo * [new branch] master-before-force-push -> origin/master-before-force-push
With this, your problem has now been reduced to the one described in the previous case:
$ git push --force origin origin/master-before-force-push:master
If you still need your work to be in the
master, just rebase on top of it:
$ git rebase origin/master
How to avoid such disaster in the future
GitHub and GitLab have a feature called “protected branches.” We can mark
stable, or any other crucial branches as protected and no one will be allowed to force push into them. And, it’s always possible to temporarily “unprotect” a branch if you really need to overwrite history.
Read the GitHub docs for more details.
Instead of the
--force-with-lease. It will halt the push operation if someone has pushed to the same branch while you were working on it (and haven’t pulled any changes).
See this article from Atlassian.
Create aliases for commands that use
git push --forceto prevent destructive actions by accident:
# ~/.gitconfig [alias] deploy = "!git push --force deis \"$(git rev-parse --abbrev-ref HEAD):master\""
Read the ProGit chapter about aliases.
Never do your experiments in the main branch of a repository!
git checkout -b experimentsis your best friend.
git push has many available options.
--all work together especially well. You can use this combo when returning to the project after several months of inactivity. Give it a try if you’re feeling extra adventurous!
Well, that wraps things up. Hopefully, you’re prepared if you should ever face this dilemma again! And now a small message from Evil Martians: if you have a problem or project in need: frontend, backend, SRE services, blockchain, or ML—Evil Martians are ready to help! Give us a shout!