你的Git日志看起来像Claude Agent的变基混战吗?

dev.to 2026-07-29T05:10:14.114816

这些情况你熟悉吗?你在同一个仓库上同时跑着三四个 Claude Code 代理。挺好的——并行运行本来就是目的。然后呢:你的 MacBook 风扇像喷气引擎一样转起来,因为两个代理在同一十秒内触发了完整构建。一个代理的推送被拒绝,它 rebase 重试——恰好落在另一个代理也在同一时间窗口完成的位置上。现在你得搞清楚到底是哪个改动最终进去了。一个测试失败,你重新跑一遍,又通过了。不是随机失败。两个代理同时重置了同一个测试数据库,它们各自的运行看起来都像坏了。你打开 Git 日志,看起来像一场争论——连续三个"Merge branch"提交,没一个有意义。你在 CLAUDE.md 里告诉每个代理"始终通过合并队列落地",结果某个代理有一次赶时间,直接 push 到了主分支。这都不是纪律问题。这是当多个快速、自信的进程共享同一个可变事物——一个分支、一个 CPU、一个数据库——而没有任何结构上的东西阻止它们互相踩踏时必然发生的事。如果你的下午就是这样的,继续往下读。或者直接看看 Claude Code 合并队列。它是一个免费的本地合并队列,专为并行 Claude Code 代理设计。零运行时依赖,不需要 PR,没有 Actions 账单。这段代码就留在代码里——FIFO 锁、git-hook 强制、配置结构。工作树(worktrees)的问题已经解决了。下面这些才是还没解决的。

Claude Code 现在原生隔离代理——一个标志,无需配置:claude --worktree <名称>。从 v2.1.49(2026 年 2 月)开始,每个会话都有自己的 Git 工作树,这样并发代理就不会互相覆盖未提交的编辑。好——那部分搞定了,如果你来是为了"如何隔离我的代理",你已经有答案了。隔离解决不了的是,当四个隔离的代理都试图同时落地、构建和测试时会发生什么。每个人都推送到同一个分支。一个人赢了,另一个人收到非快进拒绝。失败者 rebase 再推送——如果第三个代理也在同一时间窗口落地,它的重试也会陷入竞态。代理越多,问题越叠加而不是解决。一次完整构建很重。

你的 Git 日志看起来像 Claude 智能体的变基大乱斗吗?

同时跑四个任务,会把同一块 CPU、内存和磁盘折腾得狼狈不堪。如果测试还去抢共享资源——比如数据库、队列——并发跑的进程就会互相踩对方的复位操作。这时失败看起来像是“偶发故障”,其实不是,它们是明明白白的真问题。这跟智能体自身的能力无关,而是当多个快速、自信的进程共享同一个可变资源,却没有流量控制时,必然会发生的事。对它们喊“请配合一下”解决不了——一个智能体(或者一个赶时间的队友)总会在某个最不该出错的时刻,恰好违反一次文档规范,而且根本是无意的。所以别指望靠礼貌解决。要从结构上把碰撞的可能性彻底消除。

基础:一个没有超时的 FIFO 锁

这套工具里的所有东西——构建队列、落版队列——都是同一个锁扮演的两个角色。一个队列名称对应整个仓库的一个互斥锁,该仓库的所有工作树都会去抢同一个锁,所以并行的智能体线路会在这把锁后面排成一队,而不是各自以为能独占资源。同一台机器上两个不相关的仓库,彼此完全看不见对方的锁。

最值得说的一个设计决策是:所有地方都没有超时。锁不会因为过了几秒就自动释放——那种“几秒”是个魔法常量,在慢机器或大构建上早晚会出错。相反,锁会一直保持持有,直到它的主人释放它,或者直到其他等待者发现主人的进程已经不存在。检查进程是否还活着,代价很低且结果准确,所以既不需要调整“过期窗口”,也不用“假设 30 秒后进程已死,然后祈祷它真的没在运行”。这意味着,在构建中途用 kill -9 杀掉持有锁的进程,也不会卡死——下一个检查的等待者会发现进程不见了,立刻接手锁。不需要什么清除过期锁的脚本,也不用“重启笔记本再试一次”。

另一个设计点是公平性。等待者不会在锁释放的那一刻一拥而上——谁先申请,谁就先拿到锁,严格先到先得。否则,哪个智能体碰巧轮询得最快,就可能永远插在队首,让那些等了好久的智能体一直饿着。

你的 Git 日志看起来像 Claude 智能体的 Rebase 格斗俱乐部吗?

build-lockland 本质上是同一个锁的两个名字——构建永远不会与落地冲突,反之亦然。
强制执行:光靠约定,在 Claude 智能体面前根本撑不住。claude-code-merge-queue land 会把你所在的分支 rebase 到集成分支上,然后一次一个车道地、通过那把锁推上去。但“每次都从这里落地”只是个约定,一个赶时间的智能体可能跳过约定,自己手写一句 git push——而且只犯一次,偏偏在最不该犯的时候,它自己还没意识到。

所以真正的保障压根不在某个说明文件里——它藏在 git 的 pre-push 钩子中。每次 push 都会触发这个钩子,不管智能体记不记得规则。这个钩子会拦截任何直接推往集成分支(或其他受保护分支)的 push,除非这个 push 来自队列自己的落地步骤——这是唯一能翻转那个放行标志的路径。除此之外的所有直接 push 都会当场被拒绝,并且提示里会直接告诉你该执行什么命令,而不是一句模糊的警告。

同样的钩子也是你真正检查执行的地方——lint、类型检查、测试、构建,任何你在 CI 里信任的步骤都可以放进来。检查失败,push 就失败。如果你没配置检查命令,默认每一次 push 都会失败;工具不会悄悄放行未经验证的代码,它不会自作主张认为“你本意是想跳过检查”。

要绕过这一切只有一条路:一个紧急环境变量,用于真正的“砸玻璃”式 push,而不是一堆覆盖开关。老实说清楚这东西能做什么、不能做什么——它防的是失误和偷懒的捷径,而不是一个故意设置这个变量或删除钩子的恶意智能体。这里的一切都不是安全边界。它是一个协调补丁,专门对付跑得快、自信、爱忘事的智能体,而不是用来防御恶意的智能体。

为什么不用 GitHub 自带的 Merge Queue?

GitHub 本身就提供了一个 Merge Queue 功能。

你的 Git 日志看起来像 Claude 智能体的变基搏击俱乐部吗?

值得理解的是,这套工具是为一个与“一人跑几个本地智能体”完全不同的场景设计的:

如果你是一个人开发、速度优先,并且大多数 diff 的唯一审核者是你自己的测试套件,那么 PR 就成了一种没人陪跑的形式主义。而 GitHub 2026 年的定价策略(从 2026 年 3 月起,对自托管 runner 按分钟计费)正在走向更细粒度的计量,而不是减少计量。跳过 PR 不是跳过审查——而是把一个没时间逐行看完每段 diff 的人类,换成一台每次都用同样方式检查每段 diff 的机器。

最接近的已有成果及其边界

与其暗示这个领域是空白,不如直接点名——它并非空白,只是现有的组合还缺一块。

这个工具不做的事

查看原文