起因

上篇写完之后,我那套刷绿墙的东西越用越顺手:每天晚上 20:30 准时几条提交,格子绿得整整齐齐。有天盯着自己的主页,突然意识到一个问题——我能刷,别人也能刷,而且工具满天飞。那我盯着别人的绿墙时,凭什么觉得那是一片「勤劳」?

于是去搜「怎么鉴定绿墙是不是刷的」。结果很有意思:刷墙的工具一抓一大把——gitfiti 能拿格子画画,GitHub Filler 能一键填满过去几年,fake-git-history 专门生成「看起来很努力」的历史。但反过来的工具,鉴定「这面墙有多少水分」的,一个能用的都没有。仅有的讨论全停留在论坛回复的水平:「可以看看提交时间规不规律」。

矛造了一仓库,盾一片空白。行,那我自己造一个。

已经上线了:绿墙鉴定器。输入任何用户名就能测,不用登录、不用装任何东西,它就是个网页。

git 自己就把假话记下来了

抓「补写提交」靠的是一个特别美妙的冷知识:git 里一条提交有两个日期。

作者日期(author date)写着「这段改动是什么时候写的」,提交者日期(committer date)记录着「这条提交是什么时候被创建的」。平时两者相同,没人注意。但刷墙工具改日期重提的时候——git commit --date "2026-03-15T10:00"——改的是前者,后者依然诚实地记录着「这条提交实际诞生于今天下午」。

于是每条补写的提交自带一份口供:作者日期说「我 3 月 15 号写的」,committer 日期说「不,它 8 月 26 号才出生」。两个日期一比对,当场穿帮。

就算学精了把两个日期一起改,GitHub 那边还有第三份记录:公开事件流里存着每次 push 的真实时间。3 月的提交、8 月的推送——三层口供,总有一层说真话。

人类不是这个节奏

第二条证据是时间的形状。脚本刷出来的提交长这样:

1
2
3
4
2026-08-26 20:30:05  chore: daily activity #1
2026-08-26 20:30:06 chore: daily activity #2
2026-08-26 20:30:07 chore: daily activity #3
2026-08-26 20:30:08 chore: daily activity #4

而真人写代码是上午 10 点两条、下午 3 点一条、晚上 11 点改个 typo 再来一条。所以鉴定器把每个仓库每天的提交按时间排序,几条挤在几分钟内接连生成的,记一笔「秒级连击」。单看这条不算实锤——偶尔快速连续小提交是正常的——但和其他证据叠在一起就很有说服力。

message 是刷墙工具的指纹

刷墙脚本懒得给提交编理由,翻来覆去就那几句:chore: daily activity #3、commit #42、一条纯时间戳,或者干脆「update log」。鉴定器把 message 归一化——数字抹掉、转小写、去掉多余空格——再看剩下的模板。一个仓库里六成提交共用同一套模板,这面墙基本可以确定是印出来的。

最后一招:看仓库里到底在动什么

前面都是行为分析,最后一招直接验内容:抽样看提交改了哪些文件。真项目的提交动的是 src/parser.py、index.html,偶尔加个依赖;刷墙仓库一年下来,动来动去只有一个只会追加行的 log 文件。

不冤枉好人的部分

这个工具有一种最坏的可能:把一个每天准时提交的自律真人抓出来示众。为了不干这种事,证据分了等级——补写、message 指纹、空壳仓库是强证据;秒级连击是中等;像 Site updated: 2026-08-31T10:30 这种静态博客的自动部署提交,只算「由机器生成、背后是真实写作」,几乎不扣分。多条证据加权凑到门槛才判「刷」,凑不到就只标「存疑」。最后给出的是 0 到 100 的「真实度」评分,85 以上算干净。

先抓自己示众

工具写完,第一个测的是我自己,结果比想象中有教育意义。

我本来以为它会说「查无此人」——我刷墙的提交全在一个私有仓库里,公开 API 根本看不见,这是它的天然盲区。结果它转头把我博客仓库揪出来了:8 月 25 日 27 条、8 月 24 日 25 条,全是 Site updated: 复读机模板,还有好几条在我反复调样式的时候挤在几分钟里连击。判定:刷。

严格说它没冤枉我。那些确实是机器人批量生成的格子——只不过背后是真实的写作。这件事反而让我看清了一层:绿墙比我想的还要脏,连「真的在写东西」的部分,落到格子上也是机器提交的。

为了验证四条证据链都能触发,我还捏了两条标准假数据喂给它:一组 20:30 整的连击,一组作者日期写在今年 3 月的补写。全抓到了。下面截图里 3 月那个孤零零的红格子,就是「补写」证据链的现场。

绿墙鉴定器运行效果

顺手踩的坑

实现上有个 bug 值得记一笔:拉提交列表要翻页,「上一页不满 100 条就停」是常规判断。我写的版本把「还没拉第一页时的空列表」也当成了「不满」,于是一页都不拉,一条提交都读不到。修法是补一个「到底拉没拉过」的标记。这类 bug 和上次「stdout 是协议通道」那次一样烦人——它不报错,只是安静地给你空结果,让你怀疑人生。

技术上这东西真没什么神秘的:纯前端网页,GitHub 的 API 天生允许浏览器直连,不需要服务器。代价是匿名额度一小时只有 60 次,想深度扫描就自己填个 Token。支持 ?user=用户名 直达,方便把链接甩到群里。不想开网页也有命令行版:node cli/run.js 用户名,一样是四条证据链,Token 可以自动读本地已存的凭据,永不限流。

结尾

上篇结尾说「格子是假的,这些是真的」。写完这个鉴定器想补一句:格子连「假」都藏不住——git 的两个日期、事件流的时间、message 的模板,每一层都在留痕。真想靠绿墙糊弄谁之前,先想想糊弄的是不是自己。

工具地址:nbsheep.github.io/greenwall,源码开源在 greenwall-detector。想刷的,上篇的工具还开源着;想抓的,这篇的也开源着。军备竞赛,从我做起。