( worktreePath: string, branch: string, )
| 25 | * Note: `git checkout -- <branch>` is WRONG - that's file checkout syntax. |
| 26 | */ |
| 27 | export async function gitSwitchBranch( |
| 28 | worktreePath: string, |
| 29 | branch: string, |
| 30 | ): Promise<void> { |
| 31 | assertRegisteredWorktree(worktreePath); |
| 32 | |
| 33 | // Validate: reject anything that looks like a flag |
| 34 | if (branch.startsWith("-")) { |
| 35 | throw new Error("Invalid branch name: cannot start with -"); |
| 36 | } |
| 37 | |
| 38 | // Validate: reject empty branch names |
| 39 | if (!branch.trim()) { |
| 40 | throw new Error("Invalid branch name: cannot be empty"); |
| 41 | } |
| 42 | |
| 43 | const git = createGit(worktreePath); |
| 44 | |
| 45 | await withLockRetry(worktreePath, async () => { |
| 46 | try { |
| 47 | // Prefer `git switch` - unambiguous branch operation (git 2.23+) |
| 48 | await git.raw(["switch", branch]); |
| 49 | } catch (switchError) { |
| 50 | // Check if it's because `switch` command doesn't exist (old git < 2.23) |
| 51 | // Git outputs: "git: 'switch' is not a git command. See 'git --help'." |
| 52 | const errorMessage = String(switchError); |
| 53 | if (errorMessage.includes("is not a git command")) { |
| 54 | // Fallback for older git versions |
| 55 | // Note: checkout WITHOUT -- is correct for branches |
| 56 | await git.checkout(branch); |
| 57 | } else { |
| 58 | throw switchError; |
| 59 | } |
| 60 | } |
| 61 | }); |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Checkout (restore) a file path, discarding local changes. |
no test coverage detected