当我浏览 git 命令的文档时,我注意到许多命令都有一个 <pathspec>
选项。 我最初认为这只是“路径”的一种技术说法,并假设它只能接受目录和文件名。 在深入了解文档的“兔子洞”后,我发现 git 命令的 pathspec 选项的功能要强大得多。
pathspec 是 git 用于将 git 命令的范围限制到存储库子集的机制。 如果你使用过 git,那么你可能已经使用过 pathspec,无论你是否知道它。 例如,在命令 git add README.md
中,pathspec 是 README.md
。 但是,它能够提供更多细微差别和灵活性。
那么,为什么要学习 pathspec 呢? 由于它是许多命令的一部分,因此通过了解 pathspec,这些命令的功能变得更加强大。 使用 git add
,你可以只添加单个目录中的文件。 使用 git diff
,你可以检查仅对扩展名为 .scss
的文件名所做的更改。 你可以 git grep
所有文件,除了 /dist
目录中的文件。
此外,pathspec 可以帮助编写更通用的 git 别名。 例如,我有一个名为 git todo
的别名,它将在我的所有存储库文件中搜索字符串 'todo'
。 但是,我希望它显示字符串的所有实例,即使它们不在我的当前工作目录中。 使用 pathspec,我们将看到这如何成为可能。
文件或目录
使用 pathspec 的最直接方法是只使用目录和/或文件名。 例如,使用 git add
你可以执行以下操作。 .
、src/
和 README
分别是每个命令的 pathspec。
git add . # add CWD (current working directory)
git add src/ # add src/ directory
git add README # add only README directory
你还可以向命令添加多个 pathspec
git add src/ server/ # adds both src/ and server/ directories
有时,你可能会看到一个 --
在命令的 pathspec 前面。 这是用于消除 pathspec 和命令一部分之间任何歧义。
通配符
除了文件和目录之外,你还可以使用 *
、?
和 []
来匹配模式。 *
符号用作通配符,它将匹配路径中的 /
— 换句话说,它将搜索子目录。
git log '*.js' # logs all .js files in CWD and subdirectories
git log '.*' # logs all 'hidden' files and directories in CWD
git log '*/.*' # logs all 'hidden' files and directories in subdirectories
引号很重要,尤其是在使用 *
时! 它们可以防止你的 shell(如 bash 或 ZSH)自行尝试扩展通配符。 例如,让我们看看 git ls-files
如何在使用和不使用引号的情况下列出文件。
# example directory structure
#
# .
# ├── package-lock.json
# ├── package.json
# └── data
# ├── bar.json
# ├── baz.json
# └── foo.json
git ls-files *.json
# package-lock.json
# package.json
git ls-files '*.json'
# data/bar.json
# data/baz.json
# data/foo.json
# package-lock.json
# package.json
由于 shell 在第一个命令中扩展了 *
,因此 git ls-files
将命令接收为 git ls-files package-lock.json package.json
。 引号确保 git 是解析通配符的那个。
你还可以使用 ?
字符作为单个字符的通配符。 例如,要匹配 mp3
或 mp4
文件,你可以执行以下操作。
git ls-files '*.mp?'
方括号表达式
你还可以使用“方括号表达式”来匹配一组中的单个字符。 例如,如果你想在 TypeScript 或 JavaScript 文件之间进行匹配,可以使用 [tj]
。 这将匹配 t
或 j
。
git ls-files '*.[tj]s'
这将匹配 .ts
文件或 .js
文件。 除了只使用字符外,在方括号表达式中还可以引用某些字符集合。 例如,你可以在方括号表达式中使用 [:digit:]
来匹配任何十进制数字,或者可以使用 [:space:]
来匹配任何空格字符。
git ls-files '*.mp[[:digit:]]' # mp0, mp1, mp2, mp3, ..., mp9
git ls-files '*[[:space:]]*' # matches any path containing a space
要详细了解方括号表达式及其使用方法,请查看 GNU 手册。
魔术签名
pathspec 在其武器库中还有称为“魔术签名”的特殊工具,它可以为你的 pathspec 解锁一些额外的功能。 这些“魔术签名”是通过在 pathspec 的开头使用 :(signature)
来调用的。 如果这没有意义,不要担心:一些示例将有助于澄清它。
顶部
top
签名告诉 git 从 git 存储库的根目录而不是当前工作目录匹配模式。 你还可以使用简写 :/
而不是 :(top)
。
git ls-files ':(top)*.js'
git ls-files ':/*.js' # shorthand
这将列出存储库中所有扩展名为 .js
的文件。 使用 top
签名,这可以在存储库中的任何子目录中调用。 我发现这在编写通用 git 别名时特别有用!
git config --global alias.js 'ls-files -- ':(top)*.js''
你可以在存储库中的任何位置使用 git js
来获取项目中所有 JavaScript 文件的列表。
不区分大小写
icase
签名告诉 git 在匹配时不考虑大小写。 如果你不关心文件名的大小写,这可能很有用——例如,这对于匹配 jpg
文件可能很有用,这些文件有时使用大写扩展名 JPG
。
git ls-files ':(icase)*.jpg'
逐字
literal
签名告诉 git 逐字处理所有字符。 如果你想将 *
和 ?
等字符视为其本身而不是通配符,则可以使用此选项。 除非你的存储库的文件名包含 *
或 ?
,否则我不认为这个签名会被经常使用。
git log ':(literal)*.js' # returns log for the file '*.js'
glob
当我开始学习 pathspec 时,我注意到通配符的工作方式与我习惯的不同。 通常我看到单个星号 *
作为不匹配任何目录的通配符,而连续星号 (**
) 作为“深度”通配符,它确实匹配目录中的名称。 如果你更喜欢这种风格的通配符,可以使用 glob
魔术签名!
如果你想更细粒度地控制如何在项目的目录结构中搜索,这将非常有用。 作为一个示例,请查看这两个 git ls-files
如何在 React 项目中搜索。
git ls-files ':(glob)src/components/*/*.jsx' # 'top level' jsx components
git ls-files ':(glob)src/components/**/*.jsx' # 'all' jsx components
属性
Git 能够为特定文件设置“属性”。 你可以使用 .gitattributes
文件设置这些属性。
# .gitattributes
src/components/vendor/* vendored # sets 'vendored' attribute
src/styles/vendor/* vendored
使用 attr
魔术签名可以为你的 pathspec 设置属性要求。 例如,我们可能希望忽略来自供应商的上述文件。
git ls-files ':(attr:!vendored)*.js' # searches for non-vendored js files
git ls-files ':(attr:vendored)*.js' # searches for vendored js files
排除
最后,是“exclude”(排除)魔法签名(:!
或 :^
的简写)。此签名的工作方式与其他魔法签名不同。在解析所有其他路径规范后,解析所有带有 exclude
签名的路径规范,然后将其移除自返回的路径中。例如,您可以搜索所有 .js
文件,同时排除 .spec.js
测试文件。
git grep 'foo' -- '*.js' ':(exclude)*.spec.js' # search .js files excluding .spec.js
git grep 'foo' -- '*.js' ':!*.spec.js' . # shorthand for the same
组合签名
在单个路径规范中使用多个魔法签名没有任何限制!您可以在括号内用逗号分隔魔法词来使用多个签名。例如,如果您想从存储库的根目录(使用 top
)开始匹配,不区分大小写(使用 icase
),仅使用作者编写的代码(使用 attr
忽略供应商文件),并使用 glob 样式通配符(使用 glob
),则可以执行以下操作。
git ls-files -- ':(top,icase,glob,attr:!vendored)src/components/*/*.jsx'
您无法组合的两个魔法签名是 glob
和 literal
,因为它们都会影响 git
处理通配符的方式。这在 git 词汇表 中有所提及,其中包含我在任何文档中读到的我最喜欢的句子之一。
Glob 魔法与 literal 魔法不兼容。
路径规范是许多 git 命令不可或缺的一部分,但它们的灵活性并非立即可用。通过学习如何使用通配符和魔法签名,您可以增强您对 git 命令行的掌握能力。
非常有趣!我今天学习了新技巧 :) 谢谢!