.gitignore语法:bin vs bin / vs bin / * vs bin / **

在我的.gitignore文件中添加binbin/bin/*bin/**什么区别? 我一直在使用bin/ ,但是看着其他的 .gitignore文件 (在eclipse文件中 ,双精度和单精度精度都是这样一起使用的: tmp/**/*这是怎么回事?)我看到前两个模式也被广泛使用。 有人可以解释三者之间的区别吗?

bin匹配任何名为“bin”的文件目录。

bin/匹配任何名为“bin”的目录 ,这实际上意味着它的所有内容,因为Git不会单独跟踪目录。

bin/*直接在任何bin/匹配所有文件和目录。 这样可以防止Git在其子目录中自动查找任何文件,但是如果创build了bin/foo子目录,则该规则将与foo的内容匹配。

bin/**匹配任何bin/目录及其所有子目录中的所有文件和目录。

“any”这个词在这里至关重要,因为规则不是相对于版本库的根,而是应用在文件系统树的任何地方 。 您必须使用/ (或!/取消忽略)开始规则,这意味着存储库的根目录,而不是系统的根目录,以便仅匹配预期的目标。

警告:你不应该单独使用像dir/*/dir/**等规则, 除非你也不要忽略该目录内存在的东西 。 省略星号,或者你可能会永久失去一些git gcgit stash等的调用的大量数据 。

我真的不知道tmp/**/*是做什么的。 我最初认为它可以用来匹配tmp/的子目录中的文件,而不是直接存在于tmp/本身中的文件。 但是一个简单的testing似乎表明这忽略了tmp/所有文件。

binbin/区别只在于后者只能匹配一个目录。

bin/**/*bin/**相同(从1.8.2开始,根据@ VonC的回答)。

那个棘手的问题,就是我花了一个小时左右的时间把我的头发拉出来,是bin/bin/** 不太一样! 由于之前忽略了整个目录,后者忽略了其中的每个文件,git几乎在所有情况下都不关心目录,通常没有区别。 但是,如果您尝试使用! 要忽略子path,那么你会发现,如果忽略父目录,git(ahem)会忽略它! (再次,而不是目录内容)

这个例子是最清楚的,所以对于一个新创build的仓库来说:

 $ cat .gitignore ignored-file or-dir dir-only/ !dir-only/cant-reinclude dir-contents/** !dir-contents/can-reinclude $ mkdir or-dir dir-only dir-contents $ touch file ignored-file or-dir/ignored-file dir-only/cant-reinclude dir-contents/can-reinclude 

存在以下未跟踪的文件:

 $ git ls-files --other .gitignore dir-contents/can-reinclude dir-only/cant-reinclude file ignored-file or-dir/ignored-file 

但是您可以看到以下文件不被忽略:

 $ git ls-files --other --exclude-standard .gitignore dir-contents/can-reinclude file 

如果你尝试添加,你会得到:

 $ git add dir-only/cant-reinclude The following paths are ignored by one of your .gitignore files: dir-only/cant-reinclude Use -f if you really want to add them. fatal: no files added 

我认为这是一个错误。 (这是所有的git version 1.8.4.msysgit.0

请注意,严格来说,git不会跟踪目录,只会跟踪文件。 因此不可能添加一个目录,只有其内容

然而在.gitignore的背景下,git假装理解目录是唯一的原因

如果排除该文件的父目录,则不可能重新包含文件。
https://git-scm.com/docs/gitignore#_pattern_format

这对排除模式意味着什么? 我们来详细的介绍一下:

bin

这忽略了

  • 名为bin文件。
  • 名为bin的文件夹的内容

您可以通过添加随后的白名单来忽略bin文件和文件夹! 条目,但无法将名为bin的文件夹的内容列入白名单

 bin !bin/file_in_bin # has no effect, since bin/ is blacklisted! !bin/* # has no effect, since bin/ is blacklisted! !file_in_bin # has no effect, since bin/ is blacklisted! !bin # this works 

bin/

与上面相同,只是它不匹配名为bin 文件 。 添加尾随/告诉GIT只匹配目录。

bin/*

这忽略了

  • 包含在名为bin的文件夹中的文件
  • bin文件夹的直接子文件夹的内容
 bin/* # blacklists bin/file_in_bin and bin/subfolder/ !bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted! !bin # whitelists files named bin/bin, since bin/ itself is not blacklisted !bin/ # has no effect, since bin/ itself is not blacklisted !bin/file_in_bin # works since bin/ itself is not blacklisted !file_in_bin # works too !bin/subfolder # works (so implicitly whitelists bin/subfolder/file_in_sub) !bin/subfolder/ # works just as well !bin/* # works for file_in_bin and subfolder/ 

bin/**

这忽略了

  • bin内容
  • bin中子文件夹的内容(嵌套的任何级别)
 bin/** # blacklists bin/file_in_bin and # bin/subfolder/ and bin/subfolder/file_in_sub and # bin/subfolder/2/ and bin/subfolder/2/file_in_sub_2 !bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted !bin/subfolder/2/ # has no effect, since bin/subfolder is blacklisted !bin/subfolder/2/file_in_sub_2 # has no effect, since bin/subfolder is blacklisted !bin/subfolder # works only in combinations with other whitelist entries, # since all contents of subfolder are blacklisted (1) !bin/file_in_bin # works since bin itself is not blacklisted !bin/* # works for file_in_bin and subfolder; see (1) 

我刚刚做了一个新的回购,并尝试了一些东西。 这是我的结果:

新的结果

git版本2.10.1.windows.1

  1. 初始化几乎空的回购。 只有README文件
  2. bin目录填充几层
    • bin.txt
    • Test.txt
    • bin/a/b/bin.txt
    • bin/a/b/Test.txt
    • bin/a/bin/bin.txt
    • bin/a/bin/Test.txt
    • bin/a/bin.txt
    • bin/a/Test.txt
    • bin/bin.txt
    • bin/Test.txt
  3. 添加bin到gitignore:结果
    • bin目录下(和更深)的所有内容现在都被忽略了
    • 根级别不被忽略(/bin.txt和/Test.txt仍然显示)
  4. 编辑binbin/ gitignore:结果
    • 不用找了
  5. 编辑bin/bin/*
    • 不用找了
  6. 编辑bin/*bin/**
    • 不用找了
  7. 编辑bin/**bin/**/
    • bin/bin.txtbin/Test.txt不再被忽略
  8. 编辑bin/**/bin/**/*
    • bin/bin.txtbin/Test.txt重新被忽略

老结果

git版本:2.7.0.windows.1

  1. 初始化几乎空的回购。 只有README文件
  2. bin目录填充几层
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. 添加bin到gitignore:结果
    • bin目录下(和更深)的所有内容现在都被忽略了
  4. 编辑binbin/ gitignore:结果
    • bin目录下的所有东西(还有更深的)仍然被忽略(没有改变)
  5. 编辑bin/bin/*
    • bin目录下的所有东西(还有更深的)仍然被忽略(没有改变)
  6. 编辑bin/*bin/**
    • bin目录下的所有东西(和更深的)仍然被忽略(没有改变)
  7. 编辑bin/**bin/**/
    • bin/Test.txt不再被忽略
  8. 编辑bin/**/bin/**/*
    • bin目录下的所有内容(以及更深)都会被忽略

请注意, ** 和子目录**/bar结合在一起时 ,必须改变其默认行为,因为git1.8.2的发行说明现在提到:

.gitignore.gitattributes文件中的模式可以具有**/ ,作为匹配0或更多级子目录的模式。

例如“ foo/**/bar ”与“ foo ”本身或“ foo ”的子目录中的“ foo/**/bar ”相匹配。


要记住的规则(以及哪些帮助理解这些语法背后的意图的差异)是:

如果排除该文件的父目录,则不可能重新包含文件。


通常情况下,如果要从忽略文件夹f的子文件夹中排除文件,您应该:

 f/** !f/**/ !f/a/sub/folder/someFile.txt 

那是:

  • 如果第一个规则是f/ ,那么文件夹f/将被忽略,下面有关f规则将无关紧要。
  • f/**实现与f/相同,但忽略所有子元素 (文件和子文件夹)。
    这使您有机会白名单(从gitignore排除)子文件夹: !f/**/
  • 由于所有f子文件夹都不被忽略,所以可以添加一个规则来排除一个文件( !f/a/sub/folder/someFile.txt

bin/*bin/还有另一个区别。

bin/匹配foo/bin/test.txt (与预期的一样),但是bin/*没有,这看起来很奇怪,但是有logging: https : //git-scm.com/docs/gitignore

“Documentation / *。html”与“Documentation / git.html”匹配,但不匹配“Documentation / ppc / ppc.html”或“tools / perf / Documentation / perf.html”。

原因似乎是这些规则:

  • 如果模式以斜线结尾,则为了下面的描述将其删除…

  • 如果模式不包含斜杠/,Git会将其视为shell glob模式,并检查与path名相对于.gitignore文件位置的匹配项。

  • 否则,Git会将该模式视为适合fnmatch(3)与FNM_PATHNAME标志使用的shellshell…

因此,如果模式以斜线结尾,斜杠将被删除,并将其视为一个shell glob模式,在这种情况下bin匹配foo/bin/test.txt 。 如果以/*结尾,则斜杠不会被删除,并且会传递给fnmatch,这在子目录中不匹配。

但是,对于foo/bin/foo/bin/* ,因为即使从foo/bin/删除了尾部斜杠,它仍然包含一个斜杠,所以它被视为fnmatch模式,而不是glob。 即它不会匹配bar/foo/bin/test.txt