.gitignore 文件——如何在 Git 中忽略文件和文件夹译
Category(分类): git Status: 未知
原文:.gitignore File – How to Ignore Files and Folders in Git
本文保留原文的翻译结构和示例,并根据 Git 官方文档补充模式匹配、规则优先级、调试命令、已跟踪文件处理和敏感信息处理。示例中的
master可以替换为项目实际使用的main或其他默认分支。
前言
Git 是一个流行的分布式版本控制系统。开发人员通过它协作开发、记录项目历史,并在需要时查看差异、回退或恢复以前的版本。
Git 的基本工作方式是:用 git add 把确认过的内容放入暂存区,再用 git commit 保存到本地提交历史,最后根据协作需要使用 git push 推送到远程仓库。
在团队项目中,有些文件不应该提交给其他人,例如操作系统生成的文件、编辑器个人配置、依赖目录、编译产物、日志、临时文件和本地环境变量。.gitignore 就是用来声明这些尚未被 Git 跟踪的路径应该被排除的规则文件。
需要先纠正一个常见误解:.gitignore 不是“删除文件”或“隐藏文件”的命令,也不会从已经存在的提交历史中抹掉文件。它主要影响未跟踪文件是否出现在 git status 中,以及 git add 是否默认添加它们。
本文将介绍:
- 什么是
.gitignore; - 如何创建和放置
.gitignore; - 常见的文件、目录和通配模式;
!否定规则和规则优先级;- 如何检查某个文件为什么被忽略;
- 如何停止跟踪一个已经提交的文件;
- 如何安全清理被忽略的构建产物和临时文件。
什么是 .gitignore 文件?
在一个 Git 工作区中,路径通常可以从几个角度描述:
- 已跟踪(tracked):路径已经进入 Git 的索引,通常曾经被提交过,或者当前已经被
git add暂存。 - 未跟踪(untracked):工作区中存在,但还没有进入索引的路径。
- 被忽略(ignored):符合某条排除规则的未跟踪路径。它仍然存在于磁盘上,只是 Git 默认不把它作为普通未跟踪文件提示,也不会被普通的
git add添加。 - 已删除但尚未提交:原来已跟踪的文件从工作区删除后,Git 会把删除显示为工作区变更;这不是“忽略”。
“被忽略”不是与 tracked、untracked 完全并列的永久文件状态。一个文件只要被 git add 或 git add -f 加入索引,之后即使它匹配 .gitignore,Git 仍会继续跟踪它的修改。
.gitignore 是纯文本文件,里面每一行通常是一条模式:
# 这是注释
node_modules/
*.log
.env
它可以写文件名、目录名、相对路径和通配模式。规则可以放在仓库根目录,也可以放在子目录中;子目录中的 .gitignore 主要影响该目录及其后代路径。
.gitignore、.git/info/exclude 和全局排除文件
除了项目中的 .gitignore,Git 还支持其他排除规则来源:
- 命令行指定的排除规则:某些底层命令可以通过参数提供规则。
- 各级目录中的
.gitignore:适合需要提交并让团队共享的项目规则。 .git/info/exclude:只对当前本地仓库生效,不会被提交,适合个人在这个仓库中的临时文件。- 全局排除文件:通过
core.excludesFile配置,适合所有仓库都不希望看到的操作系统或个人工具文件。
查看全局排除文件配置:
git config --get core.excludesFile
例如可以设置一个用户级排除文件:
git config --global core.excludesFile ~/.config/git/ignore
不要把项目构建产物写进个人全局排除文件,否则团队其他成员无法获得这条规则;项目规则应该放进仓库中的 .gitignore 并提交。
如何创建 .gitignore 文件?
通常把 .gitignore 放在仓库根目录:
cd 项目根目录
touch .gitignore
以点号开头的文件在 Unix 系统中通常属于隐藏文件,可以使用下面的命令查看:
ls -la
Windows 用户可以在 PowerShell 中创建和查看:
New-Item .gitignore -ItemType File
Get-ChildItem -Force
如果仓库已经存在 .gitignore,不要直接覆盖它。先阅读项目已有规则,再按项目约定补充内容:
git status --short
cat .gitignore
.gitignore 本身通常应该提交到仓库,让所有协作者使用同一套项目规则。只有个人规则才适合放到 .git/info/exclude 或全局排除文件中。
.gitignore 中通常应该包括什么?
应该忽略的是“不应作为项目源文件提交”的内容,而不是所有自己不想看到的文件。常见类别包括:
- 操作系统生成的文件,例如 macOS 的
.DS_Store、Windows 的Thumbs.db; - 编辑器或 IDE 的个人配置,例如
.idea/、某些.vscode/用户配置; - 依赖目录,例如 Node.js 的
node_modules/; - 编译产物和构建目录,例如
dist/、build/、target/,但要先确认项目是否需要提交构建产物; - 临时文件、缓存和日志,例如
*.log、.cache/; - 本地环境配置,例如
.env、.env.local,其中可能含有密码、令牌和 API 密钥; - 覆盖率报告、测试快照临时目录和运行时 PID 文件。
一个通用示例:
# 操作系统文件
.DS_Store
Thumbs.db
# 日志和临时文件
*.log
*.tmp
*.swp
# Node.js 依赖和常见构建产物
node_modules/
dist/
build/
coverage/
# 本地环境变量,不要提交真实密钥
.env
.env.*
!.env.example
上面的规则只是模板,不能机械复制到所有项目:
- 某些项目需要提交
dist/,例如发布静态文件的仓库; package-lock.json、pnpm-lock.yaml、yarn.lock等依赖锁文件通常应该提交,以保证安装结果一致;.vscode/中可能有团队共享的settings.json、任务或调试配置,可以只忽略个人文件而不是整个目录;.env.example可以提交变量名和示例值,但不能放真实密码或令牌。
Node/Nuxt 项目的示例
以常见的 Node.js/Nuxt 项目为例,可以从下面的规则开始,再根据仓库实际情况调整:
node_modules/
.nuxt/
.output/
.nitro/
dist/
coverage/
*.log
.env
.env.*
!.env.example
.DS_Store
Thumbs.db
不要因为某个目录“看起来是生成的”就直接忽略;先确认它是否属于源码、是否需要用于部署,以及团队是否已有统一规则。
.gitignore 模式语法
Git 的 ignore 模式不是普通 shell 命令。每行规则都会经过 Git 的模式匹配处理,常见规则如下。
空行和注释
空行不匹配任何路径,可以用来分组。以 # 开头的行是注释:
# 依赖
node_modules/
# 日志
*.log
如果文件名本身以 # 开头,需要用反斜杠转义:
\#重要说明.txt
规则开头的 ! 有特殊含义。如果文件名本身以 ! 开头,也要使用反斜杠转义:
\!important.txt
行尾空格通常会被忽略。如果文件名或模式确实需要行尾空格,需要用反斜杠转义;实际项目中应尽量避免难以看见的尾随空格。
忽略仓库根目录下的一个文件
如果只想忽略仓库根目录下的 text.txt:
/text.txt
开头的 / 表示相对于当前 .gitignore 所在目录的根位置。根目录 .gitignore 中的 /text.txt 不会匹配子目录里的 text.txt。
忽略某个相对路径
如果根目录下有 test/text.txt,可以写:
/test/text.txt
在根目录 .gitignore 中,下面这种带斜杠的写法也表示相对路径:
test/text.txt
如果这条规则写在 test/.gitignore 中,路径的相对基准则会变成 test/,因此每个 .gitignore 文件都应结合它所在的目录理解。
忽略任意位置同名的文件或目录
如果模式中没有斜杠,通常可以匹配任意层级的同名文件或目录:
text.txt
它可以匹配:
text.txt
src/text.txt
test/fixtures/text.txt
如果只想匹配仓库根目录的文件,应写成 /text.txt。
忽略目录
在目录名后面加 /,表示匹配目录及其内容:
test/
根目录 .gitignore 中的 test/ 会匹配任意层级名为 test 的目录。只想忽略仓库根目录的 test 目录,可以写:
/test/
没有结尾斜杠的 test 则可能同时匹配名为 test 的文件和目录:
test
前缀匹配:*
如果想忽略所有名称以 img 开头的文件或目录,可以使用:
img*
这条没有斜杠的规则可以匹配不同目录中的 img.png、image-cache/ 等名称。* 在普通模式中不会跨越路径分隔符;如果需要表达多层目录,应使用路径或 **。
后缀匹配:*.md
要忽略所有 Markdown 文件,可以写:
*.md
这条没有斜杠的模式可以匹配仓库任意层级的 README.md、docs/guide.md 等文件。
原文中曾把 .md 单独作为“所有 Markdown 文件”的规则,这是错误的:.md 只会匹配名为 .md 的文件或目录,不等于“所有以 .md 结尾的文件”。需要通配符 *.md。
路径中的通配符
常见的 ? 匹配一个字符,[0-9] 匹配字符范围:
file?.tmp
cache-[0-9].json
** 用于表达跨目录层级的匹配。例如:
# 任意层级的 docs 目录中的临时文件
docs/**/tmp/
# logs 目录下的所有内容,无论嵌套多少层
logs/**
# 根目录 a 目录下的任意层级 .cache 目录
/a/**/.cache/
对于简单场景,优先使用清晰的目录和扩展名规则,不要为了“看起来高级”而滥用 **。
否定模式 !:为忽略规则添加例外
如果先忽略一个模式,再使用以 ! 开头的模式,可以重新包含某个路径:
# 忽略所有 Markdown 文件
*.md
# 但保留根目录的 README.md
!/README.md
规则通常按顺序处理,同一层级中后出现的匹配规则可以覆盖前面的规则。因此例外规则要放在被忽略规则之后。
被忽略的父目录不能直接重新包含文件
下面的写法通常无法达到预期:
# 整个 test 目录被排除
test/
# 试图重新包含其中的文件,但 Git 不会继续遍历被排除的目录
!test/example.md
原因是 Git 为了性能不会遍历已经被排除的目录,因此后面的文件例外规则没有机会生效。
如果希望保留 test/example.md,可以不要排除整个 test/ 目录,而是排除其内容:
# 不排除 test 目录本身,让 Git 可以继续遍历
test/*
# 重新包含指定文件
!test/example.md
如果还要保留更深层级的目录,也要确保通往目标文件的每一级目录没有被整体排除。复杂规则应使用 git check-ignore -v 验证,而不要只凭猜测。
例外规则的常见写法
例如忽略构建目录,但保留一个说明文件:
build/*
!build/README.md
如果使用的是 build/,则目录本身已经被排除,!build/README.md 不能直接把里面的文件重新包含。此时可以改写规则,或者把说明文件放到没有被排除的目录中。
规则优先级和作用范围
当多个规则同时匹配时,需要考虑规则来源和顺序。一般可以按下面的方向理解:
- 命令行提供的排除规则优先级最高;
.gitignore规则按目录层级参与匹配,越靠近目标文件的规则通常越具体;- 仓库级
.git/info/exclude用于本地仓库而不共享; core.excludesFile提供用户级全局排除规则;- 在同一层级和同一来源中,后出现的匹配规则通常覆盖前面的规则。
不要把不同来源的规则混在一起排查。一个文件没有出现在 git status 中,可能是项目 .gitignore、某个子目录 .gitignore、.git/info/exclude 或全局排除文件造成的。
可以使用 git check-ignore 直接查看命中的规则。
如何验证一个文件是否被忽略?
使用 git status
普通状态只显示未跟踪但未被忽略的文件:
git status --short
如果希望连被忽略的文件也显示:
git status --short --ignored
输出中的 !! 通常表示路径被忽略,例如:
!! .env
!! node_modules/
使用 git check-ignore -v
这是定位规则来源最有用的命令之一:
git check-ignore -v -- .env
可能得到类似输出:
.gitignore:12:.env .env
它会告诉你:命中的是哪个规则文件、第几行以及具体模式。检查多个路径时可以一起传入:
git check-ignore -v -- .env dist/ app.log
默认情况下,已经被跟踪的文件不会被 git check-ignore 显示,因为 ignore 规则不适用于已跟踪路径。如果想排查“为什么这个文件被 git add . 添加了”的模式问题,可以使用:
git check-ignore -v --no-index -- path/to/file
--no-index 只适合调试匹配规则,不会把文件从索引中删除。
查看所有被忽略的未跟踪文件
可以结合 git ls-files 查看:
git ls-files --others --ignored --exclude-standard
其中 --exclude-standard 会采用 .gitignore、.git/info/exclude 和全局排除规则。查看结果前要注意:被忽略的目录可能只显示目录名,不一定列出其中每一个文件。
.gitignore 不会影响已经跟踪的文件
Git 官方的排除规则主要作用于有意保持未跟踪的路径。假设某个文件已经被提交:
git add config.local.json
git commit -m "add local config"
之后再把它写进 .gitignore:
config.local.json
此时 Git 仍然会跟踪它的后续修改,因为它已经在索引中。可以用下面的命令确认:
git ls-files -- config.local.json
git status --short
如果目标是“保留本地文件,但停止跟踪并以后忽略”,需要把它从索引中移除。
停止跟踪单个文件但保留本地文件
例如误提交了 .env:
# 先加入项目忽略规则
echo ".env" >> .gitignore
# 只从索引删除,保留工作区文件
git rm --cached -- .env
# 提交规则和取消跟踪动作
git add .gitignore
git commit -m "chore: stop tracking local environment file"
--cached 的含义是只更新索引,不删除工作区文件。原文中的 -cached 少了一个连字符,正确写法是 --cached。
如果希望从索引和工作区都删除文件,可以省略 --cached:
git rm -- .env
但这不会自动清理已经存在的历史提交,也不会阻止别人从旧提交中看到它。
停止跟踪整个目录
如果已经提交了构建目录或依赖目录,需要谨慎地从索引移除:
# 先在 .gitignore 中加入对应规则,例如 dist/
echo "dist/" >> .gitignore
# 只从索引移除,保留工作区目录
git rm -r --cached -- dist/
git add .gitignore
git commit -m "chore: stop tracking generated files"
执行前先用 git status 和 git diff --cached 检查范围。不要为了“重新应用 .gitignore”而对整个仓库盲目执行破坏性命令。
文件已经包含敏感信息怎么办?
把 .env 加入 .gitignore 只能防止未来再次被普通 git add 添加,不能让已经泄露的密码、令牌或私钥失效。应按下面顺序处理:
- 立即吊销、轮换或删除泄露的凭据;
- 确认当前分支、远程仓库、代码评审和构建日志中是否仍有暴露;
- 如果需要从历史中删除,使用团队认可的历史清理工具(例如
git filter-repo或平台提供的密钥清理流程); - 历史改写后通知所有协作者重新同步,并检查其他克隆和缓存。
示意命令如下,执行前必须先阅读工具文档、备份仓库并取得团队同意:
# 示例:从历史中删除某个路径,命令会改写提交 ID
git filter-repo --path .env --invert-paths
上面命令前不需要多余空格。历史清理不是 .gitignore 的替代品,也不是发现密钥后只需要做的一步。
强制添加被忽略的文件
有时某个文件符合忽略规则,但确实需要提交,例如一个项目模板文件:
git add --force .env.example
更推荐从根本上调整规则,让需要提交的文件通过否定模式被明确包含:
.env.*
!.env.example
git add -f 会绕过忽略规则,使用后仍应检查 git diff --staged,避免误把真实密钥或大型生成文件加入提交。
清理未跟踪和被忽略的文件
.gitignore 只是不显示或不默认添加文件,并不会删除磁盘上的文件。如果要清理构建产物,可以使用 git clean,但它是破坏性命令:
# 预览将删除的未跟踪文件,不实际删除
git clean --dry-run
# 预览未跟踪目录
git clean --dry-run -d
# 只预览被忽略的文件和目录
git clean --dry-run -d -X
# 预览所有未跟踪文件,包括被忽略的文件
git clean --dry-run -d -x
确认预览结果无误后,才考虑执行:
# 删除未跟踪文件和目录
git clean -f -d
# 只删除被忽略的未跟踪文件和目录
git clean -f -d -X
不要把 git clean -f -d -x 当作日常清理命令;它会连被忽略的依赖、环境文件和构建缓存一起删除。执行前建议备份本地数据,并先使用 --dry-run。很多 Git 配置默认要求提供 -f,这是为了避免误删。
常见问题和排查清单
为什么写了 .gitignore,文件仍然出现在状态中?
按下面顺序检查:
- 文件是否已经被跟踪?如果是,需要
git rm --cached后再提交; - 忽略规则的相对路径是否正确?根目录规则和子目录规则的基准不同;
- 是否忘记了目录末尾的
/或文件扩展名开头的*; - 后面的规则或
!是否重新包含了该文件; .git/info/exclude或全局排除规则是否与项目规则冲突;- 使用
git check-ignore -v --no-index -- path查看实际命中的规则。
为什么写了否定规则,例外文件仍然被忽略?
最常见原因是父目录本身被整体排除。将:
cache/
!cache/keep.json
改为:
cache/*
!cache/keep.json
然后用 git check-ignore -v --no-index -- cache/keep.json 验证。对于多层目录,还要确保中间目录没有被整体排除。
为什么 git add . 没有添加被忽略的文件?
这是正常行为。git add 默认不会添加被忽略的文件;可以确认规则,或者在明确需要时使用:
git check-ignore -v -- path/to/file
git add --force -- path/to/file
不要为了省事对整个目录执行 git add -f .,这可能把日志、缓存和敏感信息全部加入暂存区。
空目录为什么没有被 Git 提交?
Git 主要跟踪文件和文件内容,不会单独跟踪空目录。可以放入一个有明确用途的占位文件,例如 .gitkeep,但 .gitkeep 不是 Git 的特殊文件名,只是社区惯例:
mkdir -p runtime/uploads
: > runtime/uploads/.gitkeep
git add runtime/uploads/.gitkeep
如果目录运行时会产生用户上传内容,应同时使用 .gitignore 忽略真实文件,并只提交占位文件:
runtime/uploads/*
!runtime/uploads/.gitkeep
全局忽略规则会不会影响团队?
会影响你本地的命令输出,但不会自动影响其他协作者。个人全局规则适合 .DS_Store、编辑器临时文件等;项目构建目录、依赖目录和环境模板等团队规则应提交到项目 .gitignore。
一个可复用的检查流程
修改 .gitignore 后,可以按下面流程验证:
# 查看规则文件本身和工作区状态
cat .gitignore
git status --short
# 查看被忽略的路径
git status --short --ignored
# 确认具体文件命中了哪条规则
git check-ignore -v --no-index -- .env dist/app.js logs/app.log
# 检查暂存区是否包含了不该提交的内容
git diff --staged
# 如果要清理,先预览,不要直接执行删除
git clean -ndX
提交前尤其要检查:
.env、私钥、PAT、云服务密钥和数据库密码;node_modules/、构建产物、缓存和日志;- 是否误用了过于宽泛的
*.md、*.json或*规则; - 是否把团队需要共享的锁文件、配置模板或迁移脚本忽略了;
- 否定规则是否真的让目标文件重新被包含。
总结
.gitignore 是项目规则文件,用来排除不应被 Git 默认跟踪的未跟踪路径。它不是删除命令,也不能改变已经进入索引的文件状态,更不能从历史中消除已经泄露的秘密。
核心命令可以归纳为:
# 检查规则和状态
git status --short --ignored
git check-ignore -v -- path/to/file
# 停止跟踪但保留本地文件
git rm --cached -- path/to/file
# 必须提交被忽略文件时明确强制添加
git add --force -- path/to/file
# 清理前先预览
git clean -ndX
最重要的实践是:项目规则写入 .gitignore 并提交,个人规则写入 .git/info/exclude 或全局排除文件;使用 git check-ignore -v 定位问题;处理密钥时先轮换凭据,再考虑历史清理。
参考资料
- Git 官方文档
- gitignore 官方文档
- git-check-ignore 官方文档
- git-add 官方文档
- git-rm 官方文档
- git-clean 官方文档
- git-status 官方文档
- GitHub 官方 gitignore 模板
- GitHub:忽略文件
感谢阅读,happy coding :)