从 shell 脚本中,如何过滤掉与 .gitignore 匹配的文件?

From a shell script, how can I filter out files matching .gitignore?

我正在维护一些 git 预提交挂钩,并且我一直想对所有处于或应该处于修订控制之下的文件做一些事情。 了解项目结构让我可以很好地完成这项工作,但是有关构建系统输出目录、测试日志文件和编辑器丢弃物的所有信息都已经在 .gitignore.

有没有一种简单的方法可以根据文件路径是否与 .gitignore 中的模式匹配来过滤文件路径。

哎呀,

中的WHAT GOES HERE可以用什么代替
find "$(git rev-parse --show-toplevel)" --my --filters | WHAT GOES HERE

这样我就可以得到所有且只有未被git忽略的符合我的过滤器的文件。

我想我可以通过

得到一个我可能会进入comm的负面过滤器
... | xargs git ls-files -X .gitignore -i

但我希望一步到位。

UPDATE - 正如在对此答案的评论交流中指出的那样,check-ignore 命令 says 它列出了忽略文件,但如果您的忽略规则包含异常(以 ! 开头的模式),即使文件未被忽略,也会打印与这些模式匹配的文件。虽然一些文档可以被解读为描述了这种行为,但同一文档的其他部分强烈暗示它不是预期的 - 所以我认为它是一个错误,但不管这样的解释如何,这就是软件的工作方式。

所以...如果您不使用 ! 模式,则下面的内容与宣传的一样。如果您 do 使用 ! 模式,那么您可以通过使用 --verbose 输出和 post-processing 来解决这个问题,以查看是否匹配模式是包含或排除。


使用 ls-files 获得您想要的确切行为可能并不像看起来那么容易。首先,您的意思可能不是 -i,因为那 只会 列出忽略的文件...

但无论如何,一个不同的(更多 "one-step")方法是:

在您的 find 命令中,您可以使用 -exec 操作为匹配其他过滤器的每个文件调用 git check-ignore

find "$(git rev-parse --show-toplevel)" <filters> -not -exec git check-ignore -q {} \; <actions>

这将正确解释来自所有来源的忽略规则。

默认情况下,这也意味着如果一个文件在索引中,它不会显示为"excluded",即使它在.gitignore中, 这反映了忽略规则的实际行为

但是如果你不想处理与忽略模式匹配的文件,即使它们在索引中因此并没有真正被忽略,你可以修改命令来做到这一点:

find "$(git rev-parse --show-toplevel)" <filters> -not -exec git check-ignore -q --no-index {} \; <actions>

自从您开始使用 find,我假设您无论如何只关心当前在您的工作树中的文件。

您可能还想排除 .git 目录。如果 .git 是您的顶级目录中唯一的 "dot-file",您可以说

find "$(git rev-parse --show-toplevel)"/* <filters> -not -exec git check-ignore -q --no-index {} \; <actions>

如果你不能做出那个假设,那么你可以

find "$(git rev-parse --show-toplevel)" -path "$(git rev-parse --show-toplevel)"/.git -prune -o <filters> -not -exec git check-ignore -q -no-index {} \; <actions>

由于对 rev-parse 的两次调用,这有点难看。您可以在 运行 宁 find 之前将 rev-parse 结果捕获到环境变量,但这可能 运行 与您的 "one step" 偏好相冲突。另一种选择,如果你可以安全地忽略 any 名为 .git

的目录
find "$(git rev-parse --show-toplevel)" -path */.git -prune -o <filters> -not -exec git check-ignore -q -no-index {} \; <actions>