Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXII · Force PushForcePush

What force push does — overwriting the remote tip with your local tip

Advanced⏱ ~18 mingit

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

Not yet marked complete on this device.

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

  1. A force-push is a deliberate act; type it deliberately. Aliases that hide git push --force behind a short command are an anti-pattern; they hide the most dangerous push option.
  2. 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.
  3. Never force-push a trunk branch. A trunk force-push breaks every downstream consumer at once.
  4. 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 fetch does 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

  1. Q1. What does `git push --force` do to the remote branch ref?

  2. Q2. After a force-push, the commits unique to the old tip are deleted from the server's object store immediately.

  3. Q3. Name the three categories of commits on the remote after a force-push and describe the reachability of each.

  4. 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.