PlantUML 在 git 工程里的全生命周期:pre-commit / CI / git-lfs / PR 评审

puml.online

PlantUML 图源 + 渲染产物都是 git 仓库的一部分。这一篇整理 end-to-end 实践:改 puml → 校验 → 自动渲 → commit → CI → PR 评审 → rebase → 一切顺。

PlantUML 在 git 仓库的两类「文件」

1
2
3
4
5
6
7
project/
├── docs/diagrams/ ← source
│ ├── auth.puml
│ └── system.puml
└── public/img/diagrams/ ← rendered output
├── auth.svg
└── system.svg

两个目录的工作流:

  • puml 改动 → 触发 render → SVG 改动 → 一起 commit
  • 只改 SVG → 审计告警:「应该改 puml 而不是 SVG」
  • 只改 puml → CI 失败:「图有渲染失败」

理想状态:

任何 commit 里,puml 和 SVG 要么都改,要么都不改

pre-commit hook 强制一致性

用 husky + lint-staged

1
2
npm install --save-dev husky lint-staged
npx husky install

.husky/pre-commit:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh
# .husky/pre-commit
# 1. 校验所有 staged .puml
git diff --cached --name-only --diff-filter=ACMR -- '*.puml' | \
xargs -I{} plantuml -checkonly {}

# 2. 校验 puml 修改后 SVG 也会改(不直接渲染,只校验 source)
for f in $(git diff --cached --name-only -- '*.puml'); do
echo "→ checking $f"
plantuml -checkonly "$f" || exit 1
done

.lintstagedrc.json:

1
2
3
4
5
{
"*.puml": [
"plantuml -checkonly"
]
}

检查图已被 commit(强制 puml+SVG 一并提交)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/sh
# .husky/pre-commit — 强制 puml+svg 一并提交
changed_puml=$(git diff --cached --name-only --diff-filter=ACMR -- '*.puml' | sed 's|^docs/diagrams/||;s|\.puml$||')
changed_svg=$(git diff --cached --name-only --diff-filter=ACMR -- '*.svg' | sed 's|^public/img/diagrams/||;s|\.svg$||')

missing=""
for p in $changed_puml; do
if ! echo "$changed_svg" | grep -q "^$p$"; then
missing="$missing $p.svg"
fi
done

if [ -n "$missing" ]; then
echo "❌ 修改了 puml 但 SVG 没更新: $missing"
echo " 先跑 plantuml 渲染"
exit 1
fi

实际使用:

1
2
3
4
5
6
7
8
9
10
11
12
git add docs/diagrams/auth.puml
git commit -m "改 auth 架构"
# pre-commit 阶段
# → 检测到 auth.puml 改了
# → public/img/diagrams/auth.svg 没改
# → 拒绝提交

# 修复流程
npx plantuml -tsvg docs/diagrams/auth.puml -o public/img/diagrams/
git add public/img/diagrams/auth.svg
git commit -m "改 auth 架构"
# → 通过

CI 渲染脚本

一次渲染全部图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# .github/workflows/render-diagrams.yml
name: Render PlantUML diagrams
on:
push:
paths: ['docs/diagrams/**']
pull_request:
paths: ['docs/diagrams/**']

jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install PlantUML
run: |
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk graphviz default-jdk-headless
wget -O /tmp/plantuml.jar https://github.com/plantuml/plantuml/releases/download/v1.2026.x/plantuml-1.2026.x.jar

- name: Validate syntax
run: |
while IFS= read -r f; do
java -jar /tmp/plantuml.jar -checkonly "$f"
done < <(find docs/diagrams -name '*.puml')

