Git Course 0%

Force push

Advanced ≈ 9 min

What you will learn

  • Why a push is rejected after you rewrite history
  • The difference between --force and --force-with-lease
  • What to do when someone force-pushed over your commit

After this lesson you can

  • I can force push safely on my own branch and recover if someone does it to mine

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:

Terminal
$ 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

Terminal
$ git push --force-with-lease
To /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:

Output
 ! [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:

Terminal
$ 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:

Terminal
$ git log --oneline -2 origin/docs/12-windows-install-steps
36659a0 docs: add Windows steps to installation guide (#12)
5e98836 chore: import the trailguide project

But it is in his own reflog, because his clone still remembers making it:

Terminal
$ git reflog -3
de231bf 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.git

The recovery:

  1. git branch rescue/powershell-note de231bf to keep the commit safely.
  2. Talk to whoever forced, so it does not happen twice.
  3. git reset --hard origin/<branch> to match the remote.
  4. git cherry-pick de231bf to put the commit back on top.
  5. 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

Terminal
$ git push --force-with-lease

Before it, always:

Terminal
$ git fetch
$ git log --oneline origin/<branch> -3

If those three commits are all yours, forcing is safe. If one of them is not, stop.

Common mistakes

  • Following the hint and running git pull after a rewrite, which merges the old commits back in.
  • Typing --force out 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.

  1. On a branch of your practice repository, commit and push.
  2. Amend the commit (git commit --amend), then run git push and read the rejection.
  3. Push with --force-with-lease and note the "forced update" line.
  4. Simulate a colleague: in a second clone, commit and push to the same branch.
  5. Back in the first clone, amend again and try --force-with-lease. Read the "stale info" refusal.
  6. 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.

Check yourself

1. Your push is rejected as "non-fast-forward" after amending a pushed commit. What is the right response?
2. What does --force-with-lease check that --force does not?
3. A colleague force-pushed over your commit. Is it lost?

Key terms

Push Rewriting history Remote-tracking branch Reflog