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


89

什么是加入之间的区别binbin/bin/*bin/**在我的.gitignore文件?我一直在使用bin/,但是查看其他 .gitignore文件(在eclipse文件中,甚至将双星和单星也像这样一起使用:tmp/**/*这是怎么回事?)我看到前两种模式也得到了广泛使用。有人可以解释一下两者之间的区别吗?


4
@unutbu:这个问题的公认答案显然是有争议的。最热门的评论之一声称答案实际上是一个完整的神话。
chandsie'1

该行为已在手册页中完全指定,并且我确定此处(或十个)附近包含所有信息都存在一个问题/答案。
卡斯卡贝尔2012年

Answers:


84

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

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

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

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

这里的“ any”一词非常重要,因为规则与存储库根目录无关,并且适用于文件系统树中的任何位置。您必须以/(否则!/要忽略)规则开头,这意味着存储库的根目录,而不是系统的根目录,以便仅匹配预期的目标。

警告:你应该从来没有使用类似的规则dir/*/dir/**独自一人,等等,除非你也未忽略的东西,这个目录中存在。省略星号,否则您可能会因的某些调用而永久丢失大量数据git gcgit stash甚至更多。

我真的不知道该怎么tmp/**/*做。最初,我认为它可以用来匹配子目录中的文件,tmp/但不能匹配直接存在于其tmp/自身中的文件。但是一个简单的测试似乎表明这会忽略中的所有文件tmp/


10
只是为了澄清,bin/和之间有什么区别bin/**
chandsie

1
我怀疑bin/将忽略bin目录,而bin/**将包括bin目录但不包含任何目录
Robin Winslow 2012年

1
这似乎与悉达多的答案不一致。从答案得出,bin/将忽略目录本身(包括所有子目录和文件),而bin/**将忽略bin目录及其子目录中的所有文件,但忽略bin目录本身。我不确定这是否正确。
Christopher Berman 2012年

3
请注意,如果要跟踪bin /目录中的所有文件,但忽略其子目录中的所有文件,则可以(在后续行中)进行操作bin/** \n !bin/*(因为我看不到如何在mini-Markdown中
强制换行

9
这个答案在很多方面都是错误的。首先,git不会跟踪目录,因此.gitignore条目只能匹配目录内容,而不能匹配目录。其次,bin匹配两种命名文件,bin 在内容bin文件夹中。第三,bin/* 是否匹配其子目录中的任何文件。你们都测试过了吗?
ThomasR '16

46

bin并且bin/仅在于后者仅与目录匹配。

bin/**/*bin/**(根据@VonC的答案显然是从1.8.2开始)相同。

最棘手的一个,我只花了一个小时左右撕裂我的头发了,是bin/bin/**不是一样!由于前者会忽略整个目录,而后者会忽略其中的每个文件,并且git在几乎所有情况下都不关心目录,因此通常没有区别。但是,如果您尝试使用它!来忽略子路径,那么您会发现如果忽略父目录,则git(ahem)会忽略它!(再次,而不是目录内容)

通过示例,这是最清楚的,因此对于新设置的存储库,其设置如下:

$ 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


1
的确是+1。您应该考虑为此提交实际的错误报告,因为这种行为似乎确实是意外的。
chandsie 2013年

1
正是我的用例。谢谢!
塞巴斯蒂安·格拉夫

3
dir/dir/**re的不同行为。具有未忽略!发生,因为“这是不可能再包含一个文件,如果该文件的父目录被排除” [源。令人困惑,但出于性能原因而这么做。请参阅相关的SO问题
tanius

23

请注意,严格来说,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)

9

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

新结果

git版本2.10.1.windows.1

  1. 初始化接近空的仓库。仅自述文件
  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. 在gitignore中编辑binbin/:结果
    • 没变
  5. 编辑bin/bin/*
    • 没变
  6. 编辑bin/*bin/**
    • 没变
  7. 编辑bin/**bin/**/
    • bin/bin.txt并且bin/Test.txt不再被忽略
  8. 编辑bin/**/bin/**/*
    • bin/bin.txtbin/Test.txt再次被忽略

旧结果

git版本:2.7.0.windows.1

  1. 初始化接近空的仓库。仅自述文件
  2. 填充bin目录几层
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. 添加bin到gitignore:结果
    • 在下面的一切bin目录(和深)现在被忽略
  4. 在gitignore中编辑binbin/:结果
    • bin目录(及更深层)下的所有内容仍将被忽略(不变)
  5. 编辑bin/bin/*
    • bin目录(及更深层)下的所有内容仍将被忽略(不变)
  6. 编辑bin/*bin/**
    • bin目录(及更深层)下的所有内容仍将被忽略(不变)
  7. 编辑bin/**bin/**/
    • bin/Test.txt 不再被忽略
  8. 编辑bin/**/bin/**/*
    • bin目录(及更深层)下的所有内容都将再次被忽略

1
这是一个很好的测试,我认为将text.txt重命名为bin.txt会更好地显示一些关键差异。如果您这样做,我将投票给这个答案。
马特·约翰逊

@MattJohnson True ...很抱歉,目前我正在使用较新的git版本
Joe Phillips'1

@MattJohnson我终于解决了这个问题
Joe Phillips

8

请注意,“ **与子目录**/bar)组合时,必须已更改其默认行为,因为git1.8.2发行说明现在提到:

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

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


要记住的规则(并有助于理解这些语法背后的意图差异)是:

如果排除该文件的父目录,则无法重新包含该文件。


通常,如果要从忽略文件夹f的子文件夹中排除文件,则可以执行以下操作:

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

那是:

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

这如何回答这个问题?
ThomasR '16

0

bin/*和之间还有另一个区别bin/

bin/匹配foo/bin/test.txt(按预期),但bin/*不匹配,这看起来很奇怪,但已记录在案:https//git-scm.com/docs/gitignore

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

其原因似乎是以下规则:

  • 如果图案以斜杠结尾,则出于以下描述的目的而将其删除...

  • 如果该模式不包含斜杠/,则Git会将其视为shell glob模式,并检查相对于.gitignore文件位置的路径名是否匹配…

  • 否则,Git会将模式视为适合Fnmatch(3)使用FNM_PATHNAME标志使用的shell球…

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

但是,对于foo/bin/foo/bin/*,情况并非如此,因为即使从中删除了尾部的斜杠foo/bin/,它仍然包含斜杠,因此将其视为fnmatch模式而不是glob。即它将不匹配bar/foo/bin/test.txt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.