如何在给定文件夹中查找所有 git 存储库(快速)

use*_*095 13 find git

天真的方法是find dir1 dir2 dir3 -type d -name .git | xargs -I {} dirname {} ,但对我来说太慢了,因为我在 git 存储库中有很多深层文件夹结构(至少我认为这是原因)。我已经读过关于我可以prune用来防止 find 在找到某些内容后递归到目录中的内容,但有两件事。我不确定这是如何工作的(我的意思是我不明白prune虽然我已经阅读了手册页),第二个它在我的情况下不起作用,因为它会阻止find递归到.git文件夹但不是全部其他文件夹。

所以我真正需要的是:

对于所有子目录,检查它们是否包含.git文件夹,如果它停止在此文件系统分支中搜索并报告结果。如果这也能从搜索中排除任何隐藏的目录,那就太完美了。

use*_*095 11

好的,我仍然不完全确定它是如何工作的,但我已经测试过它并且它有效。

.
??? a
?   ??? .git
?   ??? a
?       ??? .git
??? b
    ??? .git

6 directories, 0 files

% find . -type d -exec test -e '{}/.git' ';' -print -prune
./a
./b
Run Code Online (Sandbox Code Playgroud)

我期待更快地做出同样的事情。

  • `-prune` 是这样的:你从树的根部开始向下移动,当某个条件适用时,你切割整个子树(就像真正的“修剪”),所以你不会再看此子树中的节点。 (3认同)

Sté*_*las 4

理想情况下,您希望爬行目录树以查找包含条目的目录.git,并停止进一步搜索这些目录(假设您在 git 存储库中没有更多的 git 存储库)。

问题是,使用 standard 时find,执行这种检查(目录包含条目.git)涉及生成一个test使用谓词执行实用程序的进程-exec,这比列出几个目录的内容效率要低。

一个例外是,如果您使用shellfind的内置函数(由@schilybosh开发的 Bourne shell 的 POSIX 化分支),它有一个谓词来评估 shell 中的代码,而无需生成新的 sh 解释器:-call

#! /path/to/bosh -
find . -name '.?*' -prune -o \
  -type d -call '[ -e "$1/.git" ]' {} \; -prune -print
Run Code Online (Sandbox Code Playgroud)

或者perl使用File::Find

perl -MFile::Find -le '
  sub wanted {
    if (/^\../) {$File::Find::prune = 1; return}
    if (-d && -e "$_/.git") {
       print $File::Find::name; $File::Find::prune = 1
    }
  }; find \&wanted, @ARGV' .
Run Code Online (Sandbox Code Playgroud)

zsh's printf '%s\n' **/.git(:h)(下降到所有非隐藏目录)或 GNU find's (在每个非隐藏目录的新进程中find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -print运行一个命令)更长,但更快。test

2022 编辑. 最新版本的 busybox 中的小程序find能够运行其[test小程序,而无需分叉进程并在内部重新执行自身,因此,尽管它仍然不如 bosh 或 perl 方法那么快:

busybox find . -type d -exec [ -e '{}/.git' ] ';' -prune -print
Run Code Online (Sandbox Code Playgroud)

在我的测试中,比 GNU 等效项快几个数量级find(在包含 git / cvs / svn 存储库混合的本地样本上,总共超过 100000 个目录,我得到的 bosh 为 0.25 秒,perl 为 0.3 秒,busybox 为 0.7 秒)find,GNU 为 36 秒find,GNU 为 2 秒find . -name .git -printf '%h\n'(给出不同的结果,因为它还.git在 git 存储库的子目录中查找文件)。