esync

Directory watching and remote syncing
Log | Files | Refs | README | LICENSE

commit 027bd5084854d35406ddcf43fcd29ccdce8bbe5b
parent 549e484b6715c1e1b64affd6cc30be59a1e0f04a
Author: Erik Loualiche <eloualic@umn.edu>
Date:   Sun,  8 Mar 2026 14:57:06 -0400

docs: add design for include filter configuration

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

Diffstat:
Adocs/plans/2026-03-08-include-filter-design.md | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+), 0 deletions(-)

diff --git a/docs/plans/2026-03-08-include-filter-design.md b/docs/plans/2026-03-08-include-filter-design.md @@ -0,0 +1,60 @@ +# Include Filter Design + +## Problem + +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`. + +## Design Spirit + +**Include is simple and explicit.** It names the directories you care about. Leave fine-grained filtering to `ignore`. + +## TOML Config + +```toml +[settings] +include = ["src", "docs/api"] # path prefixes relative to sync.local +ignore = [".git", ".DS_Store"] # applied within included paths +``` + +- `include = []` (default) means include everything — fully backwards compatible. +- When specified, only paths under those prefixes are synced. +- `ignore` further refines within included paths. +- Patterns are path prefixes relative to `sync.local`, not globs or basenames. + +## Evaluation Order + +``` +file path relative to sync.local + -> does it fall under an include prefix? (empty = yes to all) + -> no -> skip + -> yes -> does it match an ignore pattern? + -> yes -> skip + -> no -> sync it +``` + +## Changes + +### Config (`internal/config/config.go`) + +- Add `Include []string` to `Settings` struct with `toml:"include"` tag. +- Update `DefaultTOML()` to document `include = []`. + +### Watcher (`internal/watcher/watcher.go`) + +- Add `shouldInclude(path)` using path-prefix matching against the path relative to the watched root. +- 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/`.) +- In the event loop: check include before ignore. Reject events outside included prefixes. + +### Syncer (`internal/syncer/syncer.go`) + +When include patterns are present, emit rsync filter rules: + +1. `--include=<prefix>/` for each ancestor directory needed for traversal. +2. `--include=<prefix>/**` for each include entry. +3. `--exclude=*` to block everything else. + +Existing `--exclude` patterns from ignore lists move *before* the final `--exclude=*` so they can refine within included paths. + +### No changes needed to + +SSH config, log config, rsync flags (archive/compress/etc.), CLI entry point.