为什么 git grep 中的文件 glob **/*.cs 不显示所有 *.cs 命中?
Why does the file glob **/*.cs in git grep not show me all *.cs hits?
所以我想在我的项目中找到 NLog 的用途,我使用 git grep 为我这样做,但发现的情况比我需要的多:
git grep NLog
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj: <Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj: <HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Exception("Could not transform image", exception);
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Warn("Url for cloudinary id was null");
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Warn("Could not locate file object for cloudinary id in EpiServer");
....
etc
当然,它找到了我要找的东西,但我想过滤到 仅以 .cs
结尾的文件。所以我尝试这样做:
git grep NLog **/*.cs
Web/Global.asax.cs: NLogger.Info("Meny application start");
就一击,我上面的两场比赛都没有列出来。我发现这很奇怪,我可能误解了 git grep 的 globbing 匹配。有没有人能赐教一下?
(术语说明,对于阅读此答案的任何人:扩展 *.cs
之类的东西称为 "globbing",1,其中 *.cs
是"shell glob"。一个"shell"是你的命令行解释器,它可以是sh
、bash
、zsh
、dash
、tcsh
, 等等。Git 将有自己的内置 globbing。扩展字符称为 通配符 ,它们包括 *
、?
, 和 [
。一些 shell 也特别对待 {
,这是使用 Git 的 reflog 名称时的一个问题,例如master@{yesterday}
或 stash@{2}
。所有这些都可以随时引用。)
在这种特殊情况下,问题可能会或可能不会发生在其他人身上,具体取决于他们使用的 shell 和他们的情况 - 是未受保护(未引用)*
经历 shell 通配符。某些 shell,例如 bash,将或至少可以像 Git 一样扩展 **
,即 "recurse into subdirectories"。其他人不能,或者取决于设置,不会。2
如果您的 shell 扩展 **/*.cs
以包含名称 Web/Global.asax.cs
但不包含 GETA.Seo.Sitemap/Services/CloudinaryService.cs
(因为那是目录的下一级),那么由时间 Git 获取名称,为时已晚:通配符 *
字符已消失。 Git 从未见过它们,也无法进行自己的 globbing。
简单的解决方案是通过引用来保护通配符免受 shell 通配符的影响:
git grep '**/*.cs'
(成对的双引号——如 git grep "**/*.cs"
——在大多数 shell 中也有效,前缀反斜杠在代替引号使用时也有效,如 git grep \*\*/\*.cs
:只需用反斜杠保护每个易受攻击的角色)。对于许多 Git 命令——它与 git grep
不那么重要,除非你正在 grepping 旧的提交——始终保护所有通配符是个好主意,这样它们就可以传递给 Git,因为 Git 将根据当前工作树 以外的其他内容扩展它们 。 shell 只能看到工作树。3)
虽然它是 shell 相关的,但有时通配符会匹配 nothing 然后被传递。例如,如果你没有名为 sub
的目录,而你写 sub/*
,一些——不是全部——shell 将把文字文本 sub/*
传递给你 运行.4 在这种情况下,如果命令是 Git 命令,它可以再次进行自己的通配。依靠这个是不明智的,因为一旦 是 匹配的东西,shell 就会进行匹配,而不是将原始通配符传递给程序。
1名称 "glob" 是 "global" 的缩写,在很早的 shell 年代,由名为glob
。 Early versions of Unix ran on machines with as little as 64 kilobytes of memory, so there was not a lot of room for fancy in-shell expansion. See https://en.wikipedia.org/wiki/Glob_(programming) 了解更多。
2在bash中,Git式的扩展是通过设置变量globstar
.
来控制的
3这甚至可能包括 .git
存储库子目录本身,这通常很糟糕。在bash中,这是由变量dotglob
控制的。
4在bash中,这是由failglob
控制的。
请注意,bash 提供了几乎所有可能的行为 shell。它试图成为一种通用 shell。当然,这意味着它也需要所有这些控制变量,这使得 bash 相当大。您将永远无法 运行 在 64K 非拆分 I&D PDP-11 上使用它。
所以我想在我的项目中找到 NLog 的用途,我使用 git grep 为我这样做,但发现的情况比我需要的多:
git grep NLog
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj: <Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj: <HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Exception("Could not transform image", exception);
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Warn("Url for cloudinary id was null");
GETA.Seo.Sitemap/Services/CloudinaryService.cs: NLogger.Warn("Could not locate file object for cloudinary id in EpiServer");
....
etc
当然,它找到了我要找的东西,但我想过滤到 仅以 .cs
结尾的文件。所以我尝试这样做:
git grep NLog **/*.cs
Web/Global.asax.cs: NLogger.Info("Meny application start");
就一击,我上面的两场比赛都没有列出来。我发现这很奇怪,我可能误解了 git grep 的 globbing 匹配。有没有人能赐教一下?
(术语说明,对于阅读此答案的任何人:扩展 *.cs
之类的东西称为 "globbing",1,其中 *.cs
是"shell glob"。一个"shell"是你的命令行解释器,它可以是sh
、bash
、zsh
、dash
、tcsh
, 等等。Git 将有自己的内置 globbing。扩展字符称为 通配符 ,它们包括 *
、?
, 和 [
。一些 shell 也特别对待 {
,这是使用 Git 的 reflog 名称时的一个问题,例如master@{yesterday}
或 stash@{2}
。所有这些都可以随时引用。)
在这种特殊情况下,问题可能会或可能不会发生在其他人身上,具体取决于他们使用的 shell 和他们的情况 - 是未受保护(未引用)*
经历 shell 通配符。某些 shell,例如 bash,将或至少可以像 Git 一样扩展 **
,即 "recurse into subdirectories"。其他人不能,或者取决于设置,不会。2
如果您的 shell 扩展 **/*.cs
以包含名称 Web/Global.asax.cs
但不包含 GETA.Seo.Sitemap/Services/CloudinaryService.cs
(因为那是目录的下一级),那么由时间 Git 获取名称,为时已晚:通配符 *
字符已消失。 Git 从未见过它们,也无法进行自己的 globbing。
简单的解决方案是通过引用来保护通配符免受 shell 通配符的影响:
git grep '**/*.cs'
(成对的双引号——如 git grep "**/*.cs"
——在大多数 shell 中也有效,前缀反斜杠在代替引号使用时也有效,如 git grep \*\*/\*.cs
:只需用反斜杠保护每个易受攻击的角色)。对于许多 Git 命令——它与 git grep
不那么重要,除非你正在 grepping 旧的提交——始终保护所有通配符是个好主意,这样它们就可以传递给 Git,因为 Git 将根据当前工作树 以外的其他内容扩展它们 。 shell 只能看到工作树。3)
虽然它是 shell 相关的,但有时通配符会匹配 nothing 然后被传递。例如,如果你没有名为 sub
的目录,而你写 sub/*
,一些——不是全部——shell 将把文字文本 sub/*
传递给你 运行.4 在这种情况下,如果命令是 Git 命令,它可以再次进行自己的通配。依靠这个是不明智的,因为一旦 是 匹配的东西,shell 就会进行匹配,而不是将原始通配符传递给程序。
1名称 "glob" 是 "global" 的缩写,在很早的 shell 年代,由名为glob
。 Early versions of Unix ran on machines with as little as 64 kilobytes of memory, so there was not a lot of room for fancy in-shell expansion. See https://en.wikipedia.org/wiki/Glob_(programming) 了解更多。
2在bash中,Git式的扩展是通过设置变量globstar
.
3这甚至可能包括 .git
存储库子目录本身,这通常很糟糕。在bash中,这是由变量dotglob
控制的。
4在bash中,这是由failglob
控制的。
请注意,bash 提供了几乎所有可能的行为 shell。它试图成为一种通用 shell。当然,这意味着它也需要所有这些控制变量,这使得 bash 相当大。您将永远无法 运行 在 64K 非拆分 I&D PDP-11 上使用它。