Git Push --force-with-lease: The Safer Way to Force Push
What git push --force-with-lease does, how it differs from --force, when to use it after a rebase or amend, and why --force-if-includes makes it safer.
By PC MomentUpdated 7 min read

You rebased or amended a commit that was already on the remote, and now git push is rejected. You need to force the push, but plain --force can wipe out commits a teammate pushed a minute ago. git push --force-with-lease is the safer alternative: it only overwrites the remote branch if it still looks the way you last saw it.
For the most protection, use it together with --force-if-includes:
git push --force-with-lease --force-if-includes
The rest of this guide explains what each flag checks, when to use them, and what to do when the push is refused.
Why a normal push gets rejected
By default, git push only allows fast-forward updates: the new commit you push must be a descendant of the commit the remote branch points at now. That rule exists so nobody loses history by accident.
When you rewrite commits that were already pushed (with git rebase, git commit --amend or git reset), your local branch no longer contains the remote’s tip. Git rejects the push with a message like this:
! [rejected] main -> main (fetch first)
error: failed to push some refs to '...'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally.
or (non-fast-forward) if you have the remote commits but your branch diverged from them. To replace the published history with your rewritten one, you have to bypass that rule. The git manual puts it plainly: git push --force is reserved for when you really mean to lose history.
—force vs —force-with-lease
git push --force | git push --force-with-lease | |
|---|---|---|
| Overwrites a non-fast-forward branch | Yes | Yes |
| Checks the remote first | No | Yes: remote must match your remote-tracking branch |
| If a teammate pushed after your last fetch | Their commits are overwritten | Push is rejected as stale info |
| Applies to | Every ref being pushed | Every ref being pushed, each one checked |
What —force does
-f or --force turns off the check that the remote branch is an ancestor of what you push, plus Git’s other push safety checks. The documentation warns that it “can cause the remote repository to lose commits.” It also applies to every ref you push, so depending on your push.default setting it can overwrite more than your current branch.
What —force-with-lease does
--force-with-lease also lets you overwrite a branch that isn’t a fast-forward, but only if the remote branch currently points at the commit you expect. Without extra arguments, the expected value is your remote-tracking branch (for example origin/feature), which is where the remote branch was the last time you fetched.
The git manual compares it to taking a “lease” on the branch without locking it. If someone pushed new commits since your last fetch, the remote no longer matches origin/feature, the lease is broken, and Git refuses:
! [rejected] main -> main (stale info)
error: failed to push some refs to '...'
Nothing on the remote changes, so your teammate’s work is safe.
When to use git push —force-with-lease
Typical situations:
- After a rebase. You rebased your feature branch onto the latest
mainand need to update your pull request branch. - After amending. You ran
git commit --amendon a commit you had already pushed. See git undo last commit for when amending is the right tool. - After squashing or cleaning up commits with an interactive rebase before a review.
- After a reset on your own branch to drop a commit you pushed by mistake.
Important: Any force push rewrites history that other people may have pulled. The Pro Git book’s rule is: do not rebase commits that exist outside your repository and that people may have based work on. Use force pushes on your own feature branches, or on shared branches only when everyone working on them knows it’s coming. For a shared
mainbranch, prefergit revert, which undoes a commit without rewriting history.
How to force push safely, step by step
-
Make sure you’re on the right branch and see what’s going on:
git status git log --oneline --graph -10This confirms the branch name and shows your rewritten commits.
-
Push with the lease, naming the remote and branch so you only touch one ref:
git push --force-with-lease origin featureReplace
featurewith your branch name. If the remote branch still matchesorigin/feature, Git replaces it with your local branch and prints(forced update). -
If the push is rejected with
stale info, go to what to do when the lease fails below. Don’t switch to--force.
Protect one branch explicitly
You can tell Git exactly which branch to protect, and even which commit you expect it to be at:
git push --force-with-lease=feature origin feature
This protects only feature, checking it against your remote-tracking branch for it.
git push --force-with-lease=feature:3f2a1bc origin feature
This protects feature and requires the remote to point at exactly commit 3f2a1bc, whatever your remote-tracking branch says. If the expected value is empty (--force-with-lease=feature:), the push only succeeds if the branch doesn’t exist on the remote yet.
The git documentation notes that every form except this last one, the one with an explicit expected commit, is still considered experimental and its behavior may change.
The background fetch problem, and —force-if-includes
There’s one way --force-with-lease can be fooled. Its check relies on your remote-tracking branch. If anything runs git fetch before you push (you, a script, or an editor or IDE that fetches in the background), your remote-tracking branch moves to the remote’s new tip. The lease then matches, and the push overwrites your teammate’s commits even though you never looked at them.
The git manual warns about this directly: the option “interacts very badly” with anything that fetches in the background.
--force-if-includes closes that gap. Added in Git 2.30, it forces an update only if the tip of the remote-tracking branch has been integrated locally. It checks that the remote-tracking tip can be reached from an entry in your local branch’s reflog, which proves your rewritten branch was built after you’d seen those commits.
git push --force-with-lease --force-if-includes origin feature
If a background fetch brought in commits you haven’t integrated, the push is refused:
! [rejected] main -> main (remote ref updated since checkout)
error: failed to push some refs to '...'
hint: Updates were rejected because the tip of the remote-tracking branch has
hint: been updated since the last checkout.
Two details from the docs:
--force-if-includesdoes nothing on its own. It only works alongside--force-with-leaseor--force-with-lease=<refname>.- It is also a no-op with the explicit form
--force-with-lease=<refname>:<expect>, because there you’ve already said exactly what you expect.
Make it the default
To have Git add --force-if-includes every time you use --force-with-lease:
git config --global push.useForceIfIncludes true
This sets push.useForceIfIncludes for your user account. You can still turn it off for a single push with --no-force-if-includes.
Tip: You can also add an alias so the safe version is the easy one to type, for example
git config --global alias.pushf "push --force-with-lease --force-if-includes". Thengit pushfdoes the safe force push.
What to do when the lease fails
A rejected lease means someone else pushed to the branch. Don’t reach for --force. Instead:
-
Fetch the new commits:
git fetch origin -
Look at what arrived:
git log --oneline feature..origin/featureThis lists commits that are on the remote branch but not in your local branch.
-
Integrate them, usually by rebasing your work on top:
git rebase origin/featureResolve any conflicts, then
git addthe fixed files and rungit rebase --continue. -
Push again with the lease:
git push --force-with-lease --force-if-includes origin featureNow that your branch contains your teammate’s commits, the check passes.
Force pushing on GitHub
On GitHub, protected branches block force pushes by default. If your push is rejected by the server even with --force-with-lease, the branch is probably protected. A repository admin can allow force pushes for everyone with write access or only for specific people or teams, but for a branch like main it’s usually better to leave the protection in place and use git revert instead.
If it still doesn’t work
fatal: The current branch has no upstream branch.The branch was never pushed, so there’s nothing to force. Push it normally with-u; see git push set upstream.error: src refspec ... does not match anyThe branch name you typed doesn’t exist locally. Check it withgit branch, and see our fix for error: src refspec main does not match any.fatal: not a git repositoryYour terminal isn’t inside the project folder, so Git can’t find the branch at all. See fatal: not a git repository.- Lease keeps failing even though nobody else is working on the branch: run
git fetchand comparegit log origin/featurewith your branch. A CI job or bot may be pushing to it. - You force-pushed and lost a teammate’s commits. Their commits still exist in their own clone. Ask them not to pull yet; they can fetch your new branch, rebase their commits onto it and push again. The forced-push output (
+ oldtip...newtip) also shows which commit the remote pointed at before.
For more Git and developer guides, see the programming section.
Frequently asked questions
What is the difference between --force and --force-with-lease?
--force overwrites the remote branch no matter what is on it. --force-with-lease only overwrites it if the remote branch still points where your remote-tracking branch says it does, so it refuses when someone pushed new commits you haven't seen.
When should I use git push --force-with-lease?
After you rewrite commits that were already pushed, for example with git rebase or git commit --amend, on a branch where you know who else is working. Never use any kind of force push to rewrite a shared main branch without agreement from your team.
Why did my push fail with stale info?
The remote branch moved since you last fetched, so the lease check failed. Fetch, look at the new commits, integrate them, and push again.
What does --force-if-includes do?
Used together with --force-with-lease, it also checks that the remote-tracking branch's tip appears in your local branch's reflog, so a background git fetch can't silently make the lease pass. It was added in Git 2.30.
Can I make --force-if-includes the default?
Yes. Set push.useForceIfIncludes to true with git config. It then applies whenever you push with --force-with-lease.
Related guides

Programming
Git Push Set Upstream: Fix "The Current Branch Has No Upstream Branch"
Fix "fatal: The current branch has no upstream branch" with git push -u, set tracking on an existing branch, and make Git set the upstream for you.
5 min read

Programming
Error: src refspec main does not match any (and master): How to Fix It
Git can't push main or master because that branch doesn't exist locally yet. Make your first commit, match the branch name, and push again.
5 min read

Programming
Git Undo Last Commit: Reset, Revert, Amend and How to Get It Back
Undo your last Git commit the right way: keep the changes, throw them away, or reverse a commit you already pushed, plus how to recover from a bad reset.
7 min read

Programming
Fatal: Not a Git Repository: What It Means and How to Fix It
Git says "fatal: not a git repository" when it can't find a .git folder. Here's how to find the right folder, start a repository, and fix GIT_DIR, submodule and ownership problems.
7 min read