news 2026/9/25 2:21:18

Hunk Watch Mode 实战指南:把 Terminal Diff Review 变成持续跟随源码变化的实时视图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hunk Watch Mode 实战指南:把 Terminal Diff Review 变成持续跟随源码变化的实时视图
  • 开发工具
  • 代码评审
  • CLI
  • AI 应用

【免费下载链接】hunk

Review-first terminal diff viewer for agentic coders

项目地址:https://gitcode.com/gh_mirrors/hu/hunk
点击查看免费下载

导读

Watch mode(--watch)是 Hunk 面向 Agent 化编码场景提供的一项核心工作流能力:它把一次性的 diff review 会话,升级为对不断变化的源码输入持续跟踪、自动刷新的连续视图。本指南将围绕 watch-mode.md 的官方用法展开,并结合 Hunk 仓库中packages/hunk/src/core/watch/的控制器、观察器、签名检测与 VCS 集成源码,讲清哪些输入可以被监听、底层如何以"事件提示 + 签名校验 + 周期轮询兜底"三层机制保持同步,以及r键与hunk session reload两种手动刷新方式的使用边界。读完你可以在自己的仓库里正确使用 watch mode,并理解它为什么会这样工作。

什么是 Watch Mode

Hunk 的定位是 review-first 的终端 diff 查看器(见 README.md 项目描述 "Review-first terminal diff viewer for agentic coders"),watch mode 则是把"评审一次快照"升级为"评审一条不断变化的流"。

它的核心语义可以用文档原句概括:

Watch mode turns a review into a continuous view of a changing source.

也就是说,当你用--watch启动一个 review 后,Hunk 会持续观察输入的来源(文件、补丁文件、仓库工作区或历史版本),一旦底层内容发生变化,就自动重新加载并刷新评审视图,无需你手动退出重进。这在"Agent 正在持续修改代码、评审者需要盯着最新差异"的场景下尤其有用——工作区里的文件一边被改写,diff 视图一边跟着更新。

启动一个被监听的评审

官方文档给出的最简启动方式是对工作区 diff 开启 watch:

hunk diff --watch

该命令会同时观察直接文件输入与Git-backed 输入,用于在内容变化时触发刷新;同时保留周期轮询(periodic polling)作为兜底。而对 Jujutsu(jj)与 Sapling 输入,则直接使用轮询方式(详见下文"轮询兜底"一节)。

除了工作区 diff,所有**可重新打开(reopenable)**的输入都能进入 watch mode,官方示例包括:

hunk show HEAD~1 --watch hunk diff --files before.ts after.ts --watch hunk patch changes.patch --watch

这些命令的共性在于:它们的输入在磁盘上有一个稳定、可重新读取的落点——一个 ref、两个具体文件路径、或一个补丁文件。在 CLI 参考实现 中,--watch被定义为:

export const WATCH_OPTION = { flag: "--watch", description: "auto-reload when the current diff input changes", } as const satisfies CliReferenceOption;

并且diff、show、stash show、patch、difftool等 review 命令在命令规格上都声明了watch: true(见 cli.ts),意味着它们都共享同一个--watch标志并具备自动重载能力。

哪些输入可以被监听

watch mode 有一个硬性前提:输入的来源必须能被 Hunk 再次打开。换句话说,只有"可重开"的输入才谈得上持续观察;一次性、流式的输入则不行。

官方文档明确排除了一种典型场景——stdin 输入:

# Snapshot only; --watch would fail some-command | hunk patch -

从源码看,这条规则在 resolveWatchPlan 中实现:当输入的 kind 为patch且文件参数缺失或为-(stdin)时,直接返回null,即无法构建 watch 计划:

case "patch": if (!input.file || input.file === "-") { return null; } fileTargets.push({ path: input.file, source: "content" }); break;

同理,agentContext若来自 stdin(-)也无法被监听:

if (input.options.agentContext === "-") { return null; }

为什么 stdin 不能 watch?因为 Hunk 的 watch 机制需要反复读取同一输入来计算"内容是否变化"的签名;stdin 是一次性管道,读完即尽,无法二次打开。文档给出的替代方案非常明确:把正在变化的输出落盘到文件,或者改用仓库-backed 命令(如hunk diff、hunk show),再对文件路径开启--watch。

底层同步机制:三层架构

