src — git
src — git
This document provides a technical overview and usage guide for the src/git/worktree-sessions.ts module, which manages Git worktrees and links them to application-level sessions.
Git Worktree Session Manager
The src/git/worktree-sessions.ts module provides a robust mechanism for managing Git worktrees, enabling developers to work on multiple branches simultaneously within a single repository context. It abstracts the underlying Git commands and maintains a registry of active worktree sessions, facilitating parallel development workflows.
Purpose
The primary goals of this module are:
- Parallel Branch Development: Allow users to create and manage isolated Git worktrees for different branches, enabling concurrent work without constantly switching branches in the main repository.
- Session Management: Link each Git worktree to an application-specific "session" (represented by the
WorktreeSessioninterface), providing metadata and a unique identifier for tracking. - Centralized Coordination: Implement a singleton pattern to ensure a single, globally accessible manager coordinates all worktree sessions within the application.
Core Concepts
WorktreeSession
The WorktreeSession interface defines the structure for tracking an active worktree. Each session represents a specific branch being worked on in an isolated worktree.
export interface WorktreeSession {
branch: string; class="hl-cmt">// The Git branch associated with this worktree.
worktreePath: string; class="hl-cmt">// The absolute file system path to the worktree directory.
sessionId: string; class="hl-cmt">// A unique identifier for this application session (e.g., 39;wt-feature-branch-167888640000039;).
createdAt: number; class="hl-cmt">// Timestamp when the session was created.
}
WorktreeSessionManager (Singleton)
The WorktreeSessionManager class is the central component of this module. It follows the singleton pattern, meaning only one instance of this class can exist at any given time. This ensures consistent state management for all active worktree sessions across the application.
classDiagram
direction LR
class WorktreeSessionManager {
-static instance: WorktreeSessionManager
-sessions: Map<string, WorktreeSession>
+static getInstance(): WorktreeSessionManager
+createWorktreeSession(branch, basePath): WorktreeSession
+cleanupWorktree(branch): boolean
+listWorktreeSessions(): WorktreeSession[]
+getSessionForWorktree(worktreePath): WorktreeSession | undefined
+isWorktreeActive(branch): boolean
}
class WorktreeSession {
+branch: string
+worktreePath: string
+sessionId: string
+createdAt: number
}
WorktreeSessionManager "1" *-- "0..*" WorktreeSession : manages
How It Works
1. Initialization and Singleton Access
The WorktreeSessionManager is initialized lazily via its static getInstance() method. The first call creates the instance, and subsequent calls return the same instance. This ensures global coordination of worktree states.
import { WorktreeSessionManager } from 39;./git/worktree-sessions.js39;;
const manager = WorktreeSessionManager.getInstance();
class="hl-cmt">// manager is now the single, globally accessible instance.
The resetInstance() static method is provided primarily for testing purposes, allowing the singleton to be reset between test runs.
2. Creating Worktree Sessions
The createWorktreeSession(branch: string, basePath: string) method is responsible for:
- Path Construction: It determines the worktree's location, typically within a
.worktreessubdirectory of thebasePath(e.g.,basePath/.worktrees/my-feature-branch). - Directory Creation: Ensures the parent directories for the new worktree exist using
fs.mkdirSync. - Git Worktree Creation: Executes the
git worktree addcommand.
- It first attempts
git worktree add "${worktreePath}" "${branch}". - If this fails (e.g., the branch doesn't exist or the worktree path is already in use), it retries with
git worktree add -b "${branch}" "${worktreePath}"to create the branch if it doesn't exist and then add the worktree.
- Session Registration: Upon successful Git worktree creation, a
WorktreeSessionobject is created with a uniquesessionIdand stored internally in aMap, keyed by thebranchname.
3. Managing Active Sessions
The manager maintains an internal Map to keep track of all active worktree sessions.
listWorktreeSessions(): Returns an array of all currently registeredWorktreeSessionobjects.getSessionForWorktree(worktreePath: string): Allows retrieval of aWorktreeSessionby its file system path.isWorktreeActive(branch: string): Checks if a session for a given branch is currently active.
4. Cleaning Up Worktrees
The cleanupWorktree(branch: string) method handles the removal of a worktree and its associated session:
- Session Lookup: It retrieves the
WorktreeSessionfor the specifiedbranch. - Git Worktree Removal: If a session is found, it executes
git worktree remove "${session.worktreePath}" --forceto delete the worktree from the file system and Git's internal tracking. - Session Deregistration: The session is then removed from the internal
sessionsMap.
API Reference
static getInstance(): WorktreeSessionManager: Returns the singleton instance of the manager.static resetInstance(): void: Resets the singleton instance (primarily for testing).createWorktreeSession(branch: string, basePath: string): WorktreeSession: Creates a new Git worktree and registers a session for it. Throws an error if Git command fails.listWorktreeSessions(): WorktreeSession[]: Returns an array of all active worktree sessions.getSessionForWorktree(worktreePath: string): WorktreeSession | undefined: Retrieves a session by its worktree file path.cleanupWorktree(branch: string): boolean: Removes a Git worktree and its session. Returnstrueif successful,falseif the session was not found.isWorktreeActive(branch: string): boolean: Checks if a worktree session exists for the given branch.
Integration Points
This module interacts with the following:
- Node.js Built-ins:
child_process.execSync: Used to execute Git commands directly.path: For resolving and joining file paths.fs: For file system operations like creating directories (fs.mkdirSync).- Internal Utilities:
../utils/logger.js: For logging debug, info, and warning messages.
Consumers:
The primary consumer identified is tests/features/basse-features.test.ts, which extensively uses all public methods of WorktreeSessionManager to validate its functionality. This indicates its role as a foundational utility for features requiring isolated Git environments.
Usage Example
import { WorktreeSessionManager } from 39;./git/worktree-sessions.js39;;
import * as path from 39;path39;;
import * as os from 39;os39;;
import * as fs from 39;fs39;;
async function demonstrateWorktreeManagement() {
const manager = WorktreeSessionManager.getInstance();
const repoBasePath = path.join(os.tmpdir(), 39;my-test-repo39;);
class="hl-cmt">// Ensure a dummy git repo exists for demonstration
if (!fs.existsSync(repoBasePath)) {
fs.mkdirSync(repoBasePath, { recursive: true });
execSync(39;git init39;, { cwd: repoBasePath });
execSync(39;git config user.email "test@example.com"39;, { cwd: repoBasePath });
execSync(39;git config user.name "Test User"39;, { cwd: repoBasePath });
fs.writeFileSync(path.join(repoBasePath, 39;README.md39;), 39;# My Test Repo39;);
execSync(39;git add . && git commit -m "Initial commit"39;, { cwd: repoBasePath });
execSync(39;git branch feature-a39;, { cwd: repoBasePath });
execSync(39;git branch feature-b39;, { cwd: repoBasePath });
}
console.log(39;--- Creating Worktree for feature-a ---39;);
try {
const sessionA = manager.createWorktreeSession(39;feature-a39;, repoBasePath);
console.log(`Created session for branch: ${sessionA.branch} at ${sessionA.worktreePath}`);
} catch (error) {
console.error(39;Failed to create worktree for feature-a:39;, error.message);
}
console.log(39;\n--- Creating Worktree for feature-b ---39;);
try {
const sessionB = manager.createWorktreeSession(39;feature-b39;, repoBasePath);
console.log(`Created session for branch: ${sessionB.branch} at ${sessionB.worktreePath}`);
} catch (error) {
console.error(39;Failed to create worktree for feature-b:39;, error.message);
}
console.log(39;\n--- Listing active sessions ---39;);
const activeSessions = manager.listWorktreeSessions();
activeSessions.forEach(s => console.log(`- Branch: ${s.branch}, Path: ${s.worktreePath}`));
console.log(39;\n--- Checking if feature-a is active ---39;);
console.log(`Is feature-a active? ${manager.isWorktreeActive(39;feature-a39;)}`);
console.log(39;\n--- Cleaning up worktree for feature-a ---39;);
const cleanedUp = manager.cleanupWorktree(39;feature-a39;);
console.log(`Cleaned up feature-a? ${cleanedUp}`);
console.log(39;\n--- Listing active sessions after cleanup ---39;);
manager.listWorktreeSessions().forEach(s => console.log(`- Branch: ${s.branch}, Path: ${s.worktreePath}`));
class="hl-cmt">// Clean up the remaining worktree and the dummy repo
manager.cleanupWorktree(39;feature-b39;);
execSync(`rm -rf "${repoBasePath}"`);
console.log(39;\nDemonstration complete. Temporary repo removed.39;);
}
class="hl-cmt">// Note: execSync is imported from 39;child_process39; in the actual module.
class="hl-cmt">// For this example, we39;ll assume it39;s available.
import { execSync } from 39;child_process39;;
demonstrateWorktreeManagement();