Command Palette

Search for a command to run...

Hectal
PHASE 5Advanced ~14 min· topic 4 of 4

Topic 5.4

Force-Pushing Safely (--force-with-lease) & When Never To

In one line

A normal push is rejected after rewriting history — force-pushing overrides that protection, and --force-with-lease is the version that at least checks nobody else pushed in the meantime before doing it.

0/4 · 0%

Think of it like this

Overwriting a shared document with your own local copy, without checking whether anyone else has edited the shared version since you last opened it — this is exactly what a plain force-push does, and exactly why it's dangerous on any branch other people are also using.

Key ideas

  1. 01

    After amending, rebasing, or otherwise rewriting commits that were already pushed (Topics 5.1 and 2.3's golden rule notwithstanding — sometimes it genuinely is necessary), a normal git push gets rejected, since the remote's history and your local history have diverged. git push --force overrides this rejection, making the remote match your local branch EXACTLY, discarding whatever was there before.

  2. 02

    The real danger: if someone else pushed a new commit to that branch AFTER your last pull but BEFORE your force-push, a plain --force silently OVERWRITES and permanently discards their commit, with no warning at all — they'll simply find their work missing, with no error or indication of what happened, unless they happen to still have it locally themselves.

  3. 03

    git push --force-with-lease is the safer version: it checks that the remote branch is EXACTLY where your local copy last knew it to be, and REFUSES to force-push if the remote has moved since (meaning someone else pushed something new) — turning a silent, destructive overwrite into a safe rejection you can investigate first.

  4. 04

    The genuinely safe use case for force-pushing at all: your OWN feature branch, that only you work on, after cleaning it up with an interactive rebase or amend — force-pushing there only ever overwrites your own prior commits, which is exactly what you intend. Force-pushing to a SHARED branch (main, or any branch others actively push to) should be exceedingly rare, and always coordinated with the team first if it's ever genuinely necessary.

  5. 05

    Many real teams configure BRANCH PROTECTION on their hosting platform (GitHub/GitLab) to outright disable force-pushes on main entirely — removing the possibility of this mistake at the infrastructure level, rather than relying purely on everyone remembering to be careful.

Code & diagrams

ForcePushRiskdiagram

Plain --force overwrites blindly; --force-with-lease checks first.

Rendering diagram…
force-push-safely.shmarkdown

The safe default, and why plain --force is genuinely risky by comparison.

cd ~/scratch/my-first-repo
git switch feature/my-own-branch

# Clean up your OWN branch's history before opening a pull request
git rebase -i HEAD~3
# ... squash a few messy commits ...

# The plain push is now rejected, since history diverged
git push
# ! [rejected] ... (non-fast-forward)

# --force overwrites the remote unconditionally — risky if anyone else pushed
# git push --force

# --force-with-lease checks the remote hasn't moved since you last knew about it
git push --force-with-lease
# succeeds if nobody else pushed in the meantime
# REFUSES if someone else's commit is on the remote that you don't have locally —
# investigate with "git fetch" and "git log origin/feature/my-own-branch" before deciding what to do next

Explain it without notes

01

Why is a plain git push --force considered genuinely dangerous on a branch other people also push to?

02

What does --force-with-lease check that plain --force does not, and why does that make it meaningfully safer?

Practice

01

On a personal test branch, rebase or amend a pushed commit, then use --force-with-lease to update the remote — confirm it succeeds when you're the only one who's touched that branch.

02

Simulate the dangerous scenario: have a 'teammate' (a second local clone, or ask a real collaborator) push a new commit to a branch, then attempt your own --force-with-lease push without fetching first, and observe it correctly refuse.

Trade-offs

  • ↔

    --force-with-lease is a strictly safer default than plain --force with essentially no downside, which is exactly why many experienced developers simply never use plain --force at all — the only genuine trade-off is needing to git fetch and investigate on the (rare) occasions it correctly refuses, rather than blindly overwriting and finding out something was lost only later.

Done when you can

  • I understand why plain --force can silently destroy a teammate's work with no warning.

  • I default to --force-with-lease instead of plain --force, and understand why it's safer.

  • I only force-push to branches that are genuinely mine alone, or with explicit team coordination.