.gitignore 不生效?已提交的文件怎么忽略?规则与排查一文讲清

· 约 3 分钟 🚫 .gitignore 生成器

“我明明写了 .gitignore,node_modules 怎么还在提交?“——这是 Git 新手最常见的困惑之一。问题几乎都出在同一个认知误区上。理解它,再配一张语法速查,gitignore 就不再玄学。

核心规则:只对”未跟踪”的文件生效

.gitignore 的作用是阻止 Git 开始跟踪一个还没被跟踪的文件。它管不了已经被跟踪的文件

所以如果某文件之前已经 git add / commit 过,它已进入索引,事后再往 .gitignore 里加规则,Git 不会回头忽略它——这就是”加了 gitignore 却不生效”的真相。

已提交的文件怎么忽略:git rm —cached

正确做法是先把它从索引里移除,再让 gitignore 接管:

# 单个文件
git rm --cached <>
# 整个目录(如已误提交的 node_modules)
git rm -r --cached node_modules
# 然后确认 .gitignore 里有对应规则,提交
git commit -m "stop tracking ignored files"

--cached 的关键作用:只从 Git 索引移除,磁盘上的文件原样保留。少了 --cachedgit rm 会把本地文件也删掉,别搞错。

gitignore 语法速查

写法含义
*.log当前及各层级所有 .log* 不跨目录分隔符)
build/任意层级名为 build 的目录
/build匹配仓库根的 build
**/foo任意层级的 foo
logs/**/*.tmplogs 下任意深度的 .tmp
!keep.txt否定规则,取消对 keep.txt 的忽略
# 注释注释行

否定规则(!)的父目录陷阱

想”忽略整个目录但留一个文件”是常见需求:

.vscode/*
!.vscode/settings.json

但如果你写成了 .vscode/(带尾斜杠的整体排除),Git 根本不会进入该目录,里面的 ! 否定就失效了。规则是:要保留子文件,父目录不能用 dir/ 形式整体排除,得用 dir/* 这种”排除内容但目录可进入”的写法。

全局 gitignore:别在每个项目重复写

.DS_StoreThumbs.db.idea/.vscode/ 这类系统和编辑器产物,和具体项目无关,写进每个仓库既啰嗦又容易漏。配一份全局的:

git config --global core.excludesfile ~/.gitignore_global

把系统类、编辑器类规则放进 ~/.gitignore_global,项目内的 .gitignore 就只需要关注本项目的构建产物dist/target/*.pyc 等)。

排查神器:git check-ignore

不确定某文件被哪条规则命中时,别肉眼对规则:

git check-ignore -v path/to/file

它会直接打印命中的规则文件、行号和内容。“为什么这个文件被忽略了/为什么没被忽略”,一条命令见分晓。


小结:gitignore 不生效,先想”是不是已经被跟踪了”,用 git rm --cached 解决;多技术栈项目用生成器把语言、框架、编辑器、系统模板一次合并好放进仓库根,系统/编辑器类则沉淀到全局 gitignore。

❓ 常见问题

为什么加了 .gitignore,文件还是被提交跟踪?

因为 .gitignore 只对"尚未被跟踪"的文件生效。如果文件之前已经 git add / commit 过,它已进入索引,.gitignore 不会回头去忽略它。要先把它从索引移除:git rm -r --cached <文件或目录>,再提交,之后才会被忽略——本地文件不会被删除。

git rm --cached 会删掉我的本地文件吗?

不会。--cached 只把文件从 Git 的索引(暂存区/跟踪列表)里移除,磁盘上的文件原样保留。执行后该文件会显示为"已删除(从版本库)"+"未跟踪",配合 .gitignore 提交后,Git 就不再跟踪它,但你本地还在。去掉 --cached 的 git rm 才会连本地文件一起删。

gitignore 里 ! 开头的行是什么意思?为什么有时不生效?

感叹号是"取消忽略"的否定规则,用于在已被忽略的范围里保留个别文件,例如先 .vscode/* 忽略整个目录,再 !.vscode/settings.json 留下共享配置。坑在于:如果父目录是用 dir/ 形式整体排除的,Git 不会进入该目录,否定单个文件就不生效——要保证父目录本身没被整体忽略。

* 和 ** 有什么区别?

单星号 * 匹配除路径分隔符以外的任意字符(不跨目录),如 *.log 匹配当前层级的所有 .log;双星号 可跨目录匹配,如 /build 匹配任意层级的 build 目录、logs/**/*.tmp 匹配 logs 下任意深度的 .tmp。开头的 /build 表示只匹配仓库根的 build,不带斜杠的 build 则匹配任意层级。

想让所有项目都忽略编辑器/系统临时文件,怎么配?

配置全局 gitignore,避免每个仓库重复写。执行 git config --global core.excludesfile ~/.gitignore_global,把系统类(.DS_Store、Thumbs.db)和编辑器类(.idea/、.vscode/)规则放进该文件即可。项目内的 .gitignore 则只关注本项目的构建产物。

.gitignore 放在哪?子目录能单独放吗?

通常放仓库根目录,对整个仓库生效。也可以在子目录单独放一个 .gitignore,只对该目录及其子目录生效,规则与上层叠加、就近优先。大型 monorepo 常用每个子包各自维护 .gitignore 的方式。

怎么确认某个文件到底是被哪条规则忽略的?

用 git check-ignore -v <文件路径>,它会打印出命中的 .gitignore 文件、行号和具体规则。排查"为什么这个文件被忽略了/没被忽略"时这是最快的办法,比肉眼对规则靠谱得多。

🚫 打开 .gitignore 生成器 勾选通用产物/语言/框架/IDE/系统·自动合并去重生成 .gitignore·一键复制下载·本地运行