news 2026/9/16 17:29:34

lefthook run 命令实战指南:手动触发 Git Hook、按 Job/Tag 筛选与文件模板覆盖

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
lefthook run 命令实战指南:手动触发 Git Hook、按 Job/Tag 筛选与文件模板覆盖

lefthook run 命令实战指南:手动触发 Git Hook、按 Job/Tag 筛选与文件模板覆盖

【免费下载链接】lefthookFast and powerful Git hooks manager for any type of projects.项目地址: https://gitcode.com/GitHub_Trending/le/lefthook

lefthook run是 lefthook 中最核心的执行命令:它负责加载 lefthook 配置,取出某个 hook 名下配置的全部命令与脚本并执行。安装到.git/hooks/下的 Git hooks 在触发时(如git commitgit push)也是隐式调用lefthook run <hook-name>来完成工作的。读完本文,你将掌握lefthook run的全部用法——从最基本的"手动跑一遍某个 hook",到用--job/--tag精准筛选任务、用--all-files/--file覆盖文件模板,并理解其底层执行链路(配置加载、命令替换、并行/串行调度)。

lefthook run是什么

官方文档的定义非常精炼:执行某个给定 hook 名下配置的命令和脚本(Executes the commands and scripts configured for a given hook)。安装后的 Git hooks 会隐式调用lefthook run,因此它既是手动调试的入口,也是 Git 触发场景下真正干活的那个进程。

典型配置与使用流程

假设项目根目录下有一份 lefthook.yml 风格的配置:

# lefthook.yml pre-commit: jobs: - name: lint run: yarn lint --fix {staged_files} test: jobs: - name: test run: yarn test

先安装 hook:

$ lefthook install

然后即可用lefthook run手动触发,或直接通过 Git 命令触发:

$ lefthook run test # 运行 'yarn test' $ git commit # 触发 pre-commit hook(运行 'yarn lint --fix') $ lefthook run pre-commit # 运行 pre-commit hook(同样执行 'yarn lint --fix')

值得注意的是,配置中test并非标准的 Git hook 名称,但它依然可以被lefthook run test手动触发——这说明lefthook run接受任意 hook 名,只要它在配置中被定义即可。而标准的 Git hooks(如pre-commit)既会被 Git 隐式调用,也可以手动运行。

从源码看,这一判断位于 internal/command/run.go 的resolveHook中:若传入的 hook 名不在配置里,且该名字属于 lefthook 已知的 Git hooks 列表(config.KnownHook),则打印 debug 日志并静默跳过;否则直接返回错误hook <name> doesn't exist in the config

为什么需要手动运行

手动运行的价值在于:

  • 快速调试:改完lefthook.yml不必真的去git commit,直接lefthook run pre-commit即可验证;
  • 非 Git 场景触发:自定义 hook 名(如上面的test)不会由 Git 自动触发,只能靠手动lefthook run test
  • 带参数验证:可以在命令行追加 Git 参数,模拟真实的 commit/push 场景。