watch mode 之所以能既灵敏又省资源,是因为它的同步不是单一的"纯事件"或"纯轮询",而是三层配合。对应的核心实现在 packages/hunk/src/core/watch/ 目录,由controller.ts(状态机控制器)、observer.ts(事件观察器)、signature.ts(签名检测)三个模块构成。

第一层:事件提示(Event Hints)

createWatchController(见 controller.ts)维护一个明确的状态机:

starting → idle → debouncing → checking → refreshing → closed

当观察器(observer)报告底层文件发生变化时,控制器进入debouncing阶段——事件只被当作"提示",不会立即触发刷新,而是先进入防抖窗口,等安静下来再校验签名。控制器提供了一组可调参数(源码中的默认值):

参数默认值作用
quietDelayMs200ms防抖窗口:事件安静这么久后才做一次检查
maximumDelayMs1000ms事件洪泛时的最大延迟上限,保证检查不会无限推迟
healthyCheckMs10000ms健康状态下的兜底轮询间隔
degradedCheckMs2000ms降级状态下的快速轮询间隔
duplicateErrorIntervalMs10000ms同一错误在窗口内的去重上报间隔
startupTimeoutMs2000ms事件源启动超时(见下)

第二层:签名校验(Signature Check)

防抖到期后,控制器调用getSignature计算当前输入的签名,与上一次应用过的签名比对:一致则什么都不做,不一致才触发 refresh(见 controller.ts)。这层校验过滤了大量"事件发生了但其实内容没变"的噪声(例如编辑器 touch 文件、无关元数据写入)。

签名由 computeWatchSignature 生成,不同输入类型采用不同签名策略:

  • diff/difftool(左右两个文件):对左右文件分别取path:size:mtimeMs:ino的 stat 签名;
  • patch(补丁文件):对补丁文件取同样的 stat 签名;
  • vcs/show/stash-show:委托给 VCS adapter 的watchSignature生成仓库态签名;
  • 若带有--agent-context,还会把该 sidecar 文件的 stat 签名拼进签名串。

第三层:周期轮询兜底(Polling Fallback)

事件监听并不是所有输入都能提供的。在 createVcsWatchPlan 中可以看到:

return handler.watchPlan?.(operation.input, context) ?? { coverage: "poll-only", targets: [] };

即:如果所选 VCS 操作的 adapter 没有实现watchPlan,就回退到poll-only轮询计划。这正是文档所说的"它轮询 Jujutsu 和 Sapling 输入"——这两类 VCS 适配器不提供文件系统事件计划,Hunk 便以轮询签名的方式来感知仓库变化。此外,当事件源因资源耗尽(ENOSPC、EMFILE)或启动超时而降级时,控制器也会把健康轮询间隔从 10s 收紧为 2s 的降级轮询,保证"事件不可用,同步仍不中断"(见 controller.ts 与L311-L325)。

观察器:原生递归与可移植树两种后端

事件从哪来?createWatchObserver 会按 watch 计划为每个目标构建底层 watcher,并根据平台选择后端(见 observer.ts):

  • macOS(darwin)与 Windows:使用 Bun/Node 的原生递归 watcher(fs.watch+recursive: true),注册完成后即视为 ready;
  • Linux 等可移植平台:使用 Chokidar 的分块树监听器(createChunkedTreeWatcher,见 portableTree.ts)。

分块树监听器的设计很讲究:它按PORTABLE_TREE_BATCH_SIZE = 32个目录一批地注册深度为零的 Chokidar watcher,批与批之间让出 macrotask,从而在遍历超大工作区时避免一次性扫完整个目录树导致的卡顿;每批 watcher 会暴露独立的 ready 边界,全部就绪后才向控制器报告整体 ready。目录的新增(addDir)会被动态登记,删除(unlinkDir)会先注销旧批次再重新收编存活的兄弟目录。

在事件进入控制器前,观察器还会做一层忽略根(ignored roots)过滤:VCS adapter 可以在 watch 计划中声明忽略的目录(例如.git),观察器通过createIgnoredRootMatcher(observer.ts)做带缓存的祖先链判断,把无关目录的写事件直接丢弃,避免无谓刷新。

运行前提:Bun 版本要求

watch mode 依赖运行时(Bun)的文件系统 watcher,而这里有一个真实的历史坑:Bun 1.3.14 之前存在 watcher 关闭时的锁反转(deadlock)问题。因此 runtime.ts 定义了:

export const MINIMUM_RELIABLE_WATCH_BUN_VERSION = "1.3.14";

