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


10

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

所以我真正需要的是:

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



Answers:


9

好的,我仍然不确定这是如何工作的,但我已经对其进行了测试。

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

6 directories, 0 files

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

我期待更快地实现相同目标。


3
这的-prune是这样的:你开始你向下移动它树的根,并在一定条件下适用于你砍整个子树的(像真正的“修剪”),所以你不会看到任何更多的节点在这个子树。
phk

@phk哦,谢谢。我现在似乎已经掌握了。我们搜索-type d条件test -e ...为真的目录,如果条件为真,则执行-print -prune意味着打印该条件并剪切子树的操作,对吗?
user1685095 '16

是的,我们剪切了它作为根的子树。
phk

快速解决方案,使用您的解决方案“更新”所有git仓库:find . -type d -exec test -e '{}/.git' \; -print -prune | parallel cd "{}" \&\& git pull --rebaseGNU parallel可以非常方便地替代xargs
Marcello Romani

您不会获得子模块,这也是git repos。一旦具有此命令返回的root-repos列表,您可能希望通过递归获取子模块来获取它们。
houiui

2

可能的解决方案

对于GNU find和其他支持的实现-execdir

find dir1 dir2 dir3 -type d -execdir test -d '.git' \; -print -prune

(请参阅评论)

先前讨论过的东西

如果以下修剪.git已足够,则解决方案

find dir1 dir2 dir3 -type d -path '*/.git' -print -prune | xargs -I {} dirname {}

如果-printf '%h'受支持(例如GNU的情况find),则不需要dirname

find dir1 dir2 dir3 -type d -path '*/.git' -printf '%h\n' -prune

一旦遇到.git当前路径中的文件夹,它将输出该文件夹,然后停止在子树中向下查找。

如果.git找到一个文件夹后应修剪整个文件夹树的解决方案

使用-quit如果您的find支持它:

for d in dir1 dir2 dir3; do
  find "$d" -type d -name .git -print -quit
done | xargs -I {} dirname {}

(根据StéphaneChazelas的详细帖子 -quit,GNU和FreeBSD find以及NetBSD中的支持-exit。)

再次与-printf '%h'如果支持:

for d in dir1 dir2 dir3; do
  find "$d" -type d -name .git -printf '%h\n' -quit
done

.git文件夹所在位置相同级别的修剪解决方案

有关此特定问题的当前解决方案,请参见“可能的解决方案”部分。

(哦,很显然,使用的解决方案xargs假定路径中没有换行符,否则您将需要空字节魔术。)


如果dir1包含两个目录dirxdiry并且每个目录都包含一个.git目录,则仅报告 dirx/.git
iruvar

@iruvar啊,好的,在这种情况下,我误会了您,然后我将尝试重做解决方案。
phk

您的新解决方案的问题是,如果dir1/.git存在,它仍然会下降dir1/dirx,根据我对OP要求的
理解

@iruvar好,也添加了它。关于OP可能意味着什么其他想法?;-)
phk

@iruvar正好
user1685095 '16

2

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

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

如果您使用外壳程序的find内置组件bosh(由@schily开发的Bourne外壳程序的POSIXified分支),但有一个-call谓词可以在外壳程序中评估代码而不必产生新的sh解释器,则是一个例外。

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

或使用perlFile::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' .

zshprintf '%s\n' **/.git(:h)(属于所有未隐藏的目录)或GNU find的更长或更快速,或者比在新进程中为每个非隐藏的目录find . -name '.?*' -prune -o -type d -exec test -e '{}/.git' \; -prune -print运行一个test命令的GNU 更快。


1
请注意,.git也可以是一个文件-通过git worktree
Steven Penny,

1
谢谢@StevenPenny,我没有意识到这一点。现在我已经改变了-ds到-e
斯特凡Chazelas

1

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

locate .git | grep "/.git$"

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


2
locate '*/.git'应该足够了。
斯特凡Chazelas

0

采用

find ~/GIT-REPOSITORIES \( -exec test -d '{}'/.git \; \) -print -prune

time这个,看看有和没有的区别-prune

这基于中的解决方案man find。您可以编辑CVSsvn如果不需要的话。手册页内容如下

find repo/ \( -exec test -d '{}'/.svn \; -or \
       -exec test -d {}/.git \; -or -exec test -d {}/CVS \; \) \
       -print -prune

给定以下项目目录及其关联的SCM管理目录,请有效搜索项目的根目录:

repo/project1/CVS
repo/gnu/project2/.svn
repo/gnu/project3/.svn
repo/gnu/project3/src/.svn
repo/project4/.git

在此示例中,-prune防止不必要地下降到已经发现的目录中(例如,我们不搜索project3/src,因为我们已经找到project3/.svn),但是确保找到同级目录(project2project3)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.