How Are Worktrees for a Single Repository Grouped in Pi Web
Pi Web groups worktrees for a single repository by resolving the shared Git common directory to determine a canonical projectRoot, then aggregates all sessions targeting that root into a unified project node with a worktree switcher.
Pi Web (from the agegr/pi-web repository) manages development sessions across multiple Git worktrees by treating them as a single project rather than separate entities. The application identifies the common repository root using Git’s administrative directory and uses this path to group linked worktrees in the session sidebar.
Project Resolution Architecture
The grouping mechanism begins with project resolution logic that determines whether a working directory belongs to a main repository or a linked worktree.
Resolving the Shared Root
The resolveProject(cwd) function in lib/worktree.ts (lines 12–15 and 101–106) executes git rev-parse --git-common-dir to locate the Git administrative directory shared by all worktrees. It then derives the projectRoot by taking the parent directory of this common path using dirname(commonDir). This root path serves as the unique identifier for grouping all worktrees belonging to the same repository.
Detecting Linked Worktrees
When the resolved gitDir differs from the commonDir, the function sets isWorktree: true and identifies whether the current working directory represents the top-level of the worktree via the isTopLevel flag. These properties allow the UI to distinguish between the main repository checkout and secondary linked worktrees while maintaining their shared grouping context.
Session Grouping Logic
All sessions whose working directory resolves to the same projectRoot are displayed together in the sidebar under a single project node. The UI component SessionSidebar.tsx consumes the project information returned by resolveProject and renders a worktree switcher populated by listWorktrees(cwd) (also in lib/worktree.ts). This function enumerates every worktree associated with the repository, returning an array of objects containing the worktree path, current branch, and a boolean indicating whether it is the main worktree.
Handling Removed Worktrees
If a worktree directory has been deleted from the filesystem but references remain in Git’s metadata, resolveProject invokes inferRemovedWorktree to fall back to the main repository root. This prevents orphaned sessions from losing their grouping context and ensures they remain attached to the primary project node rather than creating broken entries in the sidebar.
Implementation in Code
The following examples demonstrate how Pi Web resolves project information and lists worktrees programmatically.
Resolving Project Information
// lib/worktree.ts - Resolving the project root
import { resolveProject } from '@/lib/worktree';
const info = await resolveProject('/home/user/my-repo-feature');
/*
Returns:
{
projectRoot: '/home/user/my-repo', // Shared root for all worktrees
branch: 'feature', // Current branch (null if detached)
isWorktree: true, // True for linked worktrees
isTopLevel: true // True if cwd is the worktree root
}
*/
Listing All Worktrees
// lib/worktree.ts - Enumerating worktrees
import { listWorktrees } from '@/lib/worktree';
const worktrees = await listWorktrees('/home/user/my-repo');
// Returns:
// [
// { path: '/home/user/my-repo', branch: 'main', isMain: true },
// { path: '/home/user/my-repo-worktrees/feature', branch: 'feature', isMain: false }
// ]
The API endpoint in app/api/worktrees/route.ts invokes these functions to resolve the project and mark each worktree path as an allowed file root before returning the data to the frontend for rendering.
Summary
- Grouped by
projectRoot: Worktrees are unified under a single project node identified by the parent of Git’s common directory. - Resolved via
resolveProject(): This function inlib/worktree.tsusesgit rev-parse --git-common-dirto determine the shared repository root and detect linked worktrees. - Rendered in
SessionSidebar.tsx: The sidebar displays a unified project with a dropdown switcher populated bylistWorktrees(). - Fallback for deleted worktrees: The
inferRemovedWorktreelogic ensures sessions belonging to deleted worktrees revert to the main repository grouping.
Frequently Asked Questions
How does Pi Web determine which worktrees belong to the same repository?
It executes git rev-parse --git-common-dir via the resolveProject() function in lib/worktree.ts to find the shared Git administrative directory. All working directories whose common directory resolves to the same parent path are grouped under the same projectRoot identifier.
What happens to sessions when a worktree directory is deleted?
If the working directory no longer exists on disk, resolveProject() detects this condition and invokes inferRemovedWorktree to fall back to the main repository root. This re-groups the sessions under the primary project node rather than leaving them orphaned.
Which file handles the UI rendering for grouped worktrees?
The grouping visualization is implemented in components/SessionSidebar.tsx, which receives the projectRoot and worktree list from the API and renders a single project node containing a switcher dropdown for navigating between worktrees.
Can the API return worktree information for any repository path?
Yes, the endpoint at app/api/worktrees/route.ts accepts a path parameter and calls resolveProject() and listWorktrees() to return the complete grouping information, including flags for isWorktree and isMain, along with the resolved projectRoot.
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 →