# How Pi-Web Manages Git Worktree Operations and Project Grouping

> Discover how Pi-Web simplifies Git worktree operations and project grouping. Effortlessly manage related sessions under a single project root with its intuitive API.

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

---

**Pi-Web treats Git repositories as the fundamental organizing unit for sessions, automatically detecting worktrees to group related sessions under a single project root while exposing create, list, and delete operations through a dedicated API.**

Pi-Web, an open-source terminal session manager from the `agegr/pi-web` repository, leverages Git's worktree feature to organize development environments. When your session's working directory resides inside a worktree, the system discovers the shared repository root and current branch to enable seamless project grouping. This architecture ensures that all worktrees belonging to the same repository appear as a unified project in the UI, with new worktrees instantly accessible without server restarts.

## Resolving Projects and Worktrees

The core discovery logic lives in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts), specifically within the `resolveProject(cwd)` function spanning lines 76-110. This utility executes `git rev-parse` commands to obtain the common Git directory, the actual top-level checkout path, and the current branch name.

The function distinguishes between the main checkout and linked worktrees by setting an `isWorktree` flag. It also determines if the provided path represents the repository root through an `isTopLevel` boolean, ensuring the UI only displays the worktree switcher at the appropriate hierarchy level. To minimize performance overhead, results are cached on `globalThis.__piProjectCache` with a 60-second TTL, preventing repeated Git commands during rapid session operations.

## Session Enrichment and Project Grouping

Once project metadata is resolved, `listAllSessions()` in [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) (lines 22-46) calls `resolveProject` for each unique working directory. The returned `ProjectInfo` object enriches `SessionInfo` with two critical fields:

- **`projectRoot`** – The main repository root for Git directories, or the `cwd` itself for standard folders.
- **`worktreeBranch`** – The branch name when the session exists within a worktree (populated only when `project?.isWorktree && project.branch` evaluates true).

This enrichment allows the sidebar UI to cluster sessions by their common `projectRoot`, creating a single project node that contains all associated worktrees.

## Worktree Management API

The route file [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) exposes three HTTP endpoints for worktree operations. Each endpoint integrates with the file system through `allowFileRoot` (imported from [`lib/allowed-roots.ts`](https://github.com/agegr/pi-web/blob/main/lib/allowed-roots.ts)), which updates an in-memory whitelist that the `/api/files` endpoint references for directory access.

### List Existing Worktrees

A **GET** request to `/api/worktrees?cwd=...` returns the resolved `projectRoot`, `isGit` status, `isTopLevel` flag, and an array of existing worktrees via the `listWorktrees` function.

```bash
curl "http://localhost:3000/api/worktrees?cwd=/home/user/my-repo/feature-branch"

```

*Response:*

```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 }
  ]
}

```

### Create New Worktrees

A **POST** request to `/api/worktrees` creates a new worktree under `<repoRoot>-worktrees/<sanitized-branch>`. The `addWorktree` implementation (lines 73-88 in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts)) validates the branch name and ensures the target directory does not exist before registration.

```bash
curl -X POST "http://localhost:3000/api/worktrees" \
  -H "Content-Type: application/json" \
  -d '{"cwd": "/home/user/my-repo", "branch": "feature-xyz"}'

```

*Response:*

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

```

### Remove Worktrees

A **DELETE** request to `/api/worktrees` executes `git worktree remove`. If the worktree contains uncommitted changes, the endpoint returns a 409 status with `dirty: true`, enabling the frontend to prompt users for forced removal.

```bash
curl -X DELETE "http://localhost:3000/api/worktrees" \
  -H "Content-Type: application/json" \
  -d '{"cwd": "/home/user/my-repo", "path": "/home/user/my-repo-worktrees/old-branch", "force": true}'

```

*Response:*

```json
{ "success": true }

```

## File System Integration

Worktree directories must be explicitly whitelisted for the file explorer to browse them. The `allowFileRoot` utility updates the in-memory whitelist immediately upon worktree creation, ensuring new worktrees are browseable without requiring a server restart. This integration applies to both automatically discovered worktrees and manually added ones through the API.

## Handling Removed Worktrees

When a worktree directory disappears from disk, `resolveProject` falls back to the inferred project root via `inferRemovedWorktree`. This prevents existing sessions from becoming orphaned; instead, they are re-grouped under the main repository root, maintaining project cohesion even when worktrees are deleted outside of Pi-Web.

## Summary

- **Pi-Web** uses `resolveProject()` in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) to detect Git repositories and worktrees via `git rev-parse`, caching results for 60 seconds to optimize performance.
- **Session grouping** occurs in [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) through `listAllSessions()`, which attaches `projectRoot` and `worktreeBranch` metadata to cluster related sessions under unified project nodes.
- **API endpoints** in [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) provide RESTful interfaces for listing, creating, and deleting worktrees, with automatic file system registration via `allowFileRoot`.
- **Graceful degradation** ensures sessions point to the main repository root when worktrees are removed externally, preventing orphaned session records.

## Frequently Asked Questions

### How does Pi-Web detect if a session is running inside a Git worktree?

Pi-Web executes `git rev-parse` commands through the `resolveProject(cwd)` function in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) to identify the common Git directory and top-level path. It sets an `isWorktree` flag when the session resides in a linked worktree rather than the main checkout, distinguishing between the primary repository and additional worktrees.

### Can I create a worktree through the Pi-Web interface?

Yes, the `POST /api/worktrees` endpoint creates new worktrees under a `<repoRoot>-worktrees/<sanitized-branch>` directory structure. The system validates branch names, checks for existing directories, and automatically registers the new path with the file explorer whitelist through `allowFileRoot` from [`lib/allowed-roots.ts`](https://github.com/agegr/pi-web/blob/main/lib/allowed-roots.ts).

### What happens to my sessions if I delete a worktree outside of Pi-Web?

Pi-Web handles removed worktrees gracefully through the `inferRemovedWorktree` fallback mechanism. When `resolveProject` cannot locate the original worktree directory, it redirects the session's project root to the main repository checkout, ensuring sessions remain grouped under the correct project rather than becoming orphaned.

### Why does Pi-Web cache project resolution results?

The system caches `ProjectInfo` objects on `globalThis.__piProjectCache` for 60 seconds to avoid the performance overhead of repeatedly executing Git commands when listing or updating sessions. This cache ensures rapid UI updates while maintaining accurate worktree and branch information.