TL;DR:.gitignore 不生效,几乎总是因为文件已被跟踪。Git 忽略的是它从未见过的文件——索引里的文件对你写的任何规则免疫。用 git check-ignore -v <path> 查明真相:输出沉默意味着路径已被跟踪、没有规则生效。用 git rm --cached <path> 修复,提交之后,忽略规则从那个提交起开始工作。规则顺序、取反陷阱和 VS Code 的误导性现象,解释剩下的少数情况。
.gitignore 为什么不生效?
一个机制覆盖大多数情况:文件在规则存在之前就被提交了。.gitignore 不是给 git 隐藏文件的过滤器——它是一条关于 git add 应该从”未跟踪”状态捡起什么的规定。文件一旦进了索引,git 就会永远跟踪它的内容变化,直到你显式取消跟踪。事后编辑 .gitignore 对已跟踪文件毫无作用,这就是为什么经典的”提交了 .env、慌了、把 .env 加进 .gitignore、再提交一次”序列,每次 push 照样把密钥发出去。
这件事咬到每个人,因为 git 几乎无处不在——Stack Overflow 2022 调查显示 Git 在职业开发者中的使用率超过 93%(survey.stackoverflow.co)——而这些人迟早会为上周提交过的文件写一条忽略规则。
本文其余部分覆盖少数派情况:规则顺序错误、取反陷阱、目录边缘情况和 IDE 问题。但先跑下一节的体检——十次里有九次以上,调查到那一步就结束了。
怎么查哪条 gitignore 规则命中了某个文件?
git check-ignore 是诊断工具,它的沉默本身就是诊断结果:
# 有规则命中时,打印命中的规则 + 文件 + 行号
git check-ignore -v debug.log
# .gitignore:3:*.log debug.log
# 没有规则命中时什么都不打印——文件已被跟踪(或根本没有规则)
git check-ignore -v src/.env
# (沉默 = .gitignore 没在忽略这个路径,不管你写了什么)
# 退出码:0 = 被忽略,1 = 未被忽略——可以直接写进脚本
git check-ignore -q debug.log && echo "ignored" || echo "tracked or unruly" 读 -v 输出:先是忽略来源(.gitignore、.git/info/exclude 或你的全局忽略文件),然后 行号:模式,然后是路径。如果后一条规则让你意外,记住优先级:最后命中的规则获胜,所以 *.log 之后的 !important.log 会重新包含那一个文件。
gitignore 对已提交的文件为什么不生效?
取消跟踪,文件留在磁盘上,提交这次移除:
# 取消跟踪一个文件(本地副本安然无恙——--cached 只动索引)
git rm --cached .env
git commit -m "stop tracking .env"
# 验证忽略规则现在是否生效
git check-ignore -v .env 从这个提交起,.gitignore 接管这个路径:对它的修改不再出现在 git status 里,git add . 也不会再把它捡回来。不过文件还留在历史里——如果它是个密钥,从最新提交里删掉并不够。轮换凭据才是真正的修复,改写历史只是化妆(而且 撤销最后一个提交 只在坏提交还是分支顶端时才帮得上忙)。
一整个仓库的历史遗留垃圾——构建产物、编辑器排泄物、早期混进来的 node_modules——批量取消跟踪只要两行:
git rm -r --cached .
git add .
git commit -m "apply .gitignore to tracked files" 这条命令按当前规则重建索引:被忽略的路径掉出去,其余一切原样加回。diff 看起来很壮观(成千上万条删除),但磁盘上一个字节都没少。如果你的目标是真的删掉这些文件而不只是取消跟踪,那是 git clean -fdx 的地盘——见 git 删除未跟踪文件:安全做法。
Git 忽略它从未见过的文件。已在索引里的文件对
.gitignore免疫——你写的任何规则都无法让它视而不见。
gitignore 对某个目录为什么不生效?
三个目录特有的陷阱:
1. 尾斜杠表达意图,不影响匹配。build 和 build/ 都能匹配目录,但 build/ 表明你指的是目录——一个恰好叫 build 的文件能逃过它。不过这种对称性在重新包含时会失灵(见下一个陷阱)。
2. 取反救不回被排除目录里的文件。git 文档毫不含糊:“如果文件的一个父目录被排除,就不可能重新包含该文件”(git-scm.com/docs/gitignore)。这是个性能决策——git 对被排除的目录整棵跳过,不再走进去。所以这样不起作用:
build/
!build/keep.me # 死规则——git 根本不会看 build/ 里面 修法是排除目录的内容,而不是目录本身:
build/*
!build/keep.me # 有效——build/ 本身仍然开放检查 3. 嵌套的 .gitignore 在自己的范围内获胜。subdir/.gitignore 里的规则对 subdir 下的路径覆盖根文件。当 git check-ignore -v 报出一个你没料到的忽略来源,通常就是这个原因。
gitignore 在 VS Code 里为什么不生效?
几乎从来不是 VS Code 的原因。编辑器的源代码管理视图读的是同一个 git 索引,所以症状一模一样:文件早就被提交了,重启多少次 IDE 也不会改变索引。两件与 VS Code 相关的事实值得知道:
- 资源管理器里灰色 = 被忽略;橙色/黄色 = 已跟踪且有改动。忽略之后文件仍显示为已修改,就是它被跟踪的确证——跑上面的
git rm --cached修复。 .gitignore没出现在”更改”列表里,说明规则在工作——它从一开始就不会作为未跟踪文件出现。人们常报告”VS Code 无视我的 gitignore”,其实是 CLI 的git status和一个没刷新的 SCM 视图不一致;先重载窗口(Cmd/Ctrl+Shift+P→ “Reload Window”),再怪 git 不迟。
.gitignore、.git/info/exclude、全局文件:各管什么?
| 文件 | 作用范围 | 会被提交吗 | 用途 |
|---|---|---|---|
.gitignore(仓库内) | 所有克隆的人 | 是 | 构建产物、依赖、.env——共享规则 |
.git/info/exclude | 仅你的克隆 | 否 | 个人杂物:.scratch/、编辑器排泄物 |
core.excludesFile(全局) | 你所有的仓库 | 否 | 系统垃圾:.DS_Store、Thumbs.db、*.swp |
.gitignore + 取反 | 仓库 | 是 | 重新包含被忽略配置的例外 |
全局文件是最多开发者从没设置过、而应该设置的:
git config --global core.excludesFile ~/.gitignore_global
printf '.DS_Store\nThumbs.db\n*.swp\n' >> ~/.gitignore_global 值得抄走的家规:规则对全团队有益,就进仓库;只对你有益,就进 exclude 或全局文件。把个人忽略规则提交进仓库,就是 .gitignore 长到 300 行、没人知道哪一半还有用的原因。
怎么忽略一个已跟踪文件的改动?
有时你想要一个被跟踪的文件(配置模板、IDE 设置文件),但想让本地修改不再出现在 git status 里。git update-index 的两个 flag,都不属于团队协作流程:
git update-index --skip-worktree config/local.dev # 本地改动不再出声
git update-index --no-skip-worktree config/local.dev # ……以及恢复 --skip-worktree 是站得住脚的那个——它的意思是”我的本地版本有意分叉”。它的表亲 --assume-unchanged 是性能承诺(“这个文件不会变”),不是忽略机制,git 可能无声地打破它。两个 flag 在 pull 时如果上游也改了文件,都会大声失败——持久的解法是创建时就用 exclude 做本地专属配置,或者用一个 git 跟踪的模板文件(config.example)由你自己复制。
30 秒 gitignore 体检
git check-ignore -v <path> # 哪条规则?(沉默 = 已跟踪,无规则)
git ls-files --error-unmatch <path> # 到底有没有被跟踪?
git rm --cached <path> # 取消跟踪,磁盘保留
git commit -m "stop tracking <path>"
git check-ignore -v <path> # 规则现在会显示 诊断、取消跟踪、验证。每一条”gitignore 不生效”报告背后,都是同一个文件戴着两顶帽子——在索引的这一侧被跟踪,在那一侧被忽略——而一个 --cached flag 就能摘掉第二顶。
FAQ
.gitignore 为什么不生效?
多数情况下,文件已经被跟踪。Git 只忽略未跟踪的文件;索引里的文件对 .gitignore 免疫,除非先用 git rm --cached 取消跟踪并提交。
怎么查看哪条 gitignore 规则命中了某个文件?
运行 git check-ignore -v <path>。它会打印出确切的 .gitignore 文件、行号和规则;如果文件已被跟踪且没有规则命中,则静默退出。
git rm --cached 会删除我的本地文件吗?
不会。--cached 只把文件从索引里移除,磁盘上的副本原样保留。提交这次移除后文件变为未跟踪状态,.gitignore 随即生效。
为什么 gitignore 无法重新包含被忽略文件夹里的文件?
出于性能考虑,Git 会整体跳过被排除的目录。git 文档明确写道:如果文件的一个父目录被排除,就不可能重新包含该文件。
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?Ollama 不用 GPU?Linux、Windows 和 WSL 修复指南
喜欢这些文章?我的本职工作就是这样的工程。 雇用我