| ... | @@ -14,6 +14,10 @@ import type { | ... | @@ -14,6 +14,10 @@ import type { |
| 14 | TerminalCommandResult, | 14 | TerminalCommandResult, |
| 15 | } from "@t3tools/contracts"; | 15 | } from "@t3tools/contracts"; |
| 16 | | 16 | |
| | 17 | function escapeSingleQuotes(value: string): string { |
| | 18 | return value.replace(/'/g, "'\\''"); |
| | 19 | } |
| | 20 | |
| 17 | export async function runTerminalCommand( | 21 | export async function runTerminalCommand( |
| 18 | input: TerminalCommandInput, | 22 | input: TerminalCommandInput, |
| 19 | ): Promise<TerminalCommandResult> { | 23 | ): Promise<TerminalCommandResult> { |
| ... | @@ -111,7 +115,7 @@ export async function listGitBranches( | ... | @@ -111,7 +115,7 @@ export async function listGitBranches( |
| 111 | current: line.startsWith("* "), | 115 | current: line.startsWith("* "), |
| 112 | isDefault: line.replace(/^[*+]\s+/, "") === defaultBranch, | 116 | isDefault: line.replace(/^[*+]\s+/, "") === defaultBranch, |
| 113 | })) | 117 | })) |
| 114 | .sort((a, b) => { | 118 | .toSorted((a, b) => { |
| 115 | if (a.current !== b.current) return a.current ? -1 : 1; | 119 | if (a.current !== b.current) return a.current ? -1 : 1; |
| 116 | if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1; | 120 | if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1; |
| 117 | return a.name.localeCompare(b.name); | 121 | return a.name.localeCompare(b.name); |
| ... | @@ -123,7 +127,6 @@ export async function listGitBranches( | ... | @@ -123,7 +127,6 @@ export async function listGitBranches( |
| 123 | export async function createGitWorktree( | 127 | export async function createGitWorktree( |
| 124 | input: GitCreateWorktreeInput, | 128 | input: GitCreateWorktreeInput, |
| 125 | ): Promise<GitCreateWorktreeResult> { | 129 | ): Promise<GitCreateWorktreeResult> { |
| 126 | const esc = (s: string) => s.replace(/'/g, "'\\''"); | | |
| 127 | const sanitizedBranch = input.newBranch.replace(/\//g, "-"); | 130 | const sanitizedBranch = input.newBranch.replace(/\//g, "-"); |
| 128 | const repoName = path.basename(input.cwd); | 131 | const repoName = path.basename(input.cwd); |
| 129 | const worktreePath = | 132 | const worktreePath = |
| ... | @@ -132,7 +135,7 @@ export async function createGitWorktree( | ... | @@ -132,7 +135,7 @@ export async function createGitWorktree( |
| 132 | | 135 | |
| 133 | // Create a new branch from the base branch in a new worktree | 136 | // Create a new branch from the base branch in a new worktree |
| 134 | const result = await runTerminalCommand({ | 137 | const result = await runTerminalCommand({ |
| 135 | command: `git worktree add -b '${esc(input.newBranch)}' '${esc(worktreePath)}' '${esc(input.branch)}'`, | 138 | command: `git worktree add -b '${escapeSingleQuotes(input.newBranch)}' '${escapeSingleQuotes(worktreePath)}' '${escapeSingleQuotes(input.branch)}'`, |
| 136 | cwd: input.cwd, | 139 | cwd: input.cwd, |
| 137 | timeoutMs: 30_000, | 140 | timeoutMs: 30_000, |
| 138 | }); | 141 | }); |
| ... | @@ -153,7 +156,7 @@ export async function removeGitWorktree( | ... | @@ -153,7 +156,7 @@ export async function removeGitWorktree( |
| 153 | input: GitRemoveWorktreeInput, | 156 | input: GitRemoveWorktreeInput, |
| 154 | ): Promise<void> { | 157 | ): Promise<void> { |
| 155 | const result = await runTerminalCommand({ | 158 | const result = await runTerminalCommand({ |
| 156 | command: `git worktree remove '${input.path.replace(/'/g, "'\\''")}'`, | 159 | command: `git worktree remove '${escapeSingleQuotes(input.path)}'`, |
| 157 | cwd: input.cwd, | 160 | cwd: input.cwd, |
| 158 | timeoutMs: 15_000, | 161 | timeoutMs: 15_000, |
| 159 | }); | 162 | }); |
| ... | @@ -167,7 +170,7 @@ export async function createGitBranch( | ... | @@ -167,7 +170,7 @@ export async function createGitBranch( |
| 167 | input: GitCreateBranchInput, | 170 | input: GitCreateBranchInput, |
| 168 | ): Promise<void> { | 171 | ): Promise<void> { |
| 169 | const result = await runTerminalCommand({ | 172 | const result = await runTerminalCommand({ |
| 170 | command: `git branch '${input.branch.replace(/'/g, "'\\''")}'`, | 173 | command: `git branch '${escapeSingleQuotes(input.branch)}'`, |
| 171 | cwd: input.cwd, | 174 | cwd: input.cwd, |
| 172 | timeoutMs: 10_000, | 175 | timeoutMs: 10_000, |
| 173 | }); | 176 | }); |
| ... | @@ -181,7 +184,7 @@ export async function checkoutGitBranch( | ... | @@ -181,7 +184,7 @@ export async function checkoutGitBranch( |
| 181 | input: GitCheckoutInput, | 184 | input: GitCheckoutInput, |
| 182 | ): Promise<void> { | 185 | ): Promise<void> { |
| 183 | const result = await runTerminalCommand({ | 186 | const result = await runTerminalCommand({ |
| 184 | command: `git checkout '${input.branch.replace(/'/g, "'\\''")}'`, | 187 | command: `git checkout '${escapeSingleQuotes(input.branch)}'`, |
| 185 | cwd: input.cwd, | 188 | cwd: input.cwd, |
| 186 | timeoutMs: 10_000, | 189 | timeoutMs: 10_000, |
| 187 | }); | 190 | }); |