.gitignore — 那个能避免提交 node_modules 的文件
.gitignore 的实用指南:它做了什么,语法如何工作,node_modules 和 .env 文件的常见模式,以及会捕获所有开发者的已追踪陷阱。
每个开发者都曾至少犯过一次错误:不小心提交了 node_modules, .env 一个包含API密钥的文件,或一个200MB的构建产物。这种恐慌是真实的,修复过程繁琐,而且整个问题都可以通过一个文件避免: .gitignore.
本文解释了 .gitignore 是什么,它的工作原理,应该包含什么内容,以及一些会捕获到经验丰富的开发者的陷阱。
什么是.gitignore?
.gitignore 是一个纯文本文件,告诉Git哪些文件和目录应该从版本控制中排除。当Git看到路径在 .gitignore中列出时,它会假装该路径不存在——它不会将其加入暂存区、提交,或在 git status 输出中显示。
该文件位于仓库根目录,不过你也可以在子目录中放置项目特定的 .gitignore 文件。子目录中的规则仅适用于该目录及其子目录。
为什么需要一个(简短版)
- 安全 ——将密钥、API密钥和密码从你的Git历史记录中排除
- 仓库大小 ——防止生成文件和依赖项使仓库臃肿
- 噪音减少 ——阻止编辑器配置和操作系统临时文件在每次差异中造成混乱
- 团队的正常运作 ——每个人都会克隆一个干净的仓库,并在本地安装依赖项
语法的工作方式
规则很简单,但有一些不太明显的边界情况:
- 空行和以
#开头的行会被忽略(使用#进行注释) - 没有斜杠的模式 会匹配仓库中任意位置具有该名称的文件或目录:
*.log忽略仓库中每个深度的所有日志文件 - 尾随斜杠 仅匹配目录:
dist/忽略该目录,但不忽略名为dist - 的文件 前导斜杠
/todo.txt将模式锚定在仓库根目录:todo.txt仅忽略根目录下的 - 双星号(
**) 可以在目录边界之间匹配:**/logs匹配logs/树中的任意位置 - 感叹号(
!) 否定一个模式,重新包含之前被排除的文件
一个最小示例
# Dependencies
node_modules/
# Environment files
.env
.env.local
.env.*.local
# Build output
dist/
build/
# Editor noise
.vscode/
.idea/
*.swp
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
最常见的条目(以及它们的重要性)
node_modules/
这是最重要的条目。一个典型的Node.js项目在 node_modules/ 中包含成千上万的文件——通常有数百兆字节。这些文件都不应纳入版本控制。任何克隆你仓库的人都会运行 npm install 并从 package.json本地重新构建它。始终忽略它。
.env和秘密文件
环境文件包含数据库密码、API密钥和服务令牌。如果提交了 .env 就等于发生了一起安全事件——GitHub会扫描泄露的密钥,而机器人也会这么做。忽略该文件,提交一个包含占位符值的 .env.example 文件,以便团队成员知道应该设置哪些变量。
dist/和build/
编译或打包输出是从源代码派生的。你的CI流水线在每次部署时都会重新生成它。提交构建产物会导致合并冲突和虚假差异,从而掩盖真正的代码变更。
编辑器和操作系统文件
.DS_Store (macOS), Thumbs.db (Windows), .idea/ (JetBrains), .vscode/ (VS Code设置)——这些是个人工作区文件。提交它们会强制将你的偏好设置强加给所有其他贡献者。使用全局 ~/.gitignore_global 来处理特定机器的噪音,这样你就不必在每个项目中都添加它们。
全局.gitignore:设置一次,永久记住
你可以配置一个全局忽略文件,该文件适用于你机器上的所有仓库:
git config --global core.excludesfile ~/.gitignore_global
将编辑器文件、操作系统垃圾和个性化工具放在那里。保留项目 .gitignore 用于团队共同决定忽略的项目,例如 node_modules/ 或 dist/.
“已跟踪”的陷阱
这是开发者常遇到的问题: .gitignore 仅阻止 未被跟踪 的文件被添加。如果一个文件已经存在于你的Git历史记录中,添加到 .gitignore 将不起作用。Git仍然会跟踪它,并且会继续提交其变更。
要停止跟踪一个已经提交的文件:
# Remove the file from git tracking without deleting it locally
git rm --cached path/to/file
# Or remove a whole directory
git rm -r --cached node_modules/
# Then commit the removal
git commit -m "Stop tracking node_modules"
之后,文件将保留在磁盘上,但Git将从那以后忽略它。
否定规则:重新包含文件
有时你希望忽略一个目录,但保留一个特定文件——例如,忽略 config/ 但保留 config/defaults.json 已提交:
config/
!config/defaults.json
一个需要注意的地方是:否定规则无法取消一个被忽略目录内的文件。Git在进入被忽略的目录时会停止递归,因此 ! 规则不会有机会执行。你必须忽略目录内容而不是目录本身:
# Wrong — Git never sees defaults.json inside an ignored directory
config/
!config/defaults.json
# Right — ignore everything in config/ except defaults.json
config/*
!config/defaults.json
为你的技术栈生成.gitignore
你不需要从零开始编写。 gitignore.io (也位于 gitignore.io) 允许你选择语言、框架和编辑器,立即生成一个全面的忽略文件。GitHub也在其 github/gitignore 仓库中提供官方模板——这些模板维护良好,覆盖了数百种环境。
对于大多数Web项目,一个良好的起点是结合你所使用的语言(Node.js、Python、PHP等)、编辑器(VS Code、JetBrains)和操作系统(macOS、Windows)的模板。
检查Git忽略的内容
两个命令有助于调试意外的忽略规则:
# See which files are being ignored in the current directory
git status --ignored
# Find out exactly which rule is causing a file to be ignored
git check-ignore -v path/to/file
git check-ignore -v 在规则没有达到预期效果时尤其有用——它会打印文件名、行号和负责的模式。
快速参考:模式速查表
| 图案 | 它忽略的内容 |
|---|---|
*.log | 全部 .log 仓库中任意位置的文件 |
/debug.log | 仅在根目录 debug.log 根目录下的 |
logs/ | 任意深度的 logs |
**/logs | logs 目录 |
!important.log | 重新包含 important.log 即使 *.log 匹配它 |
doc/*.txt | .txt 直接位于 doc/ 内部的文件 |
doc/**/*.txt | .txt 位于 doc/ |
