1、学习 gitignore 的起因
在项目过程中,由于需要忽略某一个文件,所以新建了一个 .gitignore
文件。但是,与我想象的有些不太一样,和我 Pair 的小伙伴直接将 .gitignore
文件建在了当前目录下。结构大致如下:
.
├── Dockerfile
├── Gemfile
├── README.md
├── auto
│ ├── dev-environment
│ └── rubocop
├── docker-compose.yml
├── template
│ ├── README.md
│ └── auto
│ ├── kite
| └── .gitignore
├── version.txt
└── .gitignore
What?
这样可以吗?不是需要在根目录下创建的吗?难道我的理解有问题?
于是,我来学 gitignore
了!😂
2、我原本对 .gitignore 的理解
我自己学习过 Git ,也一直在用 Git ,而 .gitignore
用于忽略某些文件,使其不被 Git 追踪,这个作用我是知道的。
当然,我一直以来的理解是:
.gitignore
文件需要在项目根目录下.gitignore
只对当前项目起作用.gitignore
文件中可以有一些简单的匹配规则
3、gitignore 介绍
-
gitignore
的定义
对于gitignore
的定义,我在官网上找到的解释是这样的:Specifies intentionally untracked files to ignore. 而翻译成中文,大意是:故意指定不追踪的文件,以达到忽略的目的。
同时,我还在一篇文章中找到了一个比较浅显易懂的解释:.gitignore
,是用来显式指定哪些文件或文件夹应该被Git忽略的一个文件。
这个解释就相当得好懂了,而根据这两个定义(解释)以及我自己之前对.gitignore
的理解,很明显,.gitignore
就是用来指定需要被 Git 忽略的文件或文件夹的,以达到其不被 Git 追踪的目的,从而,在整个版本控制的生命周期中,Git 不会对在.gitignore
中指定的文件和文件夹进行版本管理。
同时,经常使用.gitignore
的一个场景是:为了在提交代码时不将某些没有意义的文件或文件夹提交到远端仓库,比如:VS Code 生成的 .vscode
目录、IDEA 生成的.idea
目录、Node 项目中的node_modules
目录等。 -
gitignore
的有效位置
$HOME/.gitignore_global
,$HOME/.config/git/ignore
,$GIT_DIR/info/exclude
,.gitignore
这些地方指定的 ignore 规则都会在 Git 仓库中生效。
4、gitignore 描述
gitignore
指定应该被 Git 忽略的文件或目录;同时,在 gitignore
文件中,每一行都指定了一种匹配模式(规则),指定了匹配到的文件或目录应该被 Git 忽略;但,有一点需要注意,即已经被 Git 追踪的文件或目录不受 gitignore
文件的影响。
这是已经在上面解释过的关于 gitignore
的定义,但是,可以注意到,在上面有写到 gitignore
的有效位置有多个,包括 $HOME/.gitignore_global
, $HOME/.config/git/ignore
, $GIT_DIR/info/exclude
, .gitignore
等。那么,这么多的 gitignore
,如果他们都指定了应该被忽略的文件或目录,那他们的顺序是怎样的呢?
这就不得不提到 Git 对 gitignore
的优先检查顺序!
以下,是按照从高到低的优先级顺序列出的各个 .gitignore 的有效位置(规则源):
-
从命令行输入的
gitignore
规则 -
项目中的
gitignore
规则:其中,当前目录下的gitignore
规则优先级最高,紧接着是父目录中的gitignore
规则…最后,是项目根目录下的gitignore
规则。总之,越靠近指定忽略文件或目录的gitignore
规则,优先级越高。 -
$GIT_DIR/info/exclude
位置的gitignore
规则。🤔️$GIT_DIR
是什么?官网给出的定义是:$GIT_DIR 是.git
文件夹的位置。如果未指定,则 Git 会沿目录树移动,在每个位置查找.git
文件夹,直至到达~
或/
。 -
Git 配置的变量
core.excludeFile
指定的规则。🤔️变量[core.excludeFile](https://stackoverflow.com/questions/7335420/global-git-ignore)
是什么?怎么用?**git config --global core.excludesfile '~/.gitignore'
指定一个.gitignore
文件,作为全局的配置** -
🤔️那
$HOME/.gitignore_global
,$HOME/.config/git/ignore
呢?
经过查找资料发现,原来[$HOME/.gitignore_global](https://digitalfortress.tech/tricks/creating-a-global-gitignore/)
就是用来配置到变量core.excludeFile
的,而这个文件名其实完全可以随便起,只是为了更加语义化一些,所以将其命名为.gitignore_global
。
那,这只是一个,另一个$HOME/.config/git/ignore
呢?原来,上面的core.excludeFile
是通过设置全局gitignore
的形式使用了$HOME/.gitignore_global
,但是,这种方法是需要我们自己手动去配置的!然而,Git 是有默认的全局gitignore
的位置的!即,$XDG_CONFIG_HOME/git/ignore
,但是,如果$XDG_CONFIG_HOME
没有设置或为空的话,则使用$HOME/.config/git/ignore
!当然,如果$HOME/.config/git/ignore
也没有设置,那还用个🍺呦!😂
那么,gitignore
有这么多的有效位置,我们在平时使用的时候,到底应该将我们的 gitignore
规则放置在什么位置呢?
官网给出了几个建议:
-
如果在一个项目中,需要 Git 忽略掉一些文件或目录,不对它们进行追踪,并且要保证其他人
**clone
下该项目后,gitignore
规则依旧生效**,则应该将.gitignore
文件定义在项目中。 -
如果某个规则只想在某一个特定的仓库中生效,就把它定义在
$GIT_DIR/info/exclude
中。 -
如果你的
gitignore
规则要在任何情况下都生效,这种规则最好放在core.excludesFile
这一变量中,这个变量定义在用户目录下-~/.gitconfig
,该变量的默认值是$XDG_CONFIG_HOME/git/ignore
,如果$XDG_CONFIG_HOME
未设置或为空,Git会使用$HOME/.config/git/ignore
以上就是一些大致的建议。同时,关于 gitignore
规则,还有一个需要注意的点,即:
-
Git 的底层管道工具,比如
git ls-files
和git read-tree
,只从命令行参数指定的文件中读取gitignore
规则。 -
上层的 Git 工具,比如
git status
和git add
,会从上述规则源中读取gitignore
规则
5、gitignore 的语法规则
- 空行不匹配任何文件,可以用空行来增强
gitignore
规则的可读性😂 - 注释行以
#
开头 - 如果指定要忽略的文件或目录名中含
#
的话,可以在#
前加反斜杠\
,即\#
- 如果每一行最后尾随的空格没有用反斜杠转义,那么这些空格是无效的,不会作为规则的一部分
- 使用
!
前缀来否定gitignore
规则中前面部分忽略掉的文件或目录。如果一个文件被前面的gitignore
规则给匹配到了,那么该文件不会被Git追踪,但是如果后面的规则使用!
匹配到了该文件,那么该文件又会被Git追踪。 - 如果一个目录被 Git 忽略了,那么无论如何,即便在后面部分指定了应该否定该目录下的某个文件,这个文件也不会被Git追踪。这是由于:出于性能考虑,Git不会遍历被忽略的目录,因此,定义在被忽略目录下的
gitignore
规则都是无效的。 - 如果某个文件或目录以感叹号
!
开头,如果要指定忽略该文件或目录的话,可以在!
前面加上反斜杠\
,即\!
。比如:\!important.txt
会匹配文件!important.txt
- 以斜杠
/
结尾代表指定的是某个目录及该目录下的所有子目录及文件 - 如果不是以斜杠
/
结尾,则代表指定的是某个文件 - 如果规则不符合以上的情况,那么 Git 就会把这个规则当成
shell
通配符规则来进行解析。举个🌰,Documentation/*.html
匹配Documentation/git.html
,但不会匹配Documentation/ppc/ppc.html
或者tools/perf/Documentation/perf.html
- 以斜杠开头的通配符规则从路径开头开始匹配。比如,
/*.c
匹配cat-file.c
,但不匹配mozilla-sha1/sha1.c
**
代表可以匹配到 0+ 个目录,比如:**/foo
会匹配到所有路径下的文件foo
或者目录foo
,无论它在哪个目录**/foo/bar
规则会匹配到所有路径下的目录foo
下直接跟的文件bar
或目录bar
abc/**
会匹配目录abc
下的所有文件及子目录…a/**/b
会匹配a/b
、a/x/b
、a/x/y/b
等,中间的目录级数可以是 0+ 个
6、停止被 Git 追踪(Track)
gitignore
文件的目的是确保某些不应该被Git追踪的文件确实没有被 track。
如果要停止追踪(track)一个已经被Git追踪的文件,请使用 git rm --cached Xxx
命令。