深入浅出 .gitignore
组成
- # 开头的行是注释
- 其他的都是模式
- 不含 / 的是 shell glob 模式
- 含 / 的是 非 shell glob 模式
glob 模式类似 shell 使用的简化版正则表达式
结论
-
* 匹配 0 到 n 个字符,但是不匹配 /
-
** 匹配 0 到 n 个字符,匹配 /
-
? 匹配任意单个字符
-
[abc ]匹配 a,b,c 任意单个字符
-
[a-9]匹配 0 到 9 简任意单个字符
-
! 表示取反
-
非斜杠结尾匹配文件和目录(特殊的文件) *xx 匹配 fxx 和 dxx/
-
斜杠结尾匹配目录(特殊的文件) *xx/ 匹配 dxx/
-
非斜杠开头表示递归(匹配顶层还匹递归配子目录) *xx 匹配 fxx, /fxx, /a/fxx, /a/b/dxx/
-
斜杠开头表示非递归(只匹配顶层目录) /*xx 匹配 /fxx, /dxx/
空行也是模式但不匹配任何东西.
匹配的对象是文件路径,是字符串.
递归表示尝试匹配子目录.
陷阱
一个仓库结构如下
.
├── foo
│ ├── bar
│ │ ├── file1
│ │ └── file2
│ ├── barfile1
│ └── barfile2
├── foofile1
└── foofile2
写法一
# .gitignore
# 错误的,没有排除了/foo/bar
/*
!/foo/bar
没有跟踪到 /foo/bar 目录,所有的文件都被忽略/排除了
haha@st ~/dir $ git st -s
haha@st ~/dir $
写法二
# .gitignore
# 正确的,排除了/foo/bar
/*
!/foo
/foo/*
!/foo/bar
跟踪到 /foo/bar 目录
haha@st ~/dir $ git st -s
?? foo/
haha@st ~/dir $ git st -s ./foo/bar/
?? foo/bar/file1
?? foo/bar/file2
分析
写法一
/*
等价于三行/foofile1
/foofile2
/foo/
,这里只有三项,foo
是目录并没有递归下去(陷阱),据说出于性能考虑!/foo/bar
中没有匹配上面三相中的任意一项
这里的说的递归是指在设计 gitignore 文件的行为的时候,匹配了目录后没有采用继续展开的策略.和前面说的文件路径模式匹配中的递归不是一个东西.试想如果像 Linux 内核那样的大项目,每次匹配到目录都递归,会多出多少计算量.但是,不递归的结果就是写的人要多写,具体见下面.
写法二
/*
等价于三行/foofile1
/foofile2
/foo/
!/foo
即/foo/
被重新跟踪了/foo/*
等价于/foo/barfile1
/foo/barfile2
/foo/bar/
!/foo/bar
即/foo/bar/
被重新跟踪了
采用不递归的策略的后果,要手动层层展开(
/*
,/foo/*
),跟踪每一个路径片段(!/foo
,!/foo/bar
).