Skip to content

Watcher keeps indexing newly-ignored files: .ignore changes don't trigger an ignore-rules refresh #104

Description

@shengyfu

Summary

While tgrep serve is running, the file watcher does not notice changes to .ignore files. As a result the watcher keeps applying the ignore rules it loaded at startup and happily indexes files and folders that a newly added/edited .ignore says to exclude — so the live overlay ends up containing content that a fresh tgrep index over the same tree would never index.

Root cause

tgrep-cli/src/serve.rs:

fn is_ignore_rules_file(root: &Path, path: &Path) -> bool {
    path.file_name().and_then(|name| name.to_str()) == Some(".gitignore")
        || path == root.join(tgrep_core::gitignore::P4IGNORE_FILENAME)
}

handle_fs_event uses this predicate to decide whether an event should set ignore_rules_dirty and schedule schedule_ignore_rules_refresh, which is what rebuilds and republishes ServerState::gitignore.

The predicate covers .gitignore (at any depth) and root-level p4ignore.ini, but it omits .ignore, even though .ignore is a first-class ignore source everywhere else in the codebase:

  • walker::walk_dir collects ignore_files separately from gitignore_files.
  • gitignore::matcher_from_ignore_paths_with_options feeds both into the matcher, and .ignore even outranks .gitignore.
  • Unlike .gitignore, .ignore is not git-gated — it applies outside a repo too, which is exactly the case covered by tgrep-cli/tests/watcher_dot_ignore.rs.

Because the event never matches, the .ignore write also falls through to the normal reindex path where should_skip_watcher_path drops it (any dot-prefixed segment is skipped), so nothing at all happens.

Impact

  • Create or edit .ignore while the server is up → files under the newly ignored directory keep getting indexed and remain searchable.
  • Delete or relax .ignore → the stale matcher keeps excluding paths that should now be indexed, until the next full stale check or restart.
  • In-memory overlay and on-disk index diverge, which is the exact class of drift should_skip_watcher_path exists to prevent.

.gitignore and root p4ignore.ini do not have this problem; only .ignore is missed.

Reproduction

  1. tgrep index <root> --index-path <idx> on a tree with no .git directory.
  2. tgrep serve --index-path <idx> <root>, wait for the watcher to come up.
  3. echo "secret/" > <root>/.ignore
  4. mkdir <root>/secret && echo "leak_marker" > <root>/secret/creds.txt
  5. Search leak_marker → it matches. Expected: no matches, because secret/ is excluded by .ignore.

The existing tgrep-cli/tests/watcher_dot_ignore.rs passes only because its .ignore exists before the initial index/walk, so the startup matcher already contains the rule.

Expected behavior

A create/modify/remove event for a .ignore file should be treated exactly like a .gitignore event: mark ignore_rules_dirty and schedule the background stale refresh so the matcher is rebuilt and republished.

Proposed fix

Extend is_ignore_rules_file to recognize .ignore alongside .gitignore (matching by file name at any depth, since nested .ignore files are honored by the matcher), keeping p4ignore.ini root-scoped as it is today. Add unit coverage in serve.rs plus an end-to-end test that writes .ignore after the server is live.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions