#!/bin/bash # 配置文件 CONFIG_FILE="submodule_config.ini" # 定义颜色常量 RED='\033[31mi' BLUE='\033[34m' YELLOW='\033[33m' GREEN='\033[32m' NC='\033[0m' # No Color # 函数:显示帮助信息 show_help() { echo "Usage: $0 [-h] [-i] [-up]" echo " -h 显示帮助信息" echo " -i 根据.gitmodules初始化子模块" echo " -up 根据配置文件更新子模块" } # 检查配置文件是否存在 if [[ ! -f "$CONFIG_FILE" ]]; then echo -e "${RED}配置文件 $CONFIG_FILE 不存在!${NC}" exit 1 fi # 检查 .gitmodules 文件是否存在 if [[ ! -f ".gitmodules" ]]; then touch .gitmodules fi # 普通数组和模拟的关联数组 module_list=() # 用来存储模块名列表 config_array=() # 用来存储键值对 # 函数:从 INI 文件中读取所有字段并存储在普通数组中 parse_ini_all() { local section="" while IFS= read -r line || [[ -n "$line" ]]; do # 去除前后空格 line="${line#"${line%%[![:space:]]*}"}" # 去掉前面的空格 line="${line%"${line##*[![:space:]]}"}" # 去掉后面的空格 # 跳过注释和空行 [[ "$line" =~ ^\s*(#|$) ]] && continue # 如果是新的 section if [[ "$line" =~ ^\[(.*)\]$ ]]; then section="${BASH_REMATCH[1]}" module_list+=("$section") # 添加 module 名到 module_list 数组中 elif [[ "$line" =~ ^([^=]+)=(.*)$ ]]; then key="${BASH_REMATCH[1]//[[:space:]]/}" # 去掉键的空格 value="${BASH_REMATCH[2]//[[:space:]]/}" # 去掉值的空格 # 构造键值对并存储在数组中 config_array+=("$section.$key=$value") fi done < "$CONFIG_FILE" } # 函数:根据键获取值 get_value() { local key=$1 for entry in "${config_array[@]}"; do if [[ "$entry" == "$key="* ]]; then echo "${entry#*=}" # 输出值 return fi done echo "" } # 函数:检查远程仓库是否包含指定版本标签 check_tag_exists() { local repo_url=$1 local version_tag=$2 git ls-remote --tags "$repo_url" | grep -q "refs/tags/$version_tag" if [[ $? -ne 0 ]]; then echo -e "${RED}仓库: $repo_url 版本标签 $version_tag 不存在!${NC}" return 1 fi return 0 } # 函数:获取当前子模块的版本标签 get_current_version() { local target_dir=$1 cd "$target_dir" || return 1 # 尝试获取当前HEAD指向的标签名 current_version=$(git tag --points-at HEAD) # 如果没有标签名,则返回commit的SHA if [[ -z "$current_version" ]]; then current_version=$(git rev-parse HEAD) fi cd - > /dev/null || return 1 echo "$current_version" } # 函数:初始化子模块 init_submodule() { local module=$1 local repo_url=$2 local target_dir=$3 local version_tag=$4 # 检查子模块路径是否已经存在 if [[ -f "$target_dir/.git" ]]; then # 存在子模块,获取当前版本 current_version=$(get_current_version "$target_dir") # 如果当前版本和目标版本相同,直接返回 if [[ "$current_version" == "$version_tag" ]]; then echo -e "${GREEN}子模块: $module 已经是版本: $version_tag 无需更新${NC}" return 0 else if ! check_tag_exists "$repo_url" "$version_tag"; then return 1 fi fi else # 子模块不存在,检查远程版本标签 echo -e "${YELLOW}子模块: $module 不存在,添加子模块...${NC}" if ! check_tag_exists "$repo_url" "$version_tag"; then return 1 fi # 添加子模块 git submodule add --force "$repo_url" "$target_dir" if [[ $? -ne 0 ]]; then echo -e "${RED}子模块: $module($target_dir) 添加失败!${NC}" return 1 fi fi # 子模块已存在或成功添加,尝试切换到指定版本 cd "$target_dir" || return 1 echo "子模块: $module 切换到版本: $version_tag" # 更新标签并切换到指定的版本标签 git fetch --tags > /dev/null 2>&1 git checkout "tags/$version_tag" > /dev/null 2>&1 if [[ $? -ne 0 ]]; then echo -e "${RED}$module 版本切换失败: $version_tag${NC}" cd - > /dev/null || return 1 return 1 fi cd - > /dev/null || return 1 echo -e "${GREEN}子模块: $module($target_dir) 初始化完成,版本: $version_tag\n${NC}" return 0 } # 函数:删除配置文件中已经不存在的子模块 remove_deleted_submodules() { # 获取当前子模块列表 current_submodules=$(git config --file .gitmodules --name-only --get-regexp path | sed 's/^submodule\.//;s/\.path$//') for submodule in $current_submodules; do # 获取当前子模块的路径 submodule_path=$(git config --file .gitmodules --get submodule."$submodule".path) # 标志:是否在配置文件中找到该子模块 found_in_config=false # 遍历配置文件中的模块,检查是否存在 for module in "${module_list[@]}"; do if [[ "$submodule_path" == "$(get_value "$module.target_dir")" ]]; then found_in_config=true break fi done # 如果在配置文件中没有找到子模块,则删除它 if [[ "$found_in_config" == false ]]; then echo -e "${YELLOW}子模块: $submodule_path 已从配置文件中删除,正在删除...${NC}" git submodule deinit -f "$submodule_path" > /dev/null 2>&1 git rm -f "$submodule_path" > /dev/null 2>&1 rm -rf .git/modules/"$submodule_path" echo -e "${GREEN}子模块: $submodule_path 已成功删除!${NC}" fi done } update_submodule(){ # 调用 parse_ini_all 函数,读取所有配置 parse_ini_all # 调用删除函数,清理已删除的子模块 remove_deleted_submodules # 遍历所有模块的 section for module in "${module_list[@]}"; do repo_url=$(get_value "$module.repo_url") target_dir=$(get_value "$module.target_dir") version_tag=$(get_value "$module.version_tag") if [[ -z "$repo_url" || -z "$target_dir" || -z "$version_tag" ]]; then echo -e "${YELLOW}跳过无效配置:$module${NC}" continue fi init_submodule "$module" "$repo_url" "$target_dir" "$version_tag" if [[ $? -ne 0 ]]; then echo -e "${RED}子模块 $target_dir 初始化失败,退出...${NC}" exit 1 fi done echo -e "${GREEN}所有子模块已成功同步到指定版本!${NC}" } # 解析脚本参数 case "$1" in -h ) show_help exit 0 ;; -i ) echo -e "${BLUE}根据.gitmodules初始化子模块...${NC}" git submodule update --init --recursive git submodule status echo -e "${GREEN}初始化子模块成功!${NC}" exit 0 ;; -up ) echo -e "${BLUE}根据配置文件更新子模块...${NC}" update_submodule exit 0 ;; * ) show_help exit 1 ;; esac