跳到主要内容

文件查询

Watchman 文件查询由一个或多个生成器组成,这些生成器通过表达式求值器传递文件。

生成器

生成器类似于您在使用 find(1) 实用程序时指定的路径列表,但在 watchman 中实现方式略有不同,因为 watchman 不需要实时爬取文件系统,而是维护树上的几个索引。

一个查询可以指定任意数量的生成器;每个生成器都会发出其文件列表,这可能意味着如果您指定使用多个生成器并且它们都生成相同的文件,您会多次看到相同的文件输出。

Watchman 提供了 5 个生成器

  • since:生成自特定 clockspec 以来修改的文件列表。
  • suffix:生成具有特定后缀的文件列表。
  • glob:根据文件名有效地模式匹配文件列表。
  • path:根据文件路径和深度生成文件列表。
  • all:生成所有已知文件的列表

删除重复结果

自 4.7 版本起。

如果您的查询使用多个生成器,或配置 path 生成器并使用产生多个结果的路径,则默认行为(出于向后兼容性原因)是在查询输出中发出这些重复结果。

您可以通过在查询中启用 dedup_results 布尔值来要求 Watchman 为您删除重复结果

$ watchman -j <<-EOT
["query", "/path/to/root", {
"path": ["bar", "bar"],
"dedup_results": true
}]
EOT

您可以使用扩展版本命令并请求功能名称 dedup_results 来测试此功能。

自生成器

since 生成器生成自特定 clockspec 以来修改的文件列表。

以下查询将考虑自上次使用命名游标 mycursor 的查询以来更改的文件集,然后将它们传递给表达式求值器,以过滤到仅限文件。

$ watchman -j <<-EOT
["query", "/path/to/root", {
"since": "n:mycursor",
"expression": ["type", "f"]
}]
EOT

如果 since 参数值为空白,是由不同的 watchman 进程生成的(换句话说,watchman 进程在获得该值的时间和发出查询的时间之间重新启动),或者是一个尚未在查询中使用的命名游标,则 since 生成器将认为该状态为全新实例,并且其行为会被修改

一个全新实例结果集将仅包括当前存在的文件,并且将生成始终被认为是 new 的文件节点。

如果查询配置了 empty_on_fresh_instance 属性设置为 true,则结果集将为空,并且 is_fresh_instance 属性将在结果对象中设置为 true

since 生成器也知道如何与源码控制通信;您可以在此处阅读更多相关信息

since 生成器不考虑符号链接的目标。 特别是,在以下情况下,since 生成器可能生成符号链接

  • 符号链接的目标是一个文件,并且该文件自上次修改。
  • 符号链接的目标是一个文件,并且该文件自上次删除或替换为不同的文件。
  • 符号链接目标的祖先已创建、删除或修改。
  • 符号链接的目标是一个目录,并且自上次从该目录添加或删除文件。

后缀生成器

suffix 生成器生成具有特定后缀或一组后缀的文件列表。 该值可以是字符串或字符串数组。

$ watchman -j <<-EOT
["query", "/path/to/root", {
"suffix": "js"
}]
EOT
$ watchman -j <<-EOT
["query", "/path/to/root", {
"suffix": ["js", "css"]
}]
EOT

如果 suffix 生成器给定一个空数组,则不会生成任何文件。

suffix 生成器可以生成符号链接。

suffix 生成器不遵循符号链接。 例如,指向 /etc 的符号链接不会导致 "suffix": "conf" 查询在 /etc 中搜索并生成 /etc/resolv.conf

Glob 生成器

自 4.7 版本起。

glob 生成器通过匹配您的模式输入列表来生成文件列表。 它通过从 glob 表达式构建树,并同时遍历表达式和内存中的文件系统树来实现这一点。

此查询将生成直接在 src 目录中找到的所有 C 源代码和头文件的列表

$ watchman -j <<-EOT
["query", "/path/to/root", {
"glob": ["src/*.c", "src/*.h"],
"fields": ["name"]
}]

此查询将生成在根目录的任何子目录中找到的所有 C 源代码和头文件的列表

