# How Pi Web Integrates Git Worktrees and Project Grouping

> Discover how Pi Web integrates git worktrees and project grouping with efficient REST endpoints for creation, deletion, and file access. Learn about its caching mechanism.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-07-25

---

**Pi Web integrates git worktrees by resolving the shared repository root via `git rev-parse`, grouping sessions under a common `projectRoot`, and exposing REST endpoints that handle worktree creation, deletion, and file access while maintaining a 60-second cache to avoid repeated Git calls.**

Pi Web, an open-source project from the `agegr/pi-web` repository, treats the Git repository as the fundamental organizational unit for terminal sessions. When your working directory resides inside a linked worktree, the system automatically discovers the main repository root and current branch to maintain proper session hierarchy. This integration ensures that all worktrees belonging to the same project appear grouped together in the UI while remaining independently manageable through a dedicated API.

## Resolving Repository Context with resolveProject

At the core of the integration is the `resolveProject(cwd)` function defined in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) (lines 76‑110). This utility executes a targeted Git command (`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 distinguishes between the main repository checkout and linked worktrees by setting an `isWorktree` flag. It also marks the top-level directory with `isTopLevel` to ensure the UI only renders the worktree switcher at the repository root. To prevent performance degradation from repeated shell calls, results are cached on `globalThis.__piProjectCache` with a 60-second TTL.

## Grouping Sessions by Project Root

Session aggregation occurs in `listAllSessions()` within [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) (lines 22‑46). For each unique working directory (`cwd`), the function invokes `resolveProject` and merges the returned `ProjectInfo` into the corresponding `SessionInfo` objects.

This enrichment adds two key fields:
- **`projectRoot`** – The main repository root (or the `cwd` itself for non-Git directories), used as the grouping key in the sidebar UI.
- **`worktreeBranch`** – The branch name when the session lives inside a worktree (populated only when `project?.isWorktree && project.branch`).

Consequently, all terminal sessions across different worktrees of the same repository cluster under a single project node in the interface.

## REST API for Worktree Management

The route file [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) exposes three endpoints that surface worktree operations to the frontend:

### Listing Worktrees

The **GET** `/api/worktrees?cwd=…` endpoint returns the resolved `projectRoot`, a boolean `isGit`, the `isTopLevel` status, and a list of existing worktrees via `listWorktrees`. The handler automatically calls `allowFileRoot` for each discovered worktree path, ensuring the file explorer can browse them immediately without manual configuration.

```http
GET /api/worktrees?cwd=/home/user/my-repo/feature-branch

```

```json
{
  "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 Worktrees

The **POST** `/api/worktrees` endpoint creates new worktrees under the convention `<repoRoot>-worktrees/<sanitized-branch>`. The `addWorktree` function in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) (lines 73‑88) validates the branch name, ensures the target directory does not already exist, executes `git worktree add`, and registers the new path with `allowFileRoot`.

```http
POST /api/worktrees
Content-Type: application/json

{
  "cwd": "/home/user/my-repo",
  "branch": "feature-xyz"
}

```

```json
{ "path": "/home/user/my-repo-worktrees/feature-xyz", "branch": "feature-xyz" }

```

### Removing Worktrees

The **DELETE** `/api/worktrees` endpoint invokes `git worktree remove`. If the worktree contains uncommitted changes, the route returns a `409` status with `dirty: true`, allowing the UI to prompt the user for a forced removal.

```http
DELETE /api/worktrees
Content-Type: application/json

{
  "cwd": "/home/user/my-repo",
  "path": "/home/user/my-repo-worktrees/old-branch",
  "force": true
}

```

```json
{ "success": true }

```

## File Access and Edge Case Handling

Worktree-aware file access is managed through `allowFileRoot`, imported from [`lib/allowed-roots.ts`](https://github.com/agegr/pi-web/blob/main/lib/allowed-roots.ts). This function updates an in-memory whitelist that the `/api/files` endpoint consults, ensuring newly created worktrees are instantly browseable even after a server restart.

When a worktree directory disappears from the filesystem, `resolveProject` falls back to `inferRemovedWorktree`, re-grouping existing sessions that pointed at the now-missing worktree under the main repository root rather than leaving them orphaned.

## Summary

- **`resolveProject()`** in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) detects worktrees via `git rev-parse` and caches results for 60 seconds to optimize performance.
- **`listAllSessions()`** enriches session metadata with `projectRoot` and `worktreeBranch`, enabling the UI to group terminals by their underlying repository.
- **REST endpoints** in [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) provide full CRUD operations, creating worktrees under `<repoRoot>-worktrees/` and handling dirty-state conflicts with `409` responses.
- **`allowFileRoot`** automatically whitelists worktree paths for the file explorer, while **`inferRemovedWorktree`** gracefully handles deleted worktrees by falling back to the main repository root.

## Frequently Asked Questions

### How does Pi Web detect the main repository root from a worktree?

Pi Web executes `git rev-parse` commands within `resolveProject()` to identify the common Git directory and top-level checkout path. When inside a linked worktree, the function resolves to the shared repository root and sets `isWorktree: true`, distinguishing it from the main checkout.

### What happens to sessions when a worktree directory is deleted?

If a worktree directory is removed outside of Pi Web, the `resolveProject` function detects the missing path and invokes `inferRemovedWorktree` to fall back to the inferred project root. Existing sessions are automatically re-grouped under the main repository node instead of becoming orphaned entries in the UI.

### Where does Pi Web create new worktrees?

New worktrees are created under the `<repoRoot>-worktrees/<sanitized-branch>` directory structure as implemented in the `addWorktree` function (lines 73‑88 of [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts)). This convention keeps worktrees organized adjacent to the main repository while preventing name collisions.

### How does Pi Web handle file access for new worktrees?

Immediately upon creation, the `allowFileRoot` utility (from [`lib/allowed-roots.ts`](https://github.com/agegr/pi-web/blob/main/lib/allowed-roots.ts)) updates the server's in-memory whitelist of accessible directories. This ensures that the `/api/files` endpoint can serve content from the new worktree path without requiring a server restart or manual configuration.