news 2026/9/16 22:00:43

Worktrunk 钩子(wt hook)完全指南:worktree 生命周期自动化、模板变量与安全审批

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Worktrunk 钩子(wt hook)完全指南:worktree 生命周期自动化、模板变量与安全审批

Worktrunk 钩子(wt hook)完全指南:worktree 生命周期自动化、模板变量与安全审批

【免费下载链接】worktrunkWorktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows项目地址: https://gitcode.com/GitHub_Trending/wo/worktrunk

wt hook是 Worktrunk 的钩子系统:它允许你把任意 shell 命令挂接到 worktree 生命周期的关键节点上,在wt switchwt mergewt remove以及 Worktrunk 发起的提交(wt step commitwt step squash)中自动执行,也可以随时通过wt hook <type>手动触发。本文以官方文档 hook.md 为骨架,结合仓库源码(hooks.rs、hook_plan.rs、hook_commands.rs、expansion.rs 等)深入讲解五种事件的 pre/post 钩子、三种 TOML 配置形态、完整的模板变量体系、安全审批模型,以及过滤器、函数和手动调用方法。读完你将能够为并行 AI Agent 工作流配置出"创建即装依赖、合并即部署、删除即清理"的完整自动化链路。

Hook 类型总览:十个钩子、五种事件

钩子按生命周期事件分为五组,每组都有pre-(阻塞)与post-(后台)两个变体:

事件pre-(阻塞)post-(后台)
switchpre-switchpost-switch
createpre-startpost-start
commitpre-commitpost-commit
mergepre-mergepost-merge
removepre-removepost-remove

两种变体的执行模型完全不同:

  • pre-*钩子阻塞命令:在操作的关键节点前同步运行,失败即中止整个操作(fail-fast)。例如pre-merge中的测试失败会让合并终止。
  • post-*钩子在后台运行:输出写入日志文件,可通过wt config state logs查找和管理日志(详见 config.md)。wt hook <type> --foreground可以让某个后置钩子改在前台运行一次,使输出直接到达终端。加-v可以看到后台钩子的模板变量;wt hook <type> --dry-run可以预览将要执行的命令。

最常用的创建钩子是post-start——它在后台运行开发服务器、文件复制、构建等任务,不阻塞 worktree 的创建。除非后续步骤必须等它完成,否则优先用post-start而不是pre-start

各钩子的用途如下:

钩子用途
pre-switch在切换前的源 worktree 中运行——覆盖创建、切换到已存在 worktree、或停留在当前三种情况
post-switch在所有切换结果(创建、切换到已存在、停留当前)后触发
pre-start新 worktree 创建时运行一次,阻塞post-start/--execute直到完成:依赖安装、env 文件生成
post-start新 worktree 创建时运行一次,后台执行:开发服务器、长构建、文件监听、缓存复制
pre-commit格式化、lint、类型检查——在任何 Worktrunk 提交(wt step commitwt step squash以及wt merge产生的提交)之前运行
post-commitCI 触发、通知、后台 lint
pre-merge测试、安全扫描、构建验证——在 rebase 之后、合并到目标分支之前运行
post-merge部署、通知、安装更新后的二进制。如果目标分支有 worktree 则在其中运行,否则在主 worktree 运行
pre-removeworktree 删除前的清理:保存测试产物、备份状态。在即将被删除的 worktree 中运行
post-remove停止开发服务器、移除容器、通知外部系统。模板变量指向已删除的 worktree

merge 的钩子执行顺序

在一次wt merge期间,阻塞钩子按如下顺序运行:

pre-commit → pre-merge → pre-remove

合并完成后,所有post-*钩子一起启动,各自锚定在自己的 worktree 上——post-mergepost-switchpost-remove在目标 worktree,post-commit在提交产生所在的 worktree。如果合并恰好删除了该 worktreepost-commit会被报告为 skipped 而不是执行——因为钩子启动时 worktree 已经不存在了。需要在删除前完成的工作请使用pre-remove,或者用--no-remove保留 worktree。完整管线见 merge.md。

