git-cliff 模板语法完全指南:基于 Tera 的 Changelog 模板引擎与自定义过滤器实战

发布时间:2026/9/23 16:24:54
git-cliff 模板语法完全指南:基于 Tera 的 Changelog 模板引擎与自定义过滤器实战 git-cliff 模板语法完全指南基于 Tera 的 Changelog 模板引擎与自定义过滤器实战【免费下载链接】git-cliffA highly customizable Changelog Generator that follows Conventional Commit specifications ⛰️项目地址: https://gitcode.com/gh_mirrors/gi/git-cliffgit-cliff 是一个遵循 Conventional Commits 规范、高度可定制的 Changelog 生成器而它的定制能力核心就落在模板系统上每个版本发布记录的排版、分组、统计信息乃至整个 changelog 的骨架都由 Tera 模板语言驱动。本文以仓库官方文档 模板语法 为主体结合 模板引擎源码 与 默认配置 中的真实用法系统讲解 git-cliff 模板的三种分隔符、六个内置自定义过滤器upper_first、find_regex、replace_regex、split_regex、commit_groups、group_by_scope的语法、参数、返回值与底层实现细节。读完本文你将能够独立编写出与官方默认配置同等水准的、可发布到生产环境的 changelog 模板。Tera 模板引擎与三种分隔符git-cliff 使用Tera作为模板引擎其语法继承自 Jinja2 与 Django 模板。Tera 是一个功能完备的模板库提供循环、条件判断、宏、过滤器等特性git-cliff 在其之上注册了一批贴合 changelog 场景的自定义过滤器详见 template.rs 中 6 个过滤器的注册代码。在 git-cliff 模板中一共有3 种定界符delimiter且不可修改定界符用途示例{{与}}表达式输出变量值、过滤器结果{{ version }}、{{ commit.message \| upper_first }}{%/{%-与%}/-%}语句控制结构循环、条件、赋值等{% for commit in commits %}、{% if commit.breaking %}{#与#}注释渲染时被忽略{# 这是一段注释 #}语句定界符支持{%-与-%}的空白控制变体在定界符内侧加上-可以吞掉语句标签相邻的空白字符用于产出整洁的 Markdown 而不产生多余空行。这是编写多行 body 模板时最常用的技巧例如 默认配置 中每个{% %}标签末尾都带有\续行符配合-%}控制空白。除 git-cliff 自定义过滤器外Tera 自带的控制结构如{% for %}、{% if %}、{% set %}、{% set_global %}、内置过滤器如group_by、concat、split、trim、date、striptags、truncate、trim_start_matches均可在模板中直接使用官方 Tera 文档对控制结构与内置过滤器有完整说明建议编写复杂模板时查阅。模板在 changelog 生成流程中的位置header / body / footer要理解模板语法首先要知道 git-cliff 会把一个 changelog 拆成三段分别渲染且各段的模板上下文context不同。从 changelog.rs 的render方法可以看到header渲染一次注入的上下文是完整的 releases 列表Releases { releases: self.releases }适合放标题、全量统计、版本索引等需要纵览全部发布记录的内容body每个 release 渲染一次注入的上下文是单个 release 对象version、commits、timestamp、commit_id等这是最常用、信息量最大的模板footer渲染一次同样注入完整的 releases 列表适合放版本对比链接、版权声明、!-- generated by git-cliff --之类的收尾标记。这也是理解本文后面group_by_scope过滤器只能用在 header 或 footer这一限制的关键body 渲染时拿不到完整的releases数组自然无法做跨 release 的分组聚合。完整的模板上下文字段version、commits[*].group/scope/message/footers/author/statistics、commit_range、submodule_commits、bump_type、previous等参见 模板上下文文档本文不再展开。另外changelog 配置文档 中提到的trim true会先在 Template::new 中对模板源码逐行trim再拼接使模板可以放心缩进排版而不会把缩进空格带进输出。六个自定义过滤器逐一详解git-cliff 在 template.rs 中通过tera.register_filter(...)注册了六个过滤器可分为字符串处理与提交/版本分组两大类。下面按原文档顺序逐一说明语法、参数、返回值与注意事项并给出仓库测试用例中的真实输出作为佐证。upper_first首字符大写将字符串的第一个字符转换为大写其余字符保持不变。语法{{ hello | upper_first }} → Hello该过滤器无需参数对空字符串返回空字符串。实现见 upper_first_filter它使用s.chars()按 Unicode 字符边界取首字符并to_uppercase()因此对带重音字母等非 ASCII 字符也能正确处理。仓库测试 test_upper_first_filter 验证了hello | upper_first输出Hello。这是整个仓库使用频率最高的过滤器默认配置、keepachangelog 示例、minimal 示例 等都用它把 commit 描述的首字母规范为大写。find_regex按正则查找所有匹配找出字符串中所有匹配某个正则模式的内容返回匹配结果的字符串数组。语法{{ hello world, hello universe | find_regex(pathello) }} → [hello, hello]关键参数pat必填正则模式字符串。缺失时会直接报错Filter find_regex expected an arg called pat见 find_regex模式中的\n与\t转义序列会被先还原为真实换行/制表符p.replace(\\n, \n).replace(\\t, \t)便于在配置文件中书写多行匹配使用 Rustregexcrate 编译模式非法正则会以received an invalid regex pattern报错返回值为数组可直接配合| length统计匹配次数或交给{% for %}遍历。测试 test_find_regex_filter 验证输出为[hello, hello]。replace_regex正则替换将字符串中所有匹配正则模式的部分替换为指定字符串。语法{{ hello world | replace_regex(fromo, toa) }} → hella warld关键参数见 replace_regexfrom必填被替换的正则模式缺失时报错to必填替换内容缺失时报错底层使用re.replace_all会替换所有匹配项而非仅第一个。测试 test_replace_regex_filter 验证hello world | replace_regex(fromo, toa)输出hella warld。此过滤器适合在模板内做轻量文本清洗若需要对整个 changelog 做全局文本加工更推荐配置层级的postprocessors见 changelog 配置。split_regex按正则切分按正则模式将字符串切分为数组。语法{{ hello world, hello universe | split_regex(pat ) }} → [hello, world,, hello, universe]关键参数见 split_regexpat必填作为切分依据的正则模式缺失时报错与find_regex一样支持\n、\t转义还原返回切分后的字符串数组注意示例中hello world, hello universe按空格切分后第二段是world,含逗号输出为[hello, world,, hello, universe]——切分是纯正则行为不会智能去标点。测试 test_split_regex_filter 验证了上述输出。split_regex常与split、first、trim组合使用例如 github-keepachangelog.toml 中的commit.message | split(pat\n) | first | upper_first | trim模式——先按换行切分取首行再大写、去空白从而只展示提交信息的第一行。commit_groups按 group 字段分组且保持自定义顺序将提交数组按group字段分组并保持配置指定的分组顺序而不是字母序。这是 git-cliff 专门为控制 changelog 分组顺序提供的过滤器。语法与commit_parsers_groups配合是官方推荐用法{% for entry in commits | commit_groups(groupscommit_parsers_groups) %} ### {{ entry.group }} {% endfor %}行为要点实现见 commit_groups返回值对象数组每个元素形如{ group: ..., commits: [...] }其中commits保持原始顺序groups参数可选接收一个分组名数组。传入commit_parsers_groups上下文变量时输出顺序与配置文件中commit_parsers的声明顺序一致commit_parsers_groups由 changelog.rs 在构建 changelog 时按commit_parsers顺序提取去重生成并注入到所有模板的额外上下文中未列出的分组不在groups数组中的分组会被追加到已列出分组的之后按首次出现顺序排列省略groups参数保持分组在提交列表中的首次出现顺序即提交时间顺序跳过空分组group字段为 null 或缺失的提交会被跳过与 Tera 内置group_by行为一致与内置group_by的区别Tera 内置group_by(attributegroup)返回的是对象iteration order 不保证且默认按 key 排序commit_groups返回数组迭代顺序确定并支持显式顺序参数。仓库测试覆盖了四种边界场景首次出现顺序不传groups时按出现顺序输出⚡ Performance → Bug Fixes → Features按groups参数排序传入指定顺序后输出变为 Features → Bug Fixes → ⚡ Performance未知分组追加groups中未列出的分组排在末尾跳过 null 分组无 group 的提交被忽略。集成测试 changelog_group_order_matches_commit_parsers 还专门回归验证了 issue #9当分组名按字母序排序恰好是错误顺序:bug::gear::rocket::zap:时commit_groups(groupscommit_parsers_groups)仍能按commit_parsers声明顺序渲染出rocket → bug → zap → gear的预期结果。默认配置 config/cliff.toml 即采用这一写法commit_parsers中为每个分组名加了!-- N --序号前缀配合commit_groups(groupscommit_parsers_groups)精确控制 Features、Bug Fixes、Documentation 等分组的展示次序。group_by_scope按语义化版本作用域聚合 release将 releases 数组按每个 release 的version字段的语义化版本作用域major/minor/patch分组。语法{% for version, releases in releases | group_by_scope(scopeminor, prefixv) %} {% set_global commits [] %} {% for release in releases %} {% set_global commits commits | concat(withrelease.commits) %} {% endfor %} {% for group, commits in commits | group_by(attributegroup) %} ### {{ group }} {% for commit in commits %} - {{ commit.message }} {% endfor %} {% endfor %} {% endfor %}行为要点实现见 group_by_scope 与 VersionScope::from_args返回值以版本作用域为键、release 数组为值的映射模板中通过{% for version, releases in ... %}遍历键值对每个键对应的值即{ version: ..., releases: [...] }形态的聚合scope参数可选取值major、minor、patch默认minor传入其他值会报错expected scope to be major, minor, or patchprefix参数可选用于带前缀的标签例如prefixv会先剥离v再解析v1.2.3不传则按原样解析作用域聚合规则major只保留主版本号1minor保留1.2patch保留完整1.2.3见 format_scoped_version无法解析的版本剥离前缀后无法按 SemVer 解析的版本保持原样unwrap_or(key)不会被丢弃version为 null 的 release即 unreleased 变更键为空字符串空输入releases 为空时返回空映射使用位置限制仅可放在header或footer中因为body是每个 release 渲染一次上下文不包含完整的releases数组见前文 render 流程。测试 test_group_by_scope_filter 用v1.0.2、v1.0.1、v0.9.0与一个无版本 release 验证了输出1:1:chore1,;v0.91:1:docs1,;v1.02:3:feat1,fix2,;minor作用域下v1.0.1与v1.0.2被合并为v1.0组无版本 release 落在空字符串键下。从源码看过滤器注册与模板渲染链路过滤器的生命周期可以从 template.rs 梳理出一条完整链路Template::new(name, content, trim)先按trim标志逐行裁剪模板源码再tera.add_raw_template注册模板并一次性注册全部 6 个过滤器L45-L50模板解析失败时错误会被包装为TemplateParseError/TemplateError渲染期错误则区分为TemplateRenderError/TemplateRenderDetailedError见 render便于定位是语法问题还是上下文数据问题get_template_variables会遍历 Tera 语法树AST递归提取模板中使用的变量名L243-L310这一能力支撑了 changelog 的header_marker机制——只有 header 模板含动态变量时才输出 marker供--prepend场景识别并替换旧 header渲染时additional_context含commit_parsers_groups通过TeraContext::from_serialize与主上下文合并L320-L331随后执行postprocessors对输出做整体文本加工。值得留意的是所有过滤器都遵循同一套参数处理范式缺失必填参数立即报错、非法正则报错并附带原始模式、非法枚举值如scope传tiny报错。这意味着模板错误会在渲染阶段以明确信息暴露而不是静默产出错误结果排错成本很低。实战组合过滤器构建生产级 body 模板把上述过滤器组合起来即可写出与官方默认配置同等效果的模板。下面这段取自 config/cliff.toml当前仓库的默认 body 模板融合了本文讲到的核心语法{% if version %}\ ## [{{ version | trim_start_matches(patv) }}] - {{ timestamp | date(format%Y-%m-%d) }} {% else %}\ ## [unreleased] {% endif %}\ {% for entry in commits | commit_groups(groupscommit_parsers_groups) %} ### {{ entry.group | striptags | trim | upper_first }} {% for commit in entry.commits %} - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ {% if commit.breaking %}[**breaking**] {% endif %}\ {{ commit.message | upper_first }}\ {% endfor %} {% endfor %}逐段解读{% if version %}区分已发布版本与 unreleased 区块timestamp | date(format%Y-%m-%d)用 Tera 内置date过滤器格式化提交时间commits | commit_groups(groupscommit_parsers_groups)是分组核心输出顺序严格跟随commit_parsers声明entry.group | striptags | trim | upper_first是过滤器链式调用的范例先剥离commit_parsers分组名中!-- N --这样的 HTML 注释striptags再去除空白trim最后首字母大写upper_first内层对commit.scope、commit.breaking做条件输出配合{%-/\控制空白产出整洁的 Markdown 列表。若需在 header 或 footer 中按大版本聚合展示可参考前文group_by_scope的完整示例外层{% for version, releases in releases | group_by_scope(scopeminor, prefixv) %}遍历版本组内层用{% set_global commits commits | concat(withrelease.commits) %}把同组 release 的提交累积起来再交给group_by(attributegroup)二次分组输出。常见误区与最佳实践综合原文档与源码实现使用 git-cliff 模板时有几点值得特别注意group_by_scope的位置限制它依赖完整的releases数组只能写在header/footer写在body中拿不到该数据。判别依据见 changelog.rs 中三段的上下文注入差异commit_groups优先于内置group_by内置group_by按 key 排序且返回对象分组顺序不可控需要与commit_parsers声明顺序一致时务必使用commit_groups(groupscommit_parsers_groups)commit_parsers_groups由配置自动推导它并非手动维护的变量而是 git-cliff 从commit_parsers中按顺序去重提取group字段自动注入的changelog.rs所以调整commit_parsers顺序会同步影响分组顺序正则过滤器注意转义find_regex/split_regex/replace_regex的模式是 Rust regex 语法\n、\t会被还原为真实字符编写跨行匹配时要留意参数缺失或正则非法时模板渲染会直接报错空白控制多行 body 模板建议配合trim true与{%-/-%}/\控制输出空白若仍嫌模板难排整齐可开启 changelog 配置 中的format true让渲染结果经过 Markdown 格式化器归一化整体文本加工优先用 postprocessors模板内过滤器适合局部清洗而针对整个 changelog 的全局替换如把占位符替换为仓库 URL、运行外部格式化工具应放在changelog.postprocessors中二者分工明确。掌握了三种定界符、六个自定义过滤器及其源码行为再结合 模板上下文文档 理解可用的数据字段你就能为任意仓库定制出结构清晰、分组有序、带统计信息的 changelog——这也是 git-cliff 被称为高度可定制的关键所在。【免费下载链接】git-cliffA highly customizable Changelog Generator that follows Conventional Commit specifications ⛰️项目地址: https://gitcode.com/gh_mirrors/gi/git-cliff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询