只运行特定的 Job(--job--tag

当某个 hook 名下配置了多个 job 时,默认全部执行;你可以用--job--tag精确挑选:

$ lefthook run pre-commit --job lints --job pretty --tag checks
  • --job <name>:只运行指定名称的 job(可重复传入多个);
  • --tag <tag>:只运行带有指定 tag 的 job(可重复传入多个)。

两者可以组合使用,筛选逻辑是"取并集":只要 job 命中了任一--job名称或任一--tag,就会被执行。

源码中的筛选逻辑

在 internal/run/controller/job.go 的runJob中可以看到精确的实现:

if len(scope.opts.RunOnlyJobs) != 0 && !slices.Contains(scope.opts.RunOnlyJobs, job.Name) { return result.Skip(job.PrintableName(id)) } if len(scope.opts.RunOnlyTags) != 0 && (!utils.Intersect(scope.opts.RunOnlyTags, job.Tags) && !utils.Intersect(scope.opts.RunOnlyTags, scope.tags)) { return result.Skip(job.PrintableName(id)) }

即:job 名称必须严格命中--job列表;而 tag 命中比较宽松——job 自身的tags或 hook 级继承下来的scope.tags中任何一个与--tag列表相交即可。被筛掉的 job 会以 Skip 结果记录,并不会执行。

对 group 的传播行为

从 internal/run/controller/scope.go 的scope.extend可见一个细节:当--job指定的是某个 group 的名字时,lefthook 会清空RunOnlyJobs,转而运行该 group 下的全部子 job——也就是说,用 group 名筛选会"展开"这个组,而不是只挑组内同名子任务。集成测试 tests/integration/cli_run_only.txt 完整验证了--job a --job c --job db --job lint--tag red的筛选结果,包括命令(commands:)也会被--job命中的行为。

指定文件(--all-files--file

命令模板中的文件占位符(如{staged_files})默认由 lefthook 根据 Git 状态自动填充。你可以用以下两个参数强制覆盖这些模板:

$ lefthook run pre-commit --all-files $ lefthook run pre-commit --file file1.js --file file2.js
  • --all-files:把所有文件模板替换为{all_files}(即仓库中的全部文件);
  • --file <path>:把文件模板替换为指定的文件列表(可重复传入多个)。

一个容易踩坑的点(原文档特别强调):如果两者同时指定,--all-files会被忽略,以--file列表为准。

源码中的实现

在 internal/command/run.go 的getFiles中可以看到覆盖逻辑:

if args.FilesFromStdin { // 从 STDIN 读取文件列表 } else if args.AllFiles { files, err := repo.AllFiles() return append(args.Files, files...) } return args.Files

--file传入的值直接进入args.Files;只有未使用--file且传了--all-files时才调用repo.AllFiles()获取全部文件。两者并存时由于args.Files已非空,--all-files分支的追加结果被跳过,印证了"忽略--all-files"的文档描述。

文件列表最终会进入命令构建器(internal/run/controller/command/build_command.go 的buildReplacer):当opts.ForceFiles非空时,会用replacer.NewMocked{staged_files}{push_files}{all_files}{files}全部替换为强制指定的文件列表,并经过 shellescape 转义(internal/run/controller/command/replacer/replacer.go),避免含空格或特殊字符的文件名破坏命令。集成测试 tests/integration/files_override.txt 验证了--all-files输出仓库全部文件(包括带逗号的文件名c,file.rb)、--file则精确输出给定列表(甚至允许传入ghost.file这类不存在的文件)。

空文件列表的处理

若强制指定的文件列表为空(或文件模板替换后为空),lefthook 默认会跳过该 job,除非加上--force。相关逻辑在 internal/run/controller/command/build_command.go:replacer.HasEmpty()为真且未启用--force时,返回SkipError{"no files for inspection"},job 以 Skip 结果呈现,不执行命令。

完整命令行参数参考

lefthook run的完整用法为:

lefthook run <hook-name> [args...] [options]

其全部 flag 定义于 cmd/run.go,汇总如下:

参数别名说明对应源码字段
--verbose-v开启 debug 日志args.Verbose
--colors <on\|off\|auto>颜色输出开关,默认autocolors
--job <name>只运行指定名称的 job,可重复args.RunOnlyJobs
--tag <tag>只运行带指定 tag 的 job,可重复args.RunOnlyTags
--command <name>只运行指定的命令,可重复args.RunOnlyCommands
--exclude <pattern>从所有文件模板中排除指定文件args.Exclude
--file <path>用指定文件覆盖文件模板,可重复args.Files
--force-f即使没有文件变更也不跳过args.Force
--all-files将文件模板替换为{all_files}args.AllFiles
--no-auto-install不隐式同步/安装 hooksargs.NoAutoInstall
--no-stage-fixed忽略配置中的stage_fixed: trueargs.NoStageFixed
--no-tty视为没有连接 TTYargs.NoTTY
--skip-lfs不运行 LFS hooksargs.SkipLFS
--fail-on-changes若有文件被改动则以退出码 1 结束args.FailOnChanges
--fail-on-changes-diff因文件改动失败时输出 diffargs.FailOnChangesDiff
--files-from-stdin从 STDIN 解析文件列表(支持\0分隔)args.FilesFromStdin

容易被忽略的几个参数

  • --files-from-stdin:从标准输入读取文件列表并合并进args.Files。解析逻辑见 internal/command/run.go 的parseFilesFromString,它同时兼容换行与\0(NUL)分隔的输入——后者正是git diff --name-only -z这类命令的输出格式,可与管道配合使用。
  • --fail-on-changes/--fail-on-changes-diff:用于"检查文件是否被改动"的守护场景。其取值优先级为"命令行参数 > hook 配置的fail_on_changes> 默认行为",且fail_on_changes支持never/always/ci/non-ci等取值,逻辑见 internal/command/run.go。同时 hook 名后追加的[args...]会以{0}{1}……形式注入命令模板,模拟真实 Git 传给 hook 的参数。
  • --no-tty/--no-stage-fixed/--skip-lfs:分别用于在 CI 等无交互终端场景禁用 spinner、临时关闭某 job 的stage_fixed: true自动暂存、以及跳过 Git LFS hook 的执行。

底层执行链路:一次lefthook run内部发生了什么

从 internal/command/run.go 的Lefthook.Run方法可以看出完整流程:

  1. 环境开关检查:若环境变量LEFTHOOK"0""false",直接返回不执行(用于 CI 中一键禁用所有 hooks);
  2. 预取 Git 状态repo.CacheGitCommands()并行预计算 staged/push 文件等,减少后续 IO 等待;
  3. 加载配置LoadConfig()读取 lefthook 配置文件;文件不存在时打印警告并正常返回;
  4. 版本校验:若配置中声明了min_version,会校验当前 lefthook 版本,不满足则报错(逻辑见 internal/command/run.go);
  5. 隐式同步 hooks:除非显式传--no-auto-install或配置关闭了no_auto_install,否则会检查并更新已安装的 Git hooks 以匹配最新配置——这正是"改完配置自动生效"机制的来源;
  6. 解析 hookresolveHook确认 hook 存在,并检测parallelpiped不能同时为true的冲突;
  7. 收集文件getFiles处理--files-from-stdin--all-files--file的优先级;
  8. 归一化任务config.CommandsToJobsconfig.ScriptsToJobs把旧的commands:/scripts:配置统一转换成jobs--command追加进RunOnlyJobs
  9. 调度执行runHook通过 internal/run/run.go 进入 controller,按 hook 的parallel设置走并发(concurrently)或串行(sequentially,支持piped断管跳过)执行,见 internal/run/controller/controller.go;
  10. 结果汇总:执行结束后打印 summary,若有任一 job 失败则以非零退出码结束(internal/command/run.go)。

值得一提的是,controller 在启动时会用utils.NewCachedReader(os.Stdin)缓存标准输入,保证多个 job(如多个use_stdin: true的脚本)都能读到同一次 Git 通过 STDIN 传入的数据。

实战建议

  • 调试配置时用--job精准定位:一个 hook 下有多个 job 时,先lefthook run <hook> --job <name>单独验证,避免其他 job 干扰输出;
  • CI 中禁用全部 hooks:设置环境变量LEFTHOOK=0即可让所有lefthook run调用直接返回,无需改配置;
  • 配合-v观察模板替换lefthook run <hook> -v会输出每条最终命令的 debug 日志([lefthook] job: ...),是排查文件模板、转义问题的利器;
  • 利用--force跳过空文件跳过逻辑:当文件列表为空但确实希望执行命令(例如只想验证脚本本身)时,加-f强制运行;
  • --all-files--file二选一:两者语义互斥,混用时结果以--file为准,保持命令可读性建议只传一种。

如果需要在安装、卸载、校验配置等其他环节做进一步排查,可参考 docs/usage/commands/install.md、docs/usage/commands/uninstall.md 与 docs/usage/commands/validate.md;lefthook run本身的全部命令行定义可随时在 cmd/run.go 中查阅。

【免费下载链接】lefthookFast and powerful Git hooks manager for any type of projects.项目地址: https://gitcode.com/GitHub_Trending/le/lefthook

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

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

System Prompt泄露风险与AI工程化防护七道防线

1. 项目概述&#xff1a;什么是 system_prompts_leaks&#xff1f;它为什么值得一线开发者警惕“system_prompts_leaks”——这个词组乍看像一串技术日志里的报错片段&#xff0c;但过去三个月&#xff0c;它已悄然成为AI工程圈内高频复现的隐性风险信号。它不指向某个具体漏洞…

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

大语言模型system prompt泄露原理与防御实战

1. 项目概述&#xff1a;什么是 system_prompts_leaks&#xff1f;它为什么突然被频繁讨论&#xff1f;最近在多个技术社区、AI开发者群组和模型调优论坛里&#xff0c;“system_prompts_leaks”这个短语出现频率明显升高——不是作为某个开源项目名&#xff0c;也不是某家公司…

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

Colibri:轻量级 Web 框架的极简实践与选型思考

说实话&#xff0c;我第一次被“colibri”这个词击中&#xff0c;是在很多年前刷 GitHub 的时候。一个只有几 KB 的 .NET 开源项目&#xff0c;却号称能让你“用一个文件写完一个 Web 应用”&#xff0c;项目名就叫 Colibri。我当时心想&#xff0c;这名字起得挺有意思——在西…

作者头像 李华
网站建设 2026/9/16 17:27:46

注意避坑!不是所有 AI 写作工具都靠谱,2026 学术圈认可工具合集

每年毕业季&#xff0c;无数同学深陷论文难题&#xff1a;开题毫无思路、搭建框架耗费数日、初稿逻辑松散、查重标红泛滥、AI检测超标、格式反复被导师驳回。面对日益严格的学术规范和查重系统&#xff0c;许多学生开始尝试使用通用型AI写作工具辅助论文撰写。然而&#xff0c;…

作者头像 李华