Skip to content

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

@vianb

Description

@vianb

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()

Metadata

Metadata

Assignees

Labels

perfIndicates a performance issue or need for optimization

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions