Skip to main content

Crate vcs_watch

Crate vcs_watch 

Source
Expand description

vcs-watch — filesystem-watch a git/jj repository and emit typed state-change events.

A RepoWatcher watches a repository’s .git/.jj state directory (and, optionally, the working tree), debounces the burst of writes a VCS operation makes, re-queries the repo state through vcs-core’s batched snapshot, and diffs it against the previous state to yield typed RepoEvents. Each settled change arrives as a RepoChange carrying both the new RepoSnapshot (to render a prompt/status line) and the deltas (to react). It’s the foundation for prompts, status bars, TUIs, and repo daemons.

Re-query-and-diff — rather than interpreting raw filesystem events — is what makes it robust: git’s ref temp-file renames, index.lock churn, and reflog noise all just coalesce into one “re-check the settled state” instead of being (mis)read as events. Noise that doesn’t move observable state emits nothing, and every emission carries the true current state, so a stray event can’t desync the consumer.

§The surface

§Recipes

Watch with the defaults and react to each settled change:

use vcs_core::Repo;
use vcs_watch::RepoWatcher;
let repo = Repo::open(".")?;
let mut watcher = RepoWatcher::watch(repo).await?;
while let Some(change) = watcher.recv().await {
    for event in &change.events {
        println!("{event:?}");
    }
    // `change.snapshot` is the fresh full state — render a status line off it.
}

Under the stream feature the watcher is a futures_core::Stream, so it drops into stream combinators and tokio::select! directly (needs futures/tokio-stream’s StreamExt in scope):

use futures::StreamExt;
use vcs_core::Repo;
use vcs_watch::RepoWatcher;
let repo = Repo::open(".")?;
let mut watcher = RepoWatcher::watch(repo).await?;
while let Some(change) = watcher.next().await {
    println!("{} event(s)", change.events.len());
}

Runtime: unlike the rest of the toolkit (which hides tokio behind processkit), vcs-watch uses tokio at runtime — the watch task and the debounce timer run on the caller’s tokio runtime, so build/await it from within one.

§Testing

The debounce → ceiling → re-query pipeline is a free function over injected seams, so it is exercised hermetically on a paused clock (no real filesystem or sleeps); a consumer’s own watch code tests the same way it tests any vcs-core consumer — build the Repo over a fake runner (processkit’s ScriptedRunner) so the re-query returns canned state. See vcs-testkit’s guide.

§In-depth guide

Beyond this page, this crate ships a full how-to guide — rendered on docs.rs from docs/. See the guide module.

Modules§

guide
vcs-watch — repo-event stream

Structs§

Builder
Builder for a RepoWatcher — set the watch scope and debounce timing, then build.
RepoChange
A batch of changes observed in one settled re-query: the new full RepoSnapshot (ready to render a prompt/status line) plus the typed RepoEvents that produced it. A RepoWatcher only yields a RepoChange when at least one event fired.
RepoSnapshot
A one-shot snapshot of the common repository state — branch, upstream tracking, ahead/behind, dirtiness, and operation state — gathered in one or two process spawns instead of a call per field. The data a prompt, status line, or TUI refresh needs. See Repo::snapshot.
RepoWatcher
A live watch over a repository, yielding RepoChanges as the repo’s state changes. Dropping it stops the filesystem watch and the background task.
WatcherStats
A cheap point-in-time copy of the watcher’s health counters — see RepoWatcher::stats. Lets a long-running consumer notice a watcher that is silently skipping re-queries (e.g. a permanently wedged repository) instead of inferring health from event silence.

Enums§

Error
An error from setting up or running a RepoWatcher.
OperationState
Whether the working copy is mid-operation, unified across the backends’ different models: git exposes an in-progress merge or rebase as on-disk state (MERGE_HEAD / a rebase-* dir), while jj has no multi-step operations — it records a conflict directly on the working-copy change.
RepoEvent
One typed change to a repository’s observable state, derived by diffing two consecutive RepoSnapshots (plus the branch set).
WatcherErrorKind
What the last skipped re-query failed on (see WatcherStats::last_error).

Constants§

DEFAULT_REQUERY_TIMEOUT
Default deadline on a single re-query (snapshot + branch list): a wedged command (e.g. a held index.lock with no client timeout configured) is killed and skipped instead of stalling the watch loop forever.

Type Aliases§

Result
Result specialised to the watcher Error.