Featured image of post Git 中你可能不知道的魔法文件.zh-cn

Git 中你可能不知道的魔法文件.zh-cn

 

Git 中你可能不知道的魔法文件

Git 会从仓库中读取一些特殊文件,用它们来控制自身行为。这些文件并不像 .git/ 里的配置那样留在本地,而是会随代码一起提交,从而影响 Git 对文件的处理方式。

如果你正在开发与 Git 仓库交互的工具(比如 git-pkgs),那么务必确保正确遵守这些配置。

.gitignore

指定 Git 永远不应追踪的文件模式。每行一个模式,支持通配符和目录标记。

1
2
3
4
node_modules/
*.log
.env
dist/

Git 会按顺序检查多个忽略文件:包括每个目录下的 .gitignore、仅限本地的 .git/info/exclude,以及位于 ~/.config/git/ignore(或 core.excludesFile 指定的路径)的全局忽略文件。全局忽略适合 .DS_StoreThumbs.db 这类操作系统专属文件,避免每个项目的 .gitignore 都要重复写这些内容。

模式匹配支持通配符(*.log)、目录标记(dist/)、取反(!important.log)和字符范围。** 模式可匹配嵌套目录。

.gitignore 只影响尚未被追踪的文件。如果某个文件在加入 .gitignore 之前已经被追踪,那么它会继续留在仓库中,并且在各代码托管平台的网页界面上依然可见(需要执行 git rm --cached 才能将其从追踪中移除)。另外,GitHub、GitLab、Forgejo 和 Gitea 的网页编辑器也不会阻止你创建符合忽略模式的文件并提交——它们不会给出任何警告。许多包管理器会附带自身的忽略模式(如 node_modules/vendor/target/),需要你手动将它们添加到 .gitignore 中。

完整的语法请参阅 gitignore 文档。另外,GitHub 维护了一份 .gitignore 模板集合,涵盖各种语言和框架。

.gitattributes

.gitattributes 用来告诉 Git 如何处理特定文件。你可以在这里配置过滤器(filter)、差异驱动(diff driver)、合并驱动(merge driver)、行尾规范化以及语言检测覆盖。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Clean/smudge filters
*.psd filter=lfs diff=lfs merge=lfs

# Line ending normalization
*.sh text eol=lf
*.bat text eol=crlf

# Treat as binary
*.png binary

# Custom diff driver
*.json diff=json

# Merge strategy
package-lock.json merge=ours

