# How no-mistakes Prevents Data Loss with Its Force-Push Safety Mechanism

> Learn how no-mistakes prevents data loss using its force-push safety mechanism. It verifies remote head advancement before allowing history rewrites for secure Git operations.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: how-to-guide
- Published: 2026-07-01

---

**no-mistakes prevents discarding upstream commits by using a lease-guarded check in `resolveForcePushDecision` that verifies the remote head has not advanced with unincorporated commits before allowing any history rewrite.**

The `kunchenguid/no-mistakes` repository implements a robust **force-push safety mechanism** that treats every push as a potential data-loss event. Instead of issuing bare `git push --force` commands, the pipeline runs a multi-layered verification system that ensures upstream work is never accidentally overwritten. This design is critical for CI/CD workflows where automated rebasing or amending could otherwise discard commits pushed by teammates between fetch and push operations.

## Core Architecture of the Safety Guard

The force-push safety mechanism centers on three critical SHA values that track the state of the repository throughout the pipeline execution. These values anchor the lease-guarded decision logic implemented in [`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go).

### The Three SHA Inputs

Every call to `resolveForcePushDecision` receives three parameters that define the boundaries of safe rewriting:

- **`lastSeenSHA`** – The exact SHA observed on the remote during the last fetch (the tip of `origin/<branch>` immediately before the push attempt).
- **`currentSHA`** – The SHA that the pipeline currently wants to push (typically after rebasing or amending).
- **`baseSHA`** – The original base commit of the run, marking the boundary of history the pipeline is allowed to rewrite.

These parameters ensure the decision engine knows exactly what the remote looked like when the pipeline started, what state it wants to impose, and which historical commits are fair game for modification.

### The Decision Logic in forcepush.go

The function `resolveForcePushDecision` in [`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go) implements a five-stage verification process. According to the repository's [`AGENTS.md`](https://github.com/kunchenguid/no-mistakes/blob/main/AGENTS.md) (lines 169-170), this logic prevents a pathological race condition where a fetch would silently update `lastSeenSHA` to the remote tip, allowing out-of-band commits to be overwritten (fixed in issue #281).

## The Five Safety Checks

The safety mechanism executes the following verification sequence before permitting any force-push:

1. **New branch detection** – If the remote does not yet have the branch, the push is permitted immediately.
2. **No-op fast-forward** – If `currentSHA == lastSeenSHA`, the remote already points at the desired commit, and the operation succeeds without pushing.
3. **Lease-guarded fast-forward** – If the remote head still equals `lastSeenSHA` and has not diverged, the push is safe to proceed.
4. **Patch-ID safety verification** – When the remote has moved, the system runs `git rev-list --cherry-pick --right-only` to compare commits. Every commit on the remote but not in `currentSHA` must match by patch-ID (duplicate content already incorporated). Commits without matching patch-IDs are flagged as "unseen upstream work."
5. **Base exclusion** – Commits reachable from `baseSHA` are excluded from patch-ID checks because the pipeline retains the right to rewrite its own base history.

If any check reveals unincorporated upstream commits, the function returns `forcePushWouldDiscardError`, aborting the push with a clear error message: "refusing to force-push … remote head carries commits the pipeline never incorporated."

## Implementation in Pipeline Steps

Two primary callers utilize this safety guard, each forwarding the three SHA parameters to `resolveForcePushDecision` before attempting any remote update.

### Normal Push Operations

In [`internal/pipeline/steps/push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/push.go), the `PushStep.Run` method captures the branch state after rebasing and invokes the safety check:

```go
func (s *PushStep) Run(ctx context.Context, run *db.Run) error {
    lastSeenSHA := run.LastFetchedBranchTip
    currentSHA   := s.repo.HeadSHA()
    baseSHA      := run.BaseSHA

    decision, err := resolveForcePushDecision(ctx, lastSeenSHA, currentSHA, baseSHA)
    if err != nil {
        return fmt.Errorf("refusing to force-push: %w", err)
    }
    if decision == forcePushWouldDiscard {
        return fmt.Errorf("refusing to force-push refs/heads/%s: remote head carries commits the pipeline never incorporated", s.branch)
    }

    return s.repo.PushWithLease(ctx, s.branch, lastSeenSHA)
}

```

This ensures that even fast-forward pushes following a rebase are verified against the last observed remote state.

### CI Auto-Fix Scenarios

The [`internal/pipeline/steps/ci_fix.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci_fix.go) file implements similar guards for CI-driven auto-fixes. When the pipeline amends commits to apply linting or formatting fixes, it must push the rewritten history. This step also calls `resolveForcePushDecision` with the same three SHA parameters to ensure that fixes pushed between the initial fetch and the amend operation are not lost.

