Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXII · Force PushForcePush

The destructive default — what `git push --force` actually destroys

Advanced⏱ ~20 mingit

What you'll learn

  • Enumerate the three categories of state destroyed by an unguarded force-push
  • Quantify the blast radius: which commits are unreachable, which teammates are divergent, which audits are invalidated
  • Identify the failure surfaces downstream of a force-push (CI cache, artifact pins, signed tags, GitOps controllers)
  • Distinguish the moment of force-push from the moment the damage is observed

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.

The previous lesson established that git push --force replaces the remote’s branch ref with the local tip. This lesson is about the cascading cost of that replacement. The loss is not a single event; it is a wave that breaks downstream surfaces one by one as the old OID stops being reachable. The wave hits teammates’ local clones first, then CI cache keys, then artifact registry pins, then signed tags and supply-chain attestations, then the audit trail. This lesson names each surface and quantifies the blast radius so that the cost of a force-push is visible before the push happens.

The three losses from a single force-push

A force-push produces three distinct kinds of loss:

  • Loss of commits. Any commit on the remote’s old tip that is not an ancestor of the new tip becomes unreachable from any branch ref. The objects stay in the object store, but no branch, tag, or note points at them. They are dead in the reachability sense; they are recoverable only through the server’s reflog within its retention window, or from a clone that fetched them before the push.
  • Loss of work based on those commits. Every local clone that pulled the old tip holds commits based on the old OIDs. Those local commits are now based on unreachable history. A git pull after the force-push reports non-fast-forward divergence; the engineer must either reset (losing any local work) or rebase (rewriting their own OIDs in turn).
  • Loss of audit trail. Any external system that pinned the old tip - a Terraform module registry, a container image registry, a CI build log, a signed tag, a supply-chain attestation - now holds a reference to an unreachable commit. The reference is technically valid (the OID is a valid SHA); the referent is unreachable from any current branch. Six months later, when the auditor asks “what changed in production?”, the answer is “the artifact registry says the change was at OID X, but X is not in the repository”.
flowchart LR
    PUSH["git push --force"] --> L1["remote commits become unreachable"]
    PUSH --> L2["teammates local clones diverge"]
    PUSH --> L3["external pins and attestations invalidated"]
    L1 --> R1["only reflog within retention window can recover"]
    L2 --> R2["each teammate must reset or rebase"]
    L3 --> R3["every pin must be re-pointed at a reachable commit"]

The wave moves outward from the branch ref. The first surface to break is the remote itself (the unreachable commits); the second is the team (the divergent local clones); the third is the audit chain (the invalidated pins). The recovery effort is proportional to the number of downstream consumers.

Quantifying the blast radius

The size of the loss depends on how many downstream surfaces were holding the old OIDs at the moment of the force-push. A rough inventory:

  • Teammates who pulled the branch. Each teammate’s local clone holds commits based on the old OIDs. The cost per teammate is a reconciliation step (reset or rebase) and any local commits they had based on the old OIDs become unmergeable or must be rebased. With three teammates, the cost is three reconciliation steps.
  • CI runs that built the old tip. Each CI run produced an artifact (a container image, a Terraform plan, a packaged binary) referenced by the old OID. After the force-push, the CI cache lookup for the new OIDs is a miss; the pipeline rebuilds from scratch. If the cache was keyed by branch name rather than OID, the cache contains artifacts from the old tip and the rebuild is silently wrong.
  • Artifact registry pins. A Terraform module registry holds the version tag and the commit OID it was built from. After the force-push, the pin still resolves in the registry’s metadata but the commit is unreachable from the registry’s reference branch. Downstream consumers that fetch by pin get an error; consumers that fetch by tag name may get an artifact built from a different commit than the registry’s metadata claims.
  • Signed tags and supply-chain attestations. A signed tag commits to a specific OID; the signature is over the OID plus the tag metadata. After the force-push, the OID is unreachable. The signature is still cryptographically valid (it signs the same OID), but the meaning of the tag is lost: “this object” no longer points at anything reachable. In-toto attestations and SLSA provenance records have the same problem.

The audit trail is the cost no one notices

The most insidious loss is the audit trail. When an artifact registry pins to a commit OID and the OID is later rewritten by a force-push, the pin still resolves in the registry’s database but points at an unreachable commit. Six months later, the auditor asks “what code produced this artifact?” and gets an OID that is not in the repository.

The recovery requires reconstructing the chain of trust from secondary sources: a teammate’s clone (if any still has the old OID), a CI build log (if any was retained), the server’s reflog (if the retention window has not expired). If none of these has the old OID, the chain of trust is permanently broken; the artifact exists, the registry has a pin, but the pin points at nothing.

This is why infrastructure repositories pin by digest, sign tags by OID, and audit by commit: every link in the chain depends on the OID being reachable. A force-push that rewrites OIDs invalidates the chain at every link simultaneously.

Production discipline

  1. Inventory the downstream consumers before force-pushing. For any branch with downstream consumers (teammates, CI, artifact registry, signed tags), the cost of the rewrite is not local; it is the size of the consumer list.
  2. Force-push immediately before a notification, not after. The discipline is heads-up first, push second; the alternative (push first, explain second) leaves the team in a divergent state while they wait for the explanation.
  3. Treat CI cache invalidation as a known cost. Every force-push of a branch that has been built by CI invalidates the cache. The rebuild is paid in pipeline minutes; budget for it before the push, not after.
  4. Pin by digest, not by tag. Container image registries that pin by tag are silent-corruption risks after a force-push; pinning by digest means the registry rejects the lookup rather than returning an artifact built from a different commit.
  5. Audit the audit trail. A force-push that breaks a signed tag leaves the tag pointing at an unreachable OID. The recovery is to create a new tag, not to fix the old one; compliance reports that referenced the old tag must be re-issued.

Cross-course references

  • Git, CI/CD & GitOps - Part XI (Rebasing) lesson 06 covers the breakage chain from a force-push on a shared branch; this lesson extends that analysis to the audit trail specifically.
  • Git, CI/CD & GitOps - Part XIX (Tags and Releases) covers signed tags and how a force-push invalidates their referents.
  • Terraform for Production Sysadmins - Part XIV (ModuleSources) covers Terraform module registry pinning by commit OID, which is the audit trail surface most affected by a force-push.

Quiz

Knowledge check · 4 questions

  1. Q1. Which of the following is the most insidious loss from a force-push on a branch that has downstream consumers?

  2. Q2. The engineer who runs `git push --force` is the engineer who pays the cost of the rewrite.

  3. Q3. Name the three categories of state destroyed by a force-push and give an example of each.

  4. Q4. Diagnose the cascade of breakage from an unguarded force-push and recommend recovery actions in priority order.

    An engineer on the platform team force-pushes `feature/iam-rotation` after a rebase. Three teammates had pulled the branch and have local commits based on it; CI has built the old tip and cached artifacts at the old OID; the artifact registry holds a Terraform module version pinned to the old OID; a signed tag `v1.4.2` points at the old OID; the team's compliance report references the signed tag. The engineer is unaware of any of this until a teammate messages them two hours later about a divergent branch.

Passing score: 75%. Answers are checked in this browser.