Skip to content

fix(server): background status fetch no longer breaks a concurrent git pull - #26568

Open
juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-26560
Open

juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-26560

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

The background status poll runs git fetch about every 30s, and that fetch rewrites .git/FETCH_HEAD. Git writes that file without a lock. When the poll overlaps a user's own git pull in the same checkout, both fetches append for-merge lines, and the pull fails with fatal: Cannot fast-forward to multiple branches. Reproduced on main 125b619 with Git 2.43: the status fetch's exact arguments ran in a loop while git pull --ff-only ran against a moving remote, and 12 of 300 pulls failed that way.

Change

fetchRemoteForStatus now passes --no-write-fetch-head. The poll only needs refs/remotes/* to compute ahead/behind, and nothing reads the FETCH_HEAD it wrote. User-initiated fetches and pulls keep writing FETCH_HEAD as before. The flag needs Git 2.29 or newer. On older Git this background fetch fails and goes through the existing failure backoff.

Scope and approval

Fixes #26560

Verification

  • New test leaves FETCH_HEAD to the user's own pulls in background upstream fetches in apps/server/src/vcs/GitVcsDriverCore.test.ts:

    • Fails on main's implementation: AssertionError: background status fetch wrote .git/FETCH_HEAD, which an overlapping \git pull` merges from: expected true to equal false`
    • Passes with the fix, and behindCount is still 1, so the fetch still runs.
  • GitVcsDriverCore.test.ts: 172/172 pass. vp run check:changed passes.

  • Runtime transcript (Git 2.43, Linux): a background loop runs the status fetch's exact command while git pull --ff-only runs 300 times against a remote that gains a commit before each pull.

    Before (125b619) After (97adfec)
    Cannot fast-forward to multiple branches failures 12/300 0/300

    Driver: repro2.sh. Output: transcript.txt

  • Server-only, no visible surface, so there are no captures.

  • Not checked: the reporter's Apple Git 2.54 on macOS, and Git older than 2.29. The changed code is platform-independent server code.

Model and harness: Claude Opus 5.5 (lead, test, implementation, review B) and GPT-6.1 Sol (review A), in Claude Code and Codex via T3 Code.

🤖 Generated with Claude Code

juliusmarminge and others added 3 commits October 28, 2026 16:40
…HEAD

The background upstream status fetch rewrites .git/FETCH_HEAD, so a poll
that overlaps the user's own `git pull` makes it fail with "Cannot
fast-forward to multiple branches". This test fails on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pull

The background upstream status poll rewrote .git/FETCH_HEAD. Git writes
that file without a lock, so a poll that overlapped the user's own
`git pull` left it with several for-merge lines and the pull failed with
"Cannot fast-forward to multiple branches". The poll only needs
refs/remotes/* updated, so pass --no-write-fetch-head (Git 2.29+).

Fixes #26560

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…EAD on its flag

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 28, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 97adfec

Macroscope's review found this PR approvable — Background status fetches stop overwriting FETCH_HEAD, avoiding interference with concurrent Git pulls. Remote-reference updates and explicit fetch operations remain unchanged, keeping the change narrowly contained.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 125b619 and 97adfec.

Files selected for processing: 2
  • apps/server/src/vcs/GitVcsDriverCore.test.ts
  • apps/server/src/vcs/GitVcsDriverCore.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

The background status fetch adds --no-write-fetch-head while retaining --no-auto-gc. A regression test checks that the fetch detects a remote commit without creating .git/FETCH_HEAD.

Changes: Background status fetch

Layer / File(s) Summary
Fetch behavior and regression test
apps/server/src/vcs/GitVcsDriverCore.ts, apps/server/src/vcs/GitVcsDriverCore.test.ts
The background fetch adds --no-write-fetch-head. The test verifies that remote status reports one commit behind while .git/FETCH_HEAD remains absent.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested labels: size:S

Suggested reviewers: ifkumrfnl, macroscopeapp

Merge Risk: Minimal · up to 97adf

Background status fetches no longer write .git/FETCH_HEAD, which removes a race with a user's own git pull. Fetches and pulls started by the user are unchanged. Git older than 2.29 will fail the background fetch, but the repository declares no such support requirement, so nothing blocks merging.

Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The description includes all required sections. It explains the problem, the implementation, the issue scope, focused test results, runtime verification, untested environments, and the agent details.
Title check Passed The title clearly and concisely describes the main fix: preventing background status fetches from breaking concurrent Git pulls.
Linked Issues check Passed Issue #26560 requires the server background status fetch to avoid writing .git/FETCH_HEAD while still updating remote-tracking refs for ahead/behind status. fetchRemoteForStatus now adds `--no-wri…
Out of Scope Changes check Passed The changes stay within issue #26560. The source change updates the background fetch arguments and clarifies the existing --no-auto-gc comment. The added test verifies the requested behavior. No unr…
Full details: Linked Issues check

Explanation

Issue #26560 requires the server background status fetch to avoid writing .git/FETCH_HEAD while still updating remote-tracking refs for ahead/behind status. fetchRemoteForStatus now adds --no-write-fetch-head to that fetch only. The regression test verifies that statusDetailsRemote reports behindCount 1 and leaves FETCH_HEAD absent. The diff does not change user-initiated fetch or pull commands.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue #26560. The source change updates the background fetch arguments and clarifies the existing --no-auto-gc comment. The added test verifies the requested behavior. No unrelated product behavior or user-facing surface changes appear in the reviewed diff.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.3 KiB 1.3 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.6 KiB 21.6 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.3 KiB 1.3 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: 125b619 · PR result: 97adfec · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Background status fetch writes FETCH_HEAD, racing user git pull ("Cannot fast-forward to multiple branches")

1 participant