The worktree service now records each checkout it creates as a `plugin:worktree/created` record, folds them in its own `worktree` projection with fork-aware ownership, and reclaims a clean, unreferenced checkout when the Session that created it is archived. The Forge panel reads the service's record and forwards its cleanup state; it no longer writes a `created` record, owns a retention projection, or gates every Session's directory access. Admission registers the attempt inside the Workspace write queue and returns; inspection runs afterwards, reading cold Sessions through the projection cache when mounted and from their log otherwise, with subagent children of an archived ancestor counted as archived. Only a path being removed holds `working-directory/access`, whose request now carries the access's `signal`, and listeners withdraw before the drain. Ignored files no longer retain a checkout, matching Git's non-force removal; processes and terminals are not consulted. The Forge bundle mounts one `worktree` service: the `forge-worktree` row and the `optional-forge-panel` group that isolated a second service are gone, and `forge-panel` is a top-level row. `canRemove`, `remove`, the `forgeWorktreeRetention` projection, and `ForgeWorktreeCleanup` are removed; `WorktreeService.cleanupOf`, `WorktreeCleanup`, and the `worktree/cleanup` Host event replace them. The upgrade guide moves to the current version directory and covers the id removals.
9.6 KiB
Git 工作树
English | 中文
实验性的 dsh-experimental-worktree 服务提供 ctx.worktrees。其消费方 create_worktree 创建新的 Git 分支与检出目录,再通过工作目录服务选中它。变更请求与工作树组合包通过 GUI 插件管理器一起加载这两个包,随安装提供但默认关闭。其他 profile 可以选择独立工作树组合包,或显式挂载这两个包。
创建与保留
调用方 Session 的当前目录决定源仓库。创建操作在分配检出目录前,将请求的本地提交、分支或标签固定为具体提交;省略时选择 HEAD。每个名称必须对应新分支与未使用的检出路径。操作既不获取缺失对象,也不复制源检出目录中的暂存、未暂存、未跟踪或被忽略文件。
默认目标为 <current checkout root>/.agents/worktrees/<name>。repositoryRoot 是调用方当前检出目录的顶层目录;因此,链接检出目录会在自身内部保存工作树父目录。仅当服务的分配操作创建了配置指定的父目录时,服务才会写入内容为 * 加换行符的 .gitignore。已有文件与忽略规则保持不变。调用方 Session 的现有沙箱策略必须允许写入目标目录与共享 Git 管理目录。Git 初始化成功后才改变工作目录。
通过 working_directory({ cd: path }) 离开时,会保留检出目录与分支。取消或失败也可能留下部分产物;错误会指出它们的位置。服务销毁时会中止待完成操作并等待它们结束。包 README拥有配置与 Git 要求。
请求与结果
请求只选择新名称与本地版本。结果标识创建的检出目录与确切提交;它不会改变 Session 的原始项目或写权限。可选的回调会在创建的每个阶段开始之前收到该阶段。恢复会在记录的目录重新检出已删除的检出目录:从它的分支检出;分支也已删除时,从它的基准提交检出。
/** A new checkout from a locally available Git commit, branch, or tag. */
interface CreateWorktreeRequest {
/** New branch name, also used as the relative checkout directory; omission generates a name. */
name?: string
/** Local revision to resolve before creation; omission selects HEAD. */
from?: string
}
/** Committed Git baseline and the calling Session's resulting working directory. */
interface CreatedWorktree {
/** Canonical absolute checkout directory in the filesystem provider's execution world. */
path: string
/** Newly created branch name. */
branch: string
/** Resolved commit object name; repository-local replacement refs can change its checked-out content. */
baseCommit: string
/** Canonical root of the source checkout. */
repositoryRoot: string
}
/**
* Stage of one creation or restoration, reported before it begins: `prepare`
* validates the request, allocates the checkout directory, and creates the
* branch, which a restoration creates again only when it is gone; `checkout`
* writes the files and, for a creation, enters the checkout.
*/
type CreateWorktreePhase = 'prepare' | 'checkout'
/** A removed checkout to check out again at its recorded directory. */
interface RestoreWorktreeRequest {
/** Canonical checkout directory the worktree had. */
path: string
/** Branch the worktree had checked out. */
branch: string
/** Commit the branch started from; a branch that no longer exists is created again here. */
baseCommit: string
/** Canonical root of the source checkout. */
repositoryRoot: string
}
/** Outcome of one restoration. */
interface RestoredWorktree {
/** Checkout directory, present again. */
path: string
/** Whether the branch no longer existed and was created again at the base commit, without the commits it had gained. */
branchRecreated: boolean
}
/** One checkout in a Session's log and whether that Session, rather than a fork ancestor, created it. */
interface WorktreeCreation {
readonly worktree: CreatedWorktree
/** False for a creation inside the fork-inherited prefix. */
readonly owned: boolean
}
/** Process-local state of one archive-triggered cleanup of a checkout. */
interface WorktreeCleanup {
/** Monotonic attempt number within this Host lifetime; a Remote waits on a specific one. */
readonly revision: number
/**
* `scheduled` until the background inspection ends; `retained` when a use,
* policy, or Git state keeps the checkout; `removed` after `git worktree
* remove`; `failed` when removal threw and may have left a partial directory.
*/
readonly status: 'scheduled' | 'retained' | 'removed' | 'failed'
}
Cordis API
Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — the language sides differ only in locale-specific paired document paths. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.worktrees — WorktreeService
Creates retained Git worktrees under the mounted filesystem and sandbox providers, records each creation in the Session log, and reclaims a clean, unreferenced checkout when the Session that created it is archived.
/**
* Create a fresh branch and checkout at a pinned local revision, then enter it.
* Existing branches or checkout paths fail. Uncommitted files stay in the source checkout.
* Checkout disables configured clean, smudge, and process filters without changing Git config.
* Repository-local replacement refs still apply; baseCommit reports the resolved object name.
* The new checkout becomes current only after Git setup succeeds. Failures may retain newly
* allocated Git/filesystem artifacts; no branch or checkout is removed automatically.
* @param agent - caller whose current directory selects the source repository and file policy.
* @param request - optional new name and local revision; defaults are generated name and HEAD.
* @param signal - cancellation of lookup, creation, and working-directory publication.
* @param onPhase - receives each stage before it begins; it must not throw.
* @returns canonical checkout path, branch name, pinned commit, and source repository root.
*/
async create( agent: Agent, request: CreateWorktreeRequest = {}, signal?: AbortSignal, onPhase?: (phase: CreateWorktreePhase) => void, ): Promise<CreatedWorktree>
/**
* Check out a removed worktree again at its recorded directory: its branch
* when the branch still exists, otherwise a new branch of the same name at
* `baseCommit`. Only the missing target's unlocked registration is removed, and an
* existing directory fails. Changes that were never committed are not
* recovered. Checkout disables configured clean, smudge, and process filters,
* as creation does. The calling Session's working directory does not change.
* Failures may retain newly allocated Git/filesystem artifacts.
* @param agent - caller whose sandbox policy confines the commands.
* @param request - recorded directory, branch, base commit, and source repository root.
* @param signal - cancellation of the restoration.
* @param onPhase - receives each stage before it begins; it must not throw.
* @returns the directory and whether the branch was created again.
*/
async restore( agent: Agent, request: RestoreWorktreeRequest, signal?: AbortSignal, onPhase?: (phase: CreateWorktreePhase) => void, ): Promise<RestoredWorktree>
/**
* Read the archive cleanup of a Session's created checkout; waiting never
* starts one. Absent without the Workspace and query providers.
* @param session - archived Session whose cleanup is read.
* @param revision - null reads at once; a matching revision waits for that attempt to settle.
* @returns the attempt state, or null when none exists, the revision was superseded, or the service is stopping.
*/
async cleanupOf(session: Session, revision: number | null): Promise<WorktreeCleanup | null>
Source: packages/experimental/worktree/src/index.ts
worktree/* events
worktree/cleanup — emit
A cleanup attempt changed state. Fires once at admission with scheduled and once at settlement with the final status.
/**
* A cleanup attempt changed state. Fires once at admission with
* `scheduled` and once at settlement with the final status.
* @param info - the archived Session, the checkout, and the attempt state.
* @mode emit
*/
'worktree/cleanup'(info: { sessionId: SessionId; path: string; cleanup: WorktreeCleanup }): void
Types: SessionId