把 .gitignore 写对:语法、按栈生成与已追踪文件的处理
讲清 .gitignore 的通配符、否定与目录三种语法,怎么按语言框架生成 node_modules 与 .env 规则,已经提交的文件如何停止追踪,以及怎么用模板少踩坑。
把 .gitignore 写对:语法、按栈生成与已追踪文件的处理
每个仓库都有一份 .gitignore,但真正写明白的人不多。常见的局面是:从别处复制一段,node_modules/ 列了两遍,.DS_Store 又漏了,等到 .env 已经推上去才发现密钥泄露。这篇把 .gitignore 的语法、按技术栈生成的思路、以及已追踪文件的善后讲透。
三种语法:通配符、否定、目录
.gitignore 的规则只有三类,记住它们基本够用。
通配符里,* 匹配单层路径内的任意字符,但不会跨过 /。所以 *.log 命中当前及各级目录里的日志文件,build/*.js 只命中 build 第一层的 js。** 才能跨目录:**/.terraform/* 表示任意层级下的 .terraform 内容,logs/** 表示 logs 下的全部子孙。问号 ? 匹配单个字符,方括号 [abc] 匹配集合内一个字符,这两个用得少。
目录写法看末尾的斜杠。dist/ 带斜杠只忽略目录,dist 不带斜杠则连同名文件一起忽略。开头的 / 表示锚定到仓库根,/config.json 只忽略根目录那一个,不带前导斜杠的 config.json 则任意层级都命中。
否定用前导 !,把前面规则已经忽略掉的文件重新加回来。
否定的顺序和那个坑
! 的关键在顺序:Git 从上往下逐条匹配,最后一条命中的胜出。dotenv 场景几乎都是这个写法:
### dotenv ###
.env
.env.*
!.env.example
宽规则先把所有 env 文件挡掉,再用取反把示例文件捞回来。注意顺序反了就不灵,把 !.env.example 写在 .env.* 上面,下面那条又会把它盖掉。
有个坑值得单独记:父目录被忽略后,你没法再单独捞回里面的文件。dir/ 后面跟 !dir/keep.txt 不会生效,因为 Git 压根不会进入被忽略的目录去看内容。正确做法是忽略目录里的内容而不是目录本身:先写 dir/*,再写 !dir/keep.txt。
按语言和框架生成
不同栈要忽略的东西差别很大,硬记不现实。Node 项目要挡 node_modules/、各种 *.log、.env.local、以及 Next.js 的 .next/;Python 要挡 __pycache__/、*.pyc、.venv/、dist/;Go 主要是编译产物和 *.test。
真正麻烦的是叠加。一个 macOS 上用 VS Code 写的 Next.js 前端,至少要拼 Node、React/Next.js、dotenv、macOS、VS Code 五份,手动合并时 *.log 和 dist/ 这种公共行就会重复混进来。用 .gitignore 生成器 勾选这几个栈,输出会按 ### 分节 ### 给每个技术单独成块,重复规则跨分节去重,同一条只在第一个声明它的分节里出现一次。涉及 Terraform 这类会泄密的栈(*.tfstate 里嵌着明文数据库密码),生成后我一般再过一遍 env-secret-scanner,兜住任何已经漏进暂存区的密钥。
已经追踪的文件怎么忽略
这是最反直觉的一点:.gitignore 只对 Git 还没开始追踪的文件生效。一旦某个文件被提交过,Git 就会一直跟着它,你后面再往 .gitignore 加多少规则都不管用。
我自己第一次被这个坑到,是把整个 node_modules/ 提交上去后才想起加忽略,结果改了 .gitignore 它还顽固地出现在 git status 里。正确做法是先从索引里移除:
git rm -r --cached node_modules/
git commit -m "stop tracking node_modules"
--cached 只把它从 Git 索引里移除,磁盘上的文件原封不动。提交之后忽略规则才接管。如果仓库已经乱成一团,可以一把梭:git rm -r --cached . && git add . && git commit -m "apply gitignore",这会重新暂存所有文件,顺手把现在 .gitignore 排除的内容全部剔掉。
模板与全局忽略
操作系统和编辑器的垃圾文件(.DS_Store、Thumbs.db、.idea/)不该写进每个项目的 .gitignore。它们跟机器走,不跟项目走,应该放进全局那一份。新建 ~/.gitignore_global,把 macOS / Windows / 编辑器分节粘进去,执行一次:
git config --global core.excludesFile ~/.gitignore_global
从此机器上每个仓库都忽略这些垃圾,而项目里的 .gitignore 只专注自己的构建产物。这是最干净的分法。日常这些 git rm --cached、git config 命令记不住的话,Git 速查表 里都能现查。
把生成的输出当成强起点,不是终点。加上你项目自己的产物目录,删掉确实想追踪的东西。.gitignore 永远不会写完,它是随仓库成长不断微调的活文件。
Made by Toolora · Updated 2026-06-13