如何高效检查Git工作树、暂存区、本地与远程仓库状态一致?
多设备Git仓库一致性检查脚本优化
我常在多台电脑上操作同一批Git仓库,工作前后会运行脚本检查仓库是否存在未暂存(unstaged)、未提交(uncommitted)、未推送(unpushed)或未拉取(unpulled)的内容,用标准Git术语来说,就是要确保本地的工作树(work tree)= 暂存区(staging area)= 本地仓库(local repo)= 远程仓库(remote repo)。
原脚本及问题
原脚本通过环境变量GITS_DB指向记录所有Git仓库目录的文本文件,可实现上述检查功能,但运行速度偏慢,即使网络状态良好也是如此。原脚本代码如下:
#!/bin/bash RED='\033[0;31m' NC='\033[0m' # no color for g in $(< ${GITS_DB}) do ( cd ${g} git fetch --all &> /dev/null|| \ echo "warning: unable to fetch ${g}, getting local status" if [ $(git status --porcelain | wc -l) != "0" \ -o $(git rev-list HEAD@{upstream}..HEAD | wc -l) != "0" \ -o $(git rev-list HEAD..HEAD@{upstream} | wc -l) != "0" ]; then echo -e "${RED}${g} is not clean${NC}" fi ) done
优化思路与最终方案
我尝试将脚本中两个rev-list命令替换为直接对比HEAD与HEAD@{upstream}的SHA-1值,简化远程与本地仓库的一致性判断:
$(git rev-parse --verify HEAD) != $(git rev-parse --verify HEAD@{upstream})
之后结合建议优化出两个脚本,将运行时间从约2分钟缩短至5-7秒:
脚本1:生成标记仓库类型的文件
该脚本会遍历系统中的Git仓库,标记每个仓库是顶级仓库还是子模块,并将结果写入GITS_DB指定的文件:
#!/bin/bash # 检查环境变量GITS_DB是否存在 if [[ -z "$GITS_DB" ]]; then echo "Environment variable GITS_DB has no value, exiting..." exit 1 fi # 创建GITS_DB所在目录(如果不存在) GITS_DIR=`dirname $GITS_DB` mkdir -p ${GITS_DIR} # 删除旧的仓库列表文件 if [ -e ${GITS_DB} ]; then rm ${GITS_DB} fi # 创建空的仓库列表文件 touch ${GITS_DB} # 按路径长度倒序遍历所有.git目录,避免重复处理子模块 for REPO_DOT_GIT in `find $HOME -name '.git' -printf "%d %p\n" | sort -nr | awk '{ print $2 }'` do REPO_DIR=`dirname ${REPO_DOT_GIT}` ( cd ${REPO_DIR} # 判断是否为子模块 if [[ $(git rev-parse --show-superproject-working-tree) ]]; then REPO_TYPE=s # s代表子模块 else REPO_TYPE=t # t代表顶级仓库 fi printf "%c %s\n" "${REPO_TYPE}" "${REPO_DIR}" >> ${GITS_DB} ) done
脚本2:并行拉取+一致性检查
该脚本会并行拉取所有顶级仓库及其子模块,然后逐一检查每个仓库的清洁度:
#!/bin/bash RED='\033[0;31m' NC='\033[0m' # 重置颜色 # 并行拉取所有顶级仓库及子模块 while read TYPE DIR; do if [[ "${TYPE}" == "t" ]]; then ( cd ${DIR} # 递归拉取子模块时启用并行(20个任务) git -c submodule.fetchJobs=20 fetch -q --all --recurse-submodules=yes || { echo -e "${RED}无法拉取${DIR}或其子模块,退出${NC}"; exit 1 } ) & fi done < ${GITS_DB} wait # 等待所有并行拉取任务完成 # 逐一检查每个仓库的清洁度 while read TYPE DIR; do ( cd ${DIR} # 检查工作区/暂存区是否有变更,或本地与远程提交是否不一致 if [[ $(git status --porcelain) || $(git rev-parse --verify HEAD) != $(git rev-parse --verify HEAD@{upstream}) ]]; then echo -e "${RED}${DIR}状态不一致${NC}" fi ) done < ${GITS_DB}
内容的提问来源于stack exchange,提问作者jhu
相关产品推荐
相关产品推荐

