One project's event loop: build the notify watcher over git metadata, then debounce raw events into coalesced syncs. On watcher construction/death, fall back to a 5-minute mtime poll for THIS project only.
(inner: Arc<GitWatcherInner>, state: Arc<WatchState>)
| 369 | /// debounce raw events into coalesced syncs. On watcher construction/death, |
| 370 | /// fall back to a 5-minute mtime poll for THIS project only. |
| 371 | async fn project_task(inner: Arc<GitWatcherInner>, state: Arc<WatchState>) { |
| 372 | let Some(common_dir) = crate::worktree::git_common_dir(&state.project_root) else { |
| 373 | // Not a resolvable git repo (yet). Degrade to polling so a later `git |
| 374 | // init` / clone is still eventually covered. |
| 375 | state.health.set_degraded(true); |
| 376 | degraded_poll_loop(&inner, &state, None).await; |
| 377 | return; |
| 378 | }; |
| 379 | |
| 380 | // Build the raw watcher. Its callback pushes into the dirty set and wakes |
| 381 | // the debounce loop — it never blocks and never syncs inline. |
| 382 | let wake_state = Arc::clone(&state); |
| 383 | let watcher = notify::recommended_watcher(move |res: notify::Result<notify::Event>| { |
| 384 | if let Ok(event) = res { |
| 385 | classify_and_mark(&wake_state, &event); |
| 386 | } |
| 387 | }); |
| 388 | |
| 389 | let mut watcher = match watcher { |
| 390 | Ok(w) => w, |
| 391 | Err(e) => { |
| 392 | log_daemon_event( |
| 393 | "git_watch_degraded", |
| 394 | &[ |
| 395 | ("project", state.project_root.display().to_string()), |
| 396 | ("reason", "watcher_build_failed".to_string()), |
| 397 | ("error", e.to_string()), |
| 398 | ], |
| 399 | ); |
| 400 | state.health.set_degraded(true); |
| 401 | degraded_poll_loop(&inner, &state, Some(&common_dir)).await; |
| 402 | return; |
| 403 | } |
| 404 | }; |
| 405 | |
| 406 | if let Err(e) = install_watches(&mut watcher, &common_dir) { |
| 407 | log_daemon_event( |
| 408 | "git_watch_degraded", |
| 409 | &[ |
| 410 | ("project", state.project_root.display().to_string()), |
| 411 | ("reason", "watch_install_failed".to_string()), |
| 412 | ("error", e.to_string()), |
| 413 | ], |
| 414 | ); |
| 415 | state.health.set_degraded(true); |
| 416 | degraded_poll_loop(&inner, &state, Some(&common_dir)).await; |
| 417 | return; |
| 418 | } |
| 419 | |
| 420 | state.health.set_degraded(false); |
| 421 | state.health.beat(); |
| 422 | |
| 423 | debounce_loop(&inner, &state, &common_dir).await; |
| 424 | // Keep the watcher alive for the whole loop. |
| 425 | drop(watcher); |
| 426 | } |
| 427 | |
| 428 | /// Installs the minimal metadata watch set: `HEAD`, `packed-refs`, in-flight |
no test coverage detected