# Language detection override for GitHub Linguist
vendor/* linguist-vendored
*.gen.go linguist-generated
docs/* linguist-documentation

text 属性告诉 Git 规范化行尾。binary 属性告诉 Git 不进行 diff 或合并,直接选取一个版本。merge=ours 策略在合并冲突时始终保留本地版本。

GitHub 的 Linguist 工具(用于语言检测)会读取 .gitattributes 来覆盖检测结果。通过 linguist-vendored 可以标记第三方代码,将其排除在语言统计之外;linguist-generated 用来标记自动生成的文件,让它们在 diff 中默认折叠;linguist-documentation 则标记文档,同样不计入统计。

.gitignore 类似,Git 会检查每个目录下的 .gitattributes,同时也支持仅限本地的 .git/info/attributes

完整属性列表请参考 gitattributes 文档,GitHub Linguist 的专属属性请参考 Linguist 覆盖文档

.lfsconfig

.lfsconfig 是随仓库一起提交的 Git LFS 配置文件。它使用 git config 格式来设定 LFS 服务器的端点 URL、传输设置以及其他选项。

1
2
3
4
[lfs]
    url = https://lfs.example.com/repo
[lfs "transfer"]
    maxretries = 3

当执行 LFS 命令时,Git LFS 会自动读取 .lfsconfig。这样一来,你就可以把 LFS 配置也纳入版本控制,让所有协作者使用相同的设置。如果没有这个文件,每个开发者都需要手动配置本地 LFS 环境。

LFS 还会使用 .gitattributes 来标记哪些文件应由 LFS 处理(即上面示例中的 *.psd filter=lfs diff=lfs merge=lfs 模式)。.lfsconfig 文件则处理 LFS 专属设置,比如 LFS 服务器地址。如果你在文件已经提交之后才添加 LFS 的文件模式,需要运行 git lfs migrate 来重写历史记录,将这些文件迁移到 LFS。

全部可用选项请参考 Git LFS config 文档

.gitmodules

.gitmodules 是 Git 子模块的配置文件。当你运行 git submodule add 时,Git 会自动写入这个文件;当运行 git submodule update 时,Git 则会读取它。

1
2
3
4
[submodule "vendor/lib"]
    path = vendor/lib
    url = https://github.com/example/lib.git
    branch = main

每个子模块对应一条记录,包含路径、URL,以及可选的追踪分支。该文件必须放在仓库根目录。

通过子模块,你可以把其他 Git 仓库作为依赖项嵌入到当前项目中。不过,git clone 默认不会拉取子模块内容,你需要额外运行 git submodule update --init --recursive,或者在克隆时加上 --recurse-submodules 参数。

子模块在版本管理上有些笨拙:你追踪的是某个特定 commit,而不是版本范围;它们会创建嵌套的 .git/ 目录;如果忘记更新,很容易导致令人困惑的状态。

不过,对于你自己维护的 vendor 代码管理,或者只需要检出部分目录的 monorepo 结构,子模块仍然是一个可用的方案。

完整工作流请参考 git submodules 文档,文件格式请参考 gitmodules 文档

.mailmap

.mailmap 用来将作者的名字和邮箱地址映射到统一的规范身份。Git 会在 git loggit shortloggit blame 的输出中使用这个映射。

1
2
3
4
5
6
7
8
9
# Map old email to new email
Jane Developer <[email protected]> <[email protected]>

# Standardize name spelling
Jane Developer <[email protected]> Jane Dev <[email protected]>

# Fix both
Jane Developer <[email protected]> <[email protected]>
Jane Developer <[email protected]> J Developer <[email protected]>

格式是 规范名称 <规范邮箱> 提交名称 <提交邮箱>。Git 会查找与提交作者匹配的条目,并在输出时替换为规范身份。

git shortlog -sngit loggit blame 都会遵循 mailmap,将同一作者的不同提交聚合到一起。不过,GitHub 的贡献者图表并不支持 mailmap,这意味着即使你的 mailmap 配置正确,网页上仍然会显示重复的条目。

没有 mailmap,那些更换过邮箱地址或修正过名字拼写的贡献者,就会被当作多个不同的人出现。有了它,他们的所有提交都会聚合到同一个身份下。

文件格式请参考 gitmailmap 文档。mailmap 默认放在仓库根目录的 .mailmap,也可以通过配置 mailmap.file 指向其他位置。

.git-blame-ignore-revs

.git-blame-ignore-revs 文件用来列出 git blame 应该跳过的提交。你可以把大规模代码格式化、lint 修复或其他无关紧要的提交的 SHA 写进去,这样 blame 就会穿透这些提交,指向真正有意义的变更。

1
2
3
4
5
6
# .git-blame-ignore-revs
# Ran prettier on entire codebase
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0

# Migrated to ESLint flat config
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1

要让 Git 使用这个文件,需要执行 git config blame.ignoreRevsFile .git-blame-ignore-revs。不过,GitHub、GitLab(15.4 及以上版本)和 Gitea 会自动读取该文件,无需额外配置。需要注意的是,如果你在全局 git config 中设置了 blame.ignoreRevsFile,那么当 git blame 在缺少该文件的仓库中运行时就会报错——因此,要么为每个仓库单独配置,要么确保所有工作仓库中都至少有一个空的 .git-blame-ignore-revs 文件。

这个文件解决了一个经典问题:对整个代码库运行格式化工具之后,git blame 会变得毫无用处。有了这个文件,blame 就会跳过那些格式化提交,显示真正的代码逻辑作者。

文件格式很简单:每行一个 commit SHA,# 开头的行为注释。详情请参考 git blame 文档

.gitmessage

.gitmessage 是提交信息的模板。通过 git config commit.template .gitmessage 配置后,Git 会在打开提交信息编辑器时自动填充这些内容。

与其他文件不同,.gitmessage 需要每次克隆仓库后手动配置。每个开发者克隆后都要运行 git config commit.template .gitmessage。有些团队会通过初始化脚本自动完成这一步,或者借助 husky 在安装时设置本地配置。正因为多了这个步骤,大多数项目更倾向于使用 commit-msg Hook 来验证提交信息的格式,而不是用模板来引导写作。

git commit 文档 中提到了模板文件。prepare-commit-msg Hook 是另一种替代方案,可以动态生成模板。

代码托管平台专属目录

各大代码托管平台也在仓库中定义了自己的“魔法目录”:.github/.gitlab/.gitea/.forgejo/.bitbucket/ 等。这些虽然不是 Git 的原生功能,但遵循同样的理念——让配置随代码一起分发。

这些目录里通常存放 CI/CD 工作流定义、Issue 和 PR 模板、CODEOWNERS 文件(用于指定路径的代码审查者),以及其他平台专属配置。这样一来,托管平台就能在不污染仓库根目录的前提下扩展功能。

Forgejo 和 Gitea 支持回退机制:Forgejo 的查找顺序是 .forgejo/.gitea/.github/;Gitea 的顺序是 .gitea/.github/。这让你可以在同时托管于多个平台时,覆盖 GitHub 的专属配置。

SourceHut 使用根目录下的 .build.yml.builds/*.yml 来配置 CI,没有专属的目录命名空间。

其他约定

.gitkeep 只是一个惯例,并非 Git 的原生功能。Git 本身不会追踪空目录。如果你想在仓库中保留一个空目录,可以往里面放一个 .gitkeep 文件,这样 Git 就有东西可以追踪了。这个文件名是约定俗成的,实际上你可以起任何名字。

.gitconfig 文件有时会出现在仓库中,作为推荐的配置供人参考。Git 不会自动加载这些文件(出于安全原因),但项目会附上说明,让你运行 git config include.path ../.gitconfig 或手动复制相关设置。这种做法常见于 monorepo 或希望统一特定 Git 设置的项目。

.gitsigners 或类似文件用于追踪可信贡献者的 GPG/SSH 签名密钥。这不是 Git 的原生功能,但一些项目(尤其是 Linux 内核)在签名工作流中使用它。Git 的 gpg.ssh.allowedSignersFile 配置可以指向一个可信 SSH 密钥文件,供 git log --show-signature 用于验证。

.gitreview 配置 Gerrit 代码审查集成。托管在 Gerrit 上的项目(如 OpenStack、Android、Eclipse)使用它来指定推送目标的 Gerrit 服务器和项目。

1
2
3
4
5
[gerrit]
host=review.opendev.org
port=29418
project=openstack/nova.git
defaultbranch=master

运行 git review 时会读取这个文件,并将提交推送到 Gerrit 进行审查,而不是直接推送到分支。这是一个通过提交配置文件来扩展 Git 工作流的典型案例。

.gitlint 配置 gitlint,用于检查提交信息格式。遵循同样的理念:把配置提交到仓库,所有人都使用相同的规则。

1
2
3
4
5
[general]
ignore=body-is-missing

[title-max-length]
line-length=72

gitlint 读取这个文件来验证提交信息格式。类似于使用 commit-msg Hook,但配置随仓库一起分发。

.jj/Jujutsu 的工作区状态目录。Jujutsu 是一个兼容 Git 的版本控制系统,它将自己的元数据存储在 .jj/ 中,同时遵守所有 Git 魔法文件的规则。如果你使用 jj,你的仓库中会同时存在 .git/.jj/,而 .gitignore.gitattributes.mailmap 的行为完全一样。

Git 之外

这种模式并不止步于 Git。其他工具遵循同样的做法:在仓库中放一个点文件,工具自动检测并改变行为。

.editorconfig 跨团队统一编辑器行为。把它放在仓库根目录,编辑器就会读取它来配置缩进风格、行尾、行尾空白和字符编码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

VS Code、Vim、Emacs、Sublime 以及大多数其他编辑器要么原生支持,要么有插件支持。完整规范请参考 editorconfig.org

.ruby-version.node-version.python-version 告诉版本管理器应该使用哪个语言版本。rbenv、nodenv、pyenv、nvm 和 asdf 等工具在你 cd 进该目录时会自动读取这些文件并切换版本。

1
2
3
4
5
# .ruby-version
3.3.0

# .node-version
20.11.0

.tool-versions 是 asdf 的多语言版本文件。用一个文件管理所有语言版本。

1
2
3
ruby 3.3.0
nodejs 20.11.0
python 3.12.0

.dockerignore 的工作方式类似 .gitignore,但针对 Docker 构建上下文。运行 docker build 时,Docker 会将文件发送给守护进程。在 .dockerignore 中列出模式,Docker 就不会发送这些文件。

1
2
3
4
.git
node_modules
*.log
.env

这可以加速构建,并防止密钥泄露到镜像中。语法与 .gitignore 一致:通配符、取反、目录标记。

支持这些文件

如果你正在开发与 Git 仓库交互的工具,可能需要留意并处理这些文件:

  • 遍历仓库目录树时,需要读取 .gitignore
  • 读取 .gitattributes,了解哪些文件是二进制文件、第三方代码或生成代码
  • 显示作者信息时,读取 .mailmap
  • 如果需要处理子模块,读取 .gitmodules

git config 格式(.gitmodules 和其他各种文件使用的格式)为 [section "subsection"] key = value。Git 自带 git config 命令可以正确地读写这些文件。大多数语言的 Git 库都内置了 git config 解析器。


  • 本文长期链接
  • 如果您觉得我的博客对你有帮助,请通过 RSS订阅我。
  • 或者在X上关注我。
  • 如果您有Medium账号,能给我个关注嘛?我的文章第一时间都会发布在Medium。
Licensed under CC BY-NC-SA 4.0
最后更新于 Feb 25, 2026 19:56 CST
使用 Hugo 构建
主题 StackJimmy 设计