彻底理解.gitignore

全面解析.gitignore文件中的模式匹配。

.gitignore文件里的模式匹配有时真的让人很无语,看似非常简单,实则到处是坑,
本文我们来个彻底解析。本文来自git文档加上自己的理解,以及总结了一些常用实例,
git文档可以参考文末的链接。

语法(pattern format)

第一部分语法规则

  1. 空行被忽略
  2. #开头的行视为注释,需要#的地方要进行转意
  3. 其余每行表示一个pattern
  4. 行尾 的空格被忽略,需要行尾空格的地方要进行转意
  5. !意味着取反,即原本被匹配上的文件被忽略,加上!后,
    这些文件则被包含进来,需要!的地方要要进行转意。
    但是如果一个文件的parent directory被忽略,则该文件不会被包含进来
  6. /结尾的pattern,/将会被移除,然后按照下面的规则进行匹配,但是只匹配目录
  7. 如果pattern中没有/,则git把该pattern当作一个shell glob去匹配文件路径
  8. 如果pattern中包含/,则git把该pattern当作一个受限的shell glob去匹配文件路径:
    此时wildcards(*)不能匹配/符号
  9. /开头的pattern,/匹配文件路径的开始,如/*.c匹配cat.c而不匹配mozilla/cat.c

语法一大堆,但是真正值得注意的地方是:

  1. /*,即pattern中有/的时候*不匹配/
    pattern中没有/的时候*可以匹配/
  2. /结尾的pattern会去掉/进行匹配,但是只匹配目录,如*/会匹配所有的目录,
    这一点常常结合!不能把父目录被忽略的文件重新包含进来这条规则来用

pattern format定义好了,而pattern要跟谁进行匹配呢,答案是跟文件路径,文件路径指的是
从git repository开始的相对文件路径。git会遍历所有的文件,用这里定义的pattern来进行
匹配,匹配的结果就是要么包含进版本库,要么忽略它。

第二部分,双星号语法规则

**匹配完整路径,它不管什么斜杠不斜杠的,所有字符通吃。

  1. **/开始的pattern匹配所有路径。如**/foo匹配所有路径下的foo文件或者文件夹,
    等同于foo pattern
  2. /**结尾的pattern匹配everything inside。如abc/**匹配abc目录下的所有文件或文件夹
  3. 中间包含/**/的pattern匹配零个或任意多个目录。如a/**/b匹配a/ba/x/ba/x/y/b等等
  4. 上述三种pattern的组合pattern

这里的**类似于正则表达式里的*

常用实例

下面列出一些常用实例,日常使用足够了。

忽略某个文件夹

1
2
3
4
5
6
7
8
9
10
11
# 忽略build目录
# 如何做到的呢? 看第6条规则
build/

# 忽略build目录中的所有内容,但是不忽略build目录本身,
# 该条pattern中包含斜杠,所以wildcards不能匹配斜杠,
# 于是这个pattern的作用就是把build/里面的文件、文件夹
# 全都忽略了,但是由于"parent dir"被忽略的文件不能被
# 包含进来,所以build目录的子目录中的所有文件、文件夹
# 也都被一并忽略了
build/*

忽略某类文件

1
2
3
4
5
# 忽略所有的.exe文件
# 由于pattern中没有斜杠(/),所以这个pattern被当作shell glob
# 来进行匹配,它可以匹配斜杠(/),所以诸如"build/outputs/aaa.exe"
# 这样的.exe文件均被忽略了,从而达到忽略所有.exe文件目的
*.exe

忽略某个文件夹,但是保留某些特殊文件

1
2
3
4
# 忽略build目录,但是保留build/config.xml文件
# build/ 是不行的,为什么呢?找找上面的规则,与parent dir相关哦
build/*
!build/config.xml

建立白名单,只保留某些文件夹或者目录

所谓的白名单就是先忽略所有的文件、文件夹,然后把需要的再重新包含进来,
但是注意:parent dir被忽略的文件是不能被重新包含进来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 忽略一切
*
# 把所有的文件夹重新包含进来
# 这是怎么做到的呢? 看第6条规则
!*/
# 把想要的文件或文件夹重新包含进来
# 这里为啥不能用: !src/* 呢,因为有斜杠时星号不能匹配斜杠,
# 导致src/里的子目录被忽略了,只包含进src/下的文件了
# 这里为啥不能用: !src/ 呢,因为以斜杠(/)结尾只能匹配目录
!src/**
!AndroidManifest.xml
# 如果不把所有文件夹包含进来,则需要先把res/目录包含进来才行
!res/drawable/**

参考文献