fix: use gitignore-aware selective watching to reduce memory usage (#1399)

* fix: skip watching heavy directories to avoid memory leak

* fix: reduce memory usage in filesystem watcher for pnpm projects

Use gitignore-aware directory collection to prevent OS-level watchers
from being set up on heavy directories like node_modules.

Changes:
- Use WalkBuilder's git_ignore(true) to respect .gitignore when collecting watch directories
- Use NonRecursive watch mode for each directory instead of single Recursive watch
- Simplify ALWAYS_SKIP_DIRS to only contain .git (not in .gitignore but should be skipped)

This fixes high memory usage when running vibe-kanban on pnpm-managed
repositories, which contain many symlinks in node_modules.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: simplify filesystem watcher error handling and fix clippy warnings

- Remove debug logs that were added during development
- Fail fast on watch errors instead of continuing in a partially broken state
- Collapse nested if statements to satisfy clippy::collapsible_if

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: check only parent directories in path_allowed

Only check parent directory names against ALWAYS_SKIP_DIRS since
should_skip_dir is meant for directories, not file names.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: add watch directories changes

* Stop leaking filesystem watcher

Use weak references so the background thread exits once the diff stream releases the debouncer.

* Memoize ignored-descendant scan

Cache per-directory results so collecting watch targets stays linear.

* Improve watcher gitignore classification

Use real metadata when available, fall back to the old heuristic only when the file is already gone, and document the limitation.

* Handle directory renames in watcher

* Deduplicate watch directories more efficiently

Sort once and do a single sweep instead of quadratic filtering.

* Document gitignore watcher limitation

* Cascade watcher removal on directory delete

Drop all descendant watchers when a parent directory disappears so stale handles don’t linger.

* Watch subdirectories when new directories are added

Track recursive parent watchers so we skip redundant child watches and prune them when a parent becomes recursive.

* Always ignore node_modules

* Use a high-performance line count diff implementation

The `similar` diff stat implementation was causing cpu spikes of up to 600% over 10 seconds.

* exclude node_modules from git diff commands

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Solomon <abcpro11051@disroot.org>
This commit is contained in:
ukwksk
2025-12-11 03:08:42 +09:00
committed by GitHub
parent 72c952626a
commit 85f6ee5237
3 changed files with 549 additions and 52 deletions

View File

@@ -1,7 +1,8 @@
use std::borrow::Cow;
use git2::{DiffOptions, Patch};
use serde::{Deserialize, Serialize};
use similar::{ChangeTag, TextDiff};
use similar::TextDiff;
use ts_rs::TS;
// Structs compatable with props: https://github.com/MrWangJustToDo/git-diff-view
@@ -74,19 +75,18 @@ pub fn compute_line_change_counts(old: &str, new: &str) -> (usize, usize) {
let old = ensure_newline(old);
let new = ensure_newline(new);
let diff = TextDiff::from_lines(&old, &new);
let mut opts = DiffOptions::new();
opts.context_lines(0);
let mut additions = 0usize;
let mut deletions = 0usize;
for change in diff.iter_all_changes() {
match change.tag() {
ChangeTag::Insert => additions += 1,
ChangeTag::Delete => deletions += 1,
ChangeTag::Equal => {}
match Patch::from_buffers(old.as_bytes(), None, new.as_bytes(), None, Some(&mut opts))
.and_then(|patch| patch.line_stats())
{
Ok((_, adds, dels)) => (adds, dels),
Err(e) => {
tracing::error!("git2 diff failed: {}", e);
(0, 0)
}
}
(additions, deletions)
}
// ensure a line ends with a newline character