$ watchman -j <<-EOT
["query", "/path/to/root", {
"glob": ["**/*.c", "**/*.h"],
"fields": ["name"]
}]

请注意,对于如此广泛范围的查询,使用 suffix 生成器与 dirname 表达式术语一起使用效率更高,因为它减少了比较次数。 此示例包含作为递归 globbing 的说明。

glob 生成器隐式启用 dedup_results 模式。

如果 glob 生成器给定一个空数组,则不会生成任何文件。

glob 生成器可以生成符号链接。

glob 生成器不遵循符号链接。 例如,指向 /etc 的符号链接不会导致 "glob": ["**/resolv.conf"] 查询在 /etc 中搜索并生成 /etc/resolv.conf

路径生成器

path 生成器根据文件路径和深度生成文件列表。 深度控制 watchman 将在目录树中向下搜索文件的距离。

path 生成器需要一个路径说明符数组。 每个路径说明符可以是字符串或对象,并且每个都会生成一组文件。

如果是字符串,则将其视为 path 的值,并将 depth 设置为无限。 如果是对象,则必须提供字段 path(一个字符串)和 depth(一个整数)。

路径相对于根目录,因此如果 watchman 正在监视 /foo/,则路径 bar 指的是 /foo/bar

depth 值为 0 表示仅包含在此路径中的文件和目录。 depth 值为 -1 表示对深度没有限制。

以下 path 生成器是等效的

$ watchman -j <<-EOT
["query", "/path/to/root", {
"path": ["bar"]
}]
EOT
$ watchman -j <<-EOT
["query", "/path/to/root", {
"path": [{"path": "bar", "depth": -1}]
}]
EOT

如果 path 生成器给定一个空数组,则不会生成任何文件。

path 生成器可以生成符号链接。

path 生成器不遵循符号链接。

所有生成器

all 生成器生成所有文件节点的列表。 它是默认生成器,并在未明确指定其他生成器的情况下使用。

$ watchman -j <<-EOT
["query", "/path/to/root", {
}]
EOT

all 生成器可以生成符号链接。

all 生成器不遵循符号链接。

表达式

一个 watchman 查询表达式由 0 个或多个表达式术语组成。 如果未提供任何术语,则每个评估的文件都被视为匹配(等效于指定单个 true 表达式术语)。

否则,将根据该文件评估表达式并生成布尔结果。 如果该结果为 true,则该文件被视为匹配,并添加到输出集。

表达式术语规范地表示为一个 JSON 数组,其第零个元素是一个包含术语名称的字符串。

["termname", arg1, arg2]

如果该术语不接受任何参数,则可以使用仅由表示为字符串的术语名称组成的短格式

"true"

与文件名匹配的表达式可能会与文件的基本名称完整名称匹配。 基本名称是文件在其包含目录中的名称。 完整名称是文件相对于被监视根目录的名称。

您可以在本页左侧的侧边栏中找到所有可能的表达式术语的列表。

相对根目录

自 3.3 版本起。

Watchman 支持有选择地根据监视根目录中的路径评估查询。 这与 relative_root 参数一起使用

["query", "/path/to/watched/root", {
"relative_root": "project1",
}]

设置相对根目录会导致对查询进行以下修改

  • path 生成器根据相对根目录进行评估。 在上面的示例中,"path": ["dir"] 将返回 /path/to/watched/root/project1/dir 中的所有文件。
  • 输入表达式根据相对根目录进行评估。 在上面的示例中,"expression": ["match", "dir/*.txt", "wholename"] 将返回 /path/to/watched/root/project1/dir/ 中与 glob *.txt 匹配的所有文件。
  • 相对根目录中的路径将返回,并删除相对根目录。 例如,路径 project1/dir/file.txt 将作为 dir/file.txt 返回。
  • 不会返回相对根目录之外的路径。

相对根目录的行为类似于对子目录进行单独的 Watchman 监视,而不会产生任何系统开销。 这对于大型存储库很有用,在大型存储库中,您的脚本或工具仅对存储库中的特定目录感兴趣。