这一"锚定 worktree 已被删除则丢弃后台管线"的行为在源码中有明确实现:HookAnnouncer::mark_worktree_removed记录被删除的 worktree,flushrun_hooks_background会先按锚点路径 partition 掉这些管线,并打印Skipped {hook_type}: {summary} — worktree removed警告与use pre-remove提示(见 hooks.rs)。源码注释指出:锚点 worktree 被清空后,若仍把管线 spawn 进去,仓库发现逻辑会向上回溯解析到主 worktree,让任意项目代码跑在用户从未选择的 worktree 中——因此"丢弃并报告"是刻意的安全设计。

安全模型:项目命令的首次审批

项目钩子(来自仓库内的.config/wt.toml)是随仓库分发的任意代码,在首次运行时需要审批。Worktrunk 把审批门禁放在命令执行之前,典型提示如下:

▲ repo needs approval to execute 3 commands: ○ pre-start install: npm ci ○ pre-start build: cargo build --release ○ pre-start env: echo 'PORT={{ branch | hash_port }}' > .env.local ❯ Allow and remember? [y/N]

要点:

  • 审批结果保存到~/.config/worktrunk/approvals.toml
  • 命令一旦变更,需要重新审批
  • 拒绝会跳过该次操作的所有项目命令(包括已经批准过的),继续执行其余部分;已保存的审批不受影响
  • 使用--yes跳过提示——适合 CI 与自动化场景
  • 使用--no-hooks完全跳过钩子——该选项被运行钩子的命令接受(wt switchwt mergewt removewt step commitwt step squash),但wt hook本身不接受

审批可通过wt config approvals addwt config approvals clear管理。

源码级的安全保障:冻结的审批计划

从源码结构看,审批与执行之间有一个刻意的"冻结"设计。操作驱动的钩子(pre-mergepost-mergepre-removepost-removepost-switchpre-startpost-start)在状态变更(如 rebase 可能重写 feature 分支自己的.config/wt.toml之前做审批,在变更之后执行。若执行时二次读取磁盘配置,变更后的配置可能选出未审批的命令——在全新 clone 上这就是远程代码执行(TOCTOU 竞态)。

ApprovedHookPlan(hook_plan.rs)从结构上堵死这个缺口:审批门禁只做一次命令选择并冻结为计划,执行器只消费这份冻结值,不持有ProjectConfig、不再次调用load_project_config()——重新派生在编译层面就不可能。HookPlan::approve负责交互式/--yes门禁,approve_readonly用于无法弹窗的 picker 与wt step prune(只运行已审批命令,未审批的项目管线被丢弃)。模块内的approved_plan_lookup_is_frozen_and_anchor_scoped等测试锁定了这一不变量。

另一类钩子(pre-commitpost-commitpre-switch、手动wt hook <type>、别名)属于"调用时解析":门禁与执行之间没有任何东西变更.config/wt.toml,且执行器通过同一个Repository实例读取(项目配置被缓存在OnceCell中,二次读取是缓存命中),因此重读是安全的(hooks.rs 模块文档)。

配置:位置与三种钩子形态

钩子可以定义在项目配置(.config/wt.toml)或用户配置(~/.config/worktrunk/config.toml)中,两者格式完全相同。项目配置从命令所在 worktree读取——即wt运行的那个 worktree(源码注释强调:无论钩子"关于"哪个 worktree,命令都从调用方 worktree 的.config/wt.toml选择,与wt config show读取的是同一个文件)。

钩子根据其 TOML 形状有三种形态。

字符串 = 单条命令:

pre-start = "npm install"

表 = 多条命令并发运行:

[post-start] server = "npm run dev" watch = "npm run watch"

管线 = 一系列按顺序执行的[[hook]]。每个块是一个步骤;块内的多个 key 并发运行。某个步骤失败会中止后续步骤:

[[post-start]] install = "npm ci" [[post-start]] build = "npm run build" server = "npm run dev"

这里install先运行,然后buildserver一起运行。

源码层面,这三种形态由CommandConfig的反序列化器处理(commands.rs):字符串 → 单个HookStep::Single(unnamed);表 → 单 key 折叠为Single(named)、多 key 生成HookStep::Concurrent;数组 → 每个元素一个步骤。内部模型HookStep只有两种:Single(串行)和Concurrent(并行)。值得注意的两个细节:

  • 命令名不能包含冒号validate_no_colons),否则会破坏日志文件名的解析与user:/project:过滤语法;
  • 空表(如所有命令都被注释掉的[post-switch])必须产生零个步骤,否则后台执行路径在空 vec 上索引会 panic——commands.rs 中有针对 issue #2634 的回归测试。