assertReliableWatchRuntime会在旧版本上直接抛出用户可读错误,提示升级 Bun 或去掉--watch。这意味着:使用 watch mode 请确保 Bun ≥ 1.3.14(bun upgrade可升级)。

手动刷新:r键与hunk session reload

不是所有场景都需要常驻 watch。文档给了两种按需刷新的途径:

1. 终端内按r键

当 review 输入可重载(reloadable)时,直接按r立即刷新,无需开启持续的--watch。从测试用例看,r触发的是"软重载"(soft reload,resetApp: false):保留当前选区及其稳定 id,仅替换底层 review 文件对象(见 AppHost.extensions.test.tsx),所以刷新后你的阅读位置不会被打回原点。

2. 活体 Agent 用hunk session reload

如果评审会话运行在 Agent 会话场景中,Agent 可以通过hunk session reload命令替换会话的整个输入。其语法要求嵌套的 Hunk review 命令放在--之后(见 cli.ts 的解析实现):

hunk session reload session-1 -- show HEAD~1

若--后缺失 review 命令,CLI 会报错:"Pass the replacement Hunk command after--"(见 cli.test.ts);session reload也不允许再嵌套另一个 session 命令或非 review 命令(见 cli.ts)。reload 在桥接层对应reloadSession处理器,可携带新的输入并保留或重置应用状态(见 bridge.test.ts)。

相比r键只重载当前输入,session reload是"整个会话换血":适合 Agent 需要切换到完全不同的评审目标(例如从 diff 切到某个历史提交)的场景。

总结:选择哪种同步方式

场景推荐方式说明
Agent 持续改工作区文件hunk diff --watch事件提示 + 签名校验,变化即刷新
盯住某个历史提交/补丁hunk show HEAD~1 --watch/hunk patch changes.patch --watch输入可重开即可监听
输入来自 stdin 管道先落盘再用--watchstdin 输入无法二次打开,watch 会失败
只想偶尔手动刷新按r软重载,保留阅读位置
Agent 要整体替换会话输入hunk session reload <id> -- <review-command>换整个输入,需--分隔符
jj / sapling 仓库hunk diff --watch(轮询兜底)事件计划缺省,靠签名轮询感知变化

更深一层的机制——状态机防抖参数、忽略根过滤、原生/可移植双后端、Bun 版本门禁——都可以在 packages/hunk/src/core/watch/ 的源码与controller.test.ts、observer.test.ts、plan.test.ts等测试中继续深入研读,作为把 watch mode 集成进自己工具链时的可靠参考。

  • 开发工具
  • 代码评审
  • CLI
  • AI 应用

【免费下载链接】hunk

Review-first terminal diff viewer for agentic coders

项目地址:https://gitcode.com/gh_mirrors/hu/hunk
点击查看免费下载

相关推荐

上一篇:开源项目推荐:me_cleaner —— 为您的系统安全与隐私护航
下一篇:【亲测免费】 数据清洗利器:DataCleaner——打造高质量数据集的捷径

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 2:17:37

Rabin密码系统原理与CTF实战解密

1. 项目背景与核心价值Rabin密码系统作为首个被证明在特定条件下与整数分解问题等价的非对称加密方案&#xff0c;在CTF密码学挑战中占据着独特地位。这道来自BUUOJ平台的"坏蛋是雷宾"题目&#xff0c;巧妙地将Rabin算法的数学特性转化为需要逆向破解的暗号系统。我在…

作者头像 李华
网站建设 2026/9/25 2:17:32

de4dot实战指南:.NET反混淆、脱壳与常见坑

碰到一个加了壳或者被混淆过.NET程序集&#xff0c;第一反应基本都是掏出de4dot来试一圈。作为 .NET 逆向圈里基本上人手一份的老牌反混淆工具&#xff0c;de4dot 从一个侧面说明了 .NET 程序集在保护层面的纠结&#xff1a;CLR 设计得太透明&#xff0c;元数据和 IL 都摆在明面…

作者头像 李华
网站建设 2026/9/25 2:14:03

VC2015编译libssh-0.10.3静态库:工控遗留项目SSH通信方案

简介&#xff1a;本资源为VC2015编译的libssh-0.10.3静态库&#xff0c;面向需要在Windows平台C/C项目中集成SSH功能的开发者。libssh是开源SSH协议实现库&#xff0c;支持SSH1与SSH2&#xff0c;可完成远程登录、文件传输及加密网络服务等任务&#xff1b;静态库形式让开发者无…

作者头像 李华