Repository navigation
fix(server): background status fetch no longer breaks a concurrent git pull - #26568
juliusmarminge wants to merge 3 commits into
Conversation
…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>
ApprovabilityVerdict: Approved at 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. |
|
No actionable comments were generated in the recent review.
|
| 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:
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 owngit 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: 
4
-
Full details: Linked Issues check
-
Explanation
Issue
#26560requires the server background status fetch to avoid writing.git/FETCH_HEADwhile still updating remote-tracking refs for ahead/behind status.fetchRemoteForStatusnow adds--no-write-fetch-headto that fetch only. The regression test verifies thatstatusDetailsRemotereportsbehindCount1 and leavesFETCH_HEADabsent. 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-gccomment. 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.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 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.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Problem
The background status poll runs
git fetchabout every 30s, and that fetch rewrites.git/FETCH_HEAD. Git writes that file without a lock. When the poll overlaps a user's owngit pullin the same checkout, both fetches append for-merge lines, and the pull fails withfatal: Cannot fast-forward to multiple branches.Reproduced on main 125b619 with Git 2.43: the status fetch's exact arguments ran in a loop whilegit pull --ff-onlyran against a moving remote, and 12 of 300 pulls failed that way.Change
fetchRemoteForStatusnow passes--no-write-fetch-head. The poll only needsrefs/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 fetchesinapps/server/src/vcs/GitVcsDriverCore.test.ts:AssertionError: background status fetch wrote .git/FETCH_HEAD, which an overlapping \git pull` merges from: expected true to equal false`behindCountis still 1, so the fetch still runs.GitVcsDriverCore.test.ts: 172/172 pass.vp run check:changedpasses.Runtime transcript (Git 2.43, Linux): a background loop runs the status fetch's exact command while
git pull --ff-onlyruns 300 times against a remote that gains a commit before each pull.Cannot fast-forward to multiple branchesfailuresDriver: 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