模板预览语义

模板在管线启动前做语法检查,在每个步骤运行时渲染——因此一个步骤可以把 per-branch 变量(wt config state vars,见 config.md)存下来,供后续步骤通过{{ vars.<key> }}读取。由于更早的步骤仍可能修改这些值,预览时用占位引用代替解析:wt hook <type> --dry-runwt hook show --expanded会把{{ vars.thing | default('none') }}渲染成{{ vars.thing }}——引用是"已定义"的,所以default永远不触发——而其他所有变量正常展开。对输入做变换的过滤器仍会运行(作用于占位文本),其输出与其他值一样做 shell 转义:{{ vars.thing | upper }}预览为'{{ VARS.THING }}'

大多数钩子不需要[[hook]]块。当存在依赖链时才用——典型场景是先装依赖、再并发跑构建与开发服务器这类"必须先完成前置步骤"的 setup。

项目钩子与用户钩子

方面项目钩子用户钩子
位置.config/wt.toml~/.config/worktrunk/config.toml
作用域单个仓库所有仓库(或按项目限定,见 config.md 中的用户级项目特定设置)
审批需要不需要
执行顺序pre-*:在用户钩子之后。post-*:与用户钩子并行pre-*:最先。post-*:与项目钩子并行

当用户与项目都定义了同名钩子时,可用user:nameproject:name语法指定运行哪一个(过滤解析见 hook_filter.rs,支持user:/project:裸前缀、user:foo/project:foo具名过滤)。

并发语义需要特别注意pre-*钩子阻塞命令,两个来源合为一条管线——用户命令先跑,其失败会跳过项目钩子。post-*钩子在后台运行,每个来源是各自独立的分离管线——它们同时启动、互不等待,一方失败不影响另一方继续运行。来源内部的顺序仍由你的[[hook]]块决定;post-*跨来源则没有任何顺序。两个post-*钩子如果写同一个文件、或在同一个 worktree 里跑git,会互相竞争——所以互相依赖的命令应放在同一个来源中。源码中into_source_groups把用户步骤与项目步骤分成独立管线(HookAnnouncer::add_groupsHookPlanBuilder::add中稳定排序保证 User 条目始终在 Project 之前),正是"用户钩子失败不得中止项目钩子"这一约束的结构化实现。

模板变量

钩子可以使用在运行时展开的模板变量:

