跳转至

关于 gitignore 文件中的语法规则

.gitignore 用来告诉 Git:哪些未被跟踪的文件或目录,不要纳入版本管理

它的作用对象主要是:

  • 临时文件
  • 缓存文件
  • 构建产物
  • 系统自动生成文件
  • 本地私有配置
  • 不想同步的大文件或目录

但有一个非常关键的前提:

.gitignore 只能影响“未被 Git 跟踪”的文件

如果某个文件以前已经 git add 并提交过,那么后来再把它写进 .gitignore,Git 仍然会继续跟踪它。


1 基本规则

.gitignore 文件本质上是一行一条规则。

例如:

node_modules/
*.log
.env
dist/

表示:

  • 忽略 node_modules 目录
  • 忽略所有以 .log 结尾的文件
  • 忽略全名为 .env 的文件,这里面一般是环境变量配置
  • 忽略 dist 目录

2 最基本的语法元素

2.1 空行

空行没有任何作用,只是为了分组、增加可读性。

例如:

*.log

node_modules/

dist/

这里的空行只是分隔,不参与匹配。

2.2 注释 #

# 开头的行表示注释。

例如:

# 忽略日志文件
*.log

# 忽略依赖目录
node_modules/

注释不会生效,只是写给人看的。

如果你真的想匹配以 # 开头的文件名,需要转义:

\#test.txt

2.3 普通文本匹配

直接写文件名或目录名,就是按名称匹配。

例如:

test.txt
cache

含义:

  • 忽略名字叫 test.txt 的文件
  • 忽略名字叫 cache 的文件或目录

注意,这种写法是 模糊位置匹配,不是只匹配根目录。

也就是说:

test.txt

可能匹配:

test.txt
a/test.txt
foo/bar/test.txt

3 通配符语法

.gitignore 最核心的就是通配符。

3.1 * 星号

* 表示匹配任意长度的任意字符,但 通常不跨目录分隔符 /

例如:

*.log

匹配:

  • a.log
  • error.log

不匹配:

  • logs/error.log 中的整个路径本身并不是单个文件名匹配语义,但如果规则作用在该层级文件名上,里面的 error.log 会匹配

更直观地理解:

temp*

匹配:

  • temp
  • temp1
  • temp_file

3.2 ? 问号

? 表示匹配任意一个字符。

例如:

file?.txt

匹配:

  • file1.txt
  • fileA.txt

不匹配:

  • file10.txt
  • file.txt

3.3 [abc] 字符集合

表示匹配方括号中的任意一个字符。

例如:

file[123].txt

匹配:

  • file1.txt
  • file2.txt
  • file3.txt

不匹配:

  • file4.txt

3.4 [a-z] 字符范围

表示匹配某个范围内的一个字符。

例如:

file[a-z].txt

匹配:

  • filea.txt
  • filem.txt

不匹配:

  • fileA.txt
  • file10.txt

3.5 ** 双星号

这是非常重要的高级写法。

** 表示可以跨目录匹配。

例如:

**/node_modules/

表示忽略任意层级下的 node_modules 目录。

匹配:

  • node_modules/
  • project/node_modules/
  • a/b/c/node_modules/

再比如:

logs/**/*.log

表示忽略 logs 目录及其任意子目录中的所有 .log 文件。

匹配:

  • logs/a.log
  • logs/app/error.log
  • logs/x/y/z/test.log

这里有必要补充一下星号和双星号的对比分析

写法 含义
* 匹配任意字符(不包含 /
** 匹配任意目录层级
*.log 当前目录 .log 文件
**/*.log 所有目录 .log 文件
temp* temp 开头的文件
logs/** logs 目录下所有内容

4 斜杠 / 的含义

斜杠在 .gitignore 里非常关键,因为它决定了规则是面向根目录,还是面向任意位置,还是明确指定目录。

4.1 前导斜杠 /

/ 开头,表示 .gitignore 所在目录开始匹配

例如:

/test.txt

只匹配仓库根目录下的:

/test.txt

不匹配:

a/test.txt
foo/bar/test.txt

再例如:

/cache

只匹配根目录下的 cache

4.2 末尾斜杠 /

/ 结尾,表示这是一个 目录规则

例如:

build/

表示忽略 build 目录,而不是名为 build 的普通文件。

匹配:

  • build/
  • a/build/ 是否匹配,取决于是否有前导 /。没有前导 / 时,任意层级同名目录都可能匹配

更准确地说:

build/

会匹配任意位置的 build 目录。

/build/

只匹配根目录的 build/

4.3 中间斜杠 /

中间出现 /,表示路径关系。

例如:

temp/cache/

表示匹配 temp 下的 cache 目录。

5 否定规则 !

! 表示 取消忽略

也就是把前面被忽略的内容重新放出来。

例如:

*.log
!important.log

含义:

  • 先忽略所有 .log
  • 再把 important.log 排除出忽略规则,让它重新被 Git 看见

5.1 否定规则的一个关键限制

如果父目录已经被忽略,那么子文件往往无法单独救回来,除非父目录本身也被重新放开。

例如:

data/
!data/keep.txt

这通常是不够的。

因为 data/ 整个目录已经被忽略了,Git 不会再深入看它里面的内容。

正确写法一般要这样:

data/
!data/
!data/keep.txt

如果目录里还有更深层级,也可能需要继续逐层放开。

6 匹配优先级与规则顺序

.gitignore 的规则是 从上到下依次读取,后面的规则可以覆盖前面的规则。

所以:

最后匹配到的规则生效

例如:

*.txt
!readme.txt

结果:

  • 所有 .txt 忽略
  • readme.txt 不忽略

如果顺序反过来:

!readme.txt
*.txt

那么最终 readme.txt 还是会被忽略,因为后面的 *.txt 又把它盖掉了。

所以 .gitignore 的书写顺序非常重要。

7 目录与文件匹配的真实理解

7.1 foo

foo

表示匹配名字叫 foo 的文件或目录,位置不限。

可能匹配:

  • foo
  • a/foo
  • x/y/foo

7.2 foo/

foo/

表示匹配名字叫 foo 的目录,位置不限。

匹配:

  • foo/
  • a/foo/
  • x/y/foo/

不匹配:

  • 普通文件 foo

7.3 /foo

/foo

表示只匹配根目录的 foo 文件或目录。

7.4 /foo/

/foo/

表示只匹配根目录的 foo 目录。

7.5 foo/bar

foo/bar

表示匹配某个 foo 目录下的 bar 文件或目录。

如果没有前导 /,一般不是严格限定仓库根目录,而是匹配相对路径模式。