dsh-git-heatmap
在 DeepSeek Harness 的 Web GUI 里,把本机多个 git 仓库的提交渲染成 GitHub 风格的 53×7 贡献热力图。
针对的是一个 GitHub 覆盖不到的场景:代码散落在一台机器的几十个仓库里,托管平台各不相同(GitHub / Gitee / 自建 GitLab / codeup),把「我今年到底写了多少」做成一目了然的格子。
早期版本(v0.1.x),接口与配置项可能变动。
功能
- 多仓库聚合:递归扫描配置的目录,发现所有 git 仓库,合并成一张图
- 只算本人:按 author email 认领提交,避免把 fork/上游历史算成自己的
- 时间窗:53 / 26 / 12 周、全部历史、自定义区间
- 仓库过滤:勾选任意仓库组合,色阶按过滤后的分布重算
- 可读的交互:hover 看当天明细(本地/远程分别计数)、点击看当天提交列表、深浅色主题自动适配
- 远程贡献:把 GitHub / Gitee 上的贡献合并进同一张网格(可选)
- 可配置外观:品牌色 / GitHub 绿 / 自定义四色,色阶可按分位自适应或按绝对值固定
- 全部在页面内设置,不需要编辑配置文件
安装
需要已安装 DSH,且版本 ≥ 0.2.1-rc.3。
git clone https://github.com/senmoxiu/dsh-git-heatmap.git
cd dsh-git-heatmap
dsh plugin --profile <你的 profile 名> add link:"$(pwd)"
也可以让 Harness 自己的 plugin_manager 安装:action: install_bundle,target 传本目录绝对路径。
装完刷新页面,左侧栏出现「Git 热力图」图标,点开是整页。
首次使用
插件不带任何预设——没有内置路径,也没有内置邮箱。所以刚装上是空的,页面顶部会提示去配置。
打开页面右上「设置」,至少填两项:
| 项 | 说明 |
|---|---|
| 扫描根 | 一行一个目录,插件在这些目录下递归找 git 仓库(深度 6 层,自动跳过 node_modules 等) |
| 邮箱别名 | 你提交时用的 author email,一行一个。只统计命中的邮箱 |
填完点「保存并刷新」即可。这两项也是权限边界:不在扫描根里的目录不会被读取,扫描只读不写。
GitHub / Gitee 远程贡献(可选)
在设置页填用户名与令牌并勾选「启用」,托管平台上的贡献会合并进同一张网格。
令牌存在哪:优先环境变量(GitHub:GITHUB_TOKEN / GH_TOKEN,Gitee:GITEE_TOKEN);没有则读 $DSH_HOME/git-heatmap.secrets.json(面板里填的令牌就写在这里,权限 0600)。令牌不会出现在任何接口响应或日志里,配置视图只回一个「已设置」布尔值。
| 有令牌 | 无令牌 | |
|---|---|---|
| GitHub | GraphQL contributionsCollection,精确日历,含私有仓库 |
公开事件流:约 90 天、最多 300 条,按 push 计数(GitHub 已不在 PushEvent 里带提交数) |
| Gitee | /users/{login}/events |
/users/{login}/events/public:最近 100 条,容易触发限流 |
长度限制带来两个已知偏差,页面上会以告警形式显示:GitHub 的接口单次最长 1 年(本插件按年切分后并发请求再合并);Gitee 的匿名事件流不支持翻页。
某个来源不可用(未配置 / 限流 / 用户不存在)只会变成状态行里的一行提示,不影响本地统计。
按天看远程提交
点某一天时,面板里能按需拉取那天的具体远程提交(不是只有数字)。两家的代价不同:
| 匿名 | 有令牌 | |
|---|---|---|
| GitHub | 可用,但只搜公开仓库,限 10 次/分钟 | 含私有仓库,30 次/分钟 |
| Gitee | 只有最近 100 条事件,更早日期查不到 | 同样受 100 条事件上限限制 |
一个必须知道的数字差异:GitHub 的「贡献数」= 提交 + PR + Issue + 评审。所以某天格子上有贡献,按天查提交却可能为 0——这是口径差异,界面会明说,不当作错误。
统计口径
- 按邮箱认领:author email 必须命中别名表。一台真实机器上 23,933 条提交里只有 694 条属于本人——不筛的话 97% 的格子是别人的。
- 全分支 + 全局去重:
git log --all天然按 SHA 去重。不要逐分支相加:某 8 分支仓库逐分支加总 5,793 条,--all只有 856 条。 - 归日按配置时区:只认
%at(epoch 秒)再按Asia/Shanghai之类的时区换算,不解析%aI自带的偏移(部分提交以Z结尾会算错)。 - 口径收敛到网格窗口:卡片上的「总提交 / 活跃天数 / 最长连续 / 最忙一天」只算当前窗口内,否则数字会对不上格子里数出来的;全时段数字另存于
stats.allTime。 - 不做增量:几十个仓库全量重扫约 1.5–2 秒,增量每次进程启动就要 ~90 ms,是负优化。结果缓存 5 分钟。
数据与隐私
- 所有 git 读取都在本机宿主进程内完成,仓库路径、提交信息不会上传
- 远程贡献只在启用后请求对应平台,且只取「某用户某区间的每日贡献计数」
- 扫描根之外的目录不会被读取
- 令牌只进内存与本地 secrets 文件,不写日志、不回显
权限与外部服务
这个插件需要比“纯 UI 装饰”更高的能力,全部列在这里,便于人工评估。没有任何隐藏行为:下面就是它访问的全部边界。
| 能力 | 具体用法 | 边界 |
|---|---|---|
| 文件读取 | 递归扫描配置的扫描根,读取目录项与 .git 是否存在 |
只读;只在扫描根内;符号链接不跟进 |
| 文件写入 | 仅两个文件:$DSH_HOME/git-heatmap.json(配置)与 $DSH_HOME/git-heatmap.secrets.json(令牌,0600) |
不写扫描根,不写仓库 |
| 子进程 | 只执行 git:git --version 与 git log --all |
不经 shell,无管道拼接;Windows 上固定 git.exe |
| 网络 | 可选。仅 api.github.com 与 gitee.com,只在启用远程贡献后发起 |
只读 GET/POST 查询;支持 proxyUrl 配置;失败只降级为一行状态 |
| 凭据 | 读取 GITHUB_TOKEN / GH_TOKEN / GITEE_TOKEN 或 secrets 文件 |
只用于上述两个域名的 Authorization 头;不落日志、不进响应 |
| 动态加载 | lib/index.js 单向 import('./remotes.js'),带 ?v=<mtime> 缓存参数实现热重载 |
同包内、单文件、路径在编译期固定;不加载远程代码、不 eval、不拼装路径;文件缺失即降级为“适配器不可用” |
失败边界:远程来源不可用时只影响远程部分(状态行提示),本地统计照常返回;git 不可用时页面显示探针失败原因;扫描目录不存在时返回空模型 + setup 提示,不抛异常。
没有生命周期脚本:package.json 不含 preinstall / install / postinstall / prepare,安装时不执行任何代码。零运行时依赖:host 半只用 node: 内建,@deepseek-ai/dsh 与 react 为 peer 依赖(由宿主提供)。插件入口 ID 唯一:ui-git-heatmap。
已知限制
- Gitee 覆盖不完整:匿名接口只给最近 100 条事件且不支持翻页,更早的日期会缺失
- GitHub 无令牌时数字偏小:公开事件流按 push 计,不等于提交数,且只覆盖约 90 天
- Windows 依赖
git.exe:.cmdshim 无法被无 shell 的 spawn 启动,见下方开发说明 - 早期版本,未做 i18n(界面文案中文,插件卡片标题与描述提供中英两份)
开发
没有构建链:lib/*.js 就是发布产物。
package.json 双面插件 manifest(exports "." → host,"./client" → 浏览器)
cordis.patch.yml 一行 insert,挂进 profile 的 bundle 列表
lib/index.js host 半:/git-heat/* 路由 + 受管子进程跑 git
lib/remotes.js 远程适配器:GitHub GraphQL / Gitee events(按 mtime 热重载)
lib/client.js 浏览器半:侧栏图标 + 整页(React.createElement,非 JSX)
locale/{zh,en}.json 插件卡片标题与描述
host 半是普通 ESM,只用 node: 内建,零运行时依赖;浏览器半是 Harness 的模块加载器工厂格式(window.__ModuleLoader__.load({ id, factory }))。
接口
| 路由 | 说明 |
|---|---|
POST git-heat/heat |
热力图模型:仓库清单 + 按日聚合 + 本人提交列表;{"refresh":true} 绕过缓存 |
POST git-heat/config |
当前生效配置(不含令牌) |
POST git-heat/config/save |
局部写入配置;字段级校验,非法字段忽略并保留原值 |
POST git-heat/probe |
在宿主进程里跑 git --version,用于排查 git 不可用 |
POST git-heat/remote |
远程源探针:每个平台「未配置 / 令牌无效 / 可用」及告警 |
改动时注意
- 路由前缀是
/git-heat:不要用/git,会和 git-graph 类插件冲突 - 两处 UI 注册都要走
slots.inject:裸register在没声明该席位的 shell 上会抛错,表现为「插件没有激活」 - 侧栏图标带
data-dsh-panel-entry:皮肤层靠这个语义锚点识别面板归属 - Windows 上必须写
git.exe,且子进程要钉LC_ALL=C(否则 zh_CN 宿主上 git 报错变中文,错误分类失效) - 颜色只用
--dsw-*变量:深浅色由body[data-ds-dark-theme]决定,不要用 JS 判断主题
No comments yet. Be the first to write one.