[BUG] Snapshot module ignores 'watcher.ignore' config, causing excessive disk usage and performance degradation #6478

Open
opened 2026-02-16 18:04:20 -05:00 by yindo · 1 comment
Owner

Originally created by @vianb on GitHub (Jan 16, 2026).

Originally assigned to: @rekram1-node on GitHub.

Description

The Snapshot module (packages/opencode/src/snapshot/index.ts), which is responsible for tracking file changes and generating session diffs, completely ignores the watcher.ignore configuration settings.

When Snapshot.track() is called, it executes:

await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`...

This command blindly adds all files in the workspace to the internal snapshot repository, regardless of whether they are listed in watcher.ignore or standard ignore patterns.

Impact

  1. Disk Space Exhaustion: If a project contains large ignored directories (e.g., data/, venv/, build artifacts), OpenCode tracks them internally. This leads to massive files accumulating in ~/.local/share/opencode/storage/session_diff/ and ~/.local/share/opencode/snapshot/.
  2. Performance degradation: High CPU and I/O usage during every agent step as git add processes large ignored files.
  3. Privacy/Security: Files explicitly ignored by the user (potentially containing sensitive data) are inadvertently tracked in the internal snapshot history.

Reproduction

  1. Create a project with a large directory (e.g., data/ with 1GB+ files).
  2. Configure opencode.json to ignore it:
    { "watcher": { "ignore": ["data/**"] } }
    
  3. Start a session and make the agent perform a task.
  4. Observe ~/.local/share/opencode/snapshot/ growing and git processes consuming CPU, despite the ignore rule.

Code Location

packages/opencode/src/snapshot/index.ts

Proposed Fix (Patch)

The following patch ensures that Snapshot.track() respects both standard ignore patterns and user-defined watcher.ignore rules by updating the internal git repository's info/exclude and cleaning the index.

--- packages/opencode/src/snapshot/index.ts.orig
+++ packages/opencode/src/snapshot/index.ts
@@ -6,6 +6,7 @@
 import z from "zod"
 import { Config } from "../config/config"
 import { Instance } from "../project/instance"
+import { FileIgnore } from "../file/ignore"
 
 export namespace Snapshot {
   const log = Log.create({ service: "snapshot" })
@@ -28,6 +29,31 @@
       await $`git --git-dir ${git} config core.autocrlf false`.quiet().nothrow()
       log.info("initialized")
     }
+
+    const ignores = [...FileIgnore.PATTERNS, ...(cfg.watcher?.ignore ?? [])]
+    const infoDir = path.join(git, "info")
+    await fs.mkdir(infoDir, { recursive: true })
+    await Bun.write(path.join(infoDir, "exclude"), ignores.join("\n"))
+
+    const ignoredFiles = await $`git --git-dir ${git} --work-tree ${Instance.worktree} ls-files -i --exclude-standard`
+      .quiet()
+      .cwd(Instance.directory)
+      .nothrow()
+      .text()
+
+    if (ignoredFiles.trim()) {
+      await $`git --git-dir ${git} --work-tree ${Instance.worktree} ls-files -i --exclude-standard -z`
+        .quiet()
+        .cwd(Instance.directory)
+        .nothrow()
+        .pipe(
+          $`xargs -0 git --git-dir ${git} --work-tree ${Instance.worktree} rm --cached -r --ignore-unmatch`
+            .quiet()
+            .cwd(Instance.directory)
+            .nothrow(),
+        )
+    }
+
     await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
     const hash = await $`git --git-dir ${git} --work-tree ${Instance.worktree} write-tree`
       .quiet()
Originally created by @vianb on GitHub (Jan 16, 2026). Originally assigned to: @rekram1-node on GitHub. ### Description The `Snapshot` module (`packages/opencode/src/snapshot/index.ts`), which is responsible for tracking file changes and generating session diffs, completely ignores the `watcher.ignore` configuration settings. When `Snapshot.track()` is called, it executes: ```typescript await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`... ``` This command blindly adds *all* files in the workspace to the internal snapshot repository, regardless of whether they are listed in `watcher.ignore` or standard ignore patterns. ### Impact 1. **Disk Space Exhaustion**: If a project contains large ignored directories (e.g., `data/`, `venv/`, build artifacts), OpenCode tracks them internally. This leads to massive files accumulating in `~/.local/share/opencode/storage/session_diff/` and `~/.local/share/opencode/snapshot/`. 2. **Performance degradation**: High CPU and I/O usage during every agent step as `git add` processes large ignored files. 3. **Privacy/Security**: Files explicitly ignored by the user (potentially containing sensitive data) are inadvertently tracked in the internal snapshot history. ### Reproduction 1. Create a project with a large directory (e.g., `data/` with 1GB+ files). 2. Configure `opencode.json` to ignore it: ```json { "watcher": { "ignore": ["data/**"] } } ``` 3. Start a session and make the agent perform a task. 4. Observe `~/.local/share/opencode/snapshot/` growing and `git` processes consuming CPU, despite the ignore rule. ### Code Location `packages/opencode/src/snapshot/index.ts` ### Proposed Fix (Patch) The following patch ensures that `Snapshot.track()` respects both standard ignore patterns and user-defined `watcher.ignore` rules by updating the internal git repository's `info/exclude` and cleaning the index. ```diff --- packages/opencode/src/snapshot/index.ts.orig +++ packages/opencode/src/snapshot/index.ts @@ -6,6 +6,7 @@ import z from "zod" import { Config } from "../config/config" import { Instance } from "../project/instance" +import { FileIgnore } from "../file/ignore" export namespace Snapshot { const log = Log.create({ service: "snapshot" }) @@ -28,6 +29,31 @@ await $`git --git-dir ${git} config core.autocrlf false`.quiet().nothrow() log.info("initialized") } + + const ignores = [...FileIgnore.PATTERNS, ...(cfg.watcher?.ignore ?? [])] + const infoDir = path.join(git, "info") + await fs.mkdir(infoDir, { recursive: true }) + await Bun.write(path.join(infoDir, "exclude"), ignores.join("\n")) + + const ignoredFiles = await $`git --git-dir ${git} --work-tree ${Instance.worktree} ls-files -i --exclude-standard` + .quiet() + .cwd(Instance.directory) + .nothrow() + .text() + + if (ignoredFiles.trim()) { + await $`git --git-dir ${git} --work-tree ${Instance.worktree} ls-files -i --exclude-standard -z` + .quiet() + .cwd(Instance.directory) + .nothrow() + .pipe( + $`xargs -0 git --git-dir ${git} --work-tree ${Instance.worktree} rm --cached -r --ignore-unmatch` + .quiet() + .cwd(Instance.directory) + .nothrow(), + ) + } + await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow() const hash = await $`git --git-dir ${git} --work-tree ${Instance.worktree} write-tree` .quiet() ```
yindo added the perf label 2026-02-16 18:04:20 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 16, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #6740: What is the point of watcher.ignore - User reports that files in watcher.ignore are still being accessed and tracked despite the configuration, showing console output with files marked as 'should have ignored'
@github-actions[bot] commented on GitHub (Jan 16, 2026): This issue might be a duplicate of existing issues. Please check: - #6740: What is the point of watcher.ignore - User reports that files in watcher.ignore are still being accessed and tracked despite the configuration, showing console output with files marked as 'should have ignored'
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6478