skip to content

questions

5

In Git, why is force-pushing to a shared branch dangerous?

level: juniorimportance: must knowfreq 70%

answer

  1. the default push refuses non-fast-forwards
  2. the check exists to protect other commits
  3. force replaces the pointer outright
  4. others already fetched the old commits
  5. prefer the lease variant

basics

~20 s

A forced push overwrites the remote branch pointer even when the new tip is not a descendant of the old one, so commits other people pushed can stop being reachable and everyone who already fetched the branch now has a history that diverges from it.

solid answer

~40 s

A normal push is only accepted when the new tip is a **descendant** of the remote's current tip — that fast-forward check is what guarantees you never drop someone else's work. `git push --force` disables the check and sets the ref to whatever you say, so if a teammate pushed in the meantime their commits are no longer reachable from the branch and will eventually be garbage-collected on the server. The second, quieter cost is that everyone who already fetched the branch keeps the old commits locally; their next pull sees two divergent histories, and a careless merge can drag the old commits back in. That is why the rule is: force-push only branches you own, prefer `git push --force-with-lease`, and never rewrite a branch other people build on.

code

console · 3 lines
console
$ git push origin feature/login
 ! [rejected]        feature/login -> feature/login (non-fast-forward)
error: failed to push some refs to 'origin'

go deeper

for a junior

Recall that the default push refuses non-fast-forward updates for a reason, that force skips that check, and that you only force-push branches you alone publish.

for a middle

Explain both consequences: commits made unreachable on the remote, and every existing clone now holding a divergent history whose merge can reintroduce the old commits.

for a senior

Show the recovery path and the prevention: reflog-based restoration inside the gc window, lease-based pushes as the default, and configuring shared branches to reject non-fast-forwards.

for a principal

Frame the policy: which branches are rewritable by whom, how that is enforced rather than merely documented, and what the convention costs in review and history quality.

## What a push actually does A push asks the remote to move a ref from one commit to another. By default the remote accepts the update only if the new value is a descendant of the old one — a **fast-forward**. That single rule is the reason ordinary collaboration is safe: an update that would make existing commits unreachable is refused, and you are told to fetch and integrate first. `git push --force` (or `-f`) tells the remote to skip that check and set the ref regardless. The commits that used to be at the tip are not deleted immediately — objects survive until they are unreachable *and* pruned — but nothing points at them anymore, so from every user's perspective they are gone, and the server will collect them in time. ## The two failure modes **Losing other people's commits.** You rebase, someone else pushes two commits, you force-push. Your new tip descends from the tip you had before their push, so their commits are simply not in it. The remote now has no ref reaching them. Unless someone still has them locally, they are lost. **Splitting everyone's history.** Rewriting a branch replaces its commits with new ones carrying the same changes but different SHAs. Every clone that already fetched the branch still has the originals. Their next `git pull` sees a remote-tracking ref that no longer descends from their local branch, and the default merge behaviour tries to reconcile the two — producing duplicated commits and conflicts, or quietly reintroducing the commits you removed. This is why a rewritten shared branch usually costs the team more than the rewrite saved. ## Why it is still a normal thing to do Force-pushing is not forbidden; it is the *published and shared* part that matters. Rewriting your own feature branch — squashing fixups, rebasing onto an updated base, amending a message — is routine, and republishing it requires a force. The distinction to state clearly is ownership: a branch only you push is yours to rewrite; a branch other people commit to or build on is not. ## Safer defaults - Use `git push --force-with-lease` instead of `--force`. It refuses the push if the remote branch is not where you last saw it, so a teammate's push turns a silent overwrite into a rejection you must think about. - Fetch and look before rewriting, so you know whether anyone else moved the branch. - Keep long-lived shared branches out of scope entirely; the receiving repository can be configured to reject non-fast-forward updates on them. - If you must rewrite a branch someone else uses, tell them first and tell them how to move their work onto the new tip. ## Recovering when it goes wrong Nothing is instantly unrecoverable. Anyone who had the branch still has the old commits in their object store and in their reflog, and `git reflog` on the machine that force-pushed shows the pre-rewrite tip. Pushing the old tip back restores the ref, after which you integrate deliberately. The window is bounded by garbage collection, so speed matters — a good reason to speak up immediately rather than quietly retry. ## The sentence to say in an interview "A forced push replaces the branch pointer without the fast-forward check, so it can make other people's commits unreachable and it splits the history of every clone that already fetched the branch. I force-push my own branches with `--force-with-lease`, and I do not rewrite branches other people build on."

  • Are the overwritten commits deleted immediately on the remote?
    No. Objects persist until they are unreachable and pruned by garbage collection, so a quickly-restored ref usually brings them back. But nothing on the remote points at them, so from every user's view they are gone, and the recovery window is bounded.
  • When is force-pushing perfectly normal?
    On a branch only you publish. Squashing fixups, amending a message, or rebasing onto an updated base all replace commits, and republishing then requires a force. The rule is about branches other people commit to or build on, not about force-pushing as such.
  • What should you do immediately after realising you clobbered someone's commits?
    Say so, then check `git reflog` on the machine that pushed to find the pre-push remote tip, or ask anyone who fetched earlier to check theirs. Pushing that old tip back restores the ref while the objects are still unpruned.

saying these in an interview costs you the question

  • Says force-push permanently deletes commits instantly
  • Treats force-push as routine on shared branches
  • Thinks a rejected push means their work is broken
  • Believes teammates just pull to get back in sync
  • Cannot distinguish own branch from shared branch

context

open as a page

What does git push --force-with-lease do that git push --force does not?

level: middleimportance: must knowfreq 65%

basics

~20 s

It makes the forced update conditional: the push succeeds only if the remote branch still points where your remote-tracking ref says it does. If someone else pushed in between, the update is rejected instead of silently discarding their commits.

open as a page

A teammate force-pushed the branch you were on — how do you recover your commits?

level: seniorimportance: must knowfreq 48%

basics

~20 s

Fetch, then find the branch's old upstream tip in your reflog and replay only your own commits onto the new tip with git rebase --onto. Never merge or pull, because that reconciles two parallel histories and drags the discarded commits back.

open as a page

How can git push --force-with-lease still overwrite a teammate's commits?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Its lease is checked against your remote-tracking ref, which any background fetch updates. If something fetched a teammate's push without you looking at it, the lease matches and the forced update discards their work. --force-if-includes closes that gap.

open as a page

How would you decide and enforce, in Git itself, which branches may be force-pushed?

level: principalimportance: should knowfreq 26%

basics

~20 s

Allow rewriting on short-lived branches with a single owner, forbid it on anything others build on or that is deployed from. Enforce it at the receiving repository — receive.denyNonFastForwards and a pre-receive hook — because client-side flags are only courtesy.

open as a page