| ... | @@ -1181,6 +1181,27 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | ... | @@ -1181,6 +1181,27 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 1181 | Effect.map((result) => result.stdout), | 1181 | Effect.map((result) => result.stdout), |
| 1182 | ); | 1182 | ); |
| 1183 | | 1183 | |
| | 1184 | /** |
| | 1185 | * Runs `git worktree list --porcelain -z`. Git before 2.36 rejects `-z`, so a failed run |
| | 1186 | * retries the line form and returns it NUL-delimited, the same shape as `-z` output. |
| | 1187 | * The line form cannot carry paths that contain newlines. |
| | 1188 | */ |
| | 1189 | const executeWorktreeList = Effect.fn("executeWorktreeList")(function* ( |
| | 1190 | operation: string, |
| | 1191 | cwd: string, |
| | 1192 | gitArgs: readonly string[], |
| | 1193 | options: ExecuteGitOptions = {}, |
| | 1194 | ) { |
| | 1195 | const args = [...gitArgs, "worktree", "list", "--porcelain"]; |
| | 1196 | const result = yield* executeGit(operation, cwd, [...args, "-z"], { |
| | 1197 | ...options, |
| | 1198 | allowNonZeroExit: true, |
| | 1199 | }); |
| | 1200 | if (result.exitCode === 0) return result; |
| | 1201 | const lineResult = yield* executeGit(operation, cwd, args, options); |
| | 1202 | return { ...lineResult, stdout: lineResult.stdout.replaceAll("\n", "\0") }; |
| | 1203 | }); |
| | 1204 | |
| 1184 | const runGitStdoutWithOptions = ( | 1205 | const runGitStdoutWithOptions = ( |
| 1185 | operation: string, | 1206 | operation: string, |
| 1186 | cwd: string, | 1207 | cwd: string, |
| ... | @@ -3163,16 +3184,11 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | ... | @@ -3163,16 +3184,11 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 3163 | allowNonZeroExit: true, | 3184 | allowNonZeroExit: true, |
| 3164 | }, | 3185 | }, |
| 3165 | ), | 3186 | ), |
| 3166 | executeGit( | 3187 | executeWorktreeList("GitVcsDriver.listRefs.worktreeList", fetchCwd, gitDirArgs, { |
| 3167 | "GitVcsDriver.listRefs.worktreeList", | 3188 | timeoutMs: 30_000, |
| 3168 | fetchCwd, | 3189 | allowNonZeroExit: true, |
| 3169 | [...gitDirArgs, "worktree", "list", "--porcelain", "-z"], | 3190 | maxOutputBytes: 16 * 1024 * 1024, |
| 3170 | { | 3191 | }), |
| 3171 | timeoutMs: 30_000, | | |
| 3172 | allowNonZeroExit: true, | | |
| 3173 | maxOutputBytes: 16 * 1024 * 1024, | | |
| 3174 | }, | | |
| 3175 | ), | | |
| 3176 | executeGit("GitVcsDriver.listRefs.remoteNames", fetchCwd, [...gitDirArgs, "remote"], { | 3192 | executeGit("GitVcsDriver.listRefs.remoteNames", fetchCwd, [...gitDirArgs, "remote"], { |
| 3177 | timeoutMs: 5_000, | 3193 | timeoutMs: 5_000, |
| 3178 | allowNonZeroExit: true, | 3194 | allowNonZeroExit: true, |
| ... | @@ -4043,15 +4059,10 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | ... | @@ -4043,15 +4059,10 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 4043 | ); | 4059 | ); |
| 4044 | | 4060 | |
| 4045 | const listWorktreePaths: GitVcsDriver.GitVcsDriver["Service"]["listWorktreePaths"] = (cwd) => | 4061 | const listWorktreePaths: GitVcsDriver.GitVcsDriver["Service"]["listWorktreePaths"] = (cwd) => |
| 4046 | runGitStdout("GitVcsDriver.listWorktreePaths", cwd, [ | 4062 | executeWorktreeList("GitVcsDriver.listWorktreePaths", cwd, []).pipe( |
| 4047 | "worktree", | | |
| 4048 | "list", | | |
| 4049 | "--porcelain", | | |
| 4050 | "-z", | | |
| 4051 | ]).pipe( | | |
| 4052 | // One record per worktree, each ended by an empty field. A `prunable` | 4063 | // One record per worktree, each ended by an empty field. A `prunable` |
| 4053 | // record's directory is gone, and another checkout may now sit there. | 4064 | // record's directory is gone, and another checkout may now sit there. |
| 4054 | Effect.map((stdout) => | 4065 | Effect.map(({ stdout }) => |
| 4055 | stdout.split("\0\0").flatMap((record) => { | 4066 | stdout.split("\0\0").flatMap((record) => { |
| 4056 | const fields = record.split("\0"); | 4067 | const fields = record.split("\0"); |
| 4057 | const worktree = fields.find((field) => field.startsWith("worktree ")); | 4068 | const worktree = fields.find((field) => field.startsWith("worktree ")); |