[控场AI]
· 9 分钟阅读· 4,708 字

并行AI编码代理留下的9类磁盘垃圾及清理命令

并行AI编码代理留下的9类磁盘垃圾及清理命令

并行AI编码代理会留下9类磁盘残留,需先查看再手动清理。

多个AI编码代理同时运行时,会留下废弃worktree、陈旧锁、无人合并的分支、隐藏refs、重复的node_modules、被忽略文件、占用端口的dev server以及膨胀的.git目录等9类残留物。每个代理只管自己的清理,但worktree之外的共享资源无人处理。文章为每类残留提供了“先查看、后删除”的两条命令:从`git worktree list`到`git clean`,从`lsof`到`git gc`。关键细节包括:Claude Code的`claude -p --worktree`不会自动释放worktree,Codex默认保留15个,git gc的延迟回收可能让空间数周后才释放。Hermes 2.2.9新增了自动清理,但仍不覆盖端口和gc。核心原则是始终先运行只读命令确认清单,再执行删除。

多个AI编码代理同时跑任务,磁盘很快就会被各种残留物塞满。Claude Code、Codex和Hermes IDE各自有清理机制,但它们管不到对方留下的东西。一位开发者在dev.to上系统梳理了9类常见的残留物,给出了先查看、后删除的完整命令清单。

核心问题在于:每个代理调用工具时创建了文件夹、分支或进程,任务结束后没有任何东西回来收拾。worktree被锁定、分支无人合并、dev server占着端口、node_modules在每个worktree里复制一份——这些东西单独看无害,积累几周后就开始吃磁盘、占端口,还让git gc无法回收本该释放的对象。

并行AI编码代理留下的磁盘残留

Worktree:文件夹、记录与锁

废弃的worktree文件夹。 先用git worktree list -v列出所有worktree及其锁定或可清理状态。Codex默认保留最近15个worktree,超出后删除旧的;Claude Code在退出未命名的交互会话时自动移除干净的worktree,命名会话则会先询问。但claude -p --worktree没有退出提示,它的worktree会一直留在磁盘上,直到后续会话的清理扫描释放它。

确认不再需要的文件夹,通过git移除,这样记录也随之清除:git worktree remove ../fix-login。Git只移除干净的worktree——没有未跟踪文件、没有修改过的已跟踪文件。不干净的worktree需要--force,被锁定的需要--force两次。这个拒绝是安全机制,先打开文件夹,提交或复制需要保留的内容,再做决定。

记录残留与陈旧锁。 用rm -rf直接删掉worktree文件夹后,git仍在$GIT_DIR/worktrees下保留记录,git worktree list会将其标记为prunable。先干跑确认,再执行清理:git worktree prune --dry-run --verbose,然后git worktree prune --verbose。git gc最终也会做这件事,但默认给三个月的宽限期。

锁是另一半问题。Claude Code在代理运行时锁定worktree,代理结束后释放。被kill的会话留下的锁要等到后续扫描才释放。git worktree list --porcelain会为每个锁显示一行locked。确认没有东西在运行时,git worktree unlock <path>可以解锁。

Worktree(工作树)是 Git 提供的一种机制,允许在同一个仓库下同时检出多个分支到不同目录。每个 worktree 拥有独立的工作目录和 HEAD,但共享同一个 .git 对象存储和引用数据库。这使得多个 AI 代理可以并行工作而互不干扰:代理 A 在 ../fix-login 修复登录问题,代理 B 在 ../feature-x 开发新功能,两者使用同一份提交历史。代价是每个 worktree 都需要自己的依赖安装、构建产物和临时文件。当代理任务结束却未清理时,这些 worktree 目录会持续占用磁盘。git worktree list 列出所有工作树,git worktree remove 安全移除,而 git worktree prune 清理已被删除的 worktree 残留的记录。理解 worktree 的共享与隔离边界,是排查代理残留的关键。

无人合并的分支与引用

已合入的任务分支。 git branch --merged main --list 'hermes/*' 'worktree-*'列出已合并的分支。在主分支已检出时用-d删除,它拒绝删除未完全合并的分支。删除分支之前,先移除它的worktree。

squash合并或rebase过的分支不会出现在这个列表里——它们的改动在main上,但分支尖端提交从main不可达,--merged会跳过它们。这类分支落入下一项。

未完成的分支。 用git for-each-ref --sort=committerdate --no-merged=main按最后提交时间排序,最旧的排在最前面,被放弃的实验就在顶部。git branch -D是强制删除,执行前先用git log main..<branch>确认没有值得保留的内容。

