<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sourcewatch.git/watcher.go, branch main</title>
<subtitle>experimental filesync for windows docker bindmounts</subtitle>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/'/>
<entry>
<title>Watch nested dirs: recursively add watchers for subdirs on create</title>
<updated>2026-07-06T20:30:55+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T20:30:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=e4c2f78abe427d9fdbfb46bb205f2210a03704fa'/>
<id>e4c2f78abe427d9fdbfb46bb205f2210a03704fa</id>
<content type='text'>
When mkdir -p creates multiple nested dirs at once, only the first
level got a watcher. Now syncCreate walks all subdirectories and
adds watchers for them. The event handler already filters ignored paths.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
When mkdir -p creates multiple nested dirs at once, only the first
level got a watcher. Now syncCreate walks all subdirectories and
adds watchers for them. The event handler already filters ignored paths.
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix event debouncing: merge ops instead of overwriting</title>
<updated>2026-07-06T20:24:18+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T20:24:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=40b39e093ce6af33e82801c1edd7f64faa5ceee7'/>
<id>40b39e093ce6af33e82801c1edd7f64faa5ceee7</id>
<content type='text'>
- touch fires CREATE then CHMOD, the CHMOD was overwriting CREATE
  in the debounce map, and CHMOD is not handled, so file was never synced
- Now debounce merges event ops (CREATE|CHMOD → treated as CREATE)
- Added sync/remove debug logging
- Tested in Docker: create, modify, delete all working
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- touch fires CREATE then CHMOD, the CHMOD was overwriting CREATE
  in the debounce map, and CHMOD is not handled, so file was never synced
- Now debounce merges event ops (CREATE|CHMOD → treated as CREATE)
- Added sync/remove debug logging
- Tested in Docker: create, modify, delete all working
</pre>
</div>
</content>
</entry>
<entry>
<title>Add log level system and sync logging</title>
<updated>2026-07-06T20:16:37+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T20:16:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=913a974eca2522ba7c6d27066481bb2c6635db53'/>
<id>913a974eca2522ba7c6d27066481bb2c6635db53</id>
<content type='text'>
- log.go: log level system (DEBUG, INFO, WARN, ERROR, NONE)
- CLI: -log-level flag (default info), -verbose as shorthand for debug
- Watcher: logs sync/remove actions at INFO level
- Watcher: logs events at DEBUG level
- Errors: structured error messages (DISK FULL, PERMISSION DENIED, etc.)
- Updated README with -log-level flag
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- log.go: log level system (DEBUG, INFO, WARN, ERROR, NONE)
- CLI: -log-level flag (default info), -verbose as shorthand for debug
- Watcher: logs sync/remove actions at INFO level
- Watcher: logs events at DEBUG level
- Errors: structured error messages (DISK FULL, PERMISSION DENIED, etc.)
- Updated README with -log-level flag
</pre>
</div>
</content>
</entry>
<entry>
<title>Add structured error handling with retry logic</title>
<updated>2026-07-06T19:58:06+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:58:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=1043b58c164ac48757cdb373967ead2b34aa7388'/>
<id>1043b58c164ac48757cdb373967ead2b34aa7388</id>
<content type='text'>
- errors.go: retry wrapper for transient errors (ENOENT),
  structured error logging (disk full, permission denied, symlink loops)
- sync.go: continue on individual file errors, log clearly, skip broken
  symlinks instead of failing entire sync
- watcher.go: log all errors with context, handle readlink failures
- Added 11 tests for error handling scenarios
- 50 tests passing
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- errors.go: retry wrapper for transient errors (ENOENT),
  structured error logging (disk full, permission denied, symlink loops)
- sync.go: continue on individual file errors, log clearly, skip broken
  symlinks instead of failing entire sync
- watcher.go: log all errors with context, handle readlink failures
- Added 11 tests for error handling scenarios
- 50 tests passing
</pre>
</div>
</content>
</entry>
<entry>
<title>Add symlink watcher support with tests</title>
<updated>2026-07-06T19:54:00+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:54:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=5a2d70be3ce8524c366f4dd303b950974f05aa7c'/>
<id>5a2d70be3ce8524c366f4dd303b950974f05aa7c</id>
<content type='text'>
- syncCreate: remove existing file before creating symlink
- syncWrite: handle symlink target changes (remove + recreate)
- Added 4 new tests:
  - CreateSymlink: new symlink synced to /data
  - UpdateSymlinkTarget: symlink target change updates /data
  - RemoveSymlink: symlink removal synced
  - ModifySymlinkTargetFile: target file change preserves symlink
- 39 tests passing
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- syncCreate: remove existing file before creating symlink
- syncWrite: handle symlink target changes (remove + recreate)
- Added 4 new tests:
  - CreateSymlink: new symlink synced to /data
  - UpdateSymlinkTarget: symlink target change updates /data
  - RemoveSymlink: symlink removal synced
  - ModifySymlinkTargetFile: target file change preserves symlink
