Git, CI/CD & GitOpsXXII · Force PushForcePush
What force push does — overwriting the remote tip with your local tip
What you'll learn
- State precisely what `git push --force` does to the remote branch ref
- Identify which commits become unreachable after a force-push and which stay reachable
- Distinguish a non-fast-forward rejection from a force-push overwrite
- Read the `forced update` line in `git push` output and identify the old and new tips
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A force-push is the operation that lets you replace the remote’s branch ref when your local history is not a fast-forward of the remote’s. It is the same wire protocol as a normal push, with the fast-forward check turned off. From the remote’s point of view, the operation is silent: the old tip is gone, the new tip is in, and nothing in between survives in any reachable position.
What the server does on a force-push
git push --force sends the same ref-update messages as a normal
push but sets a flag that tells the server to skip the fast-
forward check. The server overwrites the branch ref with the OID
you sent.
git push --force origin feature/iam-rotation
# To git@github.com:acme/iac.git
# + A1...A2 feature/iam-rotation -> forced update
The + at the start of the third line is the client telling you
the update is forced; A1...A2 is the old-to-new tip range;
forced update is the server confirming it accepted the rewrite.
flowchart LR
subgraph BEFORE["remote before force-push"]
B1["feature/iam-rotation = A1"]
end
subgraph LOCAL["local after rebase"]
L1["feature/iam-rotation = A2 (rewritten)"]
end
LOCAL -->|"git push --force"| SERVER["server overwrites ref"]
subgraph AFTER["remote after force-push"]
A1["feature/iam-rotation = A2"]
end
BEFORE -.->|"A1 no longer reachable from any ref"| LOST["A1 only in object store + reflog"]
The new tip A2 is the only object reachable from
refs/heads/feature/iam-rotation. The old tip A1 and any commits
unique to the path between A1 and A2 are unreachable from any
branch ref.
What disappears and what survives
Three categories of object have different fates after the overwrite:
- Commits that are ancestors of the new tip. Survive; they remain reachable from the branch ref.
- Commits unique to the old tip that are not ancestors of the new tip. Become unreachable from any branch or tag ref, but stay in the server’s object store until gc prunes them.
- Tree and blob objects unique to the unreachable commits. Stay in the object store until gc. No branch or tag links to them.
The important point is that “disappear” means “disappear from reachability”, not “disappear from disk”. The objects remain recoverable through the server’s reflog within its retention window; the loss is the link, not the data.
Non-fast-forward versus force-push
A normal git push checks that the new tip is a descendant of the
remote’s current tip. If not, the push is rejected:
git push origin feature/iam-rotation
# ! [rejected] feature/iam-rotation -> feature/iam-rotation (non-fast-forward)
The non-fast-forward rejection is Git’s safety net. --force
turns that safety net off. It does not check that your local view
of the remote is current, that no teammate has pushed since your
last fetch, or that CI has built the remote’s tip. It overwrites
the remote’s branch ref unconditionally.
Production discipline
- A force-push is a deliberate act; type it deliberately.
Aliases that hide
git push --forcebehind a short command are an anti-pattern; they hide the most dangerous push option. - Force-push only branches you own. A branch you own is one where you are the only engineer who has pulled it, the only CI pipeline that has built it, and the only artifact that has pinned to a commit on it.
- Never force-push a trunk branch. A trunk force-push breaks every downstream consumer at once.
- Treat the moment of the force-push as a notification event. Send a heads-up before the push, not after.
Cross-course references
- Git, CI/CD & GitOps - Part XI (Rebasing) covers the rebase that produces the rewritten history a force-push publishes.
- Git, CI/CD & GitOps - Part XXI (Fetching and Pulling)
covers what
git fetchdoes with the new tip after a force-push. - GitOps with Argo CD - Part VI (MergeStrategies) discusses how a force-pushed branch interacts with a controller’s drift detection.
Quiz
Knowledge check · 4 questions
Q1. What does `git push --force` do to the remote branch ref?
Q2. After a force-push, the commits unique to the old tip are deleted from the server's object store immediately.
Q3. Name the three categories of commits on the remote after a force-push and describe the reachability of each.
Q4. Decide whether the proposed force-push is safe and explain the failure mode if it goes ahead.
An engineer rebases `feature/iam-rotation` locally (no fetch in between) and wants to `git push --force origin feature/iam-rotation`. The engineer believes no teammate has touched the branch. The team has a CI pipeline that builds every push, an artifact registry that pins Terraform modules to commit OIDs, and no branch protection on `feature/*` branches.
Passing score: 75%. Answers are checked in this browser.