How Pi‑Web Manages and Groups Git Worktrees by Project
Pi‑Web discovers the shared Git repository root for each session, caches project metadata for 60 seconds, and groups sessions under a common project node while exposing REST endpoints to create, list, and remove worktrees.
Pi‑Web treats the Git repository as the fundamental organizational unit for terminal sessions. When a session’s working directory resides inside a linked worktree, the application resolves the common repository root and current branch to cluster related sessions together. This architecture enables seamless git worktree management directly from the web interface without manual path configuration.
Resolving Repository Roots and Worktree Status
Pi‑Web identifies whether a session operates inside a Git worktree through the resolveProject function defined in lib/worktree.ts. This utility executes Git commands to locate the shared repository root and determine if the current path represents a linked worktree or the main checkout.
The resolveProject Function
Located at lines 76‑110 of lib/worktree.ts, resolveProject(cwd) runs git rev‑parse to obtain three critical pieces of information: the common Git directory, the actual top‑level checkout path, and the current branch name. The function sets boolean flags distinguishing between the main repository (isWorktree: false) and linked worktrees (isWorktree: true), while also marking the top‑level directory (isTopLevel) to control where the UI displays the worktree switcher.
Caching and Performance Optimization
To prevent repeated shell calls, Pi‑Web caches results on globalThis.__piProjectCache with a 60‑second TTL. Subsequent requests for the same working directory return the cached ProjectInfo object immediately, reducing latency when the session list refreshes.
Grouping Sessions by Project
The session reader layer enriches metadata with repository context, enabling the sidebar UI to cluster sessions under their originating project regardless of which worktree they occupy.
Enriching Session Metadata
The listAllSessions() function in lib/session-reader.ts (lines 22‑46) invokes resolveProject for every unique cwd and merges the results into each SessionInfo record. This adds two key fields: projectRoot, which contains the main repository path, and worktreeBranch, which stores the branch name when isWorktree is true. The frontend uses projectRoot as the grouping key, ensuring all worktrees belonging to the same repository appear beneath a single project node.
Handling Removed Worktrees
When a worktree directory disappears from disk, resolveProject falls back to inferRemovedWorktree rather than failing. This logic reassigns orphaned sessions to the main repository root, preventing them from becoming detached from their project group in the UI.
Worktree Management API
The file app/api/worktrees/route.ts exposes a REST interface for worktree operations, integrating with the file system whitelist to ensure new worktrees are immediately browsable.
Listing Existing Worktrees
A GET request to /api/worktrees?cwd=/path/to/dir returns the resolved projectRoot, boolean flags isGit and isTopLevel, and an array of existing worktrees via listWorktrees. The handler automatically calls allowFileRoot (from lib/allowed-roots.ts) for each discovered path, granting the file explorer permission to browse those directories without restarting the server.
GET /api/worktrees?cwd=/home/user/my-repo/feature-branch
{
"projectRoot": "/home/user/my-repo",
"isGit": true,
"isTopLevel": true,
"worktrees": [
{ "path": "/home/user/my-repo-worktrees/feature-branch", "branch": "feature-branch", "isMain": false },
{ "path": "/home/user/my-repo", "branch": "main", "isMain": true }
]
}
Creating New Worktrees
The POST endpoint creates worktrees under a sibling directory named <repoRoot>-worktrees/<sanitized-branch>, as implemented in the addWorktree function (lines 73‑88 of lib/worktree.ts). It validates the branch name, ensures the target directory does not exist, executes git worktree add, and registers the new path with allowFileRoot.
POST /api/worktrees
Content-Type: application/json
{
"cwd": "/home/user/my-repo",
"branch": "feature-xyz"
}
{ "path": "/home/user/my-repo-worktrees/feature-xyz", "branch": "feature-xyz" }
Removing Worktrees Safely
The DELETE endpoint executes git worktree remove on the specified path. If the worktree contains uncommitted changes, the route returns a 409 Conflict response containing dirty: true, allowing the frontend to prompt the user for a forced removal.
DELETE /api/worktrees
Content-Type: application/json
{
"cwd": "/home/user/my-repo",
"path": "/home/user/my-repo-worktrees/old-branch",
"force": true
}
{ "success": true }
File System Integration
Worktree visibility depends on allowFileRoot from lib/allowed-roots.ts, which maintains an in-memory whitelist that the /api/files endpoint consults. Every time a worktree is created or listed, Pi‑Web updates this whitelist, ensuring that worktree directories remain accessible to the file explorer across server restarts.
Summary
resolveProjectinlib/worktree.tsdiscovers repository roots and distinguishes main checkouts from linked worktrees usinggit rev‑parse.- Session metadata in
lib/session-reader.tsattachesprojectRootandworktreeBranchto group sessions by repository in the UI. - REST endpoints in
app/api/worktrees/route.tsprovide CRUD operations for worktrees, with automatic file-root registration viaallowFileRoot. - Graceful degradation through
inferRemovedWorktreeprevents sessions from becoming orphaned when worktrees are deleted externally. - Caching on
globalThis.__piProjectCachefor 60 seconds minimizes Git command overhead during rapid session list updates.
Frequently Asked Questions
How does pi‑web detect if a session is inside a Git worktree?
The resolveProject function in lib/worktree.ts executes git rev‑parse to compare the current working directory against the main repository path. If the resolved top‑level directory differs from the common Git directory’s parent, Pi‑Web sets isWorktree: true and captures the branch name in the ProjectInfo object.
Where does pi‑web store newly created worktrees?
When handling a POST request to /api/worktrees, Pi‑Web creates worktrees under a sibling directory named <repoRoot>-worktrees/<sanitized-branch>, as implemented in the addWorktree function (lines 73‑88 of lib/worktree.ts). This convention keeps worktrees organized outside the main repository folder while maintaining a predictable path structure.
What happens to sessions when a worktree directory is deleted externally?
Pi‑Web handles missing worktrees gracefully through the inferRemovedWorktree fallback within resolveProject. Instead of leaving sessions orphaned, the system reassigns them to the main repository root, ensuring they remain grouped under the correct project node in the sidebar.
How does pi‑web prevent data loss when removing worktrees?
The DELETE endpoint in app/api/worktrees/route.ts attempts a standard git worktree remove and returns a 409 Conflict response with dirty: true if uncommitted changes exist. The UI can then prompt the user to confirm forced removal, preventing accidental deletion of unsaved work.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →