.gitignore 是 Git 项目中用来告诉 Git 哪些文件不需要跟踪的配置文件。看似简单,但它的匹配规则有不少细节——理解这些规则可以让你写出更精确的忽略配置,避免不该提交的文件被误提交,或者该忽略的没忽略。
一、基本语法
注释
以 # 开头的行是注释,会被 Git 忽略。
# 这是注释,不会生效
# 下面是忽略 node_modules
node_modules/空行
空行同样被忽略,用于分隔不同的规则组,提高可读性。
# 依赖目录
node_modules/
vendor/
# 构建输出
dist/
build/二、基础匹配模式
逐行规则
.gitignore 文件中,每一行是一条独立的匹配规则。
# 忽略 .log 文件
*.log
# 忽略 node_modules 目录
node_modules/忽略特定文件
直接写文件名,匹配任意目录下的同名文件:
# 匹配任意目录下的 .env 文件
.env
# 这样会忽略 a/.env、b/.env、.env 等通配符 *
* 匹配任意数量的任意字符(/ 除外)。
# 匹配所有 .log 文件
*.log
# 匹配以 test 开头的任意文件/目录
test*
# 注意:*.log 会匹配 a.log、b/test.log,但不会匹配 a/b/c.log 吗?
# 实际上会匹配任意层级的 .log 文件!因为 * 不匹配 /,但规则本身是递归的。这里有个很多人搞混的点:*.log 实际上会匹配任意目录下的 .log 文件,因为 Git 的规则默认是递归的。后面会讲如何限制到当前目录。
单字符通配符 ?
? 匹配任意一个字符(/ 除外)。
# 匹配 a.txt、b.txt,但不匹配 ab.txt
?.txt
# 匹配 1.log、a.log,但不匹配 10.log
??.log字符集 []
匹配方括号中列出的任意一个字符。
# 匹配 a.txt、b.txt,但不匹配 c.txt
[ab].txt
# 匹配 1.txt、2.txt、3.txt
[123].txt
# 范围:匹配 a.txt ~ f.txt
[a-f].txt
# 取反:匹配不是 a 或 b 的 .txt 文件
[!ab].txt三、路径匹配
目录分隔符 /
/ 在 .gitignore 中有特殊含义:
# 规则开头的 / —— 仅匹配仓库根目录
/build # 仅忽略根目录下的 build 文件/目录
# src/build 不会受影响
# 规则结尾的 / —— 仅匹配目录
dist/ # 仅忽略 dist 目录(及其内容)
# dist.zip 文件不会被忽略
# 规则中间的 / —— 匹配相对于 .gitignore 位置的路径
docs/out/ # 仅忽略 docs/out 这个具体路径
# src/docs/out 不受影响绝对路径 vs 相对路径
规则开头的 / 表示相对于 .gitignore 文件的路径,而不是从系统根开始。
# 只忽略项目根目录下的 dist 目录
/dist
# 以下会忽略任意层级下的 dist 目录(默认递归行为)
dist深度限制
如果没有 /,规则会递归匹配所有层级。如果有限制 /,只匹配指定路径。
# 递归匹配(默认)
*.log # 匹配 a.log、src/a.log、src/a/b.log……
# 仅根目录
/*.log # 只匹配项目根目录下的 .log 文件
# 仅某目录下
build/*.log # 匹配 build/a.log,但不匹配 src/build/a.log四、** 高级模式
** 是 Git 对标准 glob 的扩展,匹配任意层级的目录。
** 匹配零个或多个目录
# 匹配 a 目录下任意层级的 .log 文件
a/**/*.log # 匹配 a/b.log、a/b/c.log、a/b/c/d.log……
# 匹配所有层级下的 temp 目录
**/temp/ # 匹配 temp/、a/temp/、a/b/temp/……** 的常见用法
# 忽略所有 test 目录
**/test/
# 忽略 a 目录下的所有内容
a/**
# 从根目录开始的任意深度匹配
**/node_modules/ # 和 node_modules/ 效果一样** 与单 * 的区别
| 模式 | 匹配的文件 |
|---|---|
a/*.log |
a/1.log ✅,a/b/1.log ❌ |
a/**/*.log |
a/1.log ✅,a/b/1.log ✅,a/b/c/1.log ✅ |
五、取反 !
以 ! 开头的规则表示不忽略匹配的文件,相当于"白名单"。
# 忽略所有 .log 文件,但保留重要日志
*.log
!important.log
# 忽略所有目录下的 config,但保留根目录的
config/
!config/
!/root-config/取反的注意事项
取反只能对已经匹配的文件生效。
# ❌ 这不会生效,因为目录已被忽略
dist/
!dist/important.txt
# ✅ 要这样写
dist/*
!dist/important.txt
# 先忽略 dist 下所有内容,再保留特定文件取反不能恢复被忽略的目录中的文件
如果目录被忽略了,取反其中的文件可能不会生效,除非 Git 能"看到"那个目录。
# 以下规则可能不会生效
src/
!src/keep.txt
# 正确做法:不忽略目录,只忽略目录中的文件
src/*
!src/keep.txt六、匹配优先级
后定义的规则优先级更高
如果同一个文件匹配了多条规则,后定义的规则覆盖先定义的。
# 先忽略所有 .env 文件
.env
# 再保留 .env.example
!.env.example
# 等价于:忽略所有 .env 文件,除了 .env.example就近原则
如果一个文件被父目录的 .gitignore 忽略,但被子目录的 .gitignore 不忽略,则子目录的规则优先。
project/
├── .gitignore # 忽略 *.log
├── src/
│ ├── .gitignore # 不忽略 important.log
│ ├── app.log # 被 project/.gitignore 忽略
│ └── important.log # 被 src/.gitignore 恢复
└── api/
└── debug.log # 被 project/.gitignore 忽略七、常见场景示例
前端项目
# 依赖
node_modules/
# 构建产物
dist/
build/
*.zip
# 环境变量
.env
.env.local
.env.*.local
# IDE 配置
.vscode/
.idea/
*.swp
*.swo
# 系统文件
.DS_Store
Thumbs.db
# 日志
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 缓存
.cache/
.eslintcache后端项目
# 依赖
vendor/ # PHP
venv/ # Python
.env/
# 运行时
*.pyc
__pycache__/
*.pid
# 配置文件(保留模板)
.env
!.env.example
# 上传目录(保留目录结构)
public/uploads/*
!public/uploads/.gitkeep
# 日志
storage/logs/*
!storage/logs/.gitkeep八、全局 .gitignore
除了项目级的 .gitignore,Git 还支持全局模式,适用于所有项目。
# 设置全局 gitignore
git config --global core.excludesFile ~/.gitignore_global
# 在 ~/.gitignore_global 中添加通用规则
echo ".DS_Store" >> ~/.gitignore_global
echo "Thumbs.db" >> ~/.gitignore_global
echo "*.swp" >> ~/.gitignore_global这样系统文件和编辑器临时文件就不需要在每个项目中重复配置了。
九、验证 .gitignore 规则
查看哪些文件被忽略
# 查看所有被忽略的文件
git status --ignored
# 检查特定文件是否会被忽略
git check-ignore -v src/app.log
# 输出会显示匹配的规则和行号
# 检查多个文件
git check-ignore -v node_modules/package.json .env调试 .gitignore
# 如果文件已经被跟踪,.gitignore 不会生效
# 需要先删除缓存
git rm --cached .env
git commit -m "停止跟踪 .env"
# 然后再添加 .gitignore 规则十、总结
| 符号 | 含义 | 示例 |
|---|---|---|
* |
匹配任意字符(不含 /) |
*.log |
? |
匹配单个字符 | ?.txt |
[abc] |
匹配集合中任一字符 | [abc].txt |
[!abc] |
匹配不在集合中的字符 | [!abc].txt |
** |
匹配任意层级目录 | a/**/*.log |
/ 开头 |
仅匹配根目录 | /dist |
/ 结尾 |
仅匹配目录 | dist/ |
! 开头 |
取反,不忽略 | !important.log |
# 开头 |
注释 | # 这是注释 |
记住三条核心原则:
.gitignore规则默认是递归的——*.log匹配所有层级- 后定义的规则优先级更高——取反规则要放在被取反的规则之后
- 已跟踪的文件不受
.gitignore影响——需要先git rm --cached