- 39 tests passing
</pre>
</div>
</content>
</entry>
<entry>
<title>Docker test fixes: directory pattern matching, mtime-based sync, Go version</title>
<updated>2026-07-06T19:47:30+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:47:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=5137f2cf25a71cfaf6d7794d86ea2ed52bb001d3'/>
<id>5137f2cf25a71cfaf6d7794d86ea2ed52bb001d3</id>
<content type='text'>
- Fixed directory pattern matching: go-gitignore doesn't match 'build/'
  against bare 'build', now checks with trailing slash appended
- Added ModTime to FileMeta for reliable change detection (same-size
  file modifications now sync correctly)
- Updated Dockerfile to golang:1.24-alpine (was 1.22)
- Downgraded go.mod to Go 1.24 (system has custom 1.26.4 build)

Docker tested with podman:
- Initial sync correctly excludes ignored dirs/files
- Nested .gitignore works (sub/ ignores *.tmp)
- Watcher syncs new/modified/deleted files
- Case-sensitive rename (Foo→foo) handled correctly
- Same-size file modifications sync correctly
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- Fixed directory pattern matching: go-gitignore doesn't match 'build/'
  against bare 'build', now checks with trailing slash appended
- Added ModTime to FileMeta for reliable change detection (same-size
  file modifications now sync correctly)
- Updated Dockerfile to golang:1.24-alpine (was 1.22)
- Downgraded go.mod to Go 1.24 (system has custom 1.26.4 build)

Docker tested with podman:
- Initial sync correctly excludes ignored dirs/files
- Nested .gitignore works (sub/ ignores *.tmp)
- Watcher syncs new/modified/deleted files
- Case-sensitive rename (Foo→foo) handled correctly
- Same-size file modifications sync correctly
</pre>
</div>
</content>
</entry>
<entry>
<title>Implement nested .gitignore support with GitignoreStack</title>
<updated>2026-07-06T19:36:29+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:36:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=ad7575e85ebf6332d324ca5997db18ea81065df4'/>
<id>ad7575e85ebf6332d324ca5997db18ea81065df4</id>
<content type='text'>
- Replaced go-gitignore with custom GitignoreStack that tracks
  multiple .gitignore files hierarchically
- Patterns are processed in root-to-leaf order, last match wins
- Negation patterns in child dirs override parent ignores
- Patterns only apply within their directory scope (no leaking)
- Added 6 new tests for nested .gitignore:
  - Scoped: patterns only affect own directory
  - Dir-scoped: different dirs can ignore different things
  - Negation override: child !pattern un-ignores parent's *.pattern
  - Deep nesting: 3+ levels of .gitignore
  - Parent scope doesn't leak to siblings
- All 35 tests passing
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- Replaced go-gitignore with custom GitignoreStack that tracks
  multiple .gitignore files hierarchically
- Patterns are processed in root-to-leaf order, last match wins
- Negation patterns in child dirs override parent ignores
- Patterns only apply within their directory scope (no leaking)
- Added 6 new tests for nested .gitignore:
  - Scoped: patterns only affect own directory
  - Dir-scoped: different dirs can ignore different things
  - Negation override: child !pattern un-ignores parent's *.pattern
  - Deep nesting: 3+ levels of .gitignore
  - Parent scope doesn't leak to siblings
- All 35 tests passing
</pre>
</div>
</content>
</entry>
<entry>
<title>Add watcher tests, refactor watch() to accept context</title>
<updated>2026-07-06T19:26:29+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:26:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=8b22a36d1c068f9baee68799b0c1f51ecb999e9d'/>
<id>8b22a36d1c068f9baee68799b0c1f51ecb999e9d</id>
<content type='text'>
- Added watcher_test.go: 10 tests covering create, write, remove,
  rename, ignored files, debouncing, directory creation/removal,
  and manifest updates
- Refactored watch() to accept context.Context for clean cancellation
- Added signal handling (SIGINT/SIGTERM) for graceful shutdown in main.go
- Fixed test expectations (version two = 11 bytes, not 10)
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- Added watcher_test.go: 10 tests covering create, write, remove,
  rename, ignored files, debouncing, directory creation/removal,
  and manifest updates
- Refactored watch() to accept context.Context for clean cancellation
- Added signal handling (SIGINT/SIGTERM) for graceful shutdown in main.go
- Fixed test expectations (version two = 11 bytes, not 10)
</pre>
</div>
</content>
</entry>
<entry>
<title>Initial project scaffolding</title>
<updated>2026-07-06T19:15:17+00:00</updated>
<author>
<name>Bernhard Guillon</name>
<email>Bernhard.Guillon@begu.org</email>
</author>
<published>2026-07-06T19:15:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.begu.at/sourcewatch.git/commit/?id=b505ac2fe010fcc24b774c313d33dc099214e8ed'/>
<id>b505ac2fe010fcc24b774c313d33dc099214e8ed</id>
<content type='text'>
- main.go: CLI entry point with flag parsing
- gitignore.go: .gitignore loading and parsing
- sync.go: initial sync with manifest tracking
- watcher.go: fsnotify-based file watcher with debouncing
- Dockerfile: multi-stage build for Alpine runtime
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- main.go: CLI entry point with flag parsing
- gitignore.go: .gitignore loading and parsing
- sync.go: initial sync with manifest tracking
- watcher.go: fsnotify-based file watcher with debouncing
- Dockerfile: multi-stage build for Alpine runtime
</pre>
</div>
</content>
</entry>
</feed>
