【问题标题】:.gitignore Syntax: bin vs bin/ vs. bin/* vs. bin/**.gitignore 语法:bin vs bin/ vs. bin/* vs. bin/**
【发布时间】:2012-02-05 16:09:42
【问题描述】:

在我的 .gitignore 文件中添加 bin、bin/、bin/* 和 bin/** 有什么区别?我一直在使用bin/,但是看着other .gitignore files (在eclipse file 中,双星和单星甚至像这样一起使用:tmp/**/* 那是怎么回事?)我看到第一个两种模式也被广泛使用。谁能解释一下这三者的区别?

【问题讨论】:

  • @unutbu:这个问题的公认答案显然是有争议的。一位顶级 cmets 声称答案实际上是一个完整的神话。
  • 该行为完全在手册页中指定,我敢肯定这里(或十个)包含所有这些信息的问题/答案。

标签: git


【解决方案1】:

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

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

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

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

“任何”一词在这里很重要,因为规则与存储库根目录无关,并且在文件系统树中应用 anywhere。您必须以 /(或 !/ 取消忽略)开始规则,这意味着存储库的根目录,而不是系统的根目录,以便仅匹配预期的内容。

警告:你应该永远不要单独使用dir/*、/dir/**等规则除非你也取消忽略其中存在的东西目录。在git gc、git stash 等的某些调用中省略星号或you could permanently lose a lot of data。

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

【讨论】:

  • 澄清一下,bin/ 和 bin/** 有什么区别?
  • 我怀疑bin/ 将忽略bin 目录,而bin/** 将包含bin 目录但不包含其中的任何内容
  • 这似乎与悉达多的回答不一致。从答案中得出,bin/ 将忽略目录本身(包括所有子目录和文件),而bin/** 将忽略 bin 目录及其子目录中的所有文件,但 not bin 目录本身。这是否准确,我不确定。
  • 请注意,如果您想跟踪 bin/ 目录中的所有文件但忽略其子目录中的所有文件,您可以(在后续行中)bin/** \n !bin/*(因为我看不到如何在 mini-Markdown 中强制换行)
  • 这个答案在很多方面都是错误的。首先,git 不跟踪目录,因此 .gitignore 条目只能匹配目录内容,而不是这样的目录。其次,bin 匹配两个名为bin 的文件和bin 文件夹的内容。第三,bin/* 匹配其子目录中的任何文件。你们甚至测试过这个吗?
【解决方案2】:

bin 和bin/ 的区别仅在于后者只会匹配一个目录。

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

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

这是最清楚的例子,所以对于新初始化的存储库设置如下:

$ 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,确实如此。您应该考虑为此提交一份实际的错误报告,因为这种行为看起来确实出乎意料。
  • 正是我的用例。谢谢!
  • dir/ 和 dir/** 的不同行为。使用! 取消忽略是因为“如果排除了该文件的父目录,则无法重新包含该文件”[source]。令人困惑,但出于性能原因而完成。见related SO question。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    新结果

    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. 在 gitignore 中将 bin 编辑为 bin/:结果
      • 没有变化
    5. 将bin/ 编辑为bin/*
      • 没有变化
    6. 将bin/* 编辑为bin/**
      • 没有变化
    7. 将bin/** 编辑为bin/**/
      • bin/bin.txt 和 bin/Test.txt 不再被忽略
    8. 将bin/**/ 编辑为bin/**/*
      • bin/bin.txt 和 bin/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. 在 gitignore 中将 bin 编辑为 bin/:结果
      • bin 目录下的所有内容(以及更深的)仍然被忽略(没有变化)
    5. 将bin/ 编辑为bin/*
      • bin 目录下的所有内容(以及更深的)仍然被忽略(没有变化)
    6. 将bin/* 编辑为bin/**
      • bin 目录下的所有内容(以及更深的)仍然被忽略(没有变化)
    7. 将bin/** 编辑为bin/**/
      • bin/Test.txt 不再被忽略
    8. 将bin/**/ 编辑为bin/**/*
      • bin 目录(以及更深的目录)下的所有内容再次被忽略

    【讨论】:

    • 这是一个很好的测试,我认为将 text.txt 重命名为 bin.txt 会更好地显示一些关键差异。如果你这样做,我会投票给这个答案。
    • @MattJohnson True... 不幸的是,我目前使用的是较新的 git 版本
    • @MattJohnson 我终于搞定了
    【解决方案5】:

    请注意,'**',当与子目录结合使用时 (**/bar),必须改变其默认行为,因为release note for git1.8.2 现在提到: p>

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

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


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

    It is not possible to re-include a file if a parent directory of that file is excluded.


    通常,如果您想从忽略文件夹 f 的子文件夹中排除文件,您会这样做:

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

    即:

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

    【讨论】:

    • 这如何回答这个问题?
    【解决方案6】:

    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 会将模式视为一个 shell glob,适合带有 FNM_PATHNAME 标志的 fnmatch(3) 使用……

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

    但是,foo/bin/ 和 foo/bin/* 并非如此,因为即使从 foo/bin/ 中删除了尾部斜杠,它仍然包含一个斜杠,因此它被视为 fnmatch 模式,而不是 glob。 IE。它不会匹配bar/foo/bin/test.txt

    【讨论】:

      猜你喜欢
      • 2015-04-17
      • 2012-05-22
      • 2016-02-13
      • 1970-01-01
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-22
      相关资源
      最近更新 更多