How no-mistakes Prevents Accidental Data Loss With Force-Pushes

The no-mistakes pipeline prevents accidental data loss by anchoring force-pushes to a previously observed remote SHA and refusing the operation when patch-id analysis detects commits that would be discarded.

The kunchenguid/no-mistakes repository implements a CI/CD pipeline that treats every git push --force as a potential data-loss hazard. Before executing any forced update, the system runs a comprehensive safety decision routine that verifies no upstream work would be overwritten. This automated approach ensures that preventing accidental data loss with force-pushes is enforced at the infrastructure level, eliminating reliance on error-prone manual checks.

The Safety Decision Routine in forcepush.go

The core protection logic resides in internal/pipeline/steps/forcepush.go, where the resolveForcePushDecision function implements a seven-step verification process. This routine determines whether a force-push would discard commits that exist on the remote but not in the local pipeline state.

Resolving the Remote Reference

First, the lsRemoteSHA function queries the remote for the current HEAD of the target reference. This establishes the ground truth for what currently exists on the remote server.

  • If the reference does not exist, the push is treated as a new branch creation (newBranch: true)
  • If the remote SHA matches the intended new head exactly, the operation is a no-op (upToDate: true)

Validating the Push Anchor

The pipeline maintains a lastSeenSHA representing the tip of the remote branch as observed during the last successful operation. When the current remote SHA equals this lastSeenSHA, the force-push is permitted because only the pipeline's own history would be rewritten.

This anchor validation ensures the pipeline never trusts a freshly fetched tip without verifying it matches the previously recorded state.

Detecting Divergence with Patch-ID Comparison

When the remote has moved since the pipeline last observed it, the system must verify that all commits now on the remote are already incorporated into the new head. The remoteCommitsNotIncorporated function handles this detection by:

  1. Fetching the remote tip into FETCH_HEAD
  2. Running git rev-list --cherry-pick --right-only newHead...remote to identify commits present on the remote but absent from the new head
  3. Using patch-id comparison that is robust to rebases—recognizing that rebases rewrite commit SHAs but preserve the underlying changes

The --cherry-pick flag ensures the comparison uses patch-id semantics rather than strict SHA matching, allowing legitimate rebases while catching true data loss.

Refusing Unsafe Operations

If the dropped commit list is non-empty, the system returns a forcePushWouldDiscardError. The error's Error() method assembles a human-readable message listing sample SHAs and explaining that the push would discard upstream work.

The calling push step receives either a forcePushDecision struct (indicating safety) or this error, aborting the push immediately when data loss is detected.

Code Implementation Details

The safety routine is invoked from the push step as follows:

decision, err := resolveForcePushDecision(
    gitRun,                 // thin wrapper around git commands
    pushURL,                // remote URL
    ref,                    // e.g., "refs/heads/main"
    newHeadSHA,             // SHA intended to be pushed
    lastSeenSHA,            // SHA the pipeline last observed
    baseSHA,                // base of the run for cherry-pick exclusion
)
if err != nil {
    // Error will be *forcePushWouldDiscardError when commits would be lost
    return fmt.Errorf("cannot push: %w", err)
}
if decision.upToDate {
    return nil // Nothing to do—remote already matches
}
if decision.newBranch {
    // Push new branch normally
    gitRun("push", pushURL, fmt.Sprintf("%s:%s", newHeadSHA, ref))
}

For diagnostic purposes, you can check which commits would be discarded:

dropped, _ := remoteCommitsNotIncorporated(
    gitRun,
    pushURL,
    ref,
    newHeadSHA,
    remoteSHA,
    baseSHA,
)
if len(dropped) > 0 {
    fmt.Printf("Commits that would be discarded:\n")
    for _, c := range dropped {
        fmt.Println(shortSHA(c))
    }
}

Three Safety Invariants

The logic in internal/pipeline/steps/forcepush.go enforces three critical invariants:

  1. Never discard upstream work – Any commit appearing only on the remote must be merged or rebased before a force-push is allowed.

  2. Anchor the lease to the last observed remote SHA – The pipeline verifies the remote hasn't changed unexpectedly since the last observation, preventing race conditions where new commits appear between check and push.

  3. Use patch-id semantics – The --cherry-pick comparison recognizes that rebased commits contain identical changes despite different SHAs, reducing false positives while maintaining protection against actual data loss.

When these invariants are violated, users see an error message similar to:


refusing to force-push refs/heads/main: remote head abc1234 carries 3 commit(s) the pipeline never incorporated (e.g. def5678, 9ab0c1); pushing would discard upstream work. Re-fetch and rebase onto the current remote, or push manually if this overwrite is intended.

Summary

  • The resolveForcePushDecision function in internal/pipeline/steps/forcepush.go acts as a gatekeeper for all force-push operations.
  • Patch-id comparison via git rev-list --cherry-pick distinguishes between harmless rebases and dangerous data loss.
  • The lastSeenSHA anchor prevents race conditions where remote commits appear during pipeline execution.
  • When unincorporated commits are detected, forcePushWouldDiscardError aborts the push with specific commit SHAs listed.
  • This protection is verified by regression tests including TestCIStep_CommitAndPush_RefusesToClobberUnseenUpstreamCommit.

Frequently Asked Questions

What happens if the remote branch does not exist yet?

When lsRemoteSHA determines the reference does not exist on the remote, the system sets newBranch: true and allows the push to proceed normally. Since there is no existing history to overwrite, there is no risk of data loss.

How does no-mistakes distinguish between rebased commits and truly lost commits?

The remoteCommitsNotIncorporated function uses git rev-list --cherry-pick, which compares commits by patch-id rather than SHA. This means rebased commits that contain identical changes are recognized as incorporated, while commits with unique changes that exist only on the remote are flagged as potential data loss.

What error message appears when a force-push is blocked?

The system returns a forcePushWouldDiscardError with a message identifying the remote head SHA and listing specific commits that would be discarded. The error suggests re-fetching and rebasing onto the current remote, or pushing manually if the overwrite is truly intended.

Can I override the safety check if I need to force-push intentionally?

The pipeline blocks the operation and returns an error, but the error message explicitly mentions that you can push manually if the overwrite is intended. This requires deliberate action outside the automated pipeline, ensuring force-pushes are never accidental.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →