Git: Force push safely with --force-with-lease and --force-if-includes

Push push push.

This post is an adapted extract from my book Boost Your Git DX, available now.

When you push, Git checks that you are only adding commits to the remote branch. If you try to push an out-of-date branch, it will fail:

$ git push
To github.com:adamchainz/example.git
 ! [rejected]        camembert -> camembert (non-fast-forward)
error: failed to push some refs to 'github.com:adamchainz/example.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

This protection kicks in after you remove or rebuild commits in your local branch, such as after git commit --amend or git rebase.

Git says the push is “non-fast-forward”. Fast-forward pushes only add to the branch history, like fast-forwarding a movie to the next scene. Non-fast-forward pushes remove, or “rewind”, some commits before adding new ones.

The output hint tells you to merge the remote changes with git pull. Sometimes, this is what you want to do, such as when pushing to a shared release branch. But sometimes, it’s not, such as when you have amended a commit, rebased your branch, or removed commits. In those cases, use the Jedi-esque force push, enabled with -f (--force):

$ git push -f
...
To github.com:adamchainz/example.git
 + 1fd4fb8...e025fa5 camembert -> camembert (forced update)

A force push bypasses the fast-forward protection and always updates the remote branch, no questions asked. That allows you to proceed, although it carries the risk of potentially losing work. If someone else has pushed new commits to the remote branch, a force push will remove them without any indication!

To guard against unintentional commit removals, use the alternative option --force-with-lease. This mode requires that you have already fetched the remote branch’s latest commit.

Improve the “lease” protection with --force-if-includes. This option requires that you have also checked out the remote branch’s latest commit.

--force-with-lease provides some guarantee that you have considered any extra commits on the remote. But its protection is weak because you can fetch a commit without looking at it. --force-if-includes adds strength because it also ensures you have checked out the latest commit, as would happen with a git pull. Thus, you would have considered it when taking an action like rebasing.

Use both protective options together:

$ git push --force-with-lease --force-if-includes

That’s a bit long to type. It’s best to wrap it up in a shell alias.

oh-my-zsh’s Git aliases have this available as:

$ gpf

Short for “git push force”.

Fin

May you never lose commits,

—Adam


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: