扫描.git目录时,以下bash脚本速度很慢,因为它会查看每个目录.如果我有一个大型存储库的集合,则需要很长时间才能查找每个目录,寻找.git.一旦找到.git目录,它会更快地修剪repos中的目录.关于如何做到这一点的任何想法,还是有另一种方法来编写一个完成相同的事情的bash脚本?
#!/bin/bash
# Update all git directories below current directory or specified directory
HIGHLIGHT="\e[01;34m"
NORMAL='\e[00m'
DIR=.
if [ "$1" != "" ]; then DIR=$1; fi
cd $DIR>/dev/null; echo -e "${HIGHLIGHT}Scanning ${PWD}${NORMAL}"; cd ->/dev/null
for d in `find . -name .git -type d`; do
cd $d/.. > /dev/null
echo -e "\n${HIGHLIGHT}Updating `pwd`$NORMAL"
git pull
cd - > /dev/null
done
Run Code Online (Sandbox Code Playgroud)
具体来说,您将如何使用这些选项?对于这个问题,你不能假设repos的集合都在同一个目录中; 它们可能位于嵌套目录中.
top
repo1
dirA
dirB
dirC
repo1
Run Code Online (Sandbox Code Playgroud)
Cla*_*ley 39
查看Dennis在这篇帖子中关于find的-prune选项的回答:
find . -name .git -type d -prune
Run Code Online (Sandbox Code Playgroud)
将速度提高一点,因为find不会进入.git目录,但它仍会下降到git存储库,寻找其他.git文件夹.这可能是一项代价高昂的操作.
如果有某种查找前瞻修剪机制,如果某个文件夹有一个名为.git的子文件夹,则修剪该文件夹...
也就是说,我认为你的瓶颈在于网络操作'git pull',而不是在find命令中,正如其他人在评论中发布的那样.
Mik*_*inn 11
这是一个优化的解决方案:
#!/bin/bash
# Update all git directories below current directory or specified directory
# Skips directories that contain a file called .ignore
HIGHLIGHT="\e[01;34m"
NORMAL='\e[00m'
function update {
local d="$1"
if [ -d "$d" ]; then
if [ -e "$d/.ignore" ]; then
echo -e "\n${HIGHLIGHT}Ignoring $d${NORMAL}"
else
cd $d > /dev/null
if [ -d ".git" ]; then
echo -e "\n${HIGHLIGHT}Updating `pwd`$NORMAL"
git pull
else
scan *
fi
cd .. > /dev/null
fi
fi
#echo "Exiting update: pwd=`pwd`"
}
function scan {
#echo "`pwd`"
for x in $*; do
update "$x"
done
}
if [ "$1" != "" ]; then cd $1 > /dev/null; fi
echo -e "${HIGHLIGHT}Scanning ${PWD}${NORMAL}"
scan *
Run Code Online (Sandbox Code Playgroud)
我花时间在你的问题中复制粘贴脚本,用你自己的答案将它与脚本进行比较.这里有一些有趣的结果
请注意:
git pull
通过在前面加上一个来禁用它echo
.ignore
了bash
解决方案中的文件测试.> /dev/null
此处和那里删除了不必要的东西.pwd
了两个电话.-prune
了这个find
例子中明显缺乏的内容find
示例中也是反效果的bash
解决方案上添加了一个测试,不遵循sym链接以避免循环并表现为查找解决方案.shopt
以允许*
扩展到虚线目录名称以匹配find
解决方案的功能.因此,我们正在比较,基于查找的解决方案:
#!/bin/bash
find . -name .git -type d -prune | while read d; do
cd $d/..
echo "$PWD >" git pull
cd $OLDPWD
done
Run Code Online (Sandbox Code Playgroud)
使用bash shell构建解决方案:
#!/bin/bash
shopt -s dotglob
update() {
for d in "$@"; do
test -d "$d" -a \! -L "$d" || continue
cd "$d"
if [ -d ".git" ]; then
echo "$PWD >" git pull
else
update *
fi
cd ..
done
}
update *
Run Code Online (Sandbox Code Playgroud)
注意:内置(function
和for
)对启动过程的MAX_ARGS OS限制不敏感.因此,*
即使在非常大的目录上也不会破坏.
解决方案之间的技术差
基于查找的解决方案使用C函数来爬行存储库,它:
find
命令加载新进程.chdir
通过几个深度的子目录进行每场比赛然后回去.chdir
在find命令中执行一次,在bash部分执行一次.基于bash的解决方案使用内置(所以近C实现,但解释)来爬行存储库,请注意:
chdir
一个级别.chdir
一次查看和执行命令.解决方案之间的实际速度:
我有一个git存储库的工作开发集合,我在其上启动了脚本:
我不得不承认,我不准备从bash内置中看到这样的胜利.在分析正在发生的事情后,它变得更加明显和正常.要增加对伤害的侮辱,如果你将shell更改/bin/bash
为/bin/sh
(你必须注释掉这一shopt
行,并准备它不会解析虚线目录),你将跌到~0.008s.打败那个 !
请注意,使用find解决方案可以更加聪明:
find . -type d \( -exec /usr/bin/test -d "{}/.git" -a "{}" != "." \; -print -prune \
-o -name .git -prune \)
Run Code Online (Sandbox Code Playgroud)
这将有效地删除已发现的git存储库中的所有子存储库的爬网,其代价是为每个已爬网目录生成进程.我带来的最终查找解决方案大约是0.030秒,比之前的查找版本快两倍以上,但仍然比bash解决方案慢2倍.
请注意,/usr/bin/test
重要的是避免搜索$PATH
花费时间,而且我需要-o -name .git -prune
并-a "{}" != "."
因为我的主存储库本身就是一个git子存储库.
作为结论,我不会使用bash内置解决方案,因为它有太多的角落情况(我的第一次测试达到了限制之一).但是对我来说解释为什么它在某些情况下会更快(更快)是很重要的,但find
解决方案对我来说似乎更加强大和一致.
答案首先取决于找到“.git”存储库。然而,并不是所有的 git repos 都有这些(例如,bare repos)。以下命令将遍历所有目录并询问 git 是否将每个目录视为一个目录。如果是这样,它会从树上修剪子目录并继续。
find . -type d -exec sh -c 'cd "{}"; git rev-parse --git-dir 2> /dev/null 1>&2' \; -prune -print
Run Code Online (Sandbox Code Playgroud)
它比其他解决方案慢很多,因为它在每个目录中执行一个命令,但它不依赖于特定的存储库结构。例如,对于查找裸 git 存储库可能很有用。
我使用以下命令列出当前目录中任何位置的所有 git 存储库:
find . -type d -execdir test -d {}/.git \\; -prune -print
Run Code Online (Sandbox Code Playgroud)
这很快,因为一旦找到 git 存储库,它就会停止递归。(尽管它不处理裸存储库。)当然,您可以将 更改.
为您想要的任何目录。如果需要,您可以将 更改-print
为-print0
null 分隔值。
同时忽略包含.ignore
文件的目录:
find . -type d \( -execdir test -e {}/.ignore \; -prune \) -o \( -execdir test -d {}/.git \; -prune -print \)
Run Code Online (Sandbox Code Playgroud)
我已将此别名添加到我的~/.gitconfig
文件中:
[alias]
repos = !"find -type d -execdir test -d {}/.git \\; -prune -print"
Run Code Online (Sandbox Code Playgroud)
然后我只需要执行:
git repos
Run Code Online (Sandbox Code Playgroud)
获取当前目录中所有 git 存储库的完整列表。
这个答案结合了@Greg Barrett 提供的部分答案和我上面的优化答案。
#!/bin/bash
# Update all git directories below current directory or specified directory
# Skips directories that contain a file called .ignore
HIGHLIGHT="\e[01;34m"
NORMAL='\e[00m'
export PATH=${PATH/':./:'/:}
export PATH=${PATH/':./bin:'/:}
#echo "$PATH"
DIRS="$( find "$@" -type d \( -execdir test -e {}/.ignore \; -prune \) -o \( -execdir test -d {}/.git \; -prune -print \) )"
echo -e "${HIGHLIGHT}Scanning ${PWD}${NORMAL}"
for d in $DIRS; do
cd "$d" > /dev/null
echo -e "\n${HIGHLIGHT}Updating `pwd`$NORMAL"
git pull 2> >(sed -e 's/X11 forwarding request failed on channel 0//')
cd - > /dev/null
done
Run Code Online (Sandbox Code Playgroud)