anomalyco / anomalyco/opencode
bug: workspace diff passes --unified=2147483647, re-emitting the file once per hunk (>1 GB patch text, ~11 s CPU per filesystem event)
@Brendonovich is already working on this.
Since Sep 16, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
无法直接提交:本机没有 gh、没有 GitHub token,且 github.com web 被墙(api.github.com 可达但建 issue 需鉴权)。已把报告写成即用文件,并确认了更新通道就是 anomalyco/opencode(resources/app-update.yml)。
报告已存:C:\Users\Admin\AppData\Local\Temp\opencode\opencode-issue-unified-context.md
标题:bug: workspace diff passes --unified=2147483647, re-emitting the file once per hunk (>1 GB patch text, ~11 s CPU per filesystem event)
正文:
Environment
| OpenCode Desktop | 1.18.31 (@opencode-aidesktop, update channel anomalyco/opencode per resources/app-update.yml) |
| OS | Windows 10 企业版 10.0.19042, x64 |
| git | 2.37.1.windows.1 |
| node | v24.13.0 |
| workspace | 10 modified tracked files, two of them generated JSON (1.34 MB / 0.85 MB) |
Summary
Every filesystem event in the workspace makes OpenCode spawn a full-context working-tree diff:
git --no-optional-locks -c core.autocrlf=false -c core.fsmonitor=false -c core.longpaths=true \
-c core.symlinks=true -c core.quotepath=false diff --patch --no-ext-diff \
--no-renames --unified=2147483647 HEAD -- .
With --unified=INT32_MAX the hunks are not merged, and every hunk's context is unbounded. git therefore re-emits a large slab of the file once per changed cluster, so output scales as O(changed clusters × file size).
Measured here: one invocation produced >1 GB of patch text and ~11 s of CPU; OpenCode's node utility process held 60–88% CPU with up to 10 concurrent git.exe alive (29 distinct git PIDs in a 30 s window), each spawning a child git.exe.
Steps to reproduce
- Have a workspace containing a large-ish tracked file that is modified (a few hundred changed clusters in a 1–2 MB file is enough).
- Edit/touch any file in the repo (a bare
touch— mtime only, content unchanged — is sufficient; verified). - Observe repeated
git ... diff --patch --no-ext-diff --no-renames --unified=2147483647 HEAD -- .spawned by the OpenCode node utility process (--type=utility --utility-sub-type=node.mojom.NodeService), piling up.
Measurements
Same command, per modified file (stdout byte count):
| file | size | changed lines | changed clusters (-U0) |
output with --unified=2147483647 |
|---|---|---|---|---|
level_curriculum.json |
1.34 MB | 3049 / 1786 | 1747 | 512 MB |
knowledge_points.json |
0.85 MB | 243 / 243 | 243 | 213 MB |
content_assets_poems.json |
0.14 MB | 126 / 126 | 126 | 18.3 MB |
content_assets_english.json |
~0.1 MB | 65 / 65 | 65 | 2.8 MB |
Whole repo (same command, -- .): >1024 MB / ~11000 ms (truncated by a 1 GiB capture buffer, so the real figure is larger).
Control — same repo, same files, plain -U3 diff: 0.15 MB / 125 ms for the same file. So the flag inflates output ~1800× per file and wall time ~88×.
Why the flag does not behave as intended
--unified=2147483647 looks like "give me the whole file as context". But git merges two change clusters only when the gap between them is ≤ 2 × context. 2 × 2147483647 overflows a signed 32-bit int → negative → the merge condition can never hold, so no hunks merge, while each hunk's context stays unbounded. Net effect is the worst of both worlds: maximum output, zero consolidation.
(I have not read git's source to point at the exact overflow site; the behaviour is reproducible with stock git 2.37.1.windows.1. Note that a bounded-but-huge value such as --unified=100000 would already merge all clusters in these files while still covering the whole file — so the current value is not merely large, it is self-defeating.)
Impact
- A per-event CPU storm, not per-commit: any repo with a few large tracked files is affected.
- Watcher events are frequent and cheap (build output, test reports, sqlite WAL writes, logs,
touch), so the storm sustains itself while any tool writes into the repo. - Long sessions are worst: each invocation takes seconds, so successive spawns overlap and process count / CPU climb instead of settling.
- Memory: each invocation materialises up to ~1 GB of patch text in the child process and again over IPC in the renderer.
- The workaround users are pushed to is to change the repo (e.g.
.gitattributes-diffon generated files) to compensate for an app-side flag — which also destroys diff readability for those files.
Suggested fixes
- Don't pass an unbounded/overflowing context value. Either clamp it (to the file's line count, or a few thousand), or implement "whole file" by emitting the file body once instead of once per hunk.
- Guard by size: skip or bound the diff when
additions + deletionsor the blob size crosses a threshold — same spirit as thediffFullguard in #48617. - Single-flight / cache this diff per event burst and debounce so N rapid writes cause one invocation, not N — same spirit as #44127's TTL + single-flight work.
- Consider an env escape hatch (consistent with the existing
OPENCODE_VCS_*knobs).
Related issues
- #3176 — "Why is OpenCode massively abusing git?" (same broad symptom)
- #44127 — redundant git subprocess spawns (spawn count; this report is about per-spawn output size)
- #48617 —
snapshot.diffFullmemory exhaustion on large files (in-process Myers diff; different code path from thegit diffsubprocess) - #43769 — Windows CPU under parallel sessions
- #48638 — durable event write amplification from turn diffs
Plugins
No response
OpenCode version
1.18.31
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.