fix: resolve draft project for worktree paths (#722)

This commit is contained in:
Iuliia Ivashko
2026-03-20 16:50:13 +02:00
committed by GitHub
parent fd31a8cc2d
commit 683d0c1798
2 changed files with 140 additions and 8 deletions
@@ -1,5 +1,6 @@
import { substituteCommandVariables } from '@/lib/openchamberConfig';
import type { WorktreeMetadata } from '@/types/worktree';
import { execCommand } from '@/lib/execCommands';
import {
deleteRemoteBranch,
git,
@@ -19,6 +20,66 @@ const normalizePath = (value: string): string => {
return replaced.length > 1 ? replaced.replace(/\/+$/, '') : replaced;
};
const toAbsolutePath = (baseDir: string, maybeRelativePath: string): string => {
const normalizedBase = normalizePath(baseDir);
const normalizedInput = normalizePath(maybeRelativePath);
if (!normalizedInput) return normalizedBase;
if (normalizedInput.startsWith('/')) return normalizedInput;
const stack = normalizedBase.split('/').filter(Boolean);
const parts = normalizedInput.split('/').filter(Boolean);
for (const part of parts) {
if (part === '.') continue;
if (part === '..') {
stack.pop();
continue;
}
stack.push(part);
}
return `/${stack.join('/')}`;
};
const derivePrimaryWorktreeRootFromGitDir = (gitDir: string): string | null => {
const normalized = normalizePath(gitDir);
if (!normalized) return null;
if (normalized.endsWith('/.git')) {
return normalized.slice(0, -'/.git'.length) || null;
}
const worktreesMarker = '/.git/worktrees/';
const markerIndex = normalized.indexOf(worktreesMarker);
if (markerIndex > 0) {
return normalized.slice(0, markerIndex) || null;
}
return null;
};
const resolvePrimaryWorktreeDirectory = async (directory: string): Promise<string> => {
const normalizedDirectory = normalizePath(directory);
const absoluteGitDirResult = await execCommand('git rev-parse --absolute-git-dir', normalizedDirectory);
const absoluteGitDir = normalizePath((absoluteGitDirResult.stdout || '').trim());
if (absoluteGitDirResult.success && absoluteGitDir) {
const rootFromAbsoluteGitDir = derivePrimaryWorktreeRootFromGitDir(absoluteGitDir);
if (rootFromAbsoluteGitDir) {
return rootFromAbsoluteGitDir;
}
}
const commonDirResult = await execCommand('git rev-parse --git-common-dir', normalizedDirectory);
const rawCommonDir = normalizePath((commonDirResult.stdout || '').trim());
if (!commonDirResult.success || !rawCommonDir) {
return normalizedDirectory;
}
const commonDir = toAbsolutePath(normalizedDirectory, rawCommonDir);
const rootFromCommonDir = derivePrimaryWorktreeRootFromGitDir(commonDir);
if (rootFromCommonDir) {
return rootFromCommonDir;
}
return normalizedDirectory;
};
const slugifyWorktreeName = (value: string): string => {
return value
.trim()
@@ -113,7 +174,8 @@ const toCreatePayload = (args: {
};
export async function listProjectWorktrees(project: ProjectRef): Promise<WorktreeMetadata[]> {
const projectDirectory = project.path;
const projectDirectory = normalizePath(project.path);
const metadataProjectDirectory = await resolvePrimaryWorktreeDirectory(projectDirectory).catch(() => projectDirectory);
const normalizedProjectDirectory = normalizePath(projectDirectory);
const worktrees = await git.worktree.list(projectDirectory).catch(() => []);
@@ -127,7 +189,7 @@ export async function listProjectWorktrees(project: ProjectRef): Promise<Worktre
source: 'sdk' as const,
name: name || deriveSdkWorktreeNameFromDirectory(worktreePath),
path: worktreePath,
projectDirectory,
projectDirectory: metadataProjectDirectory,
branch,
label: branch || name || deriveSdkWorktreeNameFromDirectory(worktreePath),
};
@@ -157,7 +219,8 @@ export type CreateWorktreeArgs = {
};
export async function createWorktree(project: ProjectRef, args: CreateWorktreeArgs): Promise<WorktreeMetadata> {
const projectDirectory = project.path;
const projectDirectory = normalizePath(project.path);
const metadataProjectDirectory = await resolvePrimaryWorktreeDirectory(projectDirectory).catch(() => projectDirectory);
const payload = toCreatePayload(args, projectDirectory);
const created = await git.worktree.create(projectDirectory, payload);
@@ -173,7 +236,7 @@ export async function createWorktree(project: ProjectRef, args: CreateWorktreeAr
source: 'sdk',
name: returnedName,
path: normalizePath(returnedPath),
projectDirectory,
projectDirectory: metadataProjectDirectory,
branch: returnedBranch,
label: returnedBranch || returnedName,
};