从 PR 到架构动画:我用开源工具把每次合并请求变成可视化架构图
在日常开发中,我们经常遇到一个问题:代码仓库越来越大,每次 Pull Request(PR)改动的模块越来越多,评审者很难在几分钟内快速理解这次变更波及了哪些服务、哪些组件、哪些数据流。即便有架构图,静态图也往往跟不上代码演进的速度。前段时间我在调研可视化方案时,看到不少开源项目尝试“把每次 PR 变成动画架构图”,这个思路很新颖,也很实用。本文就来完整拆解这套方案:它解决什么问题、整体架构怎么设计、核心代码怎么写、如何接入 CI 实现自动化,以及我在实际落地中遇到的高频坑点。
需要先澄清一个容易混淆的点:这里的 PR 是 Pull Request,也就是代码合并请求,不是 Adobe Premiere Pro 的缩写。如果你搜索“pr下载”“pr安装包”搜到的是视频剪辑软件,那和本文主题无关。本文讨论的是 Git 协作工作流中的 Pull Request,以及如何通过动画架构图让代码评审更直观。
1. 背景与核心概念
1.1 PR(Pull Request)是什么
Pull Request 是 Git 协作开发中非常重要的一环。开发者把本地分支推送到远程仓库后,通过 PR 向维护者发出合并请求,请求将当前分支的变更合并到目标分支(通常是 main 或 develop)。评审者可以在 PR 中查看代码差异、发表评论、要求修改,通过后合并。
一个标准的 PR 包含:
- 源分支与目标分支;
- 变更文件列表;
- 每次文件的增删改内容;
- 提交记录;
- 评审讨论记录。
PR 最大的价值不是“合并代码”这个动作,而是“变更发生后的审查过程”。但实际审查中,一个改动可能涉及十几个文件、多个服务模块,评审者需要先看目录结构、再逐个看文件、最后自己在脑内拼出影响面,这个过程效率不高。
1.2 架构图在代码评审中的作用
架构图是表达系统结构的工具,它能展示模块划分、服务依赖、数据流向、组件关系。常见的架构图形式包括:
- 分层架构图:展示 Controller、Service、DAO 等层次;
- 微服务依赖图:展示各服务之间的调用关系;
- 数据流图:展示数据从入口到存储的流转路径;
- 领域模型图:展示业务实体之间的关系。
这些图在项目启动阶段通常画得比较完整,但代码持续迭代后,架构图容易和实际代码脱节。这是因为架构图往往是静态文档,不会随着 PR 自动更新。
1.3 动画架构图解决的痛点
把 PR 变成动画架构图的思路是:根据 PR 的变更文件集合,生成一张架构图,并用动画突出显示本次变更涉及的节点、边和调用关系。和静态架构图相比,动画架构图有几个明显优势:
- 直观展示变更影响面:哪些模块被改动,哪些依赖被新增或删除,一眼就能看出来;
- 动态演进过程:通过帧序列展示每个文件的变更先后顺序,能看出开发者的实现思路;
- 适合嵌入式评论:生成 GIF 或短视频后,可以直接贴在 PR 评论中,团队沟通成本大幅降低;
- 自动化程度高:只要 CI 触发,全流程无需人工干预。
当然,这不是要完全替代人工评审,而是给评审提供一个“第一眼概览”,让评审者更快定位重点。
2. 整体架构与核心设计
2.1 整体工作流程
这个开源方案的整体流程可以拆成下面几步:
- 监听 PR 事件,获取 PR 的变更文件列表;
- 解析变更文件的代码结构,提取包名、类名、函数名、依赖关系;
- 将源码依赖关系映射为架构图节点和边;
- 按文件提交顺序生成多帧静态架构图;
- 将多帧图片合成为动画 GIF;
- 将 GIF 上传到 PR 评论,或作为 CI 产物展示。
用一个 ASCII 简图来表示:
Git PR 事件 ↓ 获取变更文件列表(git diff --name-only) ↓ 解析代码依赖结构(AST / 正则 / 静态分析) ↓ 生成架构图节点和边(Graphviz / PlantUML) ↓ 按提交顺序渲染多帧图片 ↓ 合并为动画 GIF ↓ 上传到 PR 评论 / CI Artifacts2.2 核心模块拆解
整个工具可以拆成四个模块:
- 变更采集模块:负责和 Git 仓库交互,获取 PR 的变更文件、提交历史、目标分支信息;
- 依赖解析模块:负责读取源码文件,分析类、方法、模块之间的依赖,生成节点和边的结构化数据;
- 架构图渲染模块:把结构化数据渲染成图片,并按照时间顺序输出多帧;
- 动画合成与发布模块:把多帧图片合成为 GIF,调用 GitHub API 或 GitLab API 发布到 PR 评论区。
这四个模块建议解耦,前一个模块的输出是后一个模块的输入,中间用 JSON 或 YAML 过渡,方便调试和替换实现。
2.3 技术选型分析
这部分需要根据目标仓库的语言来决定,没有银弹。我这里给出几种常用组合:
| 用途 | 可选方案 | 说明 |
|---|---|---|
| Git 操作 | Git CLI、GitPython | CLI 最稳定,GitPython 方便跨平台 |
| 依赖解析 | Tree-sitter、AST、正则 | 精确度要求高用 AST;快速原型用正则 |
| 架构图渲染 | Graphviz、PlantUML、D2 | Graphviz 灵活,支持批量输出帧 |
| 图片合成 | Pillow、imageio、ffmpeg | GIF 场景用 imageio 最省事 |
| CI 平台 | GitHub Actions、GitLab CI | 示例以 GitHub Actions 为主 |
| PR 评论 API | PyGithub、Octokit | 认证用 Token,注意最小权限 |
以 Python 为例,后续代码示例都会围绕这个技术栈展开。如果你更熟悉 Java 或 TypeScript,完全可以用同样的流程移植,核心思路是一致的。
3. 环境准备与版本说明
3.1 开发环境
本文示例使用的环境如下,版本需要根据你的项目实际情况调整:
- 操作系统:Ubuntu 22.04(其他系统类似);
- Python:3.10 及以上;
- Git:2.39 及以上;
- Graphviz:确保
dot命令可用; - GitHub CLI:可选,用于本地调试 PR 信息;
- CI 平台:GitHub Actions。
在本地开发时,你需要准备一个测试仓库,至少包含两个分支和一个 PR。我建议先拿一个小型项目试手,比如只有一个 Controller、两个 Service、一个 Repository 的 Java 或 Python 项目。
3.2 项目结构
我建议把工具做成一个独立项目,目录结构如下:
pr-arch-animator/ ├── pyproject.toml ├── README.md ├── src/ │ └── pr_arch_animator/ │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── collector.py │ ├── parser.py │ ├── renderer.py │ ├── animator.py │ └── publisher.py ├── scripts/ │ └── run.sh └── tests/ └── test_collector.py这个结构把功能拆成模块,方便后续扩展。
3.3 依赖安装
创建虚拟环境并安装依赖:
python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install gitpython pygithub graphviz imageio pillow这里解释几个核心依赖的用途:
gitpython:在 Python 中操作 Git 仓库,获取 diff 和提交信息;pygithub:调用 GitHub REST API,获取 PR 详情、发布评论;graphviz:Python 调用 Graphviz 的接口,生成有向图;imageio:读取多张 PNG 并写入 GIF;pillow:做图片尺寸归一化,避免帧大小不一致导致动画抖动。
4. 核心代码实现
4.1 获取 PR 变更集
第一步是拿到 PR 改了哪些文件。这里有两种方式:如果工具运行在 CI 环境中,可以通过环境变量拿到 PR 信息;如果本地调试,可以用 GitPython 直接对比分支。
参考代码如下,需要放在src/pr_arch_animator/collector.py:
from pathlib import Path from typing import List from git import Repo class ChangeCollector: """采集 PR 的变更文件列表和提交顺序。""" def __init__(self, repo_path: str = "."): self.repo_path = repo_path self.repo = Repo(repo_path) def get_changed_files( self, base_branch: str = "main", head_branch: str = "feature/demo", ) -> List[str]: """对比两个分支,返回变更文件路径列表。""" # 用法说明:确保本地已经 fetch 远程分支 # 返回的结果中,M 表示修改,A 表示新增,D 表示删除 diff_index = self.repo.git.diff( base_branch, head_branch, "--name-status", ) changed_files = [] for line in diff_index.splitlines(): parts = line.split("\t") if len(parts) < 2: continue # 只保留源码文件,跳过测试和文档 file_path = parts[1] if self._is_source_file(file_path): changed_files.append(file_path) return changed_files @staticmethod def _is_source_file(file_path: str) -> bool: """过滤需要关注的源码文件。""" if file_path.startswith("test/"): return False if file_path.startswith("docs/"): return False if file_path.endswith(".md"): return False if file_path.endswith(".py"): return True if file_path.endswith(".java"): return True return False def get_commit_sequence(self, base_branch: str, head_branch: str) -> List[str]: """按时间顺序获取两个分支之间的提交列表。""" commits = self.repo.git.log( f"{base_branch}..{head_branch}", "--reverse", "--format=%h %s", ) return commits.splitlines()这段代码实现了两个功能:
get_changed_files:使用git diff --name-status获取变更文件,只保留源码文件;get_commit_sequence:使用git log --reverse获取从基础分支到特性分支的提交顺序。
需要注意,git diff输出格式在不同版本之间有细微差异,建议先跑命令确认输出格式,再决定字符串解析方式。
4.2 解析代码依赖关系
拿到文件列表后,需要从源码中提取节点和依赖边。对 Python 项目,可以使用ast模块解析import语句;对 Java 项目,可以解析package、import和类名。
这里给出一个简化版 Python 解析器,放在src/pr_arch_animator/parser.py:
import ast from pathlib import Path from typing import Dict, List, Set class DependencyParser: """从源码文件中提取模块依赖关系。""" def __init__(self, repo_path: str): self.repo_path = Path(repo_path) def parse_file(self, file_path: str) -> Dict[str, Set[str]]: """解析单个 Python 文件,返回节点和依赖集合。""" abs_path = self.repo_path / file_path if not abs_path.exists(): return {} source = abs_path.read_text(encoding="utf-8") tree = ast.parse(source) current_module = file_path.replace("/", ".").rsplit(".", 1)[0] dependencies: Set[str] = set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: dependencies.add(alias.name) elif isinstance(node, ast.ImportFrom): if node.module: dependencies.add(node.module) return {current_module: dependencies} def parse_files(self, files: List[str]) -> Dict[str, Set[str]]: """批量解析文件,合并为全局依赖图。""" graph: Dict[str, Set[str]] = {} for file_path in files: result = self.parse_file(file_path) for module, deps in result.items(): graph.setdefault(module, set()).update(deps) return graph这个解析器只做了最基本的 import 提取,没有处理:
- 相对导入 vs 绝对导入;
- 条件导入;
- 动态导入;
- 类级别的依赖(类 A 调用了类 B,但没有直接 import B)。
真实项目中,我建议使用 Tree-sitter 或 Semgrep 这类更强大的静态分析工具,这里是为了让读者先理解流程。对 Java 项目,思路类似,需要解析 package 和 import 语句。
4.3 生成架构图静态帧
有了依赖图,下一步用 Graphviz 渲染图片。为了生成动画,我们需要按提交顺序一帧一帧地渲染:第 1 帧只展示第一次提交涉及的模块,第 2 帧叠加第二次提交的模块,直到所有提交都展示完毕。
渲染器代码放在src/pr_arch_animator/renderer.py:
from graphviz import Digraph class ArchitectureRenderer: """将依赖图渲染为多帧 PNG 图片。""" def __init__(self, output_dir: str = "frames"): self.output_dir = output_dir import os os.makedirs(output_dir, exist_ok=True) def render_frame( self, graph_data: dict, frame_index: int, highlight_nodes: set, ) -> str: """渲染一帧图片,highlight_nodes 标记新增节点。""" dot = Digraph(comment="PR Architecture") dot.attr(rankdir="LR") dot.attr("node", shape="box") for module in graph_data: style = "filled" if module in highlight_nodes else "solid" color = "#ff9900" if module in highlight_nodes else "#cccccc" dot.node(module, module, style=style, fillcolor=color) for module, deps in graph_data.items(): for dep in deps: if dep in graph_data: dot.edge(module, dep) output_path = f"{self.output_dir}/frame_{frame_index:03d}" dot.render(output_path, format="png", cleanup=True) return f"{output_path}.png"这里有一个关键点:每一帧都是完整图,但高亮色标记了“新增的变更节点”。这样做的好处是:架构图始终显示整体结构,但动画过程能区分“已有模块”和“本次变更涉及模块”。
4.4 合成动画 GIF
渲染出多帧 PNG 后,用 imageio 合成为 GIF。这里需要注意:
- 所有帧的尺寸必须一致;
- 帧率不宜太高,否则评审者看不清;
- 建议每秒 1 到 2 帧,每个提交停留至少半秒。
动画合成代码放在src/pr_arch_animator/animator.py:
import glob import imageio.v2 as imageio class AnimationBuilder: """将多帧 PNG 合成为 GIF。""" def __init__(self, frame_dir: str = "frames"): self.frame_dir = frame_dir def build_gif(self, output_path: str = "pr_architecture.gif", duration: float = 0.8): """合成 GIF,duration 控制每帧停留秒数。""" frame_files = sorted(glob.glob(f"{self.frame_dir}/frame_*.png")) if not frame_files: raise RuntimeError("没有找到任何帧图片,请先执行渲染步骤") frames = [] for frame_file in frame_files: frames.append(imageio.imread(frame_file)) imageio.mimsave(output_path, frames, duration=duration) print(f"GIF 已生成:{output_path}")如果生成的 GIF 太大,可以适当减少帧数,或者用 Pillow 压缩每个 PNG 的尺寸。还有一个提示:GitHub 的 PR 评论对图片大小有限制,超过 10MB 的文件可能无法正常预览,建议控制分辨率。
4.5 在 PR 评论中发布 GIF
动画生成后,我们需要把它发布到 PR 评论里。使用 PyGithub 的参考代码如下,放在src/pr_arch_animator/publisher.py:
from github import Github from github.Repository import Repository class PRPublisher: """把 GIF 上传到 PR 评论区。""" def __init__(self, token: str, repo_name: str): self.client = Github(token) self.repo: Repository = self.client.get_repo(repo_name) def publish_gif(self, pr_number: int, gif_path: str, message: str = ""): """上传 GIF 并发表评论。""" pr = self.repo.get_pull(pr_number) with open(gif_path, "rb") as f: # GitHub 的评论接口支持直接引用图片 URL, # 简单起见,这里先打印提示。 # 实际项目中,可以先把 GIF 传到 GitHub Release 或对象存储, # 然后在评论中使用 Markdown 图片语法。 print(f"上传 GIF 到 PR #{pr_number}") default_message = "这是本次 PR 的架构变更动画,请结合动画查看影响范围。" comment_message = message or default_message pr.create_issue_comment(f"{comment_message}\n\n")要注意的是,GitHub API 不能在评论中直接上传二进制文件作为图片。通常的做法是:
- 把 GIF 上传到 GitHub Releases;
- 或者使用第三方图床/对象存储;
- 或者使用 GitHub Actions 的 Artifacts,在注释中给出下载链接。
由于各团队的安全策略不同,这部分往往需要结合实际部署环境调整。上面代码中的gif_url是示意,需要替换成你自己的托管地址。
5. 接入 GitHub Actions 实现自动化
5.1 配置工作流
为了让每次 PR 自动生成架构动画,我们需要配置 GitHub Actions。核心思路是用pull_request事件触发,然后执行上面的 Python 脚本。
Workflow 配置文件位置:.github/workflows/pr-arch-animator.yml
name: PR Architecture Animator on: pull_request: types: [opened, synchronize] jobs: generate-animation: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - name: 检出代码 uses: actions/checkout@v4 with: fetch-depth: 0 - name: 设置 Python uses: actions/setup-python@v5 with: python-version: '3.10' - name: 安装 Graphviz run: | sudo apt-get update sudo apt-get install -y graphviz - name: 安装项目依赖 run: | pip install gitpython pygithub graphviz imageio pillow - name: 生成 PR 架构动画 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} BASE_BRANCH: ${{ github.event.pull_request.base.ref }} HEAD_BRANCH: ${{ github.event.pull_request.head.ref }} run: | python scripts/run.py \ --repo-path . \ --base "$BASE_BRANCH" \ --head "$HEAD_BRANCH" \ --pr "$PR_NUMBER" - name: 上传产物 uses: actions/upload-artifact@v4 with: name: pr-architecture-gif path: pr_architecture.gif这个 Workflow 有几个关键点:
fetch-depth: 0:必须完整拉取分支历史,否则git diff拿不到目标分支的对比;permissions:只申请了contents: read和pull-requests: write,避免暴露过大的 Token 权限;GITHUB_TOKEN:GitHub 自动生成的临时 Token,不需要手动配置,但权限范围需要在 permissions 中声明;- 事件类型
opened, synchronize:表示 PR 创建或代码更新时触发。
5.2 主脚本设计
上面的 Workflow 调用了scripts/run.py,这个脚本是用来串联整个流程的入口。示例代码如下:
#!/usr/bin/env python3 """PR 架构动画生成入口脚本。""" import argparse import os from pr_arch_animator.collector import ChangeCollector from pr_arch_animator.parser import DependencyParser from pr_arch_animator.renderer import ArchitectureRenderer from pr_arch_animator.animator import AnimationBuilder from pr_arch_animator.publisher import PRPublisher def main(): parser = argparse.ArgumentParser(description="生成 PR 架构动画") parser.add_argument("--repo-path", default=".") parser.add_argument("--base", default="main") parser.add_argument("--head", default="feature/demo") parser.add_argument("--pr", type=int, required=True) args = parser.parse_args() collector = ChangeCollector(args.repo_path) changed_files = collector.get_changed_files(args.base, args.head) print(f"变更文件列表:{changed_files}") parser_engine = DependencyParser(args.repo_path) graph_data = parser_engine.parse_files(changed_files) print(f"依赖图节点数:{len(graph_data)}") renderer = ArchitectureRenderer("frames") # 这里做简化处理:把全部变更文件作为高亮节点 renderer.render_frame(graph_data, 1, set(graph_data.keys())) builder = AnimationBuilder("frames") builder.build_gif("pr_architecture.gif", duration=1.0) token = os.environ.get("GITHUB_TOKEN", "") repo_name = os.environ.get("GITHUB_REPOSITORY", "") if token and repo_name: publisher = PRPublisher(token, repo_name) publisher.publish_gif(args.pr, "pr_architecture.gif") if __name__ == "__main__": main()注意,上面的脚本做了大量简化。真实的版本需要在每次提交后生成一帧,而不是只生成一帧。你可以把get_commit_sequence结合起来,每个提交处理一批文件,渲染一帧,逐渐叠加节点。
5.3 权限与安全注意事项
使用 GitHub Token 操作 PR 评论时,务必注意:
- 不要使用个人 Token 写死在代码里;
- 优先使用 GitHub Actions 自带的
GITHUB_TOKEN,并限制权限; - 如果必须使用其他平台的密钥,请保存到 GitHub Secrets 中;
- 脚本中不要打印 Token 或任何敏感环境变量;
- 对于 fork 仓库的 PR,默认情况下
GITHUB_TOKEN是只读的,需要额外配置信任范围,建议谨慎处理。
安全边界是所有自动化工具的第一优先级,尤其是能写评论、改状态的自动化任务。
6. 常见问题与排查思路
在实际开发和部署过程中,我整理了下面几个高频问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
git diff结果为空 | 本地分支没有更新,或 fetch 深度不足 | 检查是否执行了git fetch --depth=0或完整拉取 |
Graphviz 报dot: command not found | 环境缺少 Graphviz 本体 | 安装graphviz系统包,不只是 Python 包 |
| 生成的 GIF 帧大小不一致 | PNG 尺寸不同 | 用 Pillow 统一 resize 或设置固定画布大小 |
| PR 评论没有收到图片 | Token 权限不足,或图片 URL 无法访问 | 检查pull-requests: write权限,确认图床地址可公网访问 |
| Python 解析 import 不准 | ast 只解析静态 import | 改用 Tree-sitter 或 combination 静态分析工具 |
| 动画帧数过多导致 GIF 体积过大 | 提交数量多且分辨率高 | 降低帧率、压缩尺寸、或按模块聚合提交 |
| CI 中 Python 版本过低 | Workflow 未设置版本 | 在setup-python中明确指定版本 |
| 本地调试和 CI 结果不一致 | 分支差异、绝对路径差异 | 在 CI 中打印关键环境变量,启动日志输出 |
6.1 排查清单
如果你遇到“脚本在本地正常,CI 里失败”的问题,按以下顺序排查:
- 先检查 Git 拉取深度:有没有
fetch-depth: 0; - 再检查工作目录:
run-path是否在仓库根目录; - 接着确认依赖安装:Graphviz 系统包有没有安装;
- 然后看环境变量:PR_NUMBER 是否传入;
- 最后看权限:Token 是否能写 PR 评论。
7. 最佳实践与工程建议
7.1 变更集粒度控制
不是每个 PR 都值得生成动画架构图。如果一个 PR 只改了一个 README 文件,动画只有一帧,意义不大。我建议在工具中增加一个过滤条件,只有满足以下条件之一才触发动画生成:
- 变更文件数量大于 3;
- 变更文件包含核心源码目录(如
src/、app/); - 变更涉及至少两个不同的业务模块。
这样可以减少无意义的 CI 任务,也避免污染 PR 评论区。
7.2 节点聚合策略
微服务架构中,一个服务可能包含几十个类。如果每个类都作为图上的一个节点,架构图会非常拥挤。建议按服务名或包名做聚合,只显示一级或二级依赖层级。
例如:
- 把
com.example.order.controller.OrderController聚合为order; - 把
com.example.payment.service.PaymentService聚合为payment; - 只展示
order → payment这条边。
这样动画图才具备“架构”层面的概括能力,而不是变成类关系图。
7.3 缓存与增量计算
大型仓库中,每次 PR 都全量解析所有文件是不明智的。你可以利用 CI 缓存:
- 把解析过的基础分支依赖图缓存下来;
- PR 运行时,只解析变更文件,然后合并到基础图上的增量;
- 这样既能画出“全量架构图”,又能突出“本次变更点”,效率也高。
GitHub Actions 中可以使用actions/cache来缓存中间 JSON 数据。
7.4 可观测性与日志
自动化工具最怕“跑了但不知道跑没跑”。建议设计时输出结构化日志:
[collector] 获取到 12 个变更文件 [parser] 解析完成,生成 8 个节点,5 条依赖边 [renderer] 第 1 帧渲染完成:frame_001.png [renderer] 第 2 帧渲染完成:frame_002.png [animator] GIF 生成成功:pr_architecture.gif (2.3MB) [publisher] 已发布评论到 PR #123同时,把生成 GIF 的下载链接附在 PR 评论中,方便需要详细查看的评审者点击查看原图。
7.5 安全边界与权限最小化
所有涉及 API 调用的代码,都遵循最小权限原则:
- 只让 Token 拥有操作 PR 评论的权限,不要开放
repo全部权限; - 如果工具要下载其他平台的数据,使用短期凭证;
- 不要在日志中输出 Token、密钥、用户隐私信息;
- 对来自 fork 仓库的 PR,优先验证“变更文件是否在允许列表内”,再运行解析脚本。
这一点尤其重要,因为恶意 PR 可能构造一个特殊的代码文件来触发解析器漏洞。解析外部输入时,要做好异常捕获和超时控制。
8. 总结与扩展方向
整个“把 PR 变成动画架构图”的方案,核心价值不在动画本身,而在于让代码评审者可以用更短的时间理解变更影响范围。实现上并不复杂:获取变更文件、解析依赖、渲染帧、合成 GIF、发布评论,五步闭环。把它接入 GitHub Actions 之后,每次 PR 都会自动生成架构动画,团队成员在评审时可以直接看到“哪个服务被改了、依赖了谁、新增了哪些调用”,这对维护大型仓库尤其有帮助。
如果要把这个方案做到生产级,下一步可以往这些方向深入:
- 使用更精准的静态分析工具,支持多语言解析;
- 在架构图动画中加入新增、删除、修改的区分,例如用不同颜色表示改动类型;
- 支持 GitLab CI 和自建 GitLab,扩大适用范围;
- 把架构图数据沉淀下来,支持跨 PR 的架构演进对比;
- 结合 CHANGELOG 或 commit message,自动生成架构变更说明文本。
我建议你从一个小型仓库开始,先把流程跑通,再加入过滤条件、聚合策略和增量解析,逐步完善。动手写一遍,比看十篇文章更有效。如果这篇文章对你有帮助,可以收藏备用,后续遇到 PR 评审不直观的问题时,翻出来按照步骤搭建一套试试。