类别变量描述
active{{ branch }}分支名;分离头(detached)worktree 中未定义
{{ worktree_path }}worktree 路径
{{ worktree_name }}worktree 目录名
{{ commit }}分支 HEAD SHA
{{ short_commit }}core.abbrev缩写的分支 HEAD SHA
{{ upstream }}分支上游(若跟踪远程分支)
operation{{ base }}基础分支名(仅 switch/create)
{{ base_worktree_path }}基础 worktree 路径
{{ target }}目标分支名
{{ target_worktree_path }}目标 worktree 路径(目标有 worktree 时)
{{ pr_number }}PR/MR 编号(switch 与 create 钩子;通过pr:N/mr:N切换时)
{{ pr_url }}PR/MR 网页地址(switch 与 create 钩子;通过pr:N/mr:N切换时)
repo{{ repo }}仓库目录名
{{ repo_path }}仓库根目录的绝对路径
{{ owner }}主远程的 owner 路径(可能含子组)
{{ remote_repo }}主远程 URL 中的仓库名(不含.git
{{ primary_worktree_path }}主 worktree 路径
{{ default_branch }}默认分支名
{{ remote }}主远程名
{{ remote_url }}远程 URL
exec{{ cwd }}钩子命令运行的目录
{{ hook_type }}正在运行的钩子类型(如pre-startpre-merge
{{ hook_name }}钩子命令名(如有命名)
{{ args }}从 CLI 转发的令牌——见下文"手动运行钩子"
user{{ vars.<key> }}来自wt config state vars的 per-branch 变量(见 config.md)

repo类变量(reporepo_pathownerremote_repoprimary_worktree_pathdefault_branchremoteremote_url)在整个仓库内恒定——default_branch在每个 worktree 中相同。active类变量(branchworktree_pathworktree_namecommitshort_commitupstream)则随 worktree 变化。

裸变量视角:base 与 target

裸变量(branchworktree_pathcommit)指向操作作用的分支:switch/create 时是目标,merge/remove 时是来源。basetarget给出另一侧:

操作裸变量basetarget
switch/create目标你从哪里来= 裸变量
commit(merge/squash 期间)被 squash 的 worktree= 裸变量集成目标
merge被合并的 feature= 裸变量合并目标
remove被删除的分支= 裸变量你最终所在之处

所有钩子共享同一视角——{{ branch | hash_port }}post-startpost-remove中产生相同的端口。这在源码中由TemplateVars统一组装:for_post_switch根据SwitchResult的 Created/Existing/AlreadyAt 三种结果填充 base/target,with_pr负责把 PR/MR 身份从调用参数(而非操作结果)附加到上下文中(template_vars.rs)。

cwd 的三个例外

cwd是钩子命令运行的 worktree 根目录,通常等于worktree_path,但有三个例外:

  • pre-switch:钩子在worktree 运行;worktree_path在目标 worktree 已存在时指向目标——而"创建型切换"尚无目标目录,此时worktree_path停留在源(要在新 worktree 中干活请用pre-start
  • post-remove:活动 worktree 已消失,钩子在主 worktree 运行
  • post-merge:钩子在目标分支的 worktree 运行(目标无 worktree 时为主 worktree),包括--no-remove场景——此时worktree_path指向的已合并 worktree 仍在磁盘上

未定义变量与条件渲染

未定义变量会报错——可选行为请使用条件或默认值:

[pre-start] # 若跟踪远程分支则 rebase 到上游(例如 wt switch --create feature --base origin/feature) sync = "{% if upstream %}git fetch && git rebase {{ upstream }}{% endif %}"

分离头 worktree 不在任何分支上,所以branch在那里未定义——由它派生的base/target名同样未定义,无论手动wt hook还是操作涉及分离头 worktree 都是如此(从一个分离头 worktree 触发的pre-switchbase、落到分离头 worktree 的删除的target),同样的{% if branch %}守卫适用。这与wt list --format=json对该 worktree 报告branch: null一致。

-v运行任何触发钩子的命令,可以看到本次调用的已解析变量——每个钩子打印一个template variables:块,列出每个在作用域内的变量及其值(条件变量未填充时显示(unset),如wt switch -期间的target_worktree_path)。别名在-v下也如此:wt -v <alias>在管线运行前打印该别名的在作用域变量。手动调用场景下,方向性变量由build_manual_hook_template_vars提供合理的当前 worktree 默认值(当前分支同时作为 base 与 target,见 hook_commands.rs)。

点访问与 default 过滤器

变量支持点访问与default过滤器处理缺失键。JSON 对象/数组值会自动解析,因此当值为{"port": 3000}{{ vars.config.port }}可用:

[post-start] dev = "ENV={{ vars.env | default('development') }} npm start -- --port {{ vars.config.port | default('3000') }}"

Worktrunk 过滤器

模板支持 Jinja2 过滤器来变换值:

过滤器示例描述
sanitize{{ branch \| sanitize }}/\替换为-
sanitize_db{{ branch \| sanitize_db }}数据库安全标识符,带哈希后缀([a-z0-9_],最长 48 字符)
sanitize_hash{{ branch \| sanitize_hash }}文件系统安全名称,带哈希后缀保证唯一
hash{{ branch \| hash }}输入的 3 字符 base36 摘要
hash_port{{ branch \| hash_port }}哈希到 10000-19999 端口
dirname{{ repo_path \| dirname }}去掉最后一个路径分量(/a/b/c/a/b
basename{{ repo_path \| basename }}只保留最后一个路径分量(/a/b/cc
codename(n){{ branch \| codename(2) }}确定性的友好单词

这些过滤器在 expansion.rs 中注册到模板环境(env.add_filter),并与worktree_path_of_branch函数一同暴露给所有模板。

各过滤器的细节:

  • sanitize_db产生数据库安全标识符——小写字母数字加下划线、不以数字开头,并附 3 字符哈希后缀避免冲突与保留字。其实现(sanitize_db)按序执行:转小写 → 非字母数字替换为_→ 折叠连续下划线 → 数字开头加_前缀 → 追加基于原始输入的 3 字符哈希 → 总长截断为 48 字符(远低于 PostgreSQL 的 63 字符标识符上限,为拼接路径/标识符留出余量)。
  • sanitize_hash产生文件系统安全名称,并在净化确实改变了输入时追加 3 字符哈希后缀,因此不同的原始值永不碰撞——本就安全的名称原样通过。它调用crate::path::sanitize_for_filename(path.rs)。
  • codename(n)从输入字符串产生确定性友好名称:codename(1)返回一个名词,codename(2)返回形容词-名词,更多数量则追加更多形容词。词库很大(codename(2)约 126 万种组合),因此它通常可单独作为 worktree 叶子名——config.md 的 worktree-path 模板配方展示了它单独使用与放在分支命名父目录下两种用法。实现基于petname::Petnames::medium()(1198 个形容词、1052 个名词),哈希经 SHA-256 并固定宽度取模,保证不同架构、32/64 位构建产生相同结果。
  • hash是裸的 3 字符 base36 摘要(short_hash,46,656 个唯一值),适合在输出预算紧张时自行组合"截断+防碰撞"配方(例如 Unix socket 路径上限 107 字节):
# 截断的分支名 + 哈希:前缀相同也不会碰撞 worktree-path = "/tmp/{{ (branch | sanitize)[:20] }}_{{ branch | sanitize | hash }}"
  • dirname/basename用于遍历路径。它们对位于隐藏目录(如myproject/.git)中的 bare 仓库特别有用,此时{{ repo }}解析为.git
# 把 worktree 放在 bare 仓库旁边,命名为 <wrapper>.<branch> worktree-path = "{{ repo_path }}/../{{ repo_path | dirname | basename }}.{{ branch | sanitize }}"
  • hash_port用于让每个 worktree 的开发服务器跑在唯一端口上:
[post-start] dev = "npm run dev -- --host {{ branch }}.localhost --port {{ branch | hash_port }}"

实现(string_to_port)对输入做哈希后映射到 10000-19999 区间。可以哈希任意字符串,包括拼接结果:

# 每个 repo+branch 组合唯一端口 dev = "npm run dev --port {{ (repo ~ '-' ~ branch) | hash_port }}"

变量会自动做 shell 转义——{{ ... }}外面不需要引号,加引号反而可能因特殊字符出问题。

Worktrunk 函数

模板还支持函数做动态查询:

函数示例描述
worktree_path_of_branch(branch){{ worktree_path_of_branch("main") }}查询某分支 worktree 的路径

worktree_path_of_branch给定分支名返回其 worktree 的文件系统路径;该分支没有 worktree 时返回空字符串。它适合引用其他 worktree 中的文件(源码中通过Repository::worktree_for_branch实现,返回的原始路径在输出时统一做 shell 转义):

[pre-start] # 从 main worktree 复制配置 setup = "cp {{ worktree_path_of_branch('main') }}/config.local {{ worktree_path }}"

JSON 上下文:模板表达不了的复杂逻辑

钩子会把全部模板变量以 JSON 形式通过 stdin 传入,从而支持模板无法表达的复杂逻辑。模板中未设置的变量在 JSON 中同样缺失,因此读取可选变量时要给默认值——分离头 worktree 中branch就没有默认值:

[pre-start] setup = "python3 scripts/pre-start-setup.py"
import json, sys, subprocess ctx = json.load(sys.stdin) if ctx.get('branch', '').startswith('feature/') and 'backend' in ctx['repo']: subprocess.run(['make', 'seed-db'])

复制未跟踪文件:wt step copy-ignored

有一个命令值得单独说明:wt step copy-ignored。Git worktree 共享仓库,但不共享未跟踪文件,而这个命令可以把 gitignored 文件在 worktree 之间复制:

[post-start] copy = "wt step copy-ignored"

配合[[post-start]]管线,可以"复制完再启动依赖它的后续步骤"(详见 tips-patterns.md 的冷启动消除配方)。

手动运行钩子

wt hook <type>按需运行钩子——适合开发中测试、CI 管线中运行、或失败后重跑。

$ wt hook pre-merge # 运行所有 pre-merge 钩子 $ wt hook pre-merge test # 从两个来源运行名为 "test" 的钩子 $ wt hook pre-merge test build # 运行名为 "test" 和 "build" 的钩子 $ wt hook pre-merge user: # 运行所有用户钩子 $ wt hook pre-merge project: # 运行所有项目钩子 $ wt hook pre-merge user:test # 只运行用户的 "test" 钩子 $ wt hook pre-merge --yes # 跳过审批提示(用于 CI) $ wt hook pre-start --branch=feature/test # 覆盖一个模板变量 $ wt hook pre-merge -- --extra args # 把令牌转发进 {{ args }}

user:project:前缀按来源过滤。单独使用user:project:运行该来源的全部钩子,或用user:name/project:name运行特定钩子(解析规则见 hook_filter.rs)。若过滤器没有匹配到任何命令,会报错并列出该来源作用域内可用的钩子名(HookCommandNotFound/HookSourceNotConfigured,见 hooks.rs 的no_matching_commands_error)。

运行输出示例:

$ wt hook pre-merge ◎ Running pre-merge project:test cargo test Finished test [unoptimized + debuginfo] target(s) in 0.12s Running unittests src/lib.rs (target/debug/deps/worktrunk-abc123) running 18 tests test auth::tests::test_jwt_decode ... ok test auth::tests::test_jwt_encode ... ok test auth::tests::test_token_refresh ... ok test auth::tests::test_token_validation ... ok test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s ◎ Running pre-merge project:lint cargo clippy Checking worktrunk v0.1.0 Finished dev [unoptimized + debuginfo] target(s) in 1.23s
$ wt hook post-start ◎ Running post-start: project @ ~/acme

后台钩子的来源独立性在这里同样成立:run_hook的默认路径让每个来源成为独立管线(用户钩子失败不中止项目钩子);而带名字过滤的路径(如wt hook pre-merge test build)会把用户+项目匹配项合并成一条管线——因为你按名字跨来源挑选了具体命令(hook_commands.rs)。若某钩子类型没有配置任何命令,wt hook <type>会打印No <type> hooks configured警告并以成功退出——脚本可以无条件调用wt hook <type>而无需特判空配置。

传值:--KEY=VALUE 的智能路由

--KEY=VALUE会把KEY绑定到该钩子任何命令中出现的{{ KEY }}——与wt <alias>使用的智能路由规则相同。内置变量也可以覆盖:--branch=foo设置钩子模板内的{{ branch }}(worktree 实际分支不会移动)。键中的连字符变成下划线:--my-var=x设置{{ my_var }}(源码中parse_shorthand_token做了这个规范化)。

任何--KEY=VALUE,若其键没有被钩子模板引用,就会作为字面--KEY=VALUE令牌转发进{{ args }}--之后的令牌也原样转发进{{ args }}{{ args }}渲染为空格连接、逐元素 shell 转义的字符串;可用{{ args[0] }}索引、{% for a in args %}…{% endfor %}循环、{{ args | length }}计数。源码中args以 JSON 编码的序列注入模板上下文,expand_template把它还原为ShellArgs,从而支持上述全部操作(hook_commands.rs)。

长形式--var KEY=VALUE已弃用但仍有支持:它无条件强制绑定,无论是否有钩子模板引用KEY——当模板只在条件分支里引用键时(如{% if override %}…{% endif %})有用。使用时会打印弃用警告。

实战配方

以下配方可在 tips-patterns.md 中找到完整展开:

  • 消除冷启动post-start中执行wt step copy-ignored共享构建缓存与依赖;当后续钩子依赖复制结果时,用[[post-start]]管线
  • 每 worktree 一个开发服务器post-start中执行wt step tether启动开发服务器,并在 worktree 被删除时杀掉其整个进程组,可选子域路由
  • 每 worktree 一个数据库post-start管线把容器名、端口、连接串存为 per-branch 变量(wt config state vars,见 config.md),后续钩子引用
  • 渐进式校验:快速 lint/typecheck 放pre-commit,昂贵测试与构建放pre-merge
  • 目标特定钩子post-merge中基于{{ target }}分支,实现按环境的部署

命令参考

wt hook - Run configured hooks Usage: wt hook [OPTIONS] <COMMAND> Commands: show Show configured hooks pre-switch Run pre-switch hooks post-switch Run post-switch hooks pre-start Run pre-start hooks post-start Run post-start hooks pre-commit Run pre-commit hooks post-commit Run post-commit hooks pre-merge Run pre-merge hooks post-merge Run post-merge hooks pre-remove Run pre-remove hooks post-remove Run post-remove hooks Options: -h, --help Print help (see a summary with '-h') Global Options: -C <path> Working directory for this command --config <path> User config file path --config-set <toml> Override config with inline TOML, e.g. --config-set list.full=true (repeatable) -v, --verbose... Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to apply the same level everywhere — including shell completion, which no flag can reach -y, --yes Skip approval prompts

wt hook show可以在分页器中列出已配置钩子(用户与项目分区展示,标注(requires approval)),支持--expanded渲染实际命令预览,以及--format=json输出结构化记录(每条含 type、source、name、template、needs_approval、可选的 expanded 字段,见 hook_commands.rs)。

延伸阅读

  • 配置总览与wt config state logswt config state vars:config.md
  • 合并管线的完整钩子顺序:merge.md
  • 切换与创建流程中的钩子:switch.md
  • 删除流程中的钩子:remove.md
  • wt step copy-ignored与 step 系列命令:step.md
  • 钩子实战配方:tips-patterns.md
  • 钩子配置解析(三种 TOML 形态):src/config/commands.rs
  • 钩子执行与审批门禁:src/commands/hooks.rs、src/commands/hook_plan.rs
  • 模板过滤器与函数实现:src/config/expansion.rs

【免费下载链接】worktrunkWorktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows项目地址: https://gitcode.com/GitHub_Trending/wo/worktrunk

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

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

工业CT逆向工程:无损获取内外三维模型的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 21:59:28

电力系统接地技术全解析:从接地制式到接地电阻与施工运维

接地这东西&#xff0c;在电力系统里实在太容易被忽略了。我见过不少刚入行的同事&#xff0c;一听“接地”就觉得简单——不就是往地里砸根铜棒、焊条扁钢吗&#xff1f;直到有一次亲眼看见一台设备外壳带电&#xff0c;万用表量出来对地一百多伏&#xff0c;几个人围着排查半…

作者头像 李华
网站建设 2026/9/16 21:57:26

大模型system prompt泄露:从工程误判到防御体系构建

1. 项目概述&#xff1a;这不是“泄露”&#xff0c;而是系统提示词设计失范的集体暴露最近在多个技术社区、AI产品讨论组和内部研发群聊里&#xff0c;“system_prompts_leaks”这个短语高频出现&#xff0c;不是作为某个具体漏洞编号&#xff0c;而更像一个现象级标签——它指…

作者头像 李华
网站建设 2026/9/16 21:57:13

AU-48双麦语音模组:边缘端实时语音处理硬件架构解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 21:57:05

Vibe-Coding时代,手写代码为何仍是核心竞争力?

Vibe-Coding这个词最近在圈子里聊得特别凶&#xff0c;身边好几个朋友都在用AI写代码&#xff0c;有的甚至夸张到一天堆出上千行。我上个月也认真试了一个星期&#xff0c;白天跟模型聊需求&#xff0c;晚上跟它讨论改bug&#xff0c;结果到了周五&#xff0c;一个看起来特别完…

作者头像 李华
网站建设 2026/9/16 21:54:41

TCP连接重置错误10054深度解析与复现

1. 项目概述&#xff1a;这不是一个“报错截图”&#xff0c;而是一次对网络通信底层断裂的解剖你有没有在调试一个看似稳定的TCP客户端时&#xff0c;某天突然收到一条WSARecv failed: 10054或Connection reset by peer&#xff1f;不是连接超时&#xff0c;不是拒绝连接&…

作者头像 李华