## Code Example: Lease-Guarded Decision Flow

The following excerpt from [`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go) demonstrates the core decision logic:

```go
func resolveForcePushDecision(ctx context.Context, lastSeenSHA, currentSHA, baseSHA string) (Decision, error) {
    if currentSHA == lastSeenSHA {
        return forcePushSafe, nil
    }

    remoteSHA, err := git.RemoteHead(ctx, branch)
    if err != nil {
        return forcePushError, err
    }

    if remoteSHA == lastSeenSHA {
        return forcePushSafe, nil
    }

    extra, err := git.RevListPatchID(ctx, remoteSHA, currentSHA, baseSHA)
    if err != nil {
        return forcePushError, err
    }
    if len(extra) > 0 {
        return forcePushWouldDiscard, nil
    }
    return forcePushSafe, nil
}

```

This implementation guarantees that **`lastSeenSHA` never becomes the live remote tip** during verification. The SHA lease remains anchored to the state observed immediately before the push attempt, preventing the race condition described in issue #281.

## Summary

- **Lease-guarded verification** – Every force-push checks `lastSeenSHA` against the live remote head to detect concurrent updates.
- **Patch-ID deduplication** – The mechanism allows pushes when upstream commits contain identical changes (same patch-ID) to prevent unnecessary rebase loops.
- **Explicit SHA tracking** – The three-parameter design (`lastSeenSHA`, `currentSHA`, `baseSHA`) creates clear boundaries for what constitutes safe history rewriting.
- **Fail-closed design** – When verification fails, the pipeline surfaces explicit errors rather than risking data loss, requiring manual intervention only when truly necessary.
- **Multi-caller implementation** – Both normal push operations and CI auto-fix steps in [`push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/push.go) and [`ci_fix.go`](https://github.com/kunchenguid/no-mistakes/blob/main/ci_fix.go) respectively utilize the centralized safety logic in [`forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/forcepush.go).

## Frequently Asked Questions

### What is a force-push safety mechanism?

A force-push safety mechanism is a set of verification steps that prevent Git operations from overwriting commits on a remote repository that the local system has not yet processed. In no-mistakes, this mechanism ensures that when history is rewritten (through rebasing or amending), the pipeline verifies no new commits have appeared on the remote since the last fetch, or that any new commits are duplicates of existing work by patch-ID.

### How does no-mistakes detect unseen upstream commits?

no-mistakes detects unseen upstream commits by comparing the `lastSeenSHA` (captured at fetch time) against the live remote head. If they differ, the system runs `git rev-list --cherry-pick --right-only` to generate patch-IDs for all commits on the remote but not in the local branch. Any commit whose patch-ID does not match a local commit is considered unseen upstream work, blocking the push.

### What happens when the safety check fails?

When `resolveForcePushDecision` returns `forcePushWouldDiscardError`, the calling step aborts the push operation and returns an error message to the user indicating that the remote head contains commits the pipeline never incorporated. The user must then manually re-fetch, re-rebase to include the upstream work, or explicitly override the safety check if the data loss is intentional.

### Why does no-mistakes use patch-ID comparison?

Patch-ID comparison allows no-mistakes to distinguish between genuine new work and duplicate commits. If a teammate cherry-picks or merges a commit that the pipeline has already rewritten, the SHAs will differ but the patch-IDs will match. This prevents infinite rebase loops where the pipeline would otherwise constantly rewrite history to "catch up" with content it already contains, while still blocking pushes that would truly lose novel upstream work.