Beads 的 bd delete 完全指南:安全地级联删除 issue 并清理全部引用
Beads 的 bd delete 完全指南安全地级联删除 issue 并清理全部引用【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beadsbd delete是 BeadsbdCLI中删除 issue以及与 issue 同构的 wisp的命令。它不止删除数据库中的行还会清理所有与之相关的依赖边并把直接相连 issue 文本里的引用改写为[deleted:ID]标记保证工作区中不残留指向已删除对象的悬空引用。本文基于 docs/cli-reference/delete.md 展开结合 cmd/bd/delete.go、issueops/deleter.go 与底层存储实现完整讲解该命令的三种依赖处理模式、批量删除、预览机制及其源码级设计原理。读完本文你将能安全地在本地数据库与团队服务器proxied server上执行删除操作并理解其不可撤销背后的保障机制。bd delete 命令概览一次删除的三步动作bd delete是一个破坏性操作执行一次删除会按顺序完成三件事移除所有依赖链接删除与该 issue 相关的全部依赖边任意类型、任意方向改写文本引用在直接相连有依赖边的 issue 中把对已删除 ID 的引用改写为[deleted:ID]从数据库永久删除彻底抹掉该 issue 的行记录。这三步在同一个数据库事务内完成任何一步失败都会整体回滚不会出现行已删但引用仍悬空的中间状态详见后文单事务一节。命令签名如下bd delete issue-id [issue-id...] [flags]命令源码位于 cmd/bd/delete.go其Long描述即上文语义的官方定义。基本用法删除单个或多个 issue最简单的形式是直接给出一个或多个 issue ID# 删除单个 issue未加 --force 时只显示预览 bd delete bd-1 --force # 一次删除多个 issue bd delete bd-1 bd-2 bd-3 --force命令入口支持cobra.MinimumNArgs(0)cmd/bd/delete.go允许通过 flag 提供 ID见下文--from-file。所有传入的 ID 会经过uniqueStrings去重重复 ID 不会导致重复删除。ID 支持前缀匹配bd delete在 CLI 前端front door通过resolveAndGetIssueForMutation做前缀解析与跨仓库路由解析出精确 ID 后再交给底层删除角色。这一点与issueops.DeleteRequest要求 ID 精确的语义是一致的——把前缀解析到某一行然后删掉它正是前缀便利不适合的地方cmd/bd/delete.go。若前缀存在歧义或 ID 不存在命令会直接报错且批量场景下任一 ID 不存在则整体不删除详见下文拒绝顺序。批量删除--from-file 从文件读取 ID当需要删除大量 issue 时可以用--from-file从文件中读取 ID每行一个# 从文件读取 ID 批量删除 bd delete --from-file deletions.txt --force # 先预览确认无误后再执行 bd delete --from-file deletions.txt --dry-run文件格式约定实现见 cmd/bd/delete.go 的readIssueIDsFromFile每行一个 ID行首尾空白会被TrimSpace去除空行会被跳过以#开头的行被视为注释并跳过方便在文件里写说明命令行参数与--from-file提供的 ID 会合并并统一去重。例如deletions.txt# 本轮需要清理的临时 issue bd-101 bd-102 bd-103执行bd delete --from-file deletions.txt --force会删除bd-101、bd-102、bd-103三个 issue。注意命令要求至少提供一个 ID若参数与文件均为空会报错no issue IDs providedcmd/bd/delete.go。安全预览--dry-run 与未加 --force 时的行为bd delete把--force同时当作确认执行与孤儿化模式的开关源码注释明确说明--force is this commands CONFIRMATION as well as its orphan mode。因此不加--force时命令不会真正删除而是执行与--dry-run相同的预览逻辑把DryRun置为truecmd/bd/delete.go显式使用--dry-run时预览末尾会额外打印(Dry-run mode - no changes made)提示。预览输出包含单条删除的渲染见 cmd/bd/delete.go批量见outputDeletionPreview待删除的 issue ID 与标题将要移除的依赖边列表含类型如blocks以及 inbound 方向相连 issue 中哪些包含会被改写的文本引用预估数字Would delete: N issues、Would remove: N dependencies, N labels, N events若涉及孤儿化还会列出Would orphan: N issues结尾提示真实执行所需的命令例如To proceed, run: bd delete bd-1 --force。预览有一个重要的安全性设计JSON 输出与 quiet 模式不会泄漏 issue 的敏感载荷。测试 cmd/bd/delete_test.goTestDeletePreviewJSONIsPayloadBlind验证了 JSON 预览只输出issue_ids、would_delete、dry_run等结构化字段绝不包含 issue 标题或描述文本。依赖处理三种模式默认拒绝、级联删除、强制孤儿化这是bd delete最核心的语义。当一个 issue 存在不在本次删除集合内的依赖者dependent时命令如何反应由 flag 决定默认模式有外部依赖者则拒绝bd delete bd-1 bd-2若bd-1或bd-2有请求集合之外的 issue 依赖它命令整体拒绝返回DependentsOutsideRequestError且什么都不删除。错误信息形如issue bd-1 has dependents not in deletion set; use --cascade to delete them or --force to orphan them该错误同时携带结构化字段IssueID与被挡住的Dependents列表见 issueops/deleter.go。契约测试RunDeleterRefusesDependentsOutsideTheRequest验证了这一守卫backend/conformance/deleter_contract.go。一个重要的例外如果依赖者本身也在删除集合内则不构成拒绝——bd delete a b同时命名了一条依赖边的两端此时边也会一并删除拒绝反而会误伤刻意成对列出的 ID。--cascade递归删除全部依赖者bd delete bd-1 --cascade --force--cascade会计算删除集合的传递闭包transitive closure不仅删除bd-1还递归删除所有直接或间接依赖它的 issue双向平面同时覆盖 durable 的 issue 与 ephemeral 的 wisp详见 issueops/deleter.go。几个值得注意的点级联模式下不可能产生孤儿闭包已包含所有被删行的依赖者因此DeleteResult.Orphaned恒为空--cascade与--force同时使用是合法的行为按级联处理闭包遍历有界若闭包规模超过后端的 runaway 上限整个请求中止不删除任何东西而不是删到哪算哪。契约测试RunDeleterCascadeDeletesTheClosure用 root → middle → leaf 的三层链验证传递性并断言旁观者bystander不受影响backend/conformance/deleter_contract.goRunDeleterCascadeFromAWispRootDeletesTheClosure则验证以 wisp 为根、跨越平面的级联闭包。--force删除命名行并孤儿化依赖者bd delete bd-1 --force--force删除命名的行同时留下那些依赖它的行指向被删行的边被移除但依赖者本身保留成为孤儿orphaned其 ID 会出现在结果的Orphaned字段与命令行输出中✓ Deleted 1 issue(s) Removed 1 dependency link(s) ⚠ Orphaned 1 issue(s): bd-2--force只会越过依赖者守卫这一策略层而绝不会越过前置条件如ExpectedVersion版本校验与存在性探测issueops/deleter.go。契约测试RunDeleterForceOrphansDependents断言孤儿保留行、失去边、且不会出现指向已删 ID 的悬空边backend/conformance/deleter_contract.go。三种模式决策速查场景推荐命令结果无任何依赖者bd delete bd-1 --force正常删除有外部依赖者希望连带删除bd delete bd-1 --cascade --force递归删除整个闭包有外部依赖者仅删除目标行bd delete bd-1 --force目标行删除依赖者孤儿化不确定影响范围bd delete bd-1或bd delete bd-1 --dry-run只预览不修改Flags 参考Flag简写默认值说明--cascade—false递归删除所有依赖 issue传递闭包双向平面--dry-run—false只预览将删除的内容不做任何修改也不写历史-f, --force-ffalse实际执行删除不加此 flag 时仅显示预览--from-file string—从文件读取 issue ID每行一个支持#注释与空行Flag 注册代码见 cmd/bd/delete.go并配置了issueIDCompletion补全函数。源码级原理DeleteRequest 与 DeleteResult 的语义契约CLI 层把用户意图翻译为issueops.DeleteRequest交给实现了issueops.Deleter接口的后端执行issueops/deleter.go。请求与结果的字段语义是整个删除机制的契约核心DeleteRequest关键字段IDs必填命名要删除的行支持跨平面可以混排 issue 与 wisp空切片返回ErrValidation而非静默无操作重复 ID 自动折叠每个 ID 必须命中有存储的行否则整体不删除Cascade是否删除传递闭包Force是否孤儿化外部依赖者DryRun只报告将要发生的事不改变任何东西包括历史ExpectedVersion可选乐观并发前置条件要求单 ID 请求若行的RowVersion与期望值不一致则拒绝ErrVersionMismatch且Cascade/Force都不能绕过它issueops/deleter.go。DeleteResult关键字段Deleted删除的行数级联模式下是整个闭包的数量通常大于len(IDs)Dependencies/Labels/Events随行一起移除的关联数据行数双向边、标签、事件ReferencesUpdated被改写的存活行数按行计数一个邻居引用两个已删 ID 也只计 1预览模式下恒为 0Orphaned被孤儿化的存活行 ID升序仅在Force且无Cascade时非空。这些数字描述的是同一快照守卫、级联展开、删除与引用改写全部运行在同一个事务内issueops/deleter.go。拒绝顺序一次失败只报一个最可操作的原因一个请求可能同时存在多种问题bd delete按固定顺序报告最优先的拒绝原因见 issueops/deleter.go 与 internal/storage/issueops/delete_role.go 的实现请求校验ErrValidation空 ID 列表、空白 ID、多 ID 携带ExpectedVersion存在性探测*NotFoundError匹配ErrNotFound任一 ID 无对应行整体不删除——批处理删了八个然后才报告拼写错误正是最不可逆的错误场景因此必须前置版本前置条件ErrVersionMismatch行的RowVersion已变化说明调用者读到的视图已过期依赖者守卫*DependentsOutsideRequestError未加Cascade/Force且存在集合外的依赖者。顺序的取舍逻辑很明确bd delete typo real-with-dependents会先报拼写错误调用者无需做任何决策即可修复版本不匹配排在依赖者守卫之前因为让用户基于已过期的信息去选--cascade还是--force没有意义。契约测试RunDeleterRefusesAnAbsentID专门断言了存在性探测先于版本守卫的次序backend/conformance/deleter_contract.go。无论哪种拒绝都不会删除任何行dry-run 亦然。文本引用改写单词边界匹配与[deleted:ID]标记删除的行会在直接相连邻居的四个长文本字段description、notes、design、acceptance_criteria中留下 ID 引用。改写规则由一个统一的 regex 定义internal/storage/issueops/delete_role.gofunc DeletedReferencePattern(id string) *regexp.Regexp { return regexp.MustCompile((^|[^A-Za-z0-9_-])( regexp.QuoteMeta(id) )($|[^A-Za-z0-9_-])) }即ASCII 单词边界上的字面 ID 才会被改写。因此be-1会被see (be-1).中的引用改写但不会命中xbe-1或be-12ID 内充满了连字符连字符也被视为单词字符这正是该模式的价值。改写的范围有明确边界只改写图邻居——即与被删行有依赖边任一方向且存活的行纯文本提到已删 ID 但无依赖边的行不动全工作区文本扫描的成本不值得每次删除承担多个已删 ID 在同一字段中会依次改写内存中的行会先写回再处理下一个 ID保证第二个 ID 看到的是第一个的改写结果internal/storage/issueops/delete_role.go。契约测试RunDeleterRewritesReferencesInNeighbors同时验证了正反两面邻居的四个字段都被改写、前缀相似的 lookalike ID如targetx不被误伤、无边陌生人文本保持原样backend/conformance/deleter_contract.go。单事务与版本控制为什么三步是不可分割的整体issueops.Deleter.Delete的文档明确承诺存在性探测、依赖者守卫、级联展开、删除与引用改写全部在一个事务中看到同一快照一起成功或一起失败issueops/deleter.go。这一设计针对的是旧实现的一个缺陷早期 CLI 直接路径在事务中删除行、然后在事务外单独改写邻居文本若两者之间崩溃就会出现行已删、描述仍按 ID 指向它们的工作区。现在的实现把这个窗口彻底关闭了。作为代价源码如实说明事务规模等于删除集加上其邻域邻居足够多时可能超过后端的写超时此时整体失败且不删除任何行——这是比行删了文本过期更好的失败方式但也是天花板删除超大集合时应拆分请求。版本控制方面一次删除记录恰好一条Dolt 提交a deletion is one act, not one per row服务端存储DoltStore在写事务内部完成DOLT_COMMITinternal/storage/dolt/deleter.go消息为bd: delete N issue(s)嵌入式存储embeddeddolt在 SQL 提交后通过第二条连接发布条目因此该承诺是稳态的而非崩溃原子的dry-run 不写任何历史。契约测试RunDeleterRecordsExactlyOneHistoryEntry用历史计数增量验证了一次删除恰好一条条目、dry-run 零条目backend/conformance/deleter_contract.go。此外删除还维护阻塞状态不变量BlockedStateInvariant被删行若曾是某存活行的唯一 blocker该存活行及其继承链会在删除事务内被同步解除阻塞不会留下被一个已不存在的 blocker 阻塞、任何动词都无法解除的死状态见 backend/conformance/deleter_contract.go 中三个RunDeleterSettles...用例。本地数据库与团队服务器proxied server的行为一致性bd delete在检测到使用了代理服务器usesProxiedServer()时会走 cmd/bd/delete_proxied_server.go 中的独立路由。两条路由调用同一个issueops.Deleter库表面因此在依赖守卫、级联、孤儿化与预览语义上完全一致四个 flag 在两条路由中含义相同历史上有过--cascade在代理路由被硬编码的阶段现已修复见 cmd/bd/delete_proxied_server.go预览会额外通过PreviewDelete读事务获取 connected issues 的标题列表先展示会删什么再展示守卫拒绝的原因若被拒绝与经典路由的呈现顺序一致JSON 输出同样不会序列化 issue 载荷且拒绝时的错误只承载在 JSON 的error键中不会在 stdout 上打印第二个文档。delete --json的输出结构单条与批量不同单条为deleted字符串dependencies_removedreferences_updated批量为deleted数组deleted_countdependencies_removedlabels_removedevents_removedreferences_updatedorphaned_issuescmd/bd/delete.go 与 cmd/bd/delete.go。测试与契约保障删除行为被如何钉死删除功能是三套实现Dolt 存储、嵌入式 Dolt 存储、unit-of-work 提供者共用一个Deleter契约由 backend/conformance/deleter_contract.go 中约二十个用例全面钉死覆盖畸形请求拒绝空 ID、空白 ID与调用方切片不被原地修改存在性探测的 all-or-nothing 语义依赖者守卫的四个象限durable↔durable、wisp↔durable 双向级联闭包、跨平面级联、孤儿化报告引用改写、dry-run 不改任何东西、历史条目恰好一条跨平面边计数与阻塞状态结算。CLI 层测试见 cmd/bd/delete_test.go包括 JSON 预览不泄漏载荷、强制 dry-run 不误删依赖者TestDeleteBatchDryRunHonorsForce、批量删除后无复活TestBulkDeleteNoResurrection、删除后依赖边被清空等。存储层还通过testAuditDeleteIssuesDryRunCounts在 backend/conformance/audit_issue-lifecycle.go 处钉死 dry-run 计数与存储 seam 的行为。常见错误与排查错误信息含义与应对no issue IDs provided未提供任何 ID补上参数或--from-fileissue id not foundID 不存在或前缀解析失败核对 ID 与仓库issues not found: a, b批量中部分 ID 无对应行整体未删除修正拼写后重试issue id has dependents not in deletion set; use --cascade to delete them or --force to orphan them存在集合外依赖者按业务需要选择--cascade或--force版本不匹配ErrVersionMismatch行在读取后被修改重新读取最新行后再决策bd delete的哲学可以概括为一句源码注释a list of ids cannot be accidentally too wide——它不像 sweepbd cleanup/bd wisp gc那样需要一个过滤器门槛来防止误删因为调用者必须亲手写下每个 ID它的安全机制全部围绕图展开拒绝、级联或孤儿化三者必居其一绝不允许静默地破坏工作区的依赖图。【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考