2026-03-08-include-filter-design.md (2284B)
1 # Include Filter Design 2 3 ## Problem 4 5 In a large monorepo, you may only want to sync specific subtrees (e.g., `project/src` but not `project/tmp`). The current config only supports `ignore` patterns, which means you must enumerate everything you *don't* want. An `include` list inverts this: state what you *do* want, then refine with `ignore`. 6 7 ## Design Spirit 8 9 **Include is simple and explicit.** It names the directories you care about. Leave fine-grained filtering to `ignore`. 10 11 ## TOML Config 12 13 ```toml 14 [settings] 15 include = ["src", "docs/api"] # path prefixes relative to sync.local 16 ignore = [".git", ".DS_Store"] # applied within included paths 17 ``` 18 19 - `include = []` (default) means include everything — fully backwards compatible. 20 - When specified, only paths under those prefixes are synced. 21 - `ignore` further refines within included paths. 22 - Patterns are path prefixes relative to `sync.local`, not globs or basenames. 23 24 ## Evaluation Order 25 26 ``` 27 file path relative to sync.local 28 -> does it fall under an include prefix? (empty = yes to all) 29 -> no -> skip 30 -> yes -> does it match an ignore pattern? 31 -> yes -> skip 32 -> no -> sync it 33 ``` 34 35 ## Changes 36 37 ### Config (`internal/config/config.go`) 38 39 - Add `Include []string` to `Settings` struct with `toml:"include"` tag. 40 - Update `DefaultTOML()` to document `include = []`. 41 42 ### Watcher (`internal/watcher/watcher.go`) 43 44 - Add `shouldInclude(path)` using path-prefix matching against the path relative to the watched root. 45 - In `addRecursive()`: skip directories that are neither a prefix of, nor prefixed by, an include path. (If include is `["src/api"]`, we must still traverse `src/` to reach `src/api/`.) 46 - In the event loop: check include before ignore. Reject events outside included prefixes. 47 48 ### Syncer (`internal/syncer/syncer.go`) 49 50 When include patterns are present, emit rsync filter rules: 51 52 1. `--include=<prefix>/` for each ancestor directory needed for traversal. 53 2. `--include=<prefix>/**` for each include entry. 54 3. `--exclude=*` to block everything else. 55 56 Existing `--exclude` patterns from ignore lists move *before* the final `--exclude=*` so they can refine within included paths. 57 58 ### No changes needed to 59 60 SSH config, log config, rsync flags (archive/compress/etc.), CLI entry point.