apply include/exclude globs in search.replace (T-364)

computeReplacements accepted the query's glob filters and silently
dropped them — replace could rewrite files the equivalent search
would never have matched. The grep engine's glob helpers are now
public and shared, so search and replace can't disagree on scope;
both the preview and the apply path go through the filtered list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:34:47 +02:00
co-authored by Claude Fable 5
parent 8477e302ef
commit 88d72789f4
6 changed files with 51 additions and 5 deletions
@@ -3947,3 +3947,5 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHC0HYRZ86CWW0DDQJ5CAQM', 'status', 'in_progress', 'done', NULL, '2026-06-11 22:30:24', '2026-06-11 22:30:24', '2026-06-11 22:30:24', NULL, 'aa1fe975e147be83905f24c63662254d', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBSG6356MZJ2DCCCSBMBGM', 'status', 'backlog', 'in_progress', NULL, '2026-06-11 22:30:48', '2026-06-11 22:30:48', '2026-06-11 22:30:48', NULL, '2c5c8e5cdf68c96e25705f014dcd6acc', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBSG6356MZJ2DCCCSBMBGM', 'status', 'in_progress', 'done', NULL, '2026-06-11 22:32:08', '2026-06-11 22:32:08', '2026-06-11 22:32:08', NULL, 'f3dbb31fb6f6f416a2d4d8ef37723316', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBR4636GSRJBWFJDAZ6ZA0', 'status', 'backlog', 'in_progress', NULL, '2026-06-11 22:32:29', '2026-06-11 22:32:29', '2026-06-11 22:32:29', NULL, '7b833a606da59ab523e0bb43f1753f6a', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBR4636GSRJBWFJDAZ6ZA0', 'status', 'in_progress', 'done', NULL, '2026-06-11 22:34:31', '2026-06-11 22:34:31', '2026-06-11 22:34:31', NULL, '254ae09f42b4da439a04d59d675d8f42', 2) ON CONFLICT(hash) DO NOTHING;
+6
View File
@@ -4007,3 +4007,9 @@ Fix: use lstat (FileStat via Link check / FileSystemEntity.isLinkSync on the raw
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBSG6356MZJ2DCCCSBMBGM', 'bug', '06FBHBGHNEQTAEPGNJKN42C1E8', 'listDir symlink detection is dead code; walkFiles descends symlinked dirs', 'lib/src/files/listing.dart:46-54 — stat() follows links, so isSymlink is always false; walkFiles therefore descends symlinked directories the docs claim it skips (escape hatch out of the workspace, plus cycle risk).
Fix: use lstat (FileStat via Link check / FileSystemEntity.isLinkSync on the raw path) for symlink detection. Tests: symlinked dir is reported as symlink and not descended; symlink cycle does not hang the walk.', 'done', 'high', NULL, NULL, NULL, '2026-06-11 21:55:49', '2026-06-11 22:32:08', NULL, 'f83e31fd330c98a311a3da10cba1c655', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBR4636GSRJBWFJDAZ6ZA0', 'bug', '06FBHBGHNEQTAEPGNJKN42C1E8', 'search.replace silently ignores its include/exclude globs', 'lib/src/search/replace_engine.dart:124-143 — the include/exclude glob filters the user typed are accepted but never applied; replace will happily rewrite files outside the filter.
Fix: apply the same glob filtering the search side uses before rewriting. Test: replace with an include glob touches only matching files; exclude glob is honored.', 'in_progress', 'high', NULL, NULL, NULL, '2026-06-11 21:55:38', '2026-06-11 22:32:29', NULL, '0d067ce0eded0fcc158d09ea793bb223', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBHBR4636GSRJBWFJDAZ6ZA0', 'bug', '06FBHBGHNEQTAEPGNJKN42C1E8', 'search.replace silently ignores its include/exclude globs', 'lib/src/search/replace_engine.dart:124-143 — the include/exclude glob filters the user typed are accepted but never applied; replace will happily rewrite files outside the filter.
Fix: apply the same glob filtering the search side uses before rewriting. Test: replace with an include glob touches only matching files; exclude glob is honored.', 'done', 'high', NULL, NULL, NULL, '2026-06-11 21:55:38', '2026-06-11 22:34:31', NULL, 'e86c024d6f8a6d9cd9b7273522b54511', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);