.gitignore 文件:如何让 Git 忽略文件,以及移除已添加的文件
仓库根目录中的 .gitignore 文件列出 Git 不应跟踪的文件和文件夹模式,例如依赖、构建产物、日志和机密信息。它只对尚未被跟踪的文件生效,因此已提交的文件需要用 git rm --cached 从索引中移除。
git ignore 的作用是告诉 Git:“这些文件不要管,不要在 git status 中显示,也不要通过 git add . 添加。”规则写在文本文件 .gitignore 中,它与项目一起提交,对全团队生效。需要注意的关键点是:忽略规则只对 Git 尚未跟踪的文件起作用。
.gitignore 中通常写些什么
- 依赖:
node_modules/、vendor/、.venv/,这些由包管理器恢复即可。 - 构建产物:
dist/、build/、*.class、__pycache__/。 - 日志和临时文件:
*.log、tmp/、*.swp。 - 机密信息和本地设置:
.env、密钥、本地配置文件。 - 操作系统和编辑器的杂项文件:
.DS_Store、Thumbs.db、.idea/——不过最好放进全局忽略规则中,下文会讲到。
原则很简单:仓库中应该只保存人写的内容,而不是程序生成的内容,也不是依赖于某台具体机器的内容。
如何创建 .gitignore
它是位于仓库根目录的普通文本文件。创建它,每行写一个模式,然后提交:
cat > .gitignore <<'EOF'
# 依赖
node_modules/
# 构建
dist/
# 日志
*.log
# 机密信息
.env
EOF
git add .gitignore
git commit -m "chore: add .gitignore"
针对各种语言和框架的现成模板可以在 github/gitignore 仓库中找到;创建项目时,GitHub 和 GitLab 也会让你选择模板。以它们为基础,删除不需要的部分即可。
.gitignore 的模式语法
| 模式 | 忽略的内容 |
|---|---|
*.log | 任何文件夹中所有扩展名为 .log 的文件 |
build/ | 任何层级的 build 文件夹(末尾的斜杠表示只匹配文件夹) |
/build | 仅根目录下的 build(开头的斜杠将其绑定到 .gitignore 所在的文件夹) |
docs/*.pdf | docs 中直接存放的 pdf,但不包括 docs/old/ 中的 |
docs/**/*.pdf | docs 及其所有子文件夹中的 pdf |
**/temp | 任何文件夹中的 temp |
!keep.log | 例外:不忽略 keep.log |
file?.txt | ? 代表任意一个字符 |
[ab].txt | a.txt 或 b.txt |
# 注释 | 注释行 |
需要注意:中间不含斜杠的模式(如 config.json)会在任何层级生效;包含开头或中间斜杠的模式(如 /config.json、src/config.json)则相对于该 .gitignore 所在的文件夹生效。
用感叹号设置例外
*.log
!important.log
陷阱:如果父文件夹被忽略,就无法再把其中的文件“找回来”。Git 不会进入被忽略的文件夹,因此下面的写法无效:
logs/
!logs/keep.txt # 不起作用
正确做法是忽略文件夹的内容,而不是文件夹本身:
logs/*
!logs/keep.txt
同样的技巧也用于在仓库中保留空文件夹:Git 不会保存空目录,因此在其中放一个 .gitkeep 文件,并只允许它被跟踪。
还可以在哪里设置忽略规则
| 位置 | 作用范围 | 是否进入仓库 |
|---|---|---|
根目录及子文件夹中的 .gitignore | 整个团队 | 是 |
.git/info/exclude | 仅这一个克隆 | 否 |
全局文件 core.excludesFile | 你的所有仓库 | 否 |
操作系统和 IDE 的杂项文件最好放进全局忽略规则,以免污染团队共用的 .gitignore:
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo ".idea/" >> ~/.gitignore_global
如果没有设置 core.excludesFile,并且 ~/.config/git/ignore 存在,Git 会读取它。
为什么 .gitignore 不生效
最常见的抱怨是:“我把文件加进了 .gitignore,但 Git 仍然显示它有修改。”原因几乎总是同一个:该文件已经被跟踪。忽略规则对曾经进入提交的文件没有作用。
要查看哪条规则生效(或为什么没有生效),可以使用 git check-ignore:
git check-ignore -v logs/app.log
# .gitignore:3:*.log logs/app.log
输出显示了文件和生效规则所在的行。如果没有输出,说明该文件没有被忽略。另一个常见原因是模式末尾有多余的空格,或者文件编码不正确(例如用 PowerShell 创建时产生的 UTF-16)。
如何从仓库中移除已提交的文件:git rm --cached
命令 git rm 会把文件从索引和磁盘上都删除。加上 --cached 参数时,只从索引中删除:文件仍保留在你的电脑上,但不再被跟踪。
echo ".env" >> .gitignore
git rm --cached .env
git commit -m "chore: stop tracking .env"
git push
对于文件夹,需要加上 -r:
git rm -r --cached node_modules/
如果修改很多,且不确定哪些文件已经进入仓库,可以整体重建索引:git rm -r --cached .,然后 git add . 并提交。提交之前请用 git status 检查,其中会显示被从索引中移除的文件。
注意:机密信息仍保存在历史中
git rm --cached 只能把文件从之后的提交中移除,无法删除历史中的旧提交。如果密码或令牌曾经进入仓库,任何拥有克隆副本的人都能在历史中看到它。应当把该机密视为已泄露,并且重新生成。若要改写历史,可以使用 git filter-repo 之类的工具,但这会改变之后所有提交的哈希,并需要与团队协调。
还有一个连带影响:同事执行 git pull 后,通过 --cached 移出索引的文件会从他们的磁盘上被删除。对于 .env,请提前告知团队,或在仓库中保留一个 .env.example 模板。
git rm:删除文件
git rm old.py # 从磁盘和索引中删除
git rm -r legacy/ # 删除文件夹
git rm -n '*.tmp' # 试运行:只显示将被删除的内容
git rm -f changed.py # 删除有未提交修改的文件
如果用普通的 rm 删除文件,Git 会将其标记为“deleted”,之后还需要用 git add 或 git rm 记录删除操作。git rm 会一步完成这两件事。不加 -f 时,它会拒绝删除有未提交修改的文件,这是为了防止丢失工作成果。
git mv:重命名与移动
git mv utils.py helpers.py
git mv helpers.py src/helpers.py
git mv 相当于 mv 加上对旧路径的 git rm 和对新路径的 git add。Git 并不保存“重命名”这一事实:它在显示 git status、使用 git log --follow 和 git diff 时,根据内容的相似度推断出来。因此,使用 git mv 与使用普通 mv 再执行 git add -A,结果是一样的。
实用建议:重命名和修改文件内容应分开提交。如果文件在重命名的同时被大幅修改,Git 可能无法识别它是同一个文件,--follow 查看的历史就会中断。
在 Windows 和 macOS 上,文件系统不区分大小写,如果只想修改文件名的大小写,请使用 git mv readme.md README.md——资源管理器中的简单重命名可能不会被 Git 察觉。
规则的顺序与优先级
在同一个文件中,规则从上到下读取,最后一个匹配的规则生效。因此例外规则 !keep.log 必须写在 *.log 的下方,否则会被覆盖。不同文件之间的优先级是:子文件夹中的 .gitignore 高于根目录的;全局忽略规则和 .git/info/exclude 的优先级最低。
Web 项目的 .gitignore 示例
下面是一个 Node.js 前端加 Python 后端项目的实用配置。注意其中的注释:半年以后,没有人会记得某条规则为什么存在。
# --- 依赖 ---
node_modules/
.venv/
# --- 构建与缓存 ---
/dist
/build
__pycache__/
*.pyc
.pytest_cache/
coverage/
# --- 日志 ---
*.log
npm-debug.log*
# --- 环境与机密信息 ---
.env
.env.*
!.env.example
# --- 用户上传的文件,但保留文件夹 ---
uploads/*
!uploads/.gitkeep
这里用了两种例外写法:.env.* 忽略所有本地环境变体,但 .env.example 模板保留在仓库中;uploads 文件夹通过 .gitkeep 得以在克隆中存在。
哪些文件不应该忽略
过度忽略同样有害。依赖锁文件——package-lock.json、poetry.lock、composer.lock——在应用项目中通常应当提交:它们保证所有开发者和 CI 安装的依赖版本一致。共享的格式化配置(.editorconfig、代码检查器配置)也应当保存在仓库中。
IDE 的设置是有争议的情况。.vscode/ 文件夹常被整体忽略,但有时会提交推荐扩展和构建任务:先写 .vscode/*,再写 !.vscode/extensions.json。这由团队决定,关键是不要在共享仓库中保存个人路径和密钥。
场景:不小心提交了 node_modules
新手的典型错误:在创建 .gitignore 之前执行了 git add .,结果成千上万的文件进入了提交。三步修复:
echo "node_modules/" >> .gitignore
git rm -r --cached node_modules
git commit -m "chore: remove node_modules from the repository"
如果提交还没有推送到服务器,最好改写它,使大文件不留在历史中:git reset --soft HEAD~1,然后执行相同的命令并生成新的提交。一旦推送,文件就已经进入历史,不改写历史的话,克隆的体积不会减小。
为了避免再次发生,请在第一次 git add 之前创建 .gitignore,并养成提交前查看 git status 的习惯:如果新文件列表长得异常,就该停下来检查。
总结
.gitignore 是新项目中第一个应该提交的文件:依赖、构建产物、日志和机密信息都不应该进入仓库。忽略规则只对未被跟踪的文件有效,因此已经添加的文件要用 git rm --cached 移除,并用 git check-ignore -v 检查规则。删除和重命名请使用 git rm 与 git mv,让修改直接进入索引。第一个仓库与索引的工作方式,在 Git 课程中有实践讲解。
常见问题
需要提交 .gitignore 文件本身吗?
需要,这样规则对整个团队生效。个人专用的排除项应放入 .git/info/exclude 或全局忽略文件。
一个项目中可以有多个 .gitignore 文件吗?
可以,.gitignore 可以放在任何子文件夹中,其规则相对于该文件夹生效,并与根目录的规则叠加。
如何强制把被忽略的文件加入提交?
使用 git add -f 路径/文件。之后该文件就被跟踪了,.gitignore 不再对它产生影响。
如何查看项目中所有被忽略的文件?
执行 git status --ignored,或 git ls-files --others --ignored --exclude-standard。
如何临时停止跟踪已提交文件的修改?
可以使用 git update-index --skip-worktree 文件,但这只是本地设置,并不能替代 .gitignore。