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

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

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

所以我真正需要的是:

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

答案1

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

.
├── a
│   ├── .git
│   └── a
│       └── .git
└── b
    └── .git

6 directories, 0 files

% find . -type d -exec test -e '{}/.git' ';' -print -prune
./a
./b

我期待着更快地做到这一点。

答案2

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

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

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

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

或者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' .

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

在我的测试中,比 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 存储库的子目录中查找文件)。

答案3

如果您使用locate,您可以找到具有以下内容的目录:

locate .git | grep "/.git$"

结果列表很快,进一步处理也很容易。

答案4

您也可以使用ls -d */.git然后.git在最后剥去。

这不会查找子文件夹。

相关内容