Communitygithub.com

文件历史动画生成器

将文件的 Git 历史记录逐帧渲染为动画,每一帧对应一个视觉上不同的版本——例如 Logo 的演变过程或页面的重新设计。当需要以动画形式展示 Logo、图片、图标或网页随时间变化时使用。

文件历史动画生成器 是什么?

该技能利用 Git 历史记录,将文件的不同视觉版本渲染并合成为 GIF 或 MP4 动画,特别适合展示 Logo、图片、图标或网页的演变过程。它支持图片(PNG、JPG、SVG 等)和 HTML 页面(包括静态页面和基于框架的页面,如 React、Next.js、Vue 等),并能自动进行视觉去重——只保留视觉上不同的版本。 技能通过一个核心脚本 `animate-history.sh` 实现 Git 文件搜索、版本提取、渲染和编码。用户只需用自然语言描述需求,技能会负责解析要追踪的文件路径、按文件谱系分组、处理重命名和移动操作,并自动排除构建产物和 vendored 副本。对于页面,它能在每个提交上截取屏幕截图,并支持跨仓库合并动画。 该工具依赖 `git`、`ffmpeg` 和 `rsvg-convert`(用于 SVG),HTML 渲染则需要 Chrome/Chromium。它默认使用低成本的 Sonnet 模型,判断逻辑简单(选择文件、分组谱系、确定采样率),主要计算量在脚本和 Shell 循环中。生成的动画默认保存在当前目录,不会自动提交到仓库。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/echarrod/skillsed/tree/HEAD/skills/animate-file-history

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

文件历史动画生成

构建一个 GIF(或 MP4)来展示代码仓库中某个文件随时间变化的外观。请求使用自然语言($ARGUMENTS),因此任务如下:找出请求涉及的文件,从 Git 中提取每个版本,渲染每个版本,丢弃与前一个版本视觉相同的版本,然后将剩余版本拼接成动画。

配套脚本负责 Git 遍历、渲染、视觉去重和编码: scripts/animate-history.sh,路径相对于本技能的基目录,而非用户的仓库。下文中的 $SKILL 表示该目录;使用绝对路径调用脚本。--help 列出所有选项。你的工作是为图片选择合适的文件,并为页面生成屏幕截图。

1. 检查工具

command -v git ffmpeg rsvg-convert; ls "/Applications/Google Chrome.app" 2>/dev/null; command -v chromium google-chrome

ffmpeg 是必需的。SVG 需要 rsvg-convert(brew install librsvg / apt install librsvg2-bin)或 Chrome/Chromium。--label 字幕和 HTML 页面需要 Chrome/Chromium(CHROME=/path 可覆盖检测)。如果缺少某个工具,请说明是什么以及如何安装——不要悄悄地跳过 SVG 版本。

2. 根据请求解析文件

搜索当前分支的整个历史记录,而不仅仅是今天的文件树——Logo 可能会被重命名、移动和重新导出,已删除的文件仍然属于故事的一部分:

git log --format= --name-only -- ':(glob,icase)**/*logo*' | sort -u

搜索的是该对象的名称,而不仅仅是用户的原词。Logo 也可能出现在 favicon*、apple-touch-icon*、*mark*、*lockup*、*wordmark*、*brand* 以及产品名称本身中——早期版本往往只在 favicon 里。相应地扩展 glob 列表,并查看返回的结果。

过滤出可渲染的类型:图片支持 png jpg jpeg gif webp bmp svg;静态页面支持 html。忽略构建输出和 vendored 副本(dist/、build/、node_modules/、.next/)。

然后将匹配结果分组为谱系——每个谱系生成一个动画:

  • 同一幅作品经过重命名、移动或格式变化(logo.png → brand/logo.svg)属于一个谱系。将它们的所有路径传递给一次运行;脚本会根据提交日期将它们合并到一个时间线中。
  • 同时存在的变体(logo-dark.svg、logo-mono.svg、favicon.png、不同尺寸的导出文件如 [email protected])属于独立的谱系。默认为每个谱系生成一个动画,并且只动画化请求可能涉及的那些变体(通常是主 Logo,而不是每个 favicon 尺寸)。

