(options: {
basePath: string;
nameHint?: string;
})
| 99 | } |
| 100 | |
| 101 | export async function createWorktree(options: { |
| 102 | basePath: string; |
| 103 | nameHint?: string; |
| 104 | }): Promise<WorktreeResult> { |
| 105 | const { basePath, nameHint } = options; |
| 106 | let repoRoot: string; |
| 107 | |
| 108 | try { |
| 109 | repoRoot = await resolveRepoRoot(basePath); |
| 110 | } catch (error) { |
| 111 | const message = error instanceof Error ? error.message : String(error); |
| 112 | return { |
| 113 | ok: false, |
| 114 | error: `Path is not a Git repository: ${message}` |
| 115 | }; |
| 116 | } |
| 117 | |
| 118 | const repoParent = dirname(repoRoot); |
| 119 | const repoName = basename(repoRoot); |
| 120 | const repoWorktreesRoot = join(repoParent, `${repoName}-worktrees`); |
| 121 | await mkdir(repoWorktreesRoot, { recursive: true }); |
| 122 | |
| 123 | const baseName = normalizeNameHint(nameHint) ?? makeDefaultBaseName(); |
| 124 | |
| 125 | for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) { |
| 126 | const name = attempt === 0 ? baseName : `${baseName}-${randomBytes(2).toString('hex')}`; |
| 127 | const branch = `hapi-${name}`; |
| 128 | const worktreePath = join(repoWorktreesRoot, name); |
| 129 | |
| 130 | if (await pathExists(worktreePath)) { |
| 131 | continue; |
| 132 | } |
| 133 | |
| 134 | if (await branchExists(repoRoot, branch)) { |
| 135 | continue; |
| 136 | } |
| 137 | |
| 138 | try { |
| 139 | await runGit(['worktree', 'add', '-b', branch, worktreePath], repoRoot); |
| 140 | return { |
| 141 | ok: true, |
| 142 | info: { |
| 143 | basePath: repoRoot, |
| 144 | worktreePath, |
| 145 | branch, |
| 146 | name, |
| 147 | createdAt: Date.now() |
| 148 | } |
| 149 | }; |
| 150 | } catch (error) { |
| 151 | const message = error instanceof Error ? error.message : String(error); |
| 152 | return { |
| 153 | ok: false, |
| 154 | error: `Failed to create worktree: ${message}` |
| 155 | }; |
| 156 | } |
| 157 | } |
| 158 |
no test coverage detected