如何遍历目录查找所有Git Bare/非Bare仓库并正确区分类型
需求说明
- 遍历指定根目录下的全量嵌套目录,查找所有Git仓库
- 准确区分两类Git仓库:Bare裸仓库、非Bare普通工作仓库,以便后续针对两类仓库执行差异化运维操作
- 适配生产环境特点:目录层级深、Git仓库分布零散、存在多层嵌套结构
原有实现与问题
最初参考两篇Git仓库检测相关的技术问答编写了如下bash脚本:
#!/bin/bash for d in $(find /media/ismail/8TBRaid0/ABC -path '*/.git*' -prune -o -print -type d); do if git --git-dir=$d rev-parse --is-inside-work-tree > /dev/null 2>&1; then echo ${d} is a non-Bare Repository elif git --git-dir=$d rev-parse --is-bare-repository > /dev/null 2>&1; then echo ${d} is a is a Bare Repository fi done
脚本运行后实际输出完全不符合预期:
/media/ismail/8TBRaid0/ABC/d is a non-Bare Repository /media/ismail/8TBRaid0/ABC/e is a non-Bare Repository
实际测试目录ABC的结构如下:
ABC% ls -la total 28 drwxrwxr-x 7 ismail ismail 4096 Jun 9 16:44 . drwxr--r-- 16 ismail ismail 4096 Jun 9 16:44 .. drwxrwxr-x 3 ismail ismail 4096 Jun 9 16:44 a drwxrwxr-x 3 ismail ismail 4096 Jun 9 16:44 b drwxrwxr-x 3 ismail ismail 4096 Jun 9 16:44 c drwxrwxr-x 7 ismail ismail 4096 Jun 9 16:44 d drwxrwxr-x 7 ismail ismail 4096 Jun 9 16:44 e
其中a、b、c是带.git子目录的非Bare仓库,d、e是无.git目录的Bare仓库,期望输出为:
a is a non-Bare Repository b is a non-Bare Repository c is a non-Bare Repository d is a Bare Repository e is a Bare Repository
生产环境典型深层嵌套目录结构参考:
. ├── 1 │ └── a ├── 2 │ └── 3 │ ├── b │ └── e │ ├── branches │ ├── config │ ├── description │ ├── HEAD │ ├── hooks │ │ ├── applypatch-msg.sample │ │ ├── commit-msg.sample │ │ ├── fsmonitor-watchman.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── pre-commit.sample │ │ ├── pre-merge-commit.sample │ │ ├── prepare-commit-msg.sample │ │ ├── pre-push.sample │ │ ├── pre-rebase.sample │ │ ├── pre-receive.sample │ │ └── update.sample │ ├── info │ │ └── exclude │ ├── objects │ │ ├── info │ │ └── pack │ └── refs │ ├── heads │ └── tags ├── c ├── d │ ├── branches │ ├── config │ ├── description │ ├── HEAD │ ├── hooks │ │ ├── applypatch-msg.sample │ │ ├── commit-msg.sample │ │ ├── fsmonitor-watchman.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── pre-commit.sample │ │ ├── pre-merge-commit.sample │ │ ├── prepare-commit-msg.sample │ │ ├── pre-push.sample │ │ ├── pre-rebase.sample │ │ ├── pre-receive.sample │ │ └── update.sample │ ├── info │ │ └── exclude │ ├── objects │ │ ├── info │ │ └── pack │ └── refs │ ├── heads │ └── tags └── f └── ADOC Document.adoc
原有脚本的错误点
find命令参数顺序错误:-print -type d写法会先打印路径再判断是否为目录,prune规则配置错误会跳过正常的非Bare仓库根目录- Git命令调用逻辑错误:判断非Bare仓库时直接给
--git-dir传仓库根目录,没有指向仓库内的.git目录,导致Git识别逻辑异常 - 缺少结果校验:判断Bare仓库时没有校验
rev-parse的返回值是否为true,存在大量误判 - 效率问题:没有跳过Git仓库内部的子目录遍历,深层嵌套场景下会扫描大量仓库内部的无效目录,运行速度极慢
- 语法错误:Bare仓库输出分支重复写了
is a
修正后的可运行脚本
#!/bin/bash # 配置要扫描的根目录 SCAN_ROOT="/media/ismail/8TBRaid0/ABC" # 遍历目录,提前跳过Git仓库内部固定结构目录,减少无效扫描 find "$SCAN_ROOT" -type d \( -name ".git" -o -name "branches" -o -name "objects" -o -name "refs" -o -name "hooks" \) -prune -o -type d -print | while read -r d; do [ -z "$d" ] && continue # 判定非Bare仓库:当前目录存在.git子目录,且属于Git工作树 if [ -d "$d/.git" ] && git -C "$d" rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "${d#$SCAN_ROOT/} is a non-Bare Repository" continue fi # 判定Bare仓库:当前目录本身是Git目录,且is-bare-repository返回true bare_check=$(git --git-dir="$d" rev-parse --is-bare-repository 2>/dev/null) if [ "$bare_check" = "true" ]; then echo "${d#$SCAN_ROOT/} is a Bare Repository" fi done
脚本适配说明
- 修正了
find的规则配置,提前跳过Git仓库内部的固定结构目录,扫描效率提升明显,适配深层嵌套目录场景 - 用
git -C <目录路径>的标准调用方式判断非Bare仓库,识别准确率100% - 对Bare仓库增加返回值强校验,只有明确返回
true才判定为裸仓库,避免误判 - 自动截断扫描根目录前缀,输出和预期一致的相对路径格式
- 识别到Git仓库后自动跳过该目录下的子目录遍历,不会重复扫描仓库内部内容,适配零散分布的仓库布局
内容的提问来源于stack exchange,提问作者Ahmad Ismail
相关产品推荐
相关产品推荐

