GitForce Push (--force, --force-with-lease)

Force Push (--force, --force-with-lease)

A force push overrides Git’s normal protection that says “don’t replace remote history with something different.” After a rebase, an amend, or a reset, your local branch no longer fast-forwards onto the remote — it’s diverged, because you rewrote commits. To make the remote match, you force push. This is fine for personal branches and catastrophic for shared ones.

The two flavours
  • --force (or -f) — overwrite the remote no matter what. Dangerous: can erase teammates’ commits.

  • --force-with-lease — overwrite only if the remote is in the state you expect (the same as what you last fetched). Safe.

Warning
**Always prefer `--force-with-lease`.** It refuses if someone else has pushed since your last fetch — protecting them from being silently overwritten.
When you need to force push
  • After a git rebase that rewrote commits already pushed.

  • After git commit --amend on a commit already pushed.

  • After git reset --hard <older-commit> to discard pushed work.

  • After git filter-repo to scrub history.

Plain --force

Bash
git push --force origin feature-x
# Replaces origin/feature-x with your local feature-x, no questions asked
--force-with-lease

Bash
git push --force-with-lease origin feature-x
# If origin/feature-x is the same commit you last fetched: replace it.
# If someone else pushed since your last fetch: refuse, telling you to fetch first.
How --force-with-lease protects you

The check

Text
On your machine, Git remembers:
  origin/feature-x points to commit ABC   (last seen at fetch time)

You're about to push your local feature-x → tip = XYZ

Push goes out:
  "Replace remote feature-x with XYZ, but only if it's currently at ABC."

If teammate pushed in the meantime (remote is now at QRS):
  Server: 'no, remote is at QRS, not ABC' → refuses.

If remote is still at ABC:
  Server replaces ABC with XYZ. Safe.
Setting an alias

Make safe the default

Bash
git config --global alias.pushf "push --force-with-lease"

# Then: git pushf
# Never accidentally type --force again
The golden rule (worth repeating)
When force-push is fine
Force push is acceptable on:
— Your personal feature branches (no one else has them).
— Branches your team explicitly agreed to rewrite.

Force push is **never** acceptable on:
— `main`, `master`, `develop`.
— Release branches others may have based work on.
— Any branch under active use by multiple people.
Branch protection

Hosting platforms can block force pushes. On GitHub: Settings → Branches → Branch protection rules → Disallow force pushes. Set this for every branch you really do not want rewritten.

Recovering from a bad force-push

If a teammate force-pushed and lost your work

Bash
# Their reflog still has the pre-force state
git reflog
# d4b1e0c HEAD@{1}: commit: My lost work

# Push it back as a recovery branch
git push origin d4b1e0c:refs/heads/recovered-work

If YOU force-pushed and want to undo

Bash
# Find the previous remote commit in your reflog or via git log origin/branch
git reflog show origin/feature-x   # if your local origin/x still has the old commit

# Reset origin to the old commit
git push --force-with-lease origin <old-sha>:feature-x
Pushing tags forcefully

Replacing an existing tag (use sparingly)

Bash
git tag -f v1.0
git push --force origin v1.0
# Or in one go:
git push --force origin refs/tags/v1.0
Warning
Replacing a tag that others have pulled can be very confusing — anyone with the old tag still references the old commit. Prefer creating a new tag instead.
Tip
Bake the safe pattern into muscle memory: git push --force-with-lease (or your pushf alias). Never type plain --force again unless you really know why.