- name: Render
run: |
java -jar /tmp/plantuml.jar -tsvg -thread 4 -o public/img/diagrams docs/diagrams/*.puml

- name: Check for unauthorized SVG edits
run: |
git diff --exit-code public/img/diagrams/ || {
echo "❌ SVG 改动但 puml 没动"
exit 1
}

- name: Commit rendered
run: |
git config user.name "github-actions[bot]"
git config user.email "actions@github.com"
git add public/img/diagrams/
git commit -m "ci: re-render diagrams" || echo "无渲染变化"
git push

关键git diff --exit-code 让「图渲染产物变更但 puml 没动」成为 build fail。

错误恢复

CI 渲染失败(比如某种图类型忘了装支持库):

1
2
plantuml -tsvg docs/diagrams/*.puml
# ❌ ERROR: Diagram not supported

解决:

  1. 装对应库:sudo apt-get install graphviz
  2. 客户端 / 服务端都验证:本地 CLI + 在线 / TeaVM 客户端
  3. 重 push

git-lfs 跟踪大图

何时用 git-lfs

  • 单张 SVG > 50 KB(CJK 图常见)
  • PNG > 500 KB
  • 图库多到仓库总大小 > 50 MB
  • 不想 git diff / git clone 拖慢

配置 .gitattributes

1
2
3
# .gitattributes
*.svg filter=lfs diff=svg merge=union
*.png filter=lfs diff=png merge=union

初次:

1
2
3
git lfs install
git lfs track "public/img/diagrams/*.svg"
git add .gitattributes

迁移历史(大图没经过 lfs 时):

1
git lfs migrate import --include="*.svg"

:lfs 文件 checkout 时需要 git-lfs 二进制—— CI runner 必须装:

1
2
3
4
5
6
- name: Setup git-lfs
run: |
sudo apt-get install -y git-lfs
git lfs install
git lfs fetch
git lfs checkout

PR 评审看图变更

GitHub 原生 SVG diff

GitHub 自动渲染 SVG diff——但实际上

  • SVG 默认被当二进制 diff,不显示内容变化
  • 开启方式:在 .gitattributes*.svg diff=svg

这样 PR 会显示 diff 文本(清晰看到节点位置、配色变化)。

pre-rendered raster diff

更可控:CI 跑一个「渲染并生成 diff image」步骤:

1
2
3
4
5
6
7
8
# 1. pre-merge 状态渲染
git checkout main
plantuml -tsvg docs/diagrams/auth.puml -o /tmp/before/
git checkout feature-branch
plantuml -tsvg docs/diagrams/auth.puml -o /tmp/after/

# 2. 用 image-diff 工具生成 diff image
npx lookatme compare /tmp/before/auth.svg /tmp/after/auth.svg --output diff.png

PR 评论里贴 diff.png 让人直观看到「图怎么变了」。

文本 diff 更轻量

puml 本身是文本——直接 git diff 看 source 变更:

1
2
3
git diff docs/diagrams/auth.puml
# - Alice -> Bob: hello
# + Alice -> Bob: 改 hello 为 hi

PR reviewer 一眼看出。SVG 是渲染产物,rebase / merge 时频繁冲突——源仓存 puml,render cache 由 CI 维护。

rebase / merge 的图同步问题

rebase 时冲突

1
2
3
4
.feature-branch
└── docs/diagrams/auth.puml (修改了 SVG output)
.main
└── public/img/diagrams/auth.svg (其他 commit 改过时也改了 SVG)

rebase 时 git 看到两边都对 SVG 改了,冲突

解决方案 1:源仓只存 puml

1
2
3
4
.gitignore:
public/img/diagrams/

# CI 重新渲染所有图

rebase 后:

1
2
3
plantuml -tsvg docs/diagrams/*.puml -o public/img/diagrams/
git add public/img/diagrams/ # 重新 staged
git rebase --continue

解决方案 2:git pull 时自动重新渲染

1
2
3
4
.gitattributes:
*.svg merge=union

# Git 默认 union merge 策略对 SVG 取并集

但 union merge 不能解决「两边内容冲突」(图拓扑变了),只能对「图片叠合变化」有效。对 PlantUML SVG 来说 union merge 经常生成混合——不推荐。

解决方案 3:CI 强制 SVG 一致

1
2
3
4
5
6
CI:
1. 检出代码
2. 清空 public/img/diagrams/
3. plantuml 重新渲染所有
4. git diff --exit-code
5. 如果 diff 不为空:自动 commit 一个 "sync svg" commit

这种是最干净的方案——SVG 完全由 CI 维护,不入 puml-dev 流程。

一组完整的配置样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# scripts/setup-puml-git.sh
# 在 git 项目里设置 PlantUML 环境
set -e

# 1. .gitignore
cat >> .gitignore <<EOF

# PlantUML render output - regenerated by CI
public/img/diagrams/
EOF

# 2. .gitattributes
cat > .gitattributes <<EOF
# PlantUML SVG: treat as text for diff
*.svg diff=svg merge=ours
EOF
git add .gitattributes

# 3. pre-commit hook
mkdir -p .husky
cat > .husky/pre-commit <<'EOF'
#!/bin/sh
set -e
git diff --cached --name-only --diff-filter=ACMR -- '*.puml' | \
while IFS= read -r f; do
echo "→ checking $f"
plantuml -checkonly "$f" || {
echo "❌ $f syntax error"
exit 1
}
done
EOF
chmod +x .husky/pre-commit

# 4. CI workflow
mkdir -p .github/workflows
cat > .github/workflows/render.yml <<'EOF'
name: render
on: push: paths: ['docs/diagrams/**']
jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: sudo apt-get update && sudo apt-get install -y plantuml
- run: plantuml -tsvg -thread 4 -o out/ docs/diagrams/*.puml
- run: |
git diff --exit-code out/ || {
echo "图渲染了还没 commit"
exit 1
}
EOF

# 5. 完成
echo "✓ PlantUML git workflow 已配置"
echo ""
echo "下一步:"
echo " pre-commit hook 装了,但需要 install"
echo " $ npx husky install"

单作者 vs 多作者工作流

单作者

  • 直接 git add . && git commit
  • pre-commit 校验 + 本地渲染
  • push

多作者团队

  • 中央仓库(main + feature 分支)
  • feature 分支可能改图——不要直接 push main
  • PR review 看到文本 diff + CI 看到渲染 diff
  • merge main 时 rebase + 重新渲染

Fork 派

  • fork 改 puml + 渲染 → PR upstream
  • upstream 看不到 SVG 的「本地版」(fork 私有的)
  • main 收 PR → 立即重渲 → 上公开仓库

关键检查(季度 audit)

1
2
3
4
5
6
7
8
[ ] .gitignore 阻挡 SVG 输出
[ ] .gitattributes 标记 SVG diff
[ ] pre-commit 校验 puml
[ ] CI 强制渲染一致
[ ] git-lfs 跟踪大图(如需要)
[ ] PR template 要求看图 diff
[ ] merge main 后自动重新渲染
[ ] husky 装上了(git hooks 路径)

落地 puml.online 自身的实践

我们的项目:

  • source/_posts/plantuml-*.md 是 source
  • public/blog/plantuml-*/index.html 是渲染产物
  • ✅ 不在 git 跟踪 public/ —— 在 .gitignore 里
  • ✅ CI 在 npm run build 后 hexo re-generate
  • ❌ 没有 pre-commit(我们用单独 review)
  • ❌ 没有 git-lfs(hexo 输出小)

实践:source of truth = markdown(内嵌 puml)。渲染产物不入 git。

小结

  • PlantUML 在 git 的关键是:source(puml/md)和 output(svg)分离—— source 入 git,output 由 CI 重渲
  • pre-commit hook 让作者「改 puml 必须连带渲 SVG」
  • git-lfs 解决大图仓库体积
  • rebase / merge 时强制 re-render 是最干净的姿势

下一步

  • 标题: PlantUML 在 git 工程里的全生命周期:pre-commit / CI / git-lfs / PR 评审
  • 作者: puml.online
  • 创建于 : 2026-07-30 13:15:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-git-lifecycle/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。