今天分享一下Git中一个非常重要的文件.gitignore,这个文件用来定义哪些文件或文件夹,Git不需要跟踪,从而也不需要将这些文件添加到版本管理中。
文件意义
在实际项目中,可能存在一些文件或文件夹是不需要做版本跟踪的,也不需要做版本管理,但是又必须将其放到本地的Git的工作目录中,比如,编译过程中生成的一些临时文件(Java字节码文件.class)、数据库密码的配置文件等等。
当然,他们仍然存在于本地 repostitory,但是在执行git push时不会上传到远端 repository ,因此远端 repository 中自然也不会存在这些文件。
对于这些文件或文件夹,每次git status时,都会出现Untracked files ...,感觉非常的蛋疼!
好在Git在设计之初,就考虑到了这个问题。在一个以Git做为版本管理工具的项目中,可以在.gitignore文件中定义,哪些文件或文件夹不需要被Git跟踪。定义之后,Git就会自动忽略这些文件了。
文件模板
当然,对于各个技术栈或编程语言,在GitHub中,大神们已经为我们整理好了各种各样的.gitignore,我们只要根据自己的需求稍微修改一下就好了。
随便据几个例子,比如:
- 在JetBrains.gitignore中,
.idea/是JetBrains工具生成的用于管理工程和生成一些临时文件的文件夹 - 在Node.gitignore中,
node_modules/是NPM项目用于存储第三方依赖库的文件夹,每次接手一个新的Node项目,只需要npm install即可 - 在VisualStudio.gitignore中,
coverageor*.coverage是一些项目在执行测试用例时自动生成的测试覆盖率报表**(多数的大型项目都会在持续集成系统(Continuous Intergration System)中执行相关测试,并输入测试覆盖率报表,因此即使我们在本地执行测试,也不需要将相关测试覆盖率报表push到Git中)**
忽略原则
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,**也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的
.class文件; - 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
忽略规则定义
虽然,github/gitignore中已经为各种类型的项目定义了.gitignore模板,但是我们仍然需要了解在.gitignore中对文件或文件夹定义忽略规则的方法:
首先,我们需要明确关于文件和文件夹的命名:
- 文件即可以命名为"*.*"(即以.*为后缀,比如
abc.jpg、1.a),也可以命名为"*"(比如abc、aaa) - 文件夹即可以命名为"*"(比如
abc、aaa),也可以命名为" *.*"(即存在后缀,比如abc.a、1.b,只是我们一般不这样起名罢了)
之所以强调上述的细节,是因为在网上的有些Blog中,并没有考虑到文件夹可以以.*为后缀的方式命名,故认为abc.a规则只是忽略命名为abc.a的文件,不包括忽略命名为abc.a的文件夹,这是错误的!!!!
还需要明确:
忽略文件夹意味着这个文件夹中的所有文件和文件夹(当然也包括其中的子文件和子文件夹)都会被忽略,除非添加了额外的非过滤规则
定义过滤规则
不指定绝对路径
忽略所有名为某个指定名称的文件或文件夹,无论它位于根目录(root directory)or 子目录(subdirectory):
# ignore all files or folders named "foo.txt" wherever they are located
foo.txt
忽略所有以.o 或.a结尾的文件或文件夹,无论它位于根目录(root directory)or 子目录(subdirectory):
# ignore all files or folders which name ends up with ".o" or ".a" wherever they are located
*.[oa]
忽略所有以.zip结尾的文件或文件夹,无论它位于根目录(root directory)or 子目录(subdirectory):
# ignore all files or folders which name ends up with "zip" wherever they are located
*.zip
忽略所有以a结尾的文件夹(不忽略以a结尾的文件),无论它位于根目录(root directory)or 子目录(subdirectory):
# ignore all folders which name ends up with "a" wherever they are located
*a/
指定绝对路径
只忽略根目录下名为abc的文件夹或文件,子目录中名为abc文件或文件夹不在忽略范围内:
# ignore a specified folder or file which name is "abc" and is located in root directory
/abc
需要注意的是,忽略根目录下名为abc的文件夹意味着,其中包含的子文件夹中的文件和文件夹都会被忽略,如/abc/a/1.exe、/abc/a/b/2.exe
只忽略根目录下名为abc的文件夹(不忽略根目录下名为abc的文件),子目录中名为abc文件夹不在忽略范围内:
# ignore a specified folder which name is "abc" and is located in root directory
/abc/
忽略根目录下,abc文件夹中某一个子文件夹下的a.exe:
# ignore all files which is located in /abc
/abc/*/a.exe
如/abc/a/a.exe、/abc/b/a.exe,但不包括/abc/b/c/a.exe、/abc/b/c/d/a.exe。
忽略根目录下,abc文件夹中处于任意位置下的a.exe:
# ignore all files which is located in /abc
/abc/**/a.exe
如/abc/a/a.exe、/abc/b/a.exe、/abc/b/c/a.exe、/abc/b/c/d/a.exe
注:以上两个例子想说明“*” 与 “**”的区别。一句话概括就是:\*只能匹配一级文件夹,而\*\*可以匹配一级或多级文件夹。
忽略根目录下,abc文件夹中的所有文件(包括其子文件夹中的文件或文件夹):
# ignore all files which is located in /abc
/abc/*
只忽略根目录下/abc/a.java这个文件:
# # ignore a specified folder which name is "a.java" and is located in /abc
/abc/a.java
定义非过滤规则
与过滤规则完全类似,但是只需要在上述规则前面增加!,即定义为非过滤规则。
比如:
忽略所有以.a结尾的文件或文件夹:
# ignore all files or folders which name ends up with ".a" wherever they are located
*.a
那么,不忽略所有以.a结尾的文件或文件夹:
# do not ignore all files or folders which name ends up with ".a" wherever they are located
!*.a
想象一个场景:我们只需要跟踪根目录下的abc文件夹中的one.java文件,这个文件夹中的其他文件都不需要被跟踪。那么我们可以这样定义:
/abc/
!/abc/one.java
假设我们只有过滤规则没有非过滤规则,那么我们就需要把/abc/文件夹下除了one.java以外的所有文件都列出来!
通配符匹配语法
- 斜杠
/开头表示根目录; - 星号
*通配多个字符(不包括"/"); - 双星号
**通配多个字符(包括"/"); - 问号
?通配单个字符; - 方括号
[]表示匹配其中的某单个字符;
检查文件是否被忽略
我们可以使用以下命令检查某个文件或文件夹是否被忽略,如果输出了输入的pathname,则表明这个文件或文件夹会被Git忽略。
git check-ignore pathname
通过这个命令,对于某些存在复杂忽略匹配规则的项目,在需要判断某个文件是否被忽略时,我们就不需要根据这个文件的路径和名称特征在.gitignore中一条一条地检查了。
比如,我们希望判断ddd\1.txt文件是否在忽略列表中:
git check-ignore ddd\1.txt
-> "ddd\\1.txt"
因为输出了"ddd\\1.txt",则说明Git会忽略ddd\1.txt这个文件**(事实上,我在.gitignore中增加了/ddd/这一规则)**。
.gitignore类型
.gitignore可以分为Global gitignore 和 Project gitignore。
- **Global gitignore:**即将某个
.gitignore文件设置为全局,那么这个.gitignore文件中的忽略规则对本机中所有Git项目都有效 - **Project gitignore:**即在某个仓库(repository)中添加的
.gitignore文件,这个文件中的忽略规则仅仅在这个项目中生效
添加Global gitignore
以Windows平台为例,添加Global级别的 gitignore:
- 在
C:\Users\{User Name}中增加一个.gitignore_global文件,在这个文件中添加全局过滤规则 - 在控制台中输入
git config --global core.excludesfile ~/.gitignore_global,当然这里我默认你已经将’git添加到了path`环境变量中。
添加Project gitignore
添加Project级别的 gitignore很简单,只需要在一个Git repository的任何一个位置下添加一个.gitignore文件即可。
这意味着,你可以在一个Git repository的不同位置下添加.gitignore。那么如果不同的.gitignore中包含了相互矛盾的过滤规则,就存在优先级的问题:
事实上,git会在这个repository中构建一个文件结构树,子节点的.gitignore会重写父节点的.gitignore的过滤规则。
举个例子来说,在一个repository中,文件是这样的:
- .gitignore
- a-folder
- .gitignore
- file.txt
其中,在根目录下的.gitignore中定义file.txt(即忽略file.txt),在a-folder文件夹的.gitignore中定义!file.txt(即不忽略file.txt)。
git status,我们发现该file.txt是会被git跟踪的,其实是因为a-folder文件夹的.gitignore中定义的!file.txt重写了根目录下的.gitignore中定义的file.txt忽略规则。
注意
尽量在Project创立之初,就定义好.gitignore
最后需要强调的一点是,如果你在将对应某个文件或文件夹的过滤规则添加到.gitignore之前,就将这个文件或文件夹 git push到了远端 repository,那么即使你在.gitignore中添加这些过滤规则,这些规则也不会起作用,Git仍然会对这些文件或者文件夹进行版本管理。
因此,我们尽量在Project创立之初,就定义好.gitignore。如果在之后的开发过程中,因为新增加了某些文件,而需要增加了新的过滤规则,要保证这些文件在创建之初,就在.gitignore中增加对应的过滤规则。而不是在git push这些文件后,才想起来要修改.gitignore。
已经被tracked的文件设置.gitignore无效
.gitignore规则仅对未被跟踪(tracked)的文件有效,跟踪(tracked)文件包括被暂存(staged)的文件(即该文件已经被git add了,但没有git commit),和被git commit的文件。
参考
- gitignore last updated in 2.12.2
- GitHub Help - Ignoring files
- -https://www.atlassian.com/git/tutorials/gitignore