WorktreeMgr
Defined in: src/worktree.ts:48
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new WorktreeMgr():
WorktreeMgr
Returns
Section titled “Returns”WorktreeMgr
Methods
Section titled “Methods”behindBase()
Section titled “behindBase()”behindBase(
worktreePath,baseBranch):Promise<boolean|null>
Defined in: src/worktree.ts:398
Whether the branch checked out at worktreePath is BEHIND baseBranch — i.e.
base has commits not yet in HEAD, so a strict merge train must rebase first.
Best-effort fetches origin/<base> and prefers it; falls back to the local
base ref when offline. Returns:
false → base is an ancestor of HEAD (up-to-date, safe to merge)
true → base has commits HEAD lacks (stale, rebase needed)
null → unknowable (bad worktree / git error) → caller treats as “do not merge”
Parameters
Section titled “Parameters”worktreePath
Section titled “worktreePath”string
baseBranch
Section titled “baseBranch”string
Returns
Section titled “Returns”Promise<boolean | null>
branchExists()
Section titled “branchExists()”branchExists(
repoPath,branch):boolean
Defined in: src/worktree.ts:180
True iff a local branch branch exists in repoPath. Used to pre-empt a
rename collision before touching any remote.
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
branch
Section titled “branch”string
Returns
Section titled “Returns”boolean
commitsAhead()
Section titled “commitsAhead()”commitsAhead(
repoPath,baseBranch,branch):number
Defined in: src/worktree.ts:224
Commits on branch not yet on baseBranch (git rev-list --count base..branch).
0 means the branch tip still equals base — the “nothing committed yet” window in
which an auto-rename can safely move the branch. Returns a large number on any
git error so callers treat an unknowable state as “not safe to rename”.
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
baseBranch
Section titled “baseBranch”string
branch
Section titled “branch”string
Returns
Section titled “Returns”number
containsCommit()
Section titled “containsCommit()”containsCommit(
worktreePath,sha):boolean|null
Defined in: src/worktree.ts:456
Whether sha is reachable from the branch tip checked out at worktreePath
— i.e. the commit genuinely belongs to this session’s branch. Used to reject
a PR that gh pr list --head <name> matched purely on branch NAME: a prior,
already-merged PR that reused this branch name reports a head commit that this
freshly-cut branch does not contain. Returns:
true → sha is HEAD or an ancestor of it (this branch’s own commit)
false → sha is absent locally OR not an ancestor (a foreign / stale PR)
null → unknowable (worktree unusable / git couldn’t run) → caller keeps the PR
Two known limitations of the reachability test:
- Assumes squash/rebase merges (this repo’s policy — merge commits are disabled). Under those, a merged feature commit is NOT an ancestor of main, so a reused-name branch cut from main fails to reach the old PR head → the collision is caught. On a repo using plain merge commits, the old PR’s head stays reachable from main, so a fresh same-name branch would still “own” it and the collision would slip through.
- Transient false-negative after a self-rebase. If a session’s own PR
merges and the still-active branch is then rebased/reset, the PR head is no
longer an ancestor of HEAD, so a genuine MERGED is dropped to
none. Rare (a session usually archives once its PR lands) and self-heals once the PR head returns to the history; acceptable versus the false-MERGED it prevents.
Parameters
Section titled “Parameters”worktreePath
Section titled “worktreePath”string
string
Returns
Section titled “Returns”boolean | null
create()
Section titled “create()”create(
repoPath,baseBranch,name):WorktreeResult
Defined in: src/worktree.ts:58
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
baseBranch
Section titled “baseBranch”string
string
Returns
Section titled “Returns”createDetached()
Section titled “createDetached()”createDetached(
repoPath,branch,sha,slug?,pullRef?):Promise<WorktreeResult>
Defined in: src/worktree.ts:564
Detached worktree at a specific commit, fetching it from origin first so a PR head pushed by the agent is present even when the local repo is behind. Used by the critic to review the exact PR head.
slug disambiguates the worktree path when callers do NOT have a unique sha to key
on. The PR critic detaches at the PR head sha (unique per PR), so it omits slug and
the path stays …-review-<sha> — reused-on-restart to reclaim an interrupted run (the
existsSync-reclaim below is load-bearing for that slugless path). The plan reviewer,
however, detaches every session at the SAME base-branch sha, so without a slug all plan
reviews in a repo would collide on one path: a second begin() would blow away the first’s
live worktree, and both inflight records would then read the same .shepherd-plan-review.json
— delivering one run’s plan findings to another. It passes a per-RUN unique id (the reviewer’s
pinned session id, a fresh randomUUID per spawn) as slug, so the path disambiguates across
RUNS — even two reviews of the SAME session at the SAME sha get distinct paths (#631), not
just two different sessions.
pullRef is the OPTIONAL fork escape hatch for the standalone PR critic. A fork PR’s head
sha is NOT on the base repo’s origin (it lives on the contributor’s fork), so the branch
fetch below can’t land it and worktree add --detach <sha> would fail with a missing object.
When provided (e.g. refs/pull/<n>/head, which GitHub exposes on the base repo’s origin) it
is ALSO fetched — same ---guarded grammar as the branch fetch — so the head sha reaches the
local store before the checkout. Same-repo callers (ReviewService, the plan reviewer) omit it
and behavior is unchanged.
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
branch
Section titled “branch”string
string
string
pullRef?
Section titled “pullRef?”string
Returns
Section titled “Returns”Promise<WorktreeResult>
currentBranch()
Section titled “currentBranch()”currentBranch(
worktreePath):string|null
Defined in: src/worktree.ts:207
The branch currently checked out in worktreePath, or null when HEAD is
detached or the path isn’t a readable git worktree. This is the source of
truth for which branch a session’s PR will come from — an agent that runs
git checkout -b / git branch -m moves it out from under the stored value.
Parameters
Section titled “Parameters”worktreePath
Section titled “worktreePath”string
Returns
Section titled “Returns”string | null
ensureBaseRef()
Section titled “ensureBaseRef()”ensureBaseRef(
repoPath,baseBranch):Promise<ResolvedBase>
Defined in: src/worktree.ts:264
Freshen baseBranch from upstream at task-launch time, then return a ResolvedBase
the caller uses to base the new worktree on.
Contract:
- Validates
baseBranch— fails closed (no git calls) on an invalid refname. - Calls
upstreamStatus()to fetch and evaluate the branch against origin. - Computes
baseRef:- Non-diverged branch with an upstream → upstream sha (the new worktree starts fresh even when the local fast-forward below is skipped).
- Diverged or no upstream → branch name (fall back to whatever is local).
- Best-effort local fast-forward (never throws, never blocks):
- No upstream →
localFf = "none". - Diverged → warn,
localFf = "skipped-diverged", local untouched. - Already up-to-date →
localFf = "not-needed". - Behind / origin-only → determine if
baseBranchis HEAD inrepoPath:- Checked out here (clean tree) →
git merge --ff-only <sha>→ “applied”. - Checked out here (dirty tree) → skip,
localFf = "skipped-dirty"(warn). - Not checked out here →
git branch -f <branch> <sha>(creates or ff’s the local ref without touching any working tree). Fails when the branch is HEAD in ANOTHER worktree →localFf = "skipped-checked-out-elsewhere"(warn).baseRefis unaffected — the new task still starts at the upstream sha.
- Checked out here (clean tree) →
- No upstream →
All git calls are async (no sync I/O on the server loop) and individually try/caught.
ensureBaseRef never throws.
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
baseBranch
Section titled “baseBranch”string
Returns
Section titled “Returns”Promise<ResolvedBase>
gitCommonDir()
Section titled “gitCommonDir()”gitCommonDir(
worktreePath):string
Defined in: src/worktree.ts:488
The ABSOLUTE shared git object store for worktreePath (git rev-parse --git-common-dir, resolved against the worktree). A worktree’s own .git is a
file pointing here; the bwrap membrane must bind this store rw so the agent’s
git ops reach the real refs/objects. Falls back to <worktreePath>/.git on any
git error so a non-worktree (isolated:false) still yields a usable path.
Parameters
Section titled “Parameters”worktreePath
Section titled “worktreePath”string
Returns
Section titled “Returns”string
remove()
Section titled “remove()”remove(
worktreePath,opts?):void
Defined in: src/worktree.ts:501
Parameters
Section titled “Parameters”worktreePath
Section titled “worktreePath”string
baseBranch?
Section titled “baseBranch?”string
branch?
Section titled “branch?”string | null
Returns
Section titled “Returns”void
renameBranch()
Section titled “renameBranch()”renameBranch(
repoPath,oldBranch,newBranch):void
Defined in: src/worktree.ts:196
Rename a local branch. git branch -m works even while the branch is checked
out in a worktree, so this is safe to run on a live session’s branch. Throws
when the target name is already taken (the caller surfaces that as a conflict).
Parameters
Section titled “Parameters”repoPath
Section titled “repoPath”string
oldBranch
Section titled “oldBranch”string
newBranch
Section titled “newBranch”string
Returns
Section titled “Returns”void