你不知道的.gitignore
0. 几个概念
- 被gitignore规则命中,即被gitignore规则匹配到的文件或目录,不会被Git追踪,即不会被Git给track
1. 简介
- .gitignore,是用来显式指定哪些文件或文件夹应该被Git忽略的一个文件
-
$HOME/.gitignore_global
,$HOME/.config/git/ignore
,$GIT_DIR/info/exclude
,.gitignore
这些地方指定的ignore规则都会在Git仓库中生效
2. 描述
一个.gitignore
文件显式地指定了哪些文件不应被Git追踪,即被Git忽略掉。在被gitignore之前已经被Git追踪的文件不受gitignore规则的影响。关于gitignore规则的详情请继续往下看。
.gitignore
文件中的每一行都指定了一种匹配模式。通常来说,Git会从多个可能的规则源获取gitignore
规则来决定Git是否要忽略某一个具体的路径path,如下按照优先级列出了各种规则源,越靠前的规则优先级越高(在一个规则源内部,如果有多个gitignore
匹配,以最后匹配的为准)
- 从命令行输入的规则
- 该路径下的
.gitignore
文件,或者父级目录的.gitignore
中定义的规则。其中,越靠近具体路径的.gitignore
文件的优先级越高,同目录下的.gitignore
文件优先级最高。项目仓库中通常都有.gitignore
文件,里面会包含忽略项目build自动生成的文件的规则 -
$GIT_DIR/info/exclude
中定义的规则 - Git配置变量
core.excludeFile
指定的规则
具体讲gitignore规则定义在哪个文件中取决于该规则的作用(域):
- 如果一个
gitignore
规则应该被Git追踪,别人clone仓库后规则也生效,那么它就应该被定义在.gitignore
文件中 - 如果某个规则只想在某一个特定的仓库中生效,就把它定义在
$GIT_DIR/info/exclude
中吧 - 如果你的
gitignore
规则要在任何情况下都生效,这种规则最好放在core.excludesFile
这一变量中,这个变量定义在用户目录下-~/.gitconfig
,该变量的默认值是$XDG_CONFIG_HOME/git/ignore
,如果$XDG_CONFIG_HOME
是空的,Git会使用$HOME/.config/git/ignore
Git的底层管道工具,比如git ls-files
和git read-tree
,只从命令行参数||命令行参数指定的文件中读取gitignore
规则。上层的Git工具,比如git status
和git add
,会从上述规则源中读取gitignore
规则
3. gitignore规则
- 空行不匹配任何文件,所以可以用空行来增强
gitignore
规则的可读性 - 注释行以
#
开头。可以在#
前加一个反斜杠转义之,使之能够匹配包含#字符
的文件夹或文件 - 如果每一行最后尾随的空格没有用反斜杠转义,那么这些空格是无效的,不会作为规则的一部分
- 使用
!
前缀来否定之前的规则。如果一个文件被前面的gitignore
规则给匹配到了,那么该文件不会被Git追踪,但是如果后面的规则使用!
匹配到了该文件,那么该文件又会被Git追踪。当然,如果一个文件的父目录都被Git忽略了,那么无论如何,这个文件都不会被Git追踪。出于性能考虑,Git不会遍历被忽略的目录,因此,定义在被忽略目录下的gitignore
规则都是无效的。有时候,我们真的是想忽略以感叹号!
开头的一个文件或者目录,这时,可以在感叹号!
前面加一个反斜杠转义之,比如:\!important.txt
会匹配文件!important.txt
- 如果一个规则以斜杠结尾,在实际匹配的时候,最后的斜杠会被移除掉,但是这个规则只会匹配目录,而不会匹配文件。换句话说,
foo/
会匹配到目录foo
和foo
下的子目录,但不会匹配到文件foo
或者软链接foo
- 如果规则中不包含斜杠
/
,Git就会就会把该规则当成通配符规则来进行处理,从该规则所在.gitignore
文件所在路径开始匹配。当然,如果这个规则不是放在.gitignore
文件中的,就会从work tree
的顶部开始匹配 - 如果规则不符合以上的情况,那么Git就会把这个规则当成shell通配符规则来进行解析,是以带
FNM_PATHNAME
标记的fnmatch(3)规则进行解析。但是,规则中的通配符不会匹配路径名中的斜杠/
。举个栗子,Documentation/*.html
匹配Documentation/git.html
,但不会匹配Documentation/ppc/ppc.html
或者tools/perf/Documentation/perf.html
- 以斜杠开头的通配符规则从路径开头开始匹配。比如,
/*.c
匹配cat-file.c
,但不匹配mozilla-sha1/sha1.c
两个连续的星号**
在匹配全路径名的时候可能有特殊含义:
- 规则以两个星号
**
开头,后接一个斜杠,这样的规则会在所有路径或子路径中尝试进行匹配。比如,**/foo
会匹配到文件foo
或者目录foo
,无论它在哪个目录;foo
这条规则同样会尝试匹配所有路径中的文件foo
或者目录foo
。**/foo/bar
规则会匹配任意文件或目录foo
下直接跟的文件bar
或目录bar
- 如果规则中间有连续的两个星号
**
,那这条规则会匹配下面的所有东西。比如abc/**
会匹配目录abc
下的所有文件或目录,当然,这里的目录abc
是相对于.gitignore
文件位置而言的,无限递归 - 如果规则是
斜杠/
后跟两个星号,然后再跟一个斜杠的形式,这里的两个星号就会匹配0+
个目录,这里的0+
是指可以没有,也可以是多个。再举个例子,比如a/**/b
会匹配a/b
、a/x/b
、a/x/y/b
这些 - 其他形式的连续星号都认为是非法的
4. 笔记
gitignore文件的目的是确保某些不应该被Git追踪的文件确实没有被track。如果要停止track一个已经被Git追踪的文件,请使用git rm --cached Xxx
命令
5. 示例
$ git status
[...]
# 暂未被Git追踪的文件:
[...]
# Documentation/foo.html
# Documentation/gitignore.html
# file.o
# lib.a
# src/internal.o
[...]
$ cat .git/info/exclude
# 忽略,即不再追踪仓库中所有的objects和压缩文件.
*.[oa]
$ cat Documentation/.gitignore
# 忽略自动生成的html文件,
*.html
# 排除手动维护的foo.html,即不忽略foo.html,即Git会追踪foo.html
!foo.html
$ git status
[...]
# 暂未被Git追踪的文件:
[...]
# Documentation/foo.html
[...]
一个例子不够,再来一个:
$ cat .gitignore
vmlinux*
$ ls arch/foo/kernel/vm*
arch/foo/kernel/vmlinux.lds.S
$ echo '!/vmlinux*' > arch/foo/kernel/.gitignore
在这个例子中,第二个.gitignore文件arch/foo/kernel/.gitignore
的优先级更高,它阻止了第一个.gitignore文件试图忽略arch/foo/kernel/vmlinux.lds.S
的行为,从而,Git不会忽略arch/foo/kernel/vmlinux.lds.S
,会尝试追踪它
最后再来一个例子收尾。举个忽略目录下所有文件或目录,除了某个特定的目录foo/bar
的例子吧(注意下面的/*
,就算没有前面的斜杠,通配符也会匹配包括foo/bar
在内的所有文件或目录,所以斜杠/
是可有可无的):
$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar
6. 补充示例
译文到此完毕,我再补充几个小例子吧
6.1 示例一
6.2 示例二
6.3 示例三
7. 其他
- 如果文章中有任何不妥之处或者错误的地方,欢迎在评论区留言,谢谢~