@@ -207,6 +207,61 @@ async function removeWorktreeDirBestEffortAsync(worktreePath: string): Promise<v
207207 } catch { }
208208}
209209
210+ /**
211+ * 中文说明:在已完成合并安全检查后删除 worktree 对应分支。
212+ * - 未合并且未强制时,返回 `needsForceDeleteBranch` 让上层二次确认;
213+ * - 已合并时优先尝试 `git branch -d`,保持与 Git 常规删除语义一致;
214+ * - 若 `-d` 因当前 `HEAD/上游` 与创建基分支不同而拒绝删除,则回退到 `-D`;
215+ * 因为调用方已经通过显式祖先检查确认该分支已合并到目标基线,回退为安全兜底。
216+ */
217+ async function deleteCheckedWorktreeBranchAsync ( args : {
218+ repoMainPath : string ;
219+ wtBranch : string ;
220+ isMerged : boolean ;
221+ forceDeleteBranch ?: boolean ;
222+ gitPath ?: string ;
223+ } ) : Promise <
224+ | { ok : true ; removedBranch : boolean }
225+ | { ok : false ; removedBranch : false ; needsForceDeleteBranch ?: boolean ; error : string }
226+ > {
227+ const repoMainPath = toFsPathAbs ( args . repoMainPath ) ;
228+ const wtBranch = String ( args . wtBranch || "" ) . trim ( ) ;
229+ if ( ! wtBranch ) return { ok : true , removedBranch : false } ;
230+ if ( ! repoMainPath ) return { ok : false , removedBranch : false , error : "missing repoMainPath" } ;
231+
232+ if ( ! args . isMerged && args . forceDeleteBranch !== true ) {
233+ return { ok : false , removedBranch : false , needsForceDeleteBranch : true , error : "branch not merged" } ;
234+ }
235+
236+ /**
237+ * 中文说明:执行一次分支删除,并统一抽取可读错误文本。
238+ */
239+ const runDeleteAsync = async ( mode : "-d" | "-D" ) : Promise < { ok : true } | { ok : false ; error : string } > => {
240+ const del = await execGitAsync ( {
241+ gitPath : args . gitPath ,
242+ argv : [ "-C" , repoMainPath , "branch" , mode , wtBranch ] ,
243+ timeoutMs : 10_000 ,
244+ } ) ;
245+ if ( del . ok ) return { ok : true } ;
246+ return {
247+ ok : false ,
248+ error : String ( del . error || del . stderr || del . stdout || "git branch delete failed" ) . trim ( ) || "git branch delete failed" ,
249+ } ;
250+ } ;
251+
252+ const primaryMode : "-d" | "-D" = args . isMerged ? "-d" : "-D" ;
253+ const primary = await runDeleteAsync ( primaryMode ) ;
254+ if ( primary . ok ) return { ok : true , removedBranch : true } ;
255+
256+ if ( ! args . isMerged ) {
257+ return { ok : false , removedBranch : false , error : primary . error } ;
258+ }
259+
260+ const fallback = await runDeleteAsync ( "-D" ) ;
261+ if ( fallback . ok ) return { ok : true , removedBranch : true } ;
262+ return { ok : false , removedBranch : false , error : fallback . error || primary . error } ;
263+ }
264+
210265/**
211266 * 读取当前仓库的分支信息(用于 baseBranch 下拉)。
212267 */
@@ -1791,15 +1846,23 @@ export async function removeWorktreeAsync(req: RemoveWorktreeRequest): Promise<R
17911846 return { ok : false , removedWorktree, removedBranch : false , error : merged . error || merged . stderr . trim ( ) || "git merge-base failed" } ;
17921847 }
17931848 const isMerged = merged . exitCode === 0 ;
1794- if ( ! isMerged && ! req . forceDeleteBranch ) {
1795- return { ok : false , removedWorktree, removedBranch : false , needsForceDeleteBranch : true , error : "branch not merged" } ;
1796- }
1797- const delArgv = [ "-C" , repoMainPath , "branch" , isMerged ? "-d" : "-D" , wtBranch ] ;
1798- const del = await execGitAsync ( { gitPath, argv : delArgv , timeoutMs : 10_000 } ) ;
1799- if ( ! del . ok ) {
1800- return { ok : false , removedWorktree, removedBranch : false , error : del . error || del . stderr . trim ( ) || "git branch delete failed" } ;
1849+ const deleteRes = await deleteCheckedWorktreeBranchAsync ( {
1850+ repoMainPath,
1851+ wtBranch,
1852+ isMerged,
1853+ forceDeleteBranch : req . forceDeleteBranch ,
1854+ gitPath,
1855+ } ) ;
1856+ if ( ! deleteRes . ok ) {
1857+ return {
1858+ ok : false ,
1859+ removedWorktree,
1860+ removedBranch : false ,
1861+ needsForceDeleteBranch : deleteRes . needsForceDeleteBranch ,
1862+ error : deleteRes . error ,
1863+ } ;
18011864 }
1802- removedBranch = true ;
1865+ removedBranch = deleteRes . removedBranch ;
18031866 }
18041867 }
18051868
0 commit comments