( token: string, owner: string, repo: string, branchName: string, sha: string )
| 220 | } |
| 221 | |
| 222 | export async function createBranch( |
| 223 | token: string, |
| 224 | owner: string, |
| 225 | repo: string, |
| 226 | branchName: string, |
| 227 | sha: string |
| 228 | ) { |
| 229 | // First, try to delete the branch if it already exists (from a previous run) |
| 230 | try { |
| 231 | const checkRes = await ghFetch( |
| 232 | `/repos/${owner}/${repo}/git/ref/heads/${encodeURIComponent(branchName)}`, |
| 233 | token |
| 234 | ); |
| 235 | if (checkRes.ok) { |
| 236 | // Branch exists — delete it first |
| 237 | await ghFetch( |
| 238 | `/repos/${owner}/${repo}/git/refs/heads/${encodeURIComponent(branchName)}`, |
| 239 | token, |
| 240 | { method: 'DELETE' } |
| 241 | ); |
| 242 | // Small delay to let GitHub process the deletion |
| 243 | await sleep(500); |
| 244 | } |
| 245 | } catch { |
| 246 | // Ignore errors during cleanup — branch might not exist |
| 247 | } |
| 248 | |
| 249 | const res = await ghFetch(`/repos/${owner}/${repo}/git/refs`, token, { |
| 250 | method: 'POST', |
| 251 | body: JSON.stringify({ ref: `refs/heads/${branchName}`, sha }), |
| 252 | headers: { 'Content-Type': 'application/json' }, |
| 253 | }); |
| 254 | if (!res.ok) { |
| 255 | const err = await res.json().catch(() => ({ message: res.statusText })); |
| 256 | if (res.status === 422 && err.message?.includes('Reference already exists')) |
| 257 | throw new Error(`BRANCH_EXISTS: Branch '${branchName}' already exists. Could not auto-delete it — check permissions.`); |
| 258 | if (res.status === 403 && err.message?.includes('Resource not accessible')) |
| 259 | throw new Error(`PERMISSION_DENIED: ${err.message}`); |
| 260 | if (res.status === 401) |
| 261 | throw new Error(`PERMISSION_DENIED: Token expired or invalid (401 Unauthorized)`); |
| 262 | throw new Error(`Failed to create branch: ${err.message || `HTTP ${res.status}`}`); |
| 263 | } |
| 264 | return res.json(); |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Commit a single file to a branch using the low-level Git Data API. |
no test coverage detected