# Force Push Safety Guards for Rebase and Push in no-mistakes

> Learn about rebase and push safety guards in the no-mistakes repository. Prevent accidental data loss with patch-id comparison and SHA anchoring for secure Git operations.

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

---

**The no-mistakes repository implements coordinated force push safety guards that anchor a lease on the remote-tracking SHA, verify all remote commits are incorporated via patch-id comparison, and abort the operation with a clear error if upstream work would be discarded.**

The no-mistakes tool protects both the **rebase** and **push** stages from silently discarding upstream work when a user forces a push. These safeguards are implemented as a coordinated set of checks in the `internal/pipeline/steps` directory that detect force-push conditions early and validate that no remote commits will be lost. By combining stale-ref anchoring with patch-id-aware verification, the tool ensures that legitimate rebases succeed while preventing destructive force pushes.

## Early Detection of Force-Push Conditions

In [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go), the `isForcePushAgainstRemote` function detects a force-push scenario before any remote fetch or rebase is performed. At line 48, this function examines the remote's current tip versus the pipeline's `baseSHA` to determine if the remote has diverged from the expected state.

When `forcePush` evaluates to true, the rebase step deliberately skips refreshing the remote-tracking branch between lines 55-62. This **stale-ref anchoring** is critical because it preserves the exact SHA that the pipeline last observed, which becomes the anchor for the subsequent push verification.

## Stale-Ref Anchoring and Lease Verification

During the push stage, [`internal/pipeline/steps/push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/push.go) executes the safety checks. The `lastFetchedBranchTip` function reads the remote-tracking ref at line 80 that was intentionally left untouched by the rebase step. This SHA becomes the anchor for a `--force-with-lease` operation.

In [`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go), the `resolveForcePushDecision` function (lines 66-94) re-reads the live remote head via `lsRemoteSHA` and returns one of three outcomes: new branch, up-to-date, or a decision requiring a safety-checked force-push. This three-way resolution ensures that the tool only proceeds to force-push validation when absolutely necessary.

## Patch-ID-Aware Inclusion Testing

The core safety mechanism resides in [`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go). The `remoteCommitsNotIncorporated` function (lines 29-41) executes a `git rev-list --cherry-pick --right-only` comparison when the remote head differs from the previously observed SHA. This comparison uses **patch-id** equivalence rather than strict commit hash matching.

This distinction is crucial because a clean rebase that rewrites commit hashes but preserves patch content is still considered safe, while actual new remote commits are flagged as potential data loss.

If `remoteCommitsNotIncorporated` finds any commits not represented in the new head, `resolveForcePushDecision` returns a `forcePushWouldDiscardError` at lines 90-93. The push step aborts with an explicit error message explaining the potential data loss.

## Safe Push Execution

When no remote-only commits exist, the push step proceeds safely. At lines 95-98 of [`internal/pipeline/steps/push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/push.go), the tool performs a regular `git push` with `--force-with-lease` anchored to the verified `remoteSHA`. This ensures that even if the remote has advanced since the initial check, the operation fails safely rather than overwriting unknown commits.

## Code Implementation Examples

Detecting a force-push in the rebase step:

```go
forcePush := isForcePushAgainstRemote(ctx, sctx.WorkDir,
    pushRemote, branch, branchTarget, sctx.Run.BaseSHA)
if forcePush && branch == defaultBranch && remoteDefaultBranchAdvanced(...) {
    // raise a warning finding – remote advanced after force-push
}

```

Resolving a force-push decision in the push step:

```go
decision, err := resolveForcePushDecision(gitRun, pushURL, ref,
    headBeingPushed, lastSeen, sctx.Run.BaseSHA)
switch {
case decision.newBranch:
    git.Push(..., "", false)               // plain push
case decision.upToDate:
    // nothing to do
default:
    git.Push(..., decision.remoteSHA, true) // safe force-with-lease
}

```

## Key Implementation Files

The force push safety guards for rebase and push in no-mistakes are implemented across three critical files:

- **[`internal/pipeline/steps/forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/forcepush.go)**: Contains the core logic for verifying that a force-push will not discard remote commits, including `resolveForcePushDecision`, `remoteCommitsNotIncorporated`, and the `forcePushWouldDiscardError` error type.

- **[`internal/pipeline/steps/push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/push.go)**: Executes the push operation, obtains the stale remote-tracking SHA via `lastFetchedBranchTip`, and applies the safety-checked `--force-with-lease` based on decisions from [`forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/forcepush.go).

- **[`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go)**: Detects force-push conditions early via `isForcePushAgainstRemote`, skips fetching the remote-tracking ref to preserve the anchor SHA, and handles special rebase targets for forced pushes.

## Summary

The no-mistakes tool implements comprehensive force push safety guards for rebase and push operations through the following mechanisms:

- **Early detection** via `isForcePushAgainstRemote` to separate normal pushes from force-push scenarios before any destructive operations occur.
- **Stale-ref anchoring** that deliberately preserves the remote-tracking SHA during rebase, ensuring the push step validates against the exact state observed at pipeline start.
- **Patch-id-aware verification** through `remoteCommitsNotIncorporated` that prevents loss of upstream commits while allowing legitimate rebases that rewrite SHA identifiers.
- **Clear error reporting** via `forcePushWouldDiscardError` that instructs users to re-fetch, rebase, or push manually when conflicts are detected.
- **Safe execution** using `--force-with-lease` anchored to verified SHAs, ensuring any out-of-band remote changes fail safely rather than being overwritten.

## Frequently Asked Questions

### How does no-mistakes detect a force-push before it happens?

In [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go), the `isForcePushAgainstRemote` function (line 48) compares the remote's current tip against the pipeline's `baseSHA`. If the remote has diverged from the expected state, the function returns true, triggering the force-push safety path rather than the standard fast-forward push logic.

### What is a "stale-ref anchor" and why does it matter?

A stale-ref anchor refers to the deliberate choice to skip updating the remote-tracking branch during rebase when a force-push is detected (lines 55-62 of [`rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/rebase.go)). This matters because it preserves the exact SHA that the pipeline initially observed, which `lastFetchedBranchTip` later reads in [`push.go`](https://github.com/kunchenguid/no-mistakes/blob/main/push.go) (line 80) to anchor the `--force-with-lease` operation. Without this anchoring, the tool might validate against a newer remote state and miss commits that appeared between the rebase and push operations.

### Why does no-mistakes use patch-id instead of commit hash comparison?

The `remoteCommitsNotIncorporated` function uses `git rev-list --cherry-pick --right-only` to compare commits by patch-id rather than hash (lines 29-41 of [`forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/forcepush.go)). This approach recognizes that a rebased commit with a new SHA but identical content is safe to force-push over, while actual new remote commits that aren't incorporated into the local history represent potential data loss that should block the operation.

### What error does no-mistakes return when a force-push would discard commits?

When `remoteCommitsNotIncorporated` detects commits that exist on the remote but not in the local history, `resolveForcePushDecision` returns a `forcePushWouldDiscardError` (lines 90-93 of [`forcepush.go`](https://github.com/kunchenguid/no-mistakes/blob/main/forcepush.go)). This error aborts the push operation with a clear message explaining that upstream work would be lost, prompting the user to manually re-fetch, rebase, and verify the changes before attempting another push.