# hunk 追踪 · 怎么改文件而不踩掉你的改动

> agent 在改文件，你可能同时也在改。grok 用 xai-hunk-tracker 做「行级、带归因」的改动追踪：把每处改动记成一个带作者标签的 hunk，拒绝时只回滚那几行、绝不用整份旧快照覆盖你的编辑。这一章拆它的 actor、归因与防覆盖。

- 作者：David（道雾轩）
- 专栏：Grok Build 源码解读（https://daiw.org/manual/grok-build.md）
- 最后更新：2026-09-13
- 原文：https://daiw.org/manual/grok-build/hunk-tracker
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# hunk 追踪 · 怎么改文件而不踩掉你的改动

一个会自己动手改代码的 agent，有个隐蔽的危险：它读了文件、想了半天、然后写回去——可**这中间你也改了这个文件**。它一写，你的改动就没了。grok 用 `xai-hunk-tracker` 专门防这件事，做法很讲究。

## 又一个 actor，追踪「hunk」

`HunkTrackerActor`(还是那套：独占 task + mpsc 命令 + oneshot 查询，无锁)追踪的单元叫 **hunk**——**基线**(git HEAD 内容)与**当前**内容之间一段连续的行级改动，由 `diff.rs` 基于 `similar` crate 算出。每个 hunk 带：

- 一个稳定的 `HunkId`;
- 一个 **`HunkSource`** ——这是灵魂：`AgentEdit{prompt_index}`(agent 改的)/ `ExternalEditOnAgentFile`(agent 碰过的文件被外部改了)/ `External`(纯外部)。

## 防覆盖：靠归因 + 行级写，不靠锁

grok **不锁文件**。它的「不踩掉你改动」是一套**行级、带归因**的设计：

1. **检测并发编辑**：`handle_file_change` 读盘，发现一个 agent 文件被外部改了，就把那处记成 `ExternalEditOnAgentFile` 的 hunk——**浮出来给你 review**，而不是默默吸收或覆盖。
2. **归因存续**：`recompute_hunks` 里，新 hunk 会继承匹配的旧 hunk 的 `HunkId`；若「新 hunk 是外部的、但最佳匹配是 agent 的」，agent 归因**存续**下来，同时把触发者转发给 LOC 记账——既不冤枉人，也不丢历史。
3. **拒绝时行级回滚**(真正的防覆盖保证，`actor/actions.rs`)：拒绝一处改动时，**绝不写整份旧快照**，而是 `patch_lines` 只针对那个 hunk 的行区间、打在**刚读到的最新 `current_content`** 上(按行倒序打避免偏移)，再写盘。**文件里别处你的改动纹丝不动。**

<Callout type="info">
  对比一下天真的做法：「读文件 → 内存里改 → 整份写回」。只要你在它读之后、写之前动过这文件，整份写回就把你的改动抹了。grok 用「行级 patch 打在最新内容上」把这个窗口关掉了——这是生产级编辑工具和玩具编辑工具最实在的差距之一。
</Callout>

## 接受 / 拒绝的语义

- **接受**：基线在内存里打上这处改动、`baseline_accepted=true`、hunk 移除——**不写盘**(文件本来就是你要的样子)。
- **拒绝**：文件按基线**回滚那几行**；特例 `RestoreDeleted` / `DeleteCreated`(删掉新建的文件)。

## 避免「幻影 diff」与 Git 集成

不是所有文件都能 diff。符号链接(lstat)、Git-LFS 指针(认前缀)、二进制(前 8000 字节有 NUL)、超过 1 MiB 的 `TooLarge`——统统归为**不可 diff**，清掉 hunk、改查 git dirty 缓存。Git 这一侧用的是**纯 Rust 的 `gix`**(不是 `git2`)，所有 gix 调用丢进 `spawn_blocking`，基线批量读用 `JoinSet` 并行(上限 64)。

## LOC 归因：算清谁写了多少行

`loc/` 这个 sink 把 hunk 事件转成「谁改了多少行」的 JSONL 记录(`Agent` vs `Human`)。妙在**拒绝/被取代时会写一条负记录**，让基于 `SUM` 的统计自动归零——这样「agent 写了又被拒」不会虚增它的产出。

## 和《从 LLM 到 Coding Agent》对照

教学最小实现里，「编辑文件」通常就是 `write(path, new_content)`。grok 的增量全在「多方并发改同一个文件」这个真实困境上：行级追踪、作者归因、拒绝时只回滚自己那几行。它回答的是一个玩具永远不会遇到、产品却天天遇到的问题——**怎么让 AI 大胆改代码，又不让它把人的活儿冲掉**。

---

上一篇：[三种编辑格式](https://daiw.org/manual/grok-build/edit-formats) · 下一篇：[Shell 执行与 PTY](https://daiw.org/manual/grok-build/shell-and-pty)
