import type { ForgeContext } from '../../forge/context' import { deleteLocalBranch } from '../../git/branches' import { logger } from '../../logging/logger' /** * 清理一个 feature 分支(远程 + 本地),供「拖到完成」和「手动删除 worktree」两处共用。 * * - 受保护分支(main/master/devBranch)直接跳过,绝不删。 * - 远程删除走代码托管接口,需 remote + 凭据;缺任一则只删本地,不阻断。 * - 本地删除是强删(deleteLocalBranch 内部 `git branch -D`):调用方语义是「连分支一起删」, * 未合并也照删。 * - 全程 best-effort:远程 / 本地各自 try/catch,失败只记日志——分支清理从不回滚已完成的主流程 * (worktree 已删、PR 已合并)。branch 为空直接返回。 * * 放在 handlers 层而非 `git/branches.ts`:后者刻意不依赖 `vscode`(有单测覆盖 * deleteLocalBranch),而本函数要用 logger(→ vscode)和代码托管接口。 */ export async function cleanupFeatureBranch(opts: { workspaceRoot: string branch: string issueNumber: number devBranch?: string /** 没有代码托管仓库时跳过远程删除,只删本地。 */ forge?: ForgeContext }): Promise { const { workspaceRoot, branch, issueNumber, devBranch, forge } = opts if (!branch) return const protectedBranches = new Set( ['main', 'master', devBranch].filter((b): b is string => Boolean(b)), ) if (protectedBranches.has(branch)) { logger.add({ level: 'warn', source: 'panel', message: `跳过受保护分支清理 ${branch} (issue #${issueNumber})` }) return } if (forge) { try { await forge.provider.deleteBranch(branch) logger.add({ level: 'info', source: 'panel', message: `已删除远程 feature 分支 ${branch} (issue #${issueNumber})` }) } catch (err) { logger.add({ level: 'warn', source: 'panel', message: `删除远程 feature 分支失败 ${branch} (issue #${issueNumber})`, details: err instanceof Error ? err.message : String(err), }) } } try { const deleted = await deleteLocalBranch(workspaceRoot, branch) logger.add({ level: deleted.ok ? 'info' : 'warn', source: 'panel', message: `${deleted.ok ? '已删除' : '删除失败'}本地 feature 分支 ${branch} (issue #${issueNumber})`, details: deleted.stderr || deleted.stdout, }) } catch (err) { logger.add({ level: 'warn', source: 'panel', message: `删除本地 feature 分支抛错 ${branch} (issue #${issueNumber})`, details: err instanceof Error ? err.message : String(err), }) } }