最近在使用 Trellis 辅助开发项目时,我发现了一个挺有意思的问题:明明项目的主要业务代码不是 Python,但 GitHub 仓库列表里却显示这个仓库的主要语言是 Python

一开始我还以为是 GitHub 识别错了,后来才发现,问题其实出在 GitHub 的语言统计规则上。

一、问题现象

在 GitHub 仓库列表里,仓库名称下面会显示当前仓库的主要语言,比如:

仓库

GitHub 显示语言

mermaid-edit

Python

drivepilot-android

Python

但是这些项目的核心业务代码可能是 Android、Kotlin、Java、前端代码,Python 只是 Trellis 生成的工具代码、脚本代码或者辅助代码。

这就导致一个问题:

Python 代码体积比业务代码还多,GitHub 就把整个仓库识别成了 Python 项目。

二、为什么 GitHub 会显示成 Python?

GitHub 并不是根据项目名称、README 或者框架来判断语言的,而是使用一个叫 Linguist 的工具来统计仓库中的代码语言。

它大致会根据仓库里被 Git 跟踪的文件内容、文件后缀、文件大小来计算语言占比。

简单理解就是:

文件类型

是否可能被统计

.py

会被统计为 Python

.kt

会被统计为 Kotlin

.java

会被统计为 Java

.js

会被统计为 JavaScript

生成代码

如果没有特殊标记,也可能会被统计

工具脚本

如果没有特殊标记,也可能会被统计

所以,当 Trellis 生成了大量 Python 文件,而这些文件又被提交到了 Git 仓库里,GitHub 就会认为:

这个仓库里 Python 代码最多,所以主语言是 Python。

这并不是 GitHub 出错,而是它不知道哪些代码是真正的业务代码,哪些只是工具代码或生成代码。

三、.gitattributes 是什么?

.gitattributes 是 Git 仓库里的一个配置文件,通常放在项目根目录。

它的作用是给不同文件设置处理规则。

可以这样理解:

文件

作用

.gitignore

告诉 Git 哪些文件不要提交

.gitattributes

告诉 Git 或 GitHub 这些文件应该怎么处理

比如 .gitignore 关注的是:

这个文件要不要进 Git 仓库?

.gitattributes 关注的是:

这个文件进了 Git 仓库以后,应该怎么被识别、比较、统计、处理?

四、.gitattributes 能解决什么问题?

.gitattributes 的用途很多,常见的有:

用途

示例

控制换行符

Windows 使用 CRLF,Linux/macOS 使用 LF

控制 GitHub 语言统计

排除生成代码、第三方代码

标记文档代码

让文档里的示例代码不影响语言占比

指定语言识别

强制某些文件按指定语言统计

在这次的问题里,我们主要使用的是 GitHub Linguist 相关规则。

比如:

trellis/** linguist-generated
.trellis/** linguist-generated

意思是:

trellis.trellis 目录下的文件属于生成代码,不要参与 GitHub 仓库语言统计。

五、常用的 Linguist 规则

GitHub Linguist 支持通过 .gitattributes 修改语言统计行为。

常见规则如下:

规则

含义

linguist-generated

标记为生成代码,不参与语言统计

linguist-vendored

标记为第三方代码,不参与语言统计

linguist-documentation

标记为文档代码,不参与主要语言统计

linguist-language=xxx

强制指定文件语言

例如:

generated/** linguist-generated
vendor/** linguist-vendored
docs/** linguist-documentation
*.kt linguist-language=Kotlin

它们分别表示:

配置

作用

generated/** linguist-generated

排除生成代码

vendor/** linguist-vendored

排除第三方依赖代码

docs/** linguist-documentation

标记文档目录

*.kt linguist-language=Kotlin

强制 .kt 文件识别为 Kotlin

六、如何修复 Trellis 导致的 Python 语言占比问题?

如果项目中 Trellis 生成的 Python 代码比较多,可以在仓库根目录创建或修改 .gitattributes 文件。

推荐写法是按目录排除:

# Trellis generated files
trellis/** linguist-generated
.trellis/** linguist-generated

# Generated files
generated/** linguist-generated

# Tooling scripts, if they are not business code
scripts/trellis/** linguist-generated

这种方式比较稳妥。

因为它只排除 Trellis 相关目录,不会影响其他真正的 Python 业务代码。

七、不建议直接排除所有 Python 文件

有些人可能会这样写:

*.py linguist-generated

这确实可以让 GitHub 不再统计 Python 文件。

但是这个写法有风险。

如果以后项目里真的有 Python 业务代码,比如后端接口、数据处理服务、自动化业务逻辑,那么这些 Python 文件也会被排除掉。

所以更推荐:

写法

是否推荐

原因

trellis/** linguist-generated

推荐

只排除 Trellis 目录

.trellis/** linguist-generated

推荐

只排除隐藏的 Trellis 目录

generated/** linguist-generated

推荐

只排除生成代码

*.py linguist-generated

谨慎使用

会排除所有 Python 文件

除非你非常确定:

这个仓库里的 Python 文件全部都是工具代码或生成代码,不属于业务代码。

否则不要一刀切排除所有 .py 文件。

八、Android 项目可以怎么写?

如果是 Android 项目,业务代码主要是 Kotlin 或 Java,可以这样写:

# Trellis generated files
trellis/** linguist-generated
.trellis/** linguist-generated

# Generated files
generated/** linguist-generated

# Keep Android business code language detection clear
*.kt linguist-language=Kotlin
*.java linguist-language=Java

这样做的目的不是强行“骗 GitHub”,而是告诉 GitHub:

Trellis 相关 Python 文件不是这个项目的主体业务代码,真正的业务代码应该按 Kotlin / Java 统计。

九、修改后怎么提交?

修改 .gitattributes 后,正常提交即可:

git add .gitattributes
git commit -m "Fix GitHub language detection"
git push

推送到 GitHub 后,语言统计不会一定立刻刷新,可能需要等一会儿。

如果规则写得没问题,GitHub 仓库语言显示就会逐渐恢复到更符合实际项目的状态。

十、这件事的本质

这次问题的本质不是 GitHub 识别能力差,而是 GitHub 不知道项目里的哪些代码是“核心业务代码”。

从 GitHub 的角度看,它只知道:

文件

体积

Python 文件很多

Python 占比高

Kotlin / Java 文件较少

Kotlin / Java 占比低

所以它自然会显示 Python。

.gitattributes 的作用就是补充上下文:

哪些代码是生成的,哪些代码是第三方的,哪些代码才是真正应该参与语言统计的。

总结

当使用 Trellis、代码生成器、脚手架、SDK 或其他自动化工具时,仓库里可能会出现大量非业务代码。

这些代码如果被 GitHub Linguist 统计进去,就可能导致仓库语言显示不准确。

解决方式是在仓库根目录添加 .gitattributes,把 Trellis 或生成代码标记为 linguist-generated

核心配置如下:

trellis/** linguist-generated
.trellis/** linguist-generated
generated/** linguist-generated

简单来说:

.gitignore 决定哪些文件不进仓库,.gitattributes 决定进了仓库的文件应该怎么被 GitHub 对待。

对于这次问题,.gitattributes 就是最合适的解决方案。