The safe way to force push in Git is git push --force-with-lease, not git push --force. The lease version refuses to push if the remote branch moved since you last fetched it, so you cannot silently overwrite a commit a teammate pushed while you were rebasing. Plain --force does no such check: it overwrites whatever is on the remote, full stop.
# The safe default: refuses if someone else pushed since your last fetch
git push --force-with-lease origin my-branchThat one substitution is most of the lesson. The rest of this page is when you actually need to force push at all, why --force is a foot-gun on a shared branch, and the two stronger variants for when you want the check to be airtight.
When you need to force push
You only need a force push after you have rewritten history that you already pushed. Normal pushes fast-forward: the remote tip is an ancestor of your local tip, so Git just moves the pointer. Rewriting breaks that ancestry, and Git rejects the push to protect the remote. The common causes:
git rebasemoved your commits onto a new base, so they have new SHAs.git commit --amendchanged the last commit (message or content), giving it a new SHA.- An interactive rebase to squash commits collapsed several commits into one.
In every case your local branch and the remote branch have diverged in a way that is not a fast-forward, and Git tells you so:
! [rejected] my-branch -> my-branch (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behindThat rejection is correct. The fix is not to reach for --force reflexively; it is to force push safely.
Why plain --force is dangerous
git push --force (and its short forms -f and the git push origin +my-branch plus-prefix trick) all do the same blind thing: they tell the remote to set the branch to your local commit no matter what is there now. If a teammate pushed a commit to that branch since you last fetched, you have not seen it, and --force overwrites it. Their work is gone from the branch tip. It still exists in their local reflog and possibly in the remote's reflog, but as far as the shared branch is concerned you just erased it.
# Avoid: overwrites the remote unconditionally, including commits you never fetched
git push --force origin my-branchThe danger is precisely that it works even when you are wrong about the remote state. Nothing warns you.
--force-with-lease is the safe version
--force-with-lease adds the check --force is missing. It records what your local copy thinks the remote tip is (your remote-tracking ref, refs/remotes/origin/my-branch, set the last time you fetched) and refuses the push if the real remote tip no longer matches. So if a teammate pushed since your last fetch, the lease is broken and the push is rejected instead of clobbering their commit.
git push --force-with-lease origin my-branchIf it is rejected, that is the system doing its job: you fetch, see the commit you would have destroyed, reconcile (rebase your work on top, or talk to whoever pushed), and try again. A rejected lease has never lost anyone's work; an accepted --force regularly has.
The airtight form: pin the expected SHA
The bare --force-with-lease trusts your remote-tracking ref. You can make it stricter by stating exactly which remote SHA you expect to overwrite. If the remote is not at that SHA, the push fails:
# Refuse unless origin/my-branch is exactly this commit
git push --force-with-lease=my-branch:abc1234 origin my-branchThis is the form to use in scripts and CI, where you do not want to depend on whatever happens to be in the remote-tracking ref at push time. You read the SHA you intend to replace, pass it explicitly, and the push is a no-op-or-fail rather than a guess.
--force-if-includes closes the lease gotcha
There is one way the bare lease can betray you: a background fetch. Tools like IDEs, git maintenance, and some shell prompts run git fetch on their own. A fetch updates your remote-tracking ref without integrating the new commit into your local branch. Now origin/my-branch matches the real remote, so the lease passes, but you never actually saw or merged the teammate's commit. --force-with-lease is satisfied and you overwrite it anyway.
--force-if-includes (Git 2.30+, December 2020) plugs that hole. Combined with the lease, it requires that the remote tip was actually integrated into your local branch, not merely fetched into a tracking ref:
git push --force-with-lease --force-if-includes origin my-branchPer the git-push documentation, passing --force-if-includes without --force-with-lease is a no-op, so the two go together. This pair is the strongest practical safety you can put on a force push, and it is worth aliasing as your default. If your Git is older than 2.30, --force-with-lease alone is still a large improvement over --force.
Never force-push a shared branch
No variant of force push makes it acceptable to rewrite main, master, develop, or any branch other people build on. Rewriting a shared branch changes commit SHAs out from under everyone who has pulled it; their next pull becomes a divergent mess and they have to recover by hand. The lease does not save you here, because it only guards against unfetched remote commits, not against the social cost of rewriting history that dozens of clones already contain.
The rule is simple: force push is for your own feature or pull-request branch, after you rebased or amended it, when you are the only one working on it. To undo something on a shared branch, do not rewrite it; make a new commit with git revert instead. See undo things in Git for which tool fits which situation, and essential Git branch commands for keeping branches straight in the first place.
A good habit: set the safe form as an alias so you never type plain --force out of muscle memory.
git config --global alias.fpush "push --force-with-lease --force-if-includes"
# then just: git fpush origin my-branchFAQ
See also
- How to undo things in Git: the practical guide to restore, reset, revert, and amend, including when to revert instead of force-pushing.
- How to squash Git commits with interactive rebase: the most common reason you end up needing a safe force push.
- Essential Git branch commands: current, default, previous, and deleting branches, so you force-push the right one.
Sources
Authoritative references this article was fact-checked against.
- git-push (official Git documentation)git-scm.com
- Git 2.30.0 release notes (2020-12-27)git-scm.com





