Force Pushing Safely with Git

Git's ability to rewrite a commit history is one my favourite features. When I'm coding, I regularity commit whenever I hit a minor milestone. Later, I might realize that there was some better solution than my original approach, or that my code had some bug in it. So while a series of commits I make may be a logical progression of steps, sharing all these steps with other developers is not necessary.

Enter git rebase. With it, I can rewrite my chain of commits into something that is more useful to others. Rewriting your commit history locally is no problem. Even if you really screw something up and accidentally drop a commit, you can use the git reflog command to recover it.

However, once you start pushing your changes to remote repository, things start becoming a bit more dangerous. Because of this, if you try to push to a remote with a commit history that is different than the remote one, you'll see an error message like the following.

! [rejected] master -> master (non-fast-forward)

This is because pushing to rewrite history is a potentially dangerous operation, and can result in overwriting someone else's work. If you want Git to update the remote anyways, you can do git push --force (watch out, this has the potential to force push all branches) or better yet git push origin +branch-name. However, using this option has always been something to do with care, and only after co-ordinating with other developers to ensure nothing gets accidentally lost.

Still, if you force push, you will inevitably force push something you shouldn't, and accidentally clobber someone else's work. Because of Git's distributed nature, you should be able to recover the lost work by coordinating with the other developer, but this is a waste of time and frankly embarrassing.

Since Git 1.8.5, there's a better way to force push, git push --force-with-lease. The Git documentation describes this option, but I found it rather challenging to understand.

In a nutshell, using --force-with-lease checks that your local copy of the remote branch is the same as the actual remote branch. This should prevent you in most cases from unexpectedly overwriting the origin.The only gotcha I see is if you do a git fetch and then without incorporating the fetched changes into your branch, do a git push --force-with-lease, you'll overwrite the remote changes. So you should still be careful even when using --force-with-lease, but it is an nice safety net to have to protect yourself from making embracing mistakes.


About Paul

Co-founder of Doorkeeper. Based in Tokyo. Read more about being a Japan based entrepreneur and developer at Tokyo Dev. You can also follow him on Twitter.

Become a better developer

Get tips on becoming a better and faster developer, straight to your inbox.