Force push
Advanced
Why this matters
Force pushing is the one everyday command that can destroy someone else's work. It is also completely routine on your own feature branch after a rebase. Knowing which situation you are in, and using the safer flag, is the whole skill.
Why the push is rejected
Rewrite a commit that is already on the server, and the ordinary push is refused:
$ git commit --amend -m "docs: add Windows steps to installation guide (#12)"
$ git push ! [rejected] docs/12-windows-install-steps -> docs/12-windows-install-steps (non-fast-forward)
error: failed to push some refs to '/srv/git/trailguide.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.The rejection is not a bug. Your branch and the remote's branch no longer share a straight line: the amended commit has a new hash, so from the server's point of view you are asking it to abandon a commit it has (lesson 7.6).
Following the hint here is wrong. git pull would merge the old version back in, giving you both copies. What you want is to tell the server "yes, replace it".
--force-with-lease, not --force
$ git push --force-with-leaseTo /srv/git/trailguide.git
+ 4e2c083...5181191 docs/12-windows-install-steps -> docs/12-windows-install-steps (forced update)The + and "forced update" mean the branch was moved rather than extended.
The difference between the two flags is one check:
| Flag | Behaviour |
|---|---|
--force |
Overwrite the remote branch, whatever is there |
--force-with-lease |
Overwrite only if the remote is still where you last saw it |
So if a colleague pushed to the same branch since your last fetch, --force-with-lease refuses:
! [rejected] docs/12-windows-install-steps -> docs/12-windows-install-steps (stale info)
error: failed to push some refs to '/srv/git/trailguide.git'"Stale info" means your idea of the remote is out of date, which is precisely the situation in which forcing would destroy something. Fetch, look at what arrived, and decide.
When forcing is fine, and when it is not
| Branch | Force push? |
|---|---|
| Your own feature branch, nobody else has checked it out | Yes, routinely, after a rebase or an amend |
| A feature branch with an open merge request under review | Careful: it detaches review comments from their commits (lesson 9.8) |
| A branch a colleague is also working on | Only after agreeing it, out loud |
main or any protected branch |
No, and the platform will refuse anyway |
The last row is why protected branches exist. If your team has not protected main, that is worth raising (lesson 9.5).
When someone force-pushed over you
This is recoverable, and quickly. Suppose Ben pushed a commit and Ana then forced over the branch:
$ git fetch
$ git status -sb## docs/12-windows-install-steps...origin/docs/12-windows-install-steps [ahead 2, behind 1]"Ahead 2, behind 1" on a branch nobody else should have rewritten is the signature. His commit is gone from the remote:
$ git log --oneline -2 origin/docs/12-windows-install-steps36659a0 docs: add Windows steps to installation guide (#12)
5e98836 chore: import the trailguide projectBut it is in his own reflog, because his clone still remembers making it:
$ git reflog -3de231bf HEAD@{0}: commit: docs: note the PowerShell version
5181191 HEAD@{1}: checkout: moving from main to docs/12-windows-install-steps
5e98836 HEAD@{2}: clone: from /srv/git/trailguide.gitThe recovery:
git branch rescue/powershell-note de231bfto keep the commit safely.- Talk to whoever forced, so it does not happen twice.
git reset --hard origin/<branch>to match the remote.git cherry-pick de231bfto put the commit back on top.- Push normally.
Nothing was lost. It was, however, five minutes and a conversation, which is the actual cost of a careless force push.
How to do it
$ git push --force-with-leaseBefore it, always:
$ git fetch
$ git log --oneline origin/<branch> -3If those three commits are all yours, forcing is safe. If one of them is not, stop.
The Sync button will not force. Force pushing must be enabled in settings (git.allowForcePush), and git.useForcePushWithLease is on by default, so the Push (Force) command uses the safer form. The prompt says which branch it is about to overwrite.
Git → Push, then the dropdown next to the Push button offers Force Push, with a confirmation dialog naming the branch. IntelliJ uses the lease-checked form and refuses when the remote has moved.
A protected branch rejects a force push outright, and the setting Allow force push on a protected branch is what governs it. On an unprotected branch the platform simply accepts what Git sends. A merge request whose branch was force-pushed shows a "compare with previous version" entry, so reviewers can see what changed since their comments.
The same: rulesets have Block force pushes, on by default for new rulesets, and a pull request records force-pushes in its timeline with a link to compare the old and new versions.
Common mistakes
- Following the hint and running
git pullafter a rewrite, which merges the old commits back in. - Typing
--forceout of habit. Use the lease, or an alias. - Forcing a branch with an open review, which detaches reviewers' comments from the commits they refer to.
- Forcing a shared branch to "clean it up". Nobody asked, and everyone pays.
- Assuming a force push destroyed something for good. The other person's reflog has it.
Try it yourself
Goal: force push safely and see the lease do its job.
- On a branch of your practice repository, commit and push.
- Amend the commit (
git commit --amend), then rungit pushand read the rejection. - Push with
--force-with-leaseand note the "forced update" line. - Simulate a colleague: in a second clone, commit and push to the same branch.
- Back in the first clone, amend again and try
--force-with-lease. Read the "stale info" refusal. - Set the alias:
git config --global alias.pushf "push --force-with-lease".
Expected result: one successful forced update and one refusal that protected someone else's commit, which is exactly the behaviour you want from the flag you type every day.
Show solution
Step 5 is the lesson. The refusal is not an obstacle: it is the difference between the two flags, doing its job on the one occasion in fifty when it matters. That is why the alias in step 6 is worth setting today.