首页 » 前端开发 » 正文

gitignore详解

在使用GIT开发的过程中,在开发目录中会遇到很多期望不被上传到GIT库上的文件,比如C语言编写时生成的目标文件.o,前端开发用sass时可能也并不需要.css文件放置到GIT库上,虽然能够手动地在commit时去排除这部分,但是毕竟麻烦,而且不可靠,再加上多人开发时,你无法保证其他人能够自觉遵守这些规定(人总是会犯错的),这个时候,你就需要编写gitignore文件。

编写该文件,只需要简单地在GIT库的根目录下新建一个名为.gitignore的文件即可[注1](注意小数点,同时如果你在windows上开发,右键新建的方式并不能建立一个没有文件名的文件,可以直接在记事本中新建,或者采用其他IDE新建该文件),GIT便会根据这个文件的内容,将匹配的结果作为排除项,在你提交时不会进入你的提交列表中,这部分就是被GIT忽略(ignore)的部分。

如果你并不想手动编写该文件,github维护了一份不错的各个编程语言版本的gitignore文件的列表(https://github.com/github/gitignore),如果需要,可以用在你的项目之中。

注意: gitignore文件只会针对该文件生成之后的所有文件有效,也就是说,如果你的库上已经有很多需要被排除的文件了,gitignore无法控制他们,但是新增的这部分是会根据gitignore被GIT忽略的。

 

下面切入正题。

gitignore文件的每一行都指定了一个匹配模式(pattern),来决定哪些路径(文件)是需要被排除的。下文是gitignore支持的所有匹配格式。

*下文提到的前置均表示放到一行的最开始部分,比如“前置**”表示的是一行是以**开头的;后置类似,只不过是表示一行的结尾部分。

注意:读者应当对正则表达式有所了解,因为所有的匹配都是正则匹配。如果你并不了解正则请先粗略了解以下两点,对于下文的阅读有帮助:

  1. * 用于匹配任意字符,可以是0次或者多次,比如*a匹配a或pa或tea
  2. []中的内容表示“或”关系,比如[ab]表示匹配a或b一次

匹配模式

  • 空行匹配空文件,所以可以用来作为方便阅读时的分隔符
  • #开头表示注释,如果需要匹配#,请在#前面使用反斜杠(“\“)
  • 尾随的空格将被忽略,除非他们前面使用反斜杠(“\“)
  • 一个可选的前缀!表示否定匹配,任何在上一个匹配中匹配到的文件却又满足当前匹配结果(除掉!外剩下的匹配模式)的项又会被包含进来。典型的例子是,我需要排除某个目录(比如/images),但是我又希望这个目录夹中某些文件要保留下来。后面的例子可以看到这种情况。如果只是要单纯地匹配!,请在前面使用反斜杠(“\“)。关于该项的使用有一些注意事项,请参见注2
  • 如果匹配模式用斜杠(“/“)结尾,那么这个匹配将只会匹配目录。也就是说,foo/ 会匹配foo目录和其下的路径,但是不会匹配一个普通的文件或者符号链接foo(关于符号链接,如果不明白先假想成快捷方式,虽然有本质的不同,关于符号链接可以参见附录2)
  • 如果匹配模式中不包含斜杠(“/“),GIT将将它看成一个shell的全局匹配模式(简单地说就是匹配文件和目录等所有可以匹配的内容,image既可以匹配一个image的目录,也可以匹配一个名字为image的文件),从.gitignore文件所在的路径开始查找文件名路径(如果没有.gitignore文件,将从GIT根目录查找)
  • 匹配模式中的掩码*不会匹配文件路径的/,比如Documentation/*.html 匹配 Documentation/git.html  但是不匹配 Documentation/add/d.html 或 tools/add/Documentation/d.html。[注3]
  • 一个前置的斜杠表示匹配文件目录的当前目录。比如”/*.c”匹配”abc.c”但是不会匹配”a/b.c”(因为后者不在根目录下)。

两个连续*的匹配模式(”**“)将有额外的意义:

  • 前置的** 跟上斜线表示匹配所有路径。比如”**/foo“匹配任意位置的foo文件或者目录,与”foo“模式等价。”**/foo/bar” 匹配 在目录foo下的所有的bar文件或者目录。
  • 后置的”/**“表示匹配所有。比如”abc/**” 匹配(根据.gitignore文件的路径目录下的)”abc“目录下的所有文件,不管层级有多深。
  • 斜杠后跟2个*再跟一个斜杠(“/**/“)匹配0个或者多个目录。比如 “a/**/b” 匹配 “a/b“, “a/x/b“, “a/x/y/b” 等。
  • 其他情况下,连续的星号会被认为无效。

 

 实例

