pub struct WorktreeManager<R: GitRunner> { /* private fields */ }Expand description
Manages the full lifecycle of per-subagent git worktrees.
WorktreeManager is parameterised over a GitRunner so that unit tests
can inject a FakeGitRunner (defined in the test module) without touching
the file system. Production code uses
DefaultWorktreeManager.
§Concurrency
The internal handle list is guarded by a std::sync::Mutex. All
methods acquire this lock for the minimum necessary duration —
they never hold the lock across an .await on an external resource.
§TODO
TODO(critic D1): concurrent per-agent cwd isolation requires child-process
bgIsolation or full ToolExecutor cwd-threading; in-process MVP is
concurrency-1 only.
Implementations§
Source§impl<R: GitRunner> WorktreeManager<R>
impl<R: GitRunner> WorktreeManager<R>
Sourcepub fn new(
repo_root: PathBuf,
config: WorktreeConfig,
runner: R,
) -> Result<Self, WorktreeError>
pub fn new( repo_root: PathBuf, config: WorktreeConfig, runner: R, ) -> Result<Self, WorktreeError>
Creates a new manager, validating the repository root and canonicalising the worktree root directory.
The worktree root directory is created if it does not yet exist.
§Errors
WorktreeError::RootOutsideRepoif the configured root escapes the repository.WorktreeError::Iofor filesystem errors.
§Examples
use std::path::PathBuf;
use zeph_config::WorktreeConfig;
use zeph_worktree::{DefaultWorktreeManager, git_runner::DefaultGitRunner};
let mgr = DefaultWorktreeManager::new(
PathBuf::from("/path/to/repo"),
WorktreeConfig::default(),
DefaultGitRunner::new(),
)?;Sourcepub fn repo_root(&self) -> &Path
pub fn repo_root(&self) -> &Path
Returns the repository root this manager was constructed with.
Sourcepub async fn create(
&self,
subagent_id: &str,
) -> Result<WorktreeHandle, WorktreeError>
pub async fn create( &self, subagent_id: &str, ) -> Result<WorktreeHandle, WorktreeError>
Creates a new worktree for subagent_id according to the configured
base_ref strategy.
The branch name is "{branch_prefix}{subagent_id}". The path on disk is
"{root}/{subagent_id}".
§TODO
TODO(critic D2): head worktree does not include parent uncommitted changes by design; revisit if users need stash-based propagation.
§Errors
WorktreeError::InvalidBranchNamewhensubagent_idfails validation.WorktreeError::PathExistswhen the worktree path already exists.WorktreeError::BaseRefUnresolvedwhenbase_ref = Freshand the default branch cannot be resolved.WorktreeError::GitCommandfor anygitfailure.
§Examples
let handle = mgr.create("agent-42").await?;
println!("Worktree at {:?} on branch {}", handle.path, handle.branch_name);Sourcepub async fn remove(
&self,
handle: &WorktreeHandle,
prune_branch: bool,
) -> Result<(), WorktreeError>
pub async fn remove( &self, handle: &WorktreeHandle, prune_branch: bool, ) -> Result<(), WorktreeError>
Removes the worktree identified by handle.
If prune_branch is true, also deletes the git branch after removing
the worktree directory.
§Errors
Returns WorktreeError::GitCommand if either git command fails.
§Examples
mgr.remove(&handle, false).await?;Sourcepub fn list(&self) -> Vec<WorktreeHandle>
pub fn list(&self) -> Vec<WorktreeHandle>
Returns a snapshot of the in-memory handle list for the current session.
This list only contains worktrees created in the current process. To
discover worktrees that exist in the git registry but not in memory (e.g.
after a crash), use reconcile.
§Examples
let handles = mgr.list();
println!("{} active worktrees", handles.len());Sourcepub async fn reconcile(&self) -> Result<Vec<WorktreeHandle>, WorktreeError>
pub async fn reconcile(&self) -> Result<Vec<WorktreeHandle>, WorktreeError>
Reads the git worktree registry and returns handles for worktrees that exist on disk but are not in the current session’s in-memory list.
This is used at startup (and via worktree clean) to recover from a
previous crash that left stale worktrees behind.
§Errors
Returns WorktreeError::GitCommand if git worktree list fails.
§Examples
let stale = mgr.reconcile().await?;
for h in &stale {
println!("stale worktree: {:?}", h.path);
}