(
projectPath: string,
oldName: string,
newName: string,
abortSignal?: AbortSignal
)
| 3095 | } |
| 3096 | |
| 3097 | async renameWorkspace( |
| 3098 | projectPath: string, |
| 3099 | oldName: string, |
| 3100 | newName: string, |
| 3101 | abortSignal?: AbortSignal |
| 3102 | ): Promise< |
| 3103 | { success: true; oldPath: string; newPath: string } | { success: false; error: string } |
| 3104 | > { |
| 3105 | // Check if already aborted |
| 3106 | if (abortSignal?.aborted) { |
| 3107 | return { success: false, error: "Rename operation aborted" }; |
| 3108 | } |
| 3109 | const oldPath = this.getWorkspacePath(projectPath, oldName); |
| 3110 | const newPath = path.posix.join(path.posix.dirname(oldPath), newName); |
| 3111 | |
| 3112 | try { |
| 3113 | const expandedOldPath = expandTildeForSSH(oldPath); |
| 3114 | const expandedNewPath = expandTildeForSSH(newPath); |
| 3115 | |
| 3116 | // Detect if workspace is a worktree vs legacy full clone. |
| 3117 | const isWorktree = await this.isWorktreeWorkspace(oldPath, abortSignal); |
| 3118 | |
| 3119 | let moveCommand: string; |
| 3120 | if (isWorktree) { |
| 3121 | // Worktree: use `git worktree move` to keep the workspace registered in whichever |
| 3122 | // shared base repo originally created it, including upgraded legacy SSH layouts. |
| 3123 | const baseRepoPathArg = expandTildeForSSH( |
| 3124 | await this.resolveWorktreeBaseRepoPath(projectPath, oldPath, abortSignal) |
| 3125 | ); |
| 3126 | moveCommand = `git -C ${baseRepoPathArg} worktree move ${expandedOldPath} ${expandedNewPath}`; |
| 3127 | } else { |
| 3128 | // Legacy full clone: plain mv. |
| 3129 | moveCommand = `mv ${expandedOldPath} ${expandedNewPath}`; |
| 3130 | } |
| 3131 | |
| 3132 | const stream = await this.exec(moveCommand, { |
| 3133 | cwd: this.config.srcBaseDir, |
| 3134 | timeout: 30, |
| 3135 | abortSignal, |
| 3136 | }); |
| 3137 | |
| 3138 | await stream.stdin.abort(); |
| 3139 | const exitCode = await stream.exitCode; |
| 3140 | |
| 3141 | if (exitCode !== 0) { |
| 3142 | const stderrReader = stream.stderr.getReader(); |
| 3143 | const decoder = new TextDecoder(); |
| 3144 | let stderr = ""; |
| 3145 | try { |
| 3146 | while (true) { |
| 3147 | const { done, value } = await stderrReader.read(); |
| 3148 | if (done) break; |
| 3149 | stderr += decoder.decode(value, { stream: true }); |
| 3150 | } |
| 3151 | } finally { |
| 3152 | stderrReader.releaseLock(); |
| 3153 | } |
| 3154 |
nothing calls this directly
no test coverage detected