如果分组确实存在歧义(几个不相关的“logo”文件,或者没有匹配项),则显示候选列表及其提交次数(git log --follow --oneline -- <path> | wc -l),并在渲染之前请求确认。

3a. 图片:运行脚本

"$SKILL/scripts/animate-history.sh" -o logo-history.gif --label <path> [<older-path> ...]

有用的选项:-s 512 或 -s 1200x800 画布尺寸,-t 0.25 每版停留秒数(图片默认值;页面截图的 --frames 默认值为 0.8;如果用户指定了值则使用用户的值,例如“每帧 0.5 秒” → -t 0.5),--hold 1.5 在最后一帧停留秒数,--bg '#0b0b0b' 适用于为深色背景设计的 Logo,--keep DIR 保留帧文件,-o x.mp4 输出视频,--skip <sha>(可重复)丢弃用户不想要的版本。脚本会在 stderr 上打印每个提交的 keep/skip 以及 kept N of M versions。

路径是相对于当前目录的(或 -C DIR),就像任何 Git pathspec 一样;从仓库根目录运行,并传入上面历史列表所示的路径。脚本本身会处理重命名跟踪(git log --follow),因此通常只给出当前路径就足够了,除非文件在新名称下被重新创建。

3b. 页面:在每个版本截图,然后在文件夹上运行脚本

页面需要在每个提交上进行渲染。找到页面的源文件(index.html、app/page.tsx、src/pages/index.vue 等),并列出修改了该文件或其渲染内容的提交——首页的外观也会随其组件和 CSS 而变化:

git log --format='%H %cs' --reverse -- <page file> <its components/styles dirs>

如果提交数超过约 40 个,则进行采样(每周或每月一个,加上第一个和最后一个),并说明情况。屏幕截图存放在一个文件夹中,命名为 <YYYY-MM-DD>_<short-sha>.png,这样文件按日期排序,名称即成为字幕。

  • 静态 HTML:无需构建。使用 git archive <sha> | tar -x -C "$snap" 导出提交,然后使用 "$CHROME" --headless=new --hide-scrollbars --window-size=1280,800 --screenshot=... "file://$snap/<page>.html" 截图该文件。之后删除 $snap,并将其保持在仓库之外。

  • 框架页面(React、Next.js、Vue、Astro 等):必须运行才能渲染。每个提交使用 snapshot-commit.sh:

    "$SKILL/scripts/snapshot-commit.sh" -C <repo> -r /<route> -f https://<production site> <sha> "$shots"
    

    它会导出提交,安装该提交自己的 lockfile(按 lockfile 缓存),启动开发服务器(如果不是 npm run dev -- --port {port},则使用 -d 'cmd with {port}'),并通过 Playwright 截图(scripts/screenshot-page.mjs,需要 playwright-core 和 Chrome——仓库自带的通过 PLAYWRIGHT_DIR 查找)。它会等待网络空闲和每个 <img> 加载,关闭 Cookie 横幅,并输出一行:ok、blank、error(显示错误页面,例如“无法在此浏览器中加载”)或 fail(安装或开发服务器失败)。只有 ok 会写入 PNG。如果有超过几个安装操作,请先请求许可再运行。

在制作动画之前检查结果,不要仅信任 ok:

  • 切勿借用当前的 node_modules。 旧提交会导入当前树中已删除的包(例如 react-router-dom),导致页面渲染为空白。这就是脚本需要为每个提交单独安装的原因。
  • blank / error / fail 提交:查看开发日志,尝试修复原因(缺少环境变量、错误的 -d 命令、--ignore-scripts 跳过了必要的构建步骤)一次,否则跳过该提交并说明跳过了多少个。
  • 损坏的图片(ok 行上的 broken-images=N,以及 URL):开发服务器无法提供的资源(平台托管的路径如 /__l5e/...、被 gitignore 忽略或上传的文件)。-f <production site> 会从生产环境获取它们。如果图片仍然损坏,请在报告中说明——不要默默地交付一个满是空框的 GIF。应在发现问题时立即修复,而不是事后报告。
  • 在最终运行之前,先查看帧的缩略图(ffmpeg -pattern_type glob -i '*.png' -vf scale=320:-1,tile=6x6 -frames:v 1 sheet.png)。

