allinssl/frontend/scripts/temp/git-operations.sh

515 lines
17 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/bin/bash
# ===================================================
# Git 操作脚本 - git-operations.sh
# 用于处理 Git 仓库管理、同步和提交相关功能
# ===================================================
# 依赖文件操作脚本的函数
source "$(dirname "$0")/file-operations.sh"
# Git 操作模块
prepare_git_repo() {
local git_url="$1"
local branch="$2"
local alias="$3"
local target_dir="$GIT_DIR/${alias:-$(basename "$git_url" .git)}"
# 检查目标目录是否存在
if [[ ! -d "$target_dir" ]]; then
log_info "目标目录不存在,尝试克隆仓库"
if ! git clone "$git_url" "$target_dir"; then
log_error "克隆仓库失败"
return 1
fi
fi
# 切换到目标目录
cd "$target_dir" || {
log_error "无法切换到目标目录"
return 1
}
# 检查是否是 Git 仓库
if [[ ! -d ".git" ]]; then
log_error "目标目录不是有效的 Git 仓库"
return 1
}
# 清理未提交的更改
if [[ -n "$(git status --porcelain)" ]]; then
log_warn "发现未提交的更改,正在清理..."
git reset --hard HEAD
git clean -fd
fi
# 拉取最新代码
log_info "拉取最新代码..."
if ! git pull; then
log_error "拉取代码失败"
return 1
fi
# 切换到指定分支
if [[ -n "$branch" ]]; then
log_info "切换到分支: $branch"
if ! git checkout "$branch"; then
log_error "切换分支失败"
return 1
fi
fi
log_info "Git 仓库准备完成"
return 0
}
# 获取源项目的最新提交信息
get_source_commit_info() {
cd "$PROJECT_ROOT" || return 1
local commit_hash=$(git rev-parse HEAD)
local commit_msg=$(git log -1 --pretty=%B)
echo "$commit_hash|$commit_msg"
}
# 提交更改
commit_changes() {
local commit_info
commit_info=$(get_source_commit_info) || {
log_error "获取源项目提交信息失败"
return 1
}
local commit_hash=$(echo "$commit_info" | cut -d'|' -f1)
local commit_msg=$(echo "$commit_info" | cut -d'|' -f2)
# 添加所有更改
git add .
# 提交更改
if git commit -m "sync: $commit_msg (from $commit_hash)"; then
log_info "提交成功"
return 0
else
log_error "提交失败"
return 1
fi
}
# 推送更改到远程仓库
push_changes() {
local current_branch=$(git rev-parse --abbrev-ref HEAD)
# 检查是否有远程仓库
if ! git remote | grep -q origin; then
log_error "未找到远程仓库 origin"
return 1
fi
# 推送到远程仓库
log_info "正在推送更改到远程仓库..."
if git push origin "$current_branch"; then
log_info "推送成功"
return 0
else
log_error "推送失败"
return 1
fi
}
# 选择 Git 项目目录
select_git_dirs() {
local git_dirs=()
local selected_indices=()
local SYNC_MAPPINGS=()
# 检查配置文件是否存在
if [[ ! -f "$SYNC_CONFIG_FILE" ]]; then
log_error "配置文件不存在: $SYNC_CONFIG_FILE"
return 1
fi
# 检查是否安装了 yq
if ! command -v yq &> /dev/null; then
log_error "未安装 yq无法读取配置文件"
return 1
fi
# 检查工作区是否存在
if ! yq e ".workspaces.$SELECTED_WORKSPACE" "$SYNC_CONFIG_FILE" &> /dev/null; then
log_error "工作区 $SELECTED_WORKSPACE 不存在于配置文件中"
return 1
fi
# 获取工作区的所有 Git 仓库配置
local count
count=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings | length" "$SYNC_CONFIG_FILE")
if [[ $count -eq 0 ]]; then
log_error "工作区 $SELECTED_WORKSPACE 未配置任何 Git 仓库"
return 1
fi
# 构建显示列表和映射信息
local all_mappings=()
for ((i=0; i<count; i++)); do
local git_url
local branch
local alias
local sync_dir
local git_dir
git_url=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.git_url" "$SYNC_CONFIG_FILE")
branch=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.branch" "$SYNC_CONFIG_FILE")
alias=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].source.alias" "$SYNC_CONFIG_FILE")
sync_dir=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].target.sync_dir" "$SYNC_CONFIG_FILE")
git_dir=$(yq e ".workspaces.$SELECTED_WORKSPACE.sync_mappings[$i].target.git_dir" "$SYNC_CONFIG_FILE")
if [[ -n "$git_url" ]]; then
# 格式化显示信息
local display_name
if [[ -n "$alias" ]]; then
display_name="[$alias] $git_url ($branch) -> $sync_dir:$git_dir"
else
display_name="$git_url ($branch) -> $sync_dir:$git_dir"
fi
git_dirs+=("$display_name")
# 存储完整映射信息,稍后使用
all_mappings+=("$git_url|$branch|$sync_dir|$git_dir|$alias")
fi
done
# 默认全选
for i in "${!git_dirs[@]}"; do
selected_indices+=($i)
done
local cursor_index=0
local max_index=$((${#git_dirs[@]}-1))
# 设置当前步骤
CURRENT_STEP=2
# 主循环 - 处理用户输入并更新选择
while true; do
# 显示步骤状态
show_steps
# 显示菜单标题
printf "%s%s选择目标 Git 仓库%s\n\n" "${BOLD}" "${CYAN}" "${NC}"
# 显示Git仓库列表
for i in "${!git_dirs[@]}"; do
# 检查当前索引是否已在选中列表中
local is_selected=false
for idx in "${selected_indices[@]}"; do
if [[ $i -eq $idx ]]; then
is_selected=true
break
fi
done
if [[ $i -eq $cursor_index ]]; then
# 当前光标位置项
if [[ "$is_selected" == "true" ]]; then
# 选中项
printf "%s%s %s[✓] %s%s\n" \
"${BOLD}" "${CYAN}" "${GREEN}" "${git_dirs[$i]}" "${NC}"
else
# 未选中项
printf "%s%s %s[ ] %s%s\n" \
"${BOLD}" "${CYAN}" "${DIM}" "${git_dirs[$i]}" "${NC}"
fi
else
# 非光标位置项
if [[ "$is_selected" == "true" ]]; then
# 选中项
printf " %s[✓] %s%s\n" \
"${GREEN}" "${git_dirs[$i]}" "${NC}"
else
# 未选中项
printf " %s[ ] %s%s\n" \
"${DIM}" "${git_dirs[$i]}" "${NC}"
fi
fi
done
# 显示操作提示
printf "\n%s%s使用上下箭头选择数字键1选中/0取消回车确认q键退出%s\n" \
"${BOLD}" "${CYAN}" "${NC}"
# 读取用户输入 - 统一处理方式
local key_pressed=""
local key
read -r -n 1 key
# 获取ASCII码用于调试
if [[ -z "$key" ]]; then
[[ "$DEBUG_MODE" == "true" ]] && printf "空字符,可能是回车键\n"
key_pressed="ENTER" # 空字符通常是回车键
elif [[ "$key" == "1" ]]; then
printf "检测到数字键1执行选中操作\n" # 始终输出不依赖DEBUG_MODE
key_pressed="SELECT" # 选中
elif [[ "$key" == "0" ]]; then
printf "检测到数字键0执行取消选中操作\n" # 始终输出不依赖DEBUG_MODE
key_pressed="DESELECT" # 取消选中
else
[[ "$DEBUG_MODE" == "true" ]] && printf "按键ASCII码: %d\n" "'$key"
# 处理其他按键
case "$key" in
$'\x1b') # ESC 序列,包括方向键
read -r -n 2 seq
[[ "$DEBUG_MODE" == "true" ]] && printf "ESC序列: %s\n" "$seq"
case "$seq" in
"[A") key_pressed="UP" ;; # 上箭头
"[B") key_pressed="DOWN" ;; # 下箭头
*) key_pressed="ESC" ;; # 其他ESC序列
esac
;;
"q"|"Q") # q键退出
key_pressed="QUIT"
;;
*) # 其他按键忽略
key_pressed="OTHER"
;;
esac
fi
# 调试信息
[[ "$DEBUG_MODE" == "true" ]] && log_debug "按键被解析为: $key_pressed"
# 处理操作
case "$key_pressed" in
"UP") # 上箭头
if [[ $cursor_index -gt 0 ]]; then
cursor_index=$((cursor_index-1))
else
# 如果已经是第一项,跳到最后一项
cursor_index=$max_index
fi
;;
"DOWN") # 下箭头
if [[ $cursor_index -lt $max_index ]]; then
cursor_index=$((cursor_index+1))
else
# 如果已经是最后一项,回到第一项
cursor_index=0
fi
;;
"SELECT") # 数字键1 - 选中当前项
printf "处理SELECT操作 - 选中当前项: ${cursor_index}\n"
# 检查当前索引是否已在选中列表中
local found=false
for idx in "${selected_indices[@]}"; do
if [[ $idx -eq $cursor_index ]]; then
found=true
break
fi
done
# 如果未选中,则添加到选中列表
if [[ "$found" == "false" ]]; then
selected_indices+=($cursor_index)
printf "已添加索引 ${cursor_index} 到选中列表\n"
# 对选中项排序
if [[ ${#selected_indices[@]} -gt 0 ]]; then
IFS=$'\n'
selected_indices=($(sort -n <<<"${selected_indices[*]}"))
unset IFS
fi
else
printf "索引 ${cursor_index} 已在选中列表中\n"
fi
;;
"DESELECT") # 数字键0 - 取消选中当前项
printf "处理DESELECT操作 - 取消选中当前项: ${cursor_index}\n"
# 检查当前索引是否已在选中列表中
local found=false
local new_indices=()
# 创建新数组,排除当前索引
for idx in "${selected_indices[@]}"; do
if [[ $idx -ne $cursor_index ]]; then
new_indices+=($idx)
else
found=true # 标记找到了要删除的索引
fi
done
# 只有在找到并删除了索引的情况下才更新选中列表
if [[ "$found" == "true" ]]; then
selected_indices=("${new_indices[@]}")
printf "已从选中列表中移除索引 ${cursor_index}\n"
else
printf "索引 ${cursor_index} 不在选中列表中\n"
fi
;;
"ENTER") # 回车 - 确认选择
if [[ ${#selected_indices[@]} -eq 0 ]]; then
log_error "请至少选择一个 Git 仓库"
sleep 2 # 暂停显示错误信息
continue
fi
# 根据选中的索引获取对应的映射信息
SYNC_MAPPINGS=()
for idx in "${selected_indices[@]}"; do
SYNC_MAPPINGS+=("${all_mappings[$idx]}")
done
# 更新步骤状态
STEP_GIT_REPOS="${#selected_indices[@]} 个仓库"
CURRENT_STEP=3
return 0
;;
"QUIT") # 退出
log_error "操作已取消"
return 1
;;
esac
done
}
# 读取工作区配置
read_workspace_config() {
local workspace="$1"
if ! command -v yq &> /dev/null; then
log_warn "未安装 yq将使用默认配置"
return 1
fi
# 使用 yq 解析 YAML
local mappings
mappings=$(yq e ".workspaces.$workspace.sync_mappings" "$SYNC_CONFIG_FILE")
if [[ "$mappings" != "null" ]]; then
# 解析每个映射
local count
count=$(yq e ".workspaces.$workspace.sync_mappings | length" "$SYNC_CONFIG_FILE")
for ((i=0; i<count; i++)); do
local git_url
local branch
local sync_dir
local git_dir
local alias
git_url=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.git_url" "$SYNC_CONFIG_FILE")
branch=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.branch" "$SYNC_CONFIG_FILE")
sync_dir=$(yq e ".workspaces.$workspace.sync_mappings[$i].target.sync_dir" "$SYNC_CONFIG_FILE")
git_dir=$(yq e ".workspaces.$workspace.sync_mappings[$i].target.git_dir" "$SYNC_CONFIG_FILE")
alias=$(yq e ".workspaces.$workspace.sync_mappings[$i].source.alias" "$SYNC_CONFIG_FILE")
if [[ -n "$git_url" && -n "$sync_dir" ]]; then
# 存储映射信息,添加别名
SYNC_MAPPINGS+=("$git_url|$branch|$sync_dir|$git_dir|$alias")
fi
done
return 0
fi
return 1
}
# 更新工作区配置
update_workspace_config() {
local workspace="$1"
if ! command -v yq &> /dev/null; then
log_warn "未安装 yq无法更新配置"
return 1
fi
# 创建备份
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
cp "$SYNC_CONFIG_FILE" "${SYNC_CONFIG_FILE}.bak"
fi
# 清空现有映射
yq e -i ".workspaces.$workspace.sync_mappings = []" "$SYNC_CONFIG_FILE"
# 添加新的映射
for mapping in "${SYNC_MAPPINGS[@]}"; do
IFS='|' read -r git_url branch sync_dir git_dir alias <<< "$mapping"
yq e -i ".workspaces.$workspace.sync_mappings += [{\"source\": {\"git_url\": \"$git_url\", \"branch\": \"$branch\", \"alias\": \"$alias\"}, \"target\": {\"sync_dir\": \"$sync_dir\", \"git_dir\": \"$git_dir\"}}]" "$SYNC_CONFIG_FILE"
done
# 删除备份
rm -f "${SYNC_CONFIG_FILE}.bak"
log_info "工作区配置已更新"
return 0
}
# 读取配置
read_config() {
if [[ -z "$SYNC_CONFIG_FILE" ]]; then
log_error "配置文件未初始化,请确保已找到项目根目录"
return 1
fi
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
# 使用 yq 解析 YAML如果安装了的话
if command -v yq &> /dev/null; then
SELECTED_WORKSPACE=$(yq e '.config.workspace' "$SYNC_CONFIG_FILE")
TARGET_GIT_DIR=$(yq e '.config.target_git_dir' "$SYNC_CONFIG_FILE")
BRANCH=$(yq e '.config.branch' "$SYNC_CONFIG_FILE")
SYNC_STRUCTURE=$(yq e '.config.sync_structure' "$SYNC_CONFIG_FILE")
PARALLEL_BUILD=$(yq e '.config.parallel_build' "$SYNC_CONFIG_FILE")
DRY_RUN=$(yq e '.config.dry_run' "$SYNC_CONFIG_FILE")
else
log_warn "未安装 yq将使用默认配置"
fi
else
log_error "配置文件不存在: $SYNC_CONFIG_FILE"
return 1
fi
}
# 保存配置
save_config() {
if [[ -z "$SYNC_CONFIG_FILE" ]]; then
log_error "配置文件未初始化,请确保已找到项目根目录"
return 1
fi
if ! command -v yq &> /dev/null; then
log_warn "未安装 yq无法保存配置"
return 1
fi
# 创建备份
if [[ -f "$SYNC_CONFIG_FILE" ]]; then
cp "$SYNC_CONFIG_FILE" "${SYNC_CONFIG_FILE}.bak"
fi
# 保存配置
if ! yq e -i ".config.workspace = \"$SELECTED_WORKSPACE\"" "$SYNC_CONFIG_FILE" \
&& yq e -i ".config.target_git_dir = \"$TARGET_GIT_DIR\"" "$SYNC_CONFIG_FILE" \
&& yq e -i ".config.branch = \"$BRANCH\"" "$SYNC_CONFIG_FILE" \
&& yq e -i ".config.sync_structure = $SYNC_STRUCTURE" "$SYNC_CONFIG_FILE" \
&& yq e -i ".config.parallel_build = $PARALLEL_BUILD" "$SYNC_CONFIG_FILE" \
&& yq e -i ".config.dry_run = $DRY_RUN" "$SYNC_CONFIG_FILE"; then
log_error "保存配置失败"
# 恢复备份
if [[ -f "${SYNC_CONFIG_FILE}.bak" ]]; then
mv "${SYNC_CONFIG_FILE}.bak" "$SYNC_CONFIG_FILE"
fi
return 1
fi
# 删除备份
rm -f "${SYNC_CONFIG_FILE}.bak"
log_info "配置已保存"
return 0
}
# 导出函数
export -f prepare_git_repo
export -f get_source_commit_info
export -f commit_changes
export -f push_changes
export -f select_git_dirs
export -f read_workspace_config
export -f update_workspace_config
export -f read_config
export -f save_config