# How Are Worktrees for a Single Repository Grouped in Pi Web

> Learn how Pi Web groups worktrees for a single repository. Discover the projectRoot resolution and session aggregation for a unified project node with a worktree switcher.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-07-30

---

**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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/SessionSidebar.tsx) consumes the project information returned by `resolveProject` and renders a worktree switcher populated by `listWorktrees(cwd)` (also in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/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

```typescript
// 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

```typescript
// 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`](https://github.com/agegr/pi-web/blob/main/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 in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) uses `git rev-parse --git-common-dir` to determine the shared repository root and detect linked worktrees.
- **Rendered in [`SessionSidebar.tsx`](https://github.com/agegr/pi-web/blob/main/SessionSidebar.tsx):** The sidebar displays a unified project with a dropdown switcher populated by `listWorktrees()`.
- **Fallback for deleted worktrees:** The `inferRemovedWorktree` logic 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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`.