Git 路径规范及其使用方法

Avatar of Adam Giese
Adam Giese

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

当我浏览 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 是解析通配符的那个。

你还可以使用 ? 字符作为单个字符的通配符。 例如,要匹配 mp3mp4 文件,你可以执行以下操作。

git ls-files '*.mp?'

方括号表达式

你还可以使用“方括号表达式”来匹配一组中的单个字符。 例如,如果你想在 TypeScript 或 JavaScript 文件之间进行匹配,可以使用 [tj]。 这将匹配 tj

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'

您无法组合的两个魔法签名是 globliteral,因为它们都会影响 git 处理通配符的方式。这在 git 词汇表 中有所提及,其中包含我在任何文档中读到的我最喜欢的句子之一。

Glob 魔法与 literal 魔法不兼容。


路径规范是许多 git 命令不可或缺的一部分,但它们的灵活性并非立即可用。通过学习如何使用通配符和魔法签名,您可以增强您对 git 命令行的掌握能力。