Force Push Safety Guards for Rebase and Push in no-mistakes
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, 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 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, 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. 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, 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:
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:
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: Contains the core logic for verifying that a force-push will not discard remote commits, includingresolveForcePushDecision,remoteCommitsNotIncorporated, and theforcePushWouldDiscardErrorerror type. -
internal/pipeline/steps/push.go: Executes the push operation, obtains the stale remote-tracking SHA vialastFetchedBranchTip, and applies the safety-checked--force-with-leasebased on decisions fromforcepush.go. -
internal/pipeline/steps/rebase.go: Detects force-push conditions early viaisForcePushAgainstRemote, 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
isForcePushAgainstRemoteto 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
remoteCommitsNotIncorporatedthat prevents loss of upstream commits while allowing legitimate rebases that rewrite SHA identifiers. - Clear error reporting via
forcePushWouldDiscardErrorthat instructs users to re-fetch, rebase, or push manually when conflicts are detected. - Safe execution using
--force-with-leaseanchored 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, 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). This matters because it preserves the exact SHA that the pipeline initially observed, which lastFetchedBranchTip later reads in 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). 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). 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →