feat(sessions): move sessions to existing worktrees
This commit is contained in:
@@ -374,7 +374,7 @@ export const partitionWorktreesByRegisteredProject = (
|
||||
|
||||
// Cache worktree listings to avoid repeated git worktree list + rev-parse calls
|
||||
const _worktreeListCache = new Map<string, { value: WorktreeMetadata[]; at: number }>();
|
||||
const _worktreeListInflight = new Map<string, Promise<WorktreeMetadata[]>>();
|
||||
const _worktreeListInflight = new Map<string, { generation: number; promise: Promise<WorktreeMetadata[]> }>();
|
||||
const _worktreeListGeneration = new Map<string, number>();
|
||||
const WORKTREE_LIST_CACHE_TTL = 30_000; // 30 seconds
|
||||
|
||||
@@ -391,7 +391,7 @@ const readProjectWorktrees = async (projectDirectory: string): Promise<WorktreeM
|
||||
const metadataProjectDirectory = await resolveProjectRoot(projectDirectory).catch(() => projectDirectory);
|
||||
const normalizedProjectDirectory = normalizePath(projectDirectory);
|
||||
|
||||
const worktrees = await git.worktree.list(projectDirectory).catch(() => []);
|
||||
const worktrees = await git.worktree.list(projectDirectory);
|
||||
const results: WorktreeMetadata[] = worktrees
|
||||
.filter((entry) => typeof entry.path === 'string' && entry.path.trim().length > 0)
|
||||
.map((entry) => {
|
||||
@@ -424,38 +424,48 @@ const readProjectWorktrees = async (projectDirectory: string): Promise<WorktreeM
|
||||
});
|
||||
};
|
||||
|
||||
const readStableProjectWorktrees = async (projectDirectory: string): Promise<WorktreeMetadata[]> => {
|
||||
const readStableProjectWorktrees = async (
|
||||
projectDirectory: string,
|
||||
minimumGeneration = getWorktreeListGeneration(projectDirectory),
|
||||
): Promise<WorktreeMetadata[]> => {
|
||||
while (true) {
|
||||
const generation = getWorktreeListGeneration(projectDirectory);
|
||||
const worktrees = await readProjectWorktrees(projectDirectory);
|
||||
|
||||
if (generation === getWorktreeListGeneration(projectDirectory)) {
|
||||
if (generation >= minimumGeneration && generation === getWorktreeListGeneration(projectDirectory)) {
|
||||
_worktreeListCache.set(projectDirectory, { value: worktrees, at: Date.now() });
|
||||
return worktrees;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function listProjectWorktrees(project: ProjectRef): Promise<WorktreeMetadata[]> {
|
||||
export async function listProjectWorktrees(project: ProjectRef, options?: { force?: boolean }): Promise<WorktreeMetadata[]> {
|
||||
const projectDirectory = normalizePath(project.path);
|
||||
const force = options?.force === true;
|
||||
|
||||
if (force) {
|
||||
invalidateWorktreeList(projectDirectory);
|
||||
}
|
||||
|
||||
const generation = getWorktreeListGeneration(projectDirectory);
|
||||
|
||||
// Return cached if fresh
|
||||
const cached = _worktreeListCache.get(projectDirectory);
|
||||
if (cached && Date.now() - cached.at < WORKTREE_LIST_CACHE_TTL) {
|
||||
if (!force && cached && Date.now() - cached.at < WORKTREE_LIST_CACHE_TTL) {
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
// Dedup in-flight requests
|
||||
const inflight = _worktreeListInflight.get(projectDirectory);
|
||||
if (inflight) return inflight;
|
||||
if (inflight && inflight.generation === generation) return inflight.promise;
|
||||
|
||||
const promise = readStableProjectWorktrees(projectDirectory).finally(() => {
|
||||
if (_worktreeListInflight.get(projectDirectory) === promise) {
|
||||
const promise = readStableProjectWorktrees(projectDirectory, generation).finally(() => {
|
||||
if (_worktreeListInflight.get(projectDirectory)?.promise === promise) {
|
||||
_worktreeListInflight.delete(projectDirectory);
|
||||
}
|
||||
});
|
||||
|
||||
_worktreeListInflight.set(projectDirectory, promise);
|
||||
_worktreeListInflight.set(projectDirectory, { generation, promise });
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user