然后:

"$SKILL/scripts/animate-history.sh" --frames "$shots" -s 1280x800 --label -o home-page-history.gif

与处理图片时一样,相同的截图会被丢弃,空白(单一纯色)截图也会被丢弃(使用 --keep-blank 保留)。每次截图使用固定的窗口尺寸。对于较长的或细节丰富的页面,建议使用 -o x.mp4——全页面的 GIF 会变得很大。

3c. 跨多个仓库

当 Logo 存在于多个仓库中(例如一个应用、一个仪表板、一个网站)时,在每个仓库中使用 --keep <dir> 运行脚本(不要加 --label),然后将每个保留的帧复制到一个文件夹中,命名为 <YYYY-MM-DD>_<repo>-<short-sha>.png——stderr 上的 keep 行按顺序给出了每个保留帧的日期和 SHA。对于名称冲突(一个提交中修改了多个文件),在名称后添加 -2、-3。然后在合并后的文件夹上运行 --frames <folder> --label -t 0.25,以生成跨所有仓库的统一时间线。

4. 报告

给出输出路径、找到的版本数量以及实际进入动画的版本数量、日期范围,以及任何被跳过的内容(不可渲染、构建失败、采样排除)。如果被要求则打开它(open x.gif)。将输出写入用户希望的任何位置,默认为当前目录——除非明确要求,否则不要提交它。

注意事项

  • 去重基于渲染后的像素,而非字节。 如果重新导出的 PNG 或重新格式化的 SVG 渲染结果相同,则会被跳过;一个像素的偏移都算作一次变化。这就是重点——不要再在其上添加字节级别的过滤器。
  • 引用外部文件的 SVG(字体、<image href>)会在没有这些外部文件的情况下渲染,因为只有 SVG 本身是从历史记录中提取的。内嵌内容没有问题。
  • 动画 GIF 源仅贡献其第一帧。
  • 合并提交不会被遍历——git log 显示的是文件在创建该更改的分支上的变化,这正是时间线所需的信息。
  • 浅克隆(CI 环境、--depth 1)没有历史记录。检查 git rev-parse --is-shallow-repository,如果返回 true,建议运行 git fetch --unshallow。
  • 字幕需要 Chrome/Chromium,因为捆绑的 ffmpeg 版本通常缺少 drawtext。

模型与计算量

这里的判断工作量很小(选择文件、分组谱系、确定采样率);繁重的工作由脚本和 Shell 循环完成。默认使用 Sonnet 低计算量。Opus 或更高计算量对此任务没有额外收益,也不需要使用分支或子代理。仅当谱系分组确实存在歧义时才提高计算量。

一次运行的大部分 token 消耗来自会话的继承上下文(CLAUDE.md、技能和工具列表,约 10 万+ token),每次轮换都会从缓存中重新读取,而非来自任务本身。在轻量会话中的运行成本远低于在负载会话中的成本。

基准测试

使用会话记录进行测量(汇总助手轮次中的 usage):

f=$(ls -t ~/.claude/projects/<project-dir>/*.jsonl | head -1)
jq -s '[.[]|select(.message.usage)|.message.usage]|{turns:length,out:(map(.output_tokens)|add),cache_read:(map(.cache_read_input_tokens)|add),cache_create:(map(.cache_creation_input_tokens)|add)}' "$f"
日期模型 / 计算量任务轮次输出 token缓存读取缓存创建结果
2026-10-23Sonnet 5.5 / 低storra 首页,仅发现阶段(渲染前)93.6k731k134k约 7 次调用找到文件和路由历史

每次完整运行添加一行(不仅仅是发现阶段);仅当 Sonnet 低计算量运行出现谱系或采样错误时,才与 Opus / 更高计算量进行比较。

相关技能