git branch看不到的引用。 工具可以在refs/下保留自己的数据。用git for-each-ref --format='%(refname:rstrip=-2)' | sort | uniq -c按命名空间统计。git gc会保留所有被分支、标签、reflog、索引以及“refs/*命名空间中的任何其他东西”引用的对象。一个满是快照引用的命名空间可以钉住大量旧blob。删除工具写入的引用前,先查该工具的文档——那可能是它的撤销历史。确认安全后用git update-ref -d <ref>删除。

git for-each-ref 是 Git 底层的一个引用遍历命令,比 git branch 更通用且更适合脚本化处理。git branch 只显示 refs/heads/ 下的分支,而 git for-each-ref 可以遍历所有引用命名空间,包括 refs/tags/、refs/remotes/ 以及工具自定义的命名空间(如 refs/hermes/、refs/codex/)。它的 --format 参数支持类似 %(refname:rstrip=-2) 的占位符语法,可以截断引用路径的最后一段,用于按命名空间分组统计。--sort=committerdate 按最后提交时间排序,--no-merged=main 筛选未合入主分支的引用。这些能力让 git for-each-ref 成为发现“隐藏引用残留”的核心工具,尤其是当工具在 refs/ 下写入快照或撤销历史时,只有它能完整列出。

副本:依赖与忽略文件

每个worktree里的node_modules。 新worktree只包含已跟踪文件,所以每个代理跑过install的worktree都有自己的依赖文件夹。用git worktree list --porcelain配合awk和du -sh可以统计所有worktree中node_modules的大小。对于以后还会回来的worktree,删掉node_modules成本很低——回来时重新安装即可。对于不再需要的,直接移除整个文件夹。

被忽略和未跟踪的文件。 代理和操作它们的人会在worktree里留下粘贴的截图、日志文件和笔记。git clean -nd显示未跟踪且未被忽略的文件,git clean -ndX只显示被忽略的文件。-n只展示将要删除的内容。仔细阅读-X列表:.env和node_modules也是被忽略的文件,一刀切的git clean -fdX会把它们一起带走。按路径删除更安全。

进程与.git目录

仍占用端口的dev server。 后台启动的dev server可能比启动它的代理会话活得更久,下一个worktree的服务器发现端口已被占用。用lsof -nP -iTCP -sTCP:LISTEN列出所有监听中的TCP socket。-iTCP -sTCP:LISTEN是man page自己的示例,-n和-P跳过主机名和端口名查找。

要确认进程运行在哪个文件夹、由哪个worktree启动,查询它的工作目录:lsof -a -p "$PID" -d cwd,确认是自己的进程后再终止:kill "$PID"。

只增不减的.git目录。 所有worktree共享一个对象存储,每个代理的提交都落在同一个地方。用git count-objects -vH测量:size是松散对象占用的空间,size-pack是pack文件,size-garbage是两者都不是的文件。然后运行git gc。

在执行分支删除后,gc可以丢弃不再被引用的对象,但不是立刻。默认只清理超过两周的松散对象,不可达提交的reflog条目保留30天。删除分支带来的磁盘下降可能几周后才出现。要立即回收空间,运行git reflog expire --expire-unreachable=now --all和git gc --prune=now。这会丢弃用来恢复误删提交的reflog条目,所以要在清理完成之后执行,而不是之前。

git gc(垃圾回收)是 Git 的维护命令,负责将松散对象打包、删除不可达对象、清理 reflog 和过期引用。它的回收行为有默认的宽限期:gc.pruneExpire 默认为两周,只清理超过两周的松散对象;gc.reflogExpireUnreachable 默认为 30 天,不可达提交的 reflog 条目保留 30 天。这意味着即使删除了分支,只要 reflog 还引用着旧提交,这些对象就不会被回收。要立即释放空间,必须显式运行 git reflog expire --expire-unreachable=now --all 清除所有不可达的 reflog 条目,再运行 git gc --prune=now 强制立即清理。跳过 reflog 过期步骤直接运行 git gc 通常无法达到预期效果。理解这两个时间参数,才能解释为什么删除分支后磁盘空间没有立刻下降。

工具能替你清理什么

Hermes IDE作者在文章末尾说明了自家工具的清理能力,并提醒读者“考虑到我在开发Hermes,请带着这个前提看这段”。Hermes 2.2.9修复了应用长时间运行后积累的问题:worktree中运行的任务的历史快照在会话关闭14天后从仓库移除;会话关闭时无法移除的worktree在下次启动时重试;用户选择保留的worktree不再被自动删除;一周前的粘贴截图、一个月前关闭的会话输出、14天后的落地任务撤回数据、90天后的worktree备份都会被清理。

不过2.2.9的说明没有提到端口和git gc,作者建议仍然手动执行第8和第9项。

Claude Code运行定期扫描,移除超过cleanupPeriodDays设置的子代理和后台会话worktree,跳过仍有工作内容的。Codex在删除托管worktree前保存快照,方便之后恢复该对话的工作。

这些工具各管各的,作者给出的清单覆盖的是剩下那部分。如果一个开发者同时用多家代理,每个工具清理自己的worktree,而worktree之外的残留——端口、共享对象存储、手动创建的分支——需要手动处理。

一个值得记住的原则:先运行只读命令看清单,确认无误后再执行删除。

#AI编码代理#git worktree清理#Claude Code worktree#Codex worktree#git gc#node_modules清理#dev server端口占用#Hermes IDE
分享:

相关推荐