如果没有特殊说明,注释均解释了直到下一个注释开始之前的部分的含义。

#排除所有的.o或者.a文件
*.[oa]
#排除所有~结尾的文件(夹)
*~

再次注意,注释解释了该gitignore执行时的最终结果的意义,比如*.[oa]实际上是匹配了所有的.o或者.a文件,但是放在gitignore中,匹配到意味着需要排除掉。

#排除所有.a文件(除了下面一行的匹配结果)
*.a

# 但是不排除掉lib.a
!lib.a

# 只排除掉当前目录下的TODO目录,其他目录下的TODO目录不受影响
/TODO

# 排除掉所有目录下的build目录
build/

# 排除 doc/notes.txt, 但是不排除 doc/server/arch.txt
doc/*.txt

# 排除doc/下所有的 .pdf 文件(不管目录层级有多深)
doc/**/*.pdf
#排除除了foo/bar外的一切
/*
!/foo
/foo/*
!/foo/bar

其他参考资料请参见附录部分。

 

注意事项

注1: 实际上,GIT是按照如下顺序去理解要排除哪些文件的:

  1. 从正在执行的命令行中
  2. 从相同目录层级的.gitignore中,或者父亲/祖先目录中(当前级的优先级最高,向上越来越低)
  3. 从$GIT_DIR/info/exclude中
  4. 配置中的变量core.excludesFile

一般不太复杂的工程上,都是在GIT根目录上建立一个gitignore文件以方便管理。$GIT_DIR/info/exclude 和 core.excludesFile 的使用场景不太一致,这里不多介绍,读者可以参考附录1提供的文档,里边有详细记载。

注2:在他们的父级目录被排除的情况下,要重新包含文件或目录,必须满足以下条件:

  1. 这个排除的目录和重新包含的子目录的规则必须是在同一个.gitignore文件中
  2. 排除父级目录的规则必须不是以空格结尾的
  3. 排除父级目录的规则至少要有一个斜杠(“/”)
  4. 重新包含规则中目录部分必须是文本的(也就是没有掩码*)

关于以上第4点,官方文档原文(参见附录1)是:The directory part in the re-include rules must be literal (i.e. no wildcards) 。但是,经过实际测试,在我的win10+git2.6.3上发现是可以用*去匹配的,比如以下内容匹配到这样的结果:

所有/images/下q开头的部分保留,其他/images/下的文件(夹)被排除

/images/*
!/images/q*

注3: 这里官方文档(参见附录1)原文是:Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname.  关于fnmatch的flag可以参考一下PHP中该函数的实现(附录5),这里不多做介绍。

 

附录

附录1: 《gitignore官方英文文档

附录2:《维基百科-符号链接

附录3: 《廖雪峰-忽略特殊文件

附录4:《Ignoring-Files

附录5:《PHP: fnmatch – Manual

 

 

本文共 5 个回复

  • 涵涵 2016/01/30 16:28

    666666、很实用

    • coolguy 博主 2016/01/30 21:58

      @ 涵涵 恩,算是个备忘吧,哪天忘了怎么写就拿出来翻一下~ 😛

  • Kobe 2016/01/31 12:51

    感谢分享 🙄

  • Conan06 2016/03/08 21:45

    不错~mark

    • coolguy 博主 2016/03/11 14:28

      @ Conan06 哈哈,一直忘回复消息了:谢啦~以后博客记得常更新啦,我会经常去逛逛的~

发表评论