If you rebase, squash or amend, you will eventually need to force-push. The question is never whether force-pushing is evil - it is whether the push can silently delete someone else’s work. Plain --force can. --force-with-lease cannot.
The legitimate case#
Your PR branch, reviewed once, now rebased onto current main with the fixups squashed in. The remote still holds the pre-rebase version, and the only way to update it is a force-push. That is fine: the branch is yours, and nobody should be building on it.
What —force actually says#
git push --force
“Make the remote branch point here, whatever it points at now.” If a teammate pushed a commit to your branch since your last fetch - a reviewer fixing a typo through the web UI is the classic way - that commit is gone from the branch, and nobody was asked.
What —force-with-lease says#
git push --force-with-lease
“Make the remote branch point here, but only if it still points where my last fetch left it.” The lease is your local record of the remote ref. If the remote moved - anyone pushed anything - the push is refused and nothing is overwritten. You fetch, look at what arrived, rebase onto it, and push again.
For another layer, --force-if-includes additionally checks that the remote’s current commits are already part of what you are pushing, which catches some stale-reflog corner cases the lease alone misses.
The two rules that make it boring#
- Never force-push a branch other people build on. Main, release branches, long-lived feature branches: these get merges and reverts, not rewrites. Branch protection should simply forbid it there.
- On your own short-lived branches, always
--force-with-lease, never bare--force. Put it in muscle memory or in an alias, because the one time you reach for plain--forceis the one time the remote had a surprise waiting.
If the worst already happened and a force-push ate a commit, it is recoverable: the commit still exists on the machine that made it, and that machine’s reflog knows the hash. A git push <hash>:refs/heads/branch-name from there puts it back.
Share this post
Keep reading
Git Bisect: Find the Commit That Broke It
The tests passed last week and fail now. Bisect turns 'somewhere in these 40 commits' into six test runs.
The Reflog: Undoing Almost Anything in Git
Reset too far, deleted the wrong branch, botched a rebase? The reflog remembers where HEAD has been, and that is usually enough.
What Not to Commit in a Laravel Project
vendor, .env, caches and build output: the short list of what stays out of the repo, and the .gitignore that keeps it out.