( worktreePath: string, branchName: string, )
| 528 | } |
| 529 | |
| 530 | export async function branchExistsOnRemote( |
| 531 | worktreePath: string, |
| 532 | branchName: string, |
| 533 | ): Promise<BranchExistsResult> { |
| 534 | const env = await getGitEnv(); |
| 535 | |
| 536 | try { |
| 537 | // Use execFileAsync directly to get reliable exit codes |
| 538 | // simple-git doesn't expose exit codes in a predictable way |
| 539 | await execFileAsync( |
| 540 | "git", |
| 541 | [ |
| 542 | "-C", |
| 543 | worktreePath, |
| 544 | "ls-remote", |
| 545 | "--exit-code", |
| 546 | "--heads", |
| 547 | "origin", |
| 548 | branchName, |
| 549 | ], |
| 550 | { env, timeout: 30_000 }, |
| 551 | ); |
| 552 | // Exit code 0 = branch exists (--exit-code flag ensures this) |
| 553 | return { status: "exists" }; |
| 554 | } catch (error) { |
| 555 | // Use type guard to safely access ExecFileException properties |
| 556 | if (!isExecFileException(error)) { |
| 557 | return { |
| 558 | status: "error", |
| 559 | message: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, |
| 560 | }; |
| 561 | } |
| 562 | |
| 563 | // Handle spawn/system errors first (code is a string like "ENOENT") |
| 564 | if (typeof error.code === "string") { |
| 565 | if (error.code === "ENOENT") { |
| 566 | return { |
| 567 | status: "error", |
| 568 | message: "Git is not installed or not found in PATH.", |
| 569 | }; |
| 570 | } |
| 571 | if (error.code === "ETIMEDOUT") { |
| 572 | return { |
| 573 | status: "error", |
| 574 | message: "Git command timed out. Check your network connection.", |
| 575 | }; |
| 576 | } |
| 577 | // Other system errors |
| 578 | return { |
| 579 | status: "error", |
| 580 | message: `System error: ${error.code}`, |
| 581 | }; |
| 582 | } |
| 583 | |
| 584 | // Handle killed/timed out processes (timeout option triggers this) |
| 585 | if (error.killed || error.signal) { |
| 586 | return { |
| 587 | status: "error", |
no test coverage detected