How the Branch Synchronization Service Handles Blocked States Without Force-Pushing in no-mistakes

The branch synchronization service in no-mistakes never force-pushes; instead, it classifies every non-fast-forward scenario as a blocked state and returns a safe, non-destructive plan with explicit remediation instructions.

The no-mistakes repository implements a branch synchronization service that acts as the single authority for moving worktrees toward published pipeline commits. Unlike traditional Git workflows that might resolve conflicts with force-pushes, this service treats any situation requiring non-fast-forward updates as a blocked state requiring user intervention. By analyzing the relationship between local branches, remote state, and pipeline metadata in internal/branchsync/sync.go, the service ensures data integrity through explicit classification rather than destructive operations.

Detecting Blocked States During Inspection

All state inspection happens in (*Service).inspect (lines 601‑679), which builds a comprehensive State object tracking local branches, pipeline runs, and remote bindings. The function uses this data to classify the relationship between the current worktree and the target commit, determining whether synchronization can proceed safely.

Pipeline-Owned Run Detection

When a pipeline run is still active, the service immediately blocks synchronization through classifyPipelineOwned (lines 1009‑1020). This returns StatePipelineOwned (blocked), preventing any modifications while the pipeline maintains custody of the branch. This check ensures that concurrent pipeline operations never conflict with local synchronization attempts.

Relation Classification and Divergence Handling

For completed runs where the pipeline has published a commit, classifyRelation (lines 998‑1054) determines the relationship between local and remote state:

  • Equal: Returns StateSynchronized—no work needed.
  • Behind: Returns StateBehind—eligible for fast-forward.
  • Ahead: Returns StateLocalAhead—blocked until the user runs the pipeline.
  • Diverged: Returns StateDiverged—blocked unless equivalentDivergence (lines 616‑674) proves the merge tree preserves the remote head.

When diverged, the service only permits an equivalent-diverge fast-forward if the tree hashes match, ensuring no remote commits are lost.

Refresh: Read-Only Remote Validation

The Refresh method (lines 8‑76) performs a read-only validation pass using git ls-remote to inspect the live remote HEAD. If the remote state differs from the stored push binding, the function returns a blocked plan without modifying any files or refs:

  • Remote missing: Returns StateRemoteMissing (blocked)
  • Remote advanced: Returns StateRemoteAdvanced (blocked)
  • Remote rewritten: Returns StateRemoteRewritten (blocked)

Each blocked result includes a Safety code (such as blocked_remote_advanced or blocked_remote_rewritten) and a NextAction suggestion like no-mistakes sync --check. This validation ensures the service never operates on stale remote information.


# Shows the current state; may suggest a fast-forward or a blocked reason

no-mistakes sync --check

Apply: Strict Safety Enforcement

The Apply method (lines 32‑84) implements the actual synchronization logic with strict pre-conditions. The method first repeats the same pre-checks as Refresh, and if the plan's Safety value is not SafetySafeFastForward or SafetySafeEquivalentAdvance, it immediately returns the blocked State (lines 45‑47).

When conditions permit safe advancement, the service performs only:

  1. Strict fast-forward: git merge --ff-only when behind
  2. Anchor-based reset: For equivalent-diverge cases (lines 92‑100)

If the remote state changes between refresh and apply (e.g., history is rewritten during the operation), the function returns blocked_remote_changed_before_apply (lines 71‑73) and exits without modifying the repository.


# Will fast-forward only when StateBehind and clean

no-mistakes sync

Recover: Handling Terminal Runs Without Pushing

When a run terminates (completes, fails, or cancels) without pushing the pipeline head, the branch becomes stranded. The Recover method (lines 30‑55) provides safe custody return through several paths:

  • Equal/Ahead: Anchors the preserved head locally without moving the worktree
  • Behind & Clean: Fast-forwards to the preserved head via recoverFastForward
  • Behind & Dirty: Blocks with instructions to commit or stash first
  • Diverged: Returns blocked_recover_diverged and requires manual reconciliation

All recovery paths manipulate only the gate repository using compare-and-swap updates (git update-ref …) and never push to the remote, guaranteeing no accidental overwrites.


# Returns custody without a push; use --keep-local to keep current HEAD

no-mistakes sync --recover
no-mistakes sync --recover --keep-local

Why Force-Push Is Never an Option

The architecture treats any non-fast-forward push as potential data loss. The CanApply helper (lines 13‑18) gates the Apply method to safe moves only, while the blockedPlan utility (lines 113‑119) standardizes error reporting for every blocked path. By refusing destructive operations and returning detailed State objects with specific Safety codes, the CLI informs users exactly why operations cannot proceed and how to resolve conflicts.


# The service will refuse; user must reconcile manually

no-mistakes sync

# Output (example):

# State: blocked_diverged

# Action: inspect_and_reconcile_manually

# Command: git log --oneline --left-right HEAD...refs/no-mistakes/sync-anchor/<runID>

Summary

  • The branch synchronization service in kunchenguid/no-mistakes classifies every non-fast-forward scenario as a blocked state rather than performing destructive operations.
  • State inspection in (*Service).inspect identifies pipeline-owned runs, divergence, and remote changes before any modifications occur.
  • Refresh validation performs read-only checks via git ls-remote to detect remote advancement or rewriting without modifying local state.
  • Apply enforcement requires SafetySafeFastForward or SafetySafeEquivalentAdvance status, returning blocked states if pre-conditions change during execution.
  • Recover logic handles terminal runs through anchor-based updates in the gate repository, never pushing to remote branches.
  • No force-push guarantee is enforced by the CanApply helper and blockedPlan utility, ensuring data integrity through explicit user intervention.

Frequently Asked Questions

What triggers a blocked state in no-mistakes branch synchronization?

A blocked state occurs when the service detects any condition that would require a non-fast-forward update to synchronize the worktree. Common triggers include: the local branch being ahead of the remote (StateLocalAhead), diverged history that fails equivalentDivergence checks (StateDiverged), active pipeline runs (StatePipelineOwned), remote branches that advanced or were rewritten, or dirty worktrees during recovery operations. Each blocked state returns a specific Safety code and NextAction recommendation rather than modifying the repository.

How does the service handle divergent branches without force-pushing?

When classifyRelation detects divergence (lines 998‑1054), the service invokes equivalentDivergence (lines 616‑674) to check if the merge tree preserves the remote head. Only if the trees are equivalent does it permit an anchor-based reset classified as SafetySafeEquivalentAdvance. Otherwise, it returns StateDiverged with a blocked status and instructions to manually reconcile using commands like git log --oneline --left-right HEAD...refs/no-mistakes/sync-anchor/<runID>.

What happens if the remote branch changes during synchronization?

The Apply method implements double-check locking: it re-validates remote state after the initial Refresh check. If the remote HEAD changes between validation and application—whether through new commits or history rewriting—the service detects this at lines 71‑73 and returns blocked_remote_changed_before_apply. This prevents race conditions where the service might accidentally overwrite commits published between the check and the push.

Can I recover a failed pipeline run without losing local changes?

Yes, the Recover method handles terminal runs (completed, failed, or cancelled) through safe custody transfer. If the worktree is clean and behind the preserved pipeline head, it fast-forwards via recoverFastForward. If diverged, it blocks with blocked_recover_diverged and requires manual reconciliation. You can also use the --keep-local flag to anchor the preserved head without moving your current worktree, ensuring local changes remain intact while returning custody to the gate repository.

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 →