The problem with --force
A plain git push --force overwrites the remote branch with whatever you have locally. If a teammate pushed in between your last fetch and your push, their commits are silently destroyed.
What --force-with-lease does
--force-with-lease only force-pushes if the remote branch is at the SHA you last fetched. If anything new has appeared on the remote, the push is rejected and you can investigate before clobbering work.
git push --force-with-lease origin feature/login
A worked example
# You rebase your branch:
git rebase origin/main
# Meanwhile, a teammate pushes a commit you missed.
git push --force # overwrites their commit silently
git push --force-with-lease # rejected:
# ! [rejected] feature/login -> feature/login (stale info)
You then git fetch, see the new commit, integrate it, and push again.
Specifying the expected SHA
For extra safety, pin the expected remote tip explicitly:
git push --force-with-lease=feature/login:abc123 origin feature/login
The push proceeds only if origin/feature/login is exactly at abc123.
Making it the default
Alias the command so you never accidentally use plain --force:
git config --global alias.pf 'push --force-with-lease'
# or for the truly cautious:
git config --global alias.force-push '!echo Use git pf instead'
The fetch trap
--force-with-lease compares against your local remote-tracking ref. If you ran git fetch right before pushing, you "saw" the new commits even if you did not look, and the lease will let you overwrite them.
# SAFER:
git fetch
git log origin/feature/login # actually look
git push --force-with-lease
Newer alternative: --force-if-includes
Git 2.30+ adds --force-if-includes, which checks that your local has integrated the remote's latest tip - regardless of whether you fetched recently:
git push --force-with-lease --force-if-includes
This closes the fetch-trap hole.
When you must use --force
Genuine cases: rewriting history on a branch that you and your tools agree is yours alone, or after a destructive event like leaked secrets where you must overwrite shared history. Coordinate with the team and document the action.
The discipline
Treat plain --force as a smell. Default to --force-with-lease, alias it, and read its rejections as gifts - it just told you about a teammate's commit you would have destroyed.