#!/bin/bash # 快速 Git 提交脚本 # 自动执行 git add . git commit -m 'update' git push # 自动输入密码: Git@2018 PASSWORD="Git@2018" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 查找所有 Git 仓库 find_git_repos() { local current_dir="$1" local repos=() # 查找所有 .git 目录(包括当前目录和子目录,最多搜索 3 层深度) while IFS= read -r -d '' git_dir; do # 获取仓库根目录(去掉 .git 部分) repo_path=$(dirname "$git_dir") # 标准化路径(去除相对路径符号) repo_path=$(cd "$repo_path" && pwd) repos+=("$repo_path") done < <(find "$current_dir" -maxdepth 3 -type d -name ".git" -print0 2>/dev/null) # 去重并排序 printf '%s\n' "${repos[@]}" | sort -u } # 执行 Git 操作的函数 execute_git_operations() { local repo_path="$1" echo "" echo "==========================================" echo "正在处理仓库: $repo_path" echo "==========================================" echo "" # 切换到仓库目录 cd "$repo_path" || { echo "错误: 无法切换到目录 $repo_path" return 1 } # 执行 git add . echo "执行: git add ." git add . if [ $? -ne 0 ]; then echo "错误: git add 失败" return 1 fi # 执行 git commit echo "执行: git commit -m 'update'" git commit -m 'update' if [ $? -ne 0 ]; then echo "警告: git commit 可能没有新的更改需要提交" fi # 检查是否有 expect 命令 if ! command -v expect &> /dev/null; then echo "" echo "错误: 未找到 expect 命令" echo "请先安装 expect: brew install expect" echo "" echo "或者手动执行: git push" return 1 fi # 获取所有远程仓库 local remotes=($(git remote)) if [ ${#remotes[@]} -eq 0 ]; then echo "" echo "警告: 当前仓库没有配置远程仓库,跳过 push" return 0 fi # 获取当前分支名 local current_branch=$(git branch --show-current) if [ -z "$current_branch" ]; then current_branch="master" fi # 选择远程仓库 local selected_remote="" if [ ${#remotes[@]} -eq 1 ]; then # 只有一个远程仓库,直接使用 selected_remote="${remotes[0]}" echo "" echo "检测到 1 个远程仓库: $selected_remote" else # 多个远程仓库,让用户选择 echo "" echo "检测到 ${#remotes[@]} 个远程仓库:" echo "" for i in "${!remotes[@]}"; do local_index=$((i + 1)) local remote_url=$(git remote get-url "${remotes[$i]}") echo " [$local_index] ${remotes[$i]} ($remote_url)" done echo "" # 读取用户选择 while true; do read -p "请选择要推送的远程仓库编号 (1-${#remotes[@]}): " choice # 检查输入是否为数字 if ! [[ "$choice" =~ ^[0-9]+$ ]]; then echo "错误: 请输入有效的数字" continue fi # 处理选择 if [ "$choice" -ge 1 ] && [ "$choice" -le ${#remotes[@]} ]; then selected_remote="${remotes[$((choice - 1))]}" break else echo "错误: 请输入 1-${#remotes[@]} 之间的数字" fi done fi # 执行 git pull 的函数 do_git_pull() { local password="$1" local remote="$2" local branch="$3" local temp_output=$(mktemp) # 使用 tee 同时输出到终端和文件,以便后续检查错误 expect << EOF 2>&1 | tee "$temp_output" set timeout 30 spawn git pull $remote $branch expect { "Password:" { send "$password\r" exp_continue } "password:" { send "$password\r" exp_continue } "passphrase:" { send "$password\r" exp_continue } "Username:" { exp_continue } "Authentication failed" { exp_continue } "Permission denied" { exp_continue } "Merge conflict" { exp_continue } "CONFLICT" { exp_continue } eof { catch wait result set exit_code [lindex \$result 3] exit \$exit_code } timeout { exit 1 } } EOF local exit_code=$? local output=$(cat "$temp_output" 2>/dev/null) rm -f "$temp_output" # 检查退出码和输出中的错误信息 if [ $exit_code -ne 0 ]; then return 1 fi # 检查是否有合并冲突 if echo "$output" | grep -qiE "(CONFLICT|merge conflict|Automatic merge failed)"; then echo "" echo "⚠️ 检测到合并冲突,请手动解决冲突后再执行脚本" return 2 fi # 检查输出中是否包含认证失败相关的错误 if echo "$output" | grep -qiE "(Authentication failed|Permission denied|fatal:.*authentication|error:.*authentication)"; then return 1 fi return 0 } # 执行 git push 的函数 do_git_push() { local password="$1" local remote="$2" local branch="$3" local temp_output=$(mktemp) local push_failed=0 # 使用 tee 同时输出到终端和文件,以便后续检查错误 expect << EOF 2>&1 | tee "$temp_output" set timeout 30 spawn git push $remote $branch expect { "Password:" { send "$password\r" exp_continue } "password:" { send "$password\r" exp_continue } "passphrase:" { send "$password\r" exp_continue } "Username:" { exp_continue } "Authentication failed" { set push_failed 1 exp_continue } "Permission denied" { set push_failed 1 exp_continue } eof { catch wait result set exit_code [lindex \$result 3] if {\$exit_code != 0} { set push_failed 1 } exit \$exit_code } timeout { set push_failed 1 exit 1 } } EOF local exit_code=$? local output=$(cat "$temp_output" 2>/dev/null) rm -f "$temp_output" # 检查退出码和输出中的错误信息 if [ $exit_code -ne 0 ]; then return 1 fi # 检查输出中是否包含认证失败相关的错误 if echo "$output" | grep -qiE "(Authentication failed|Permission denied|fatal:.*authentication|error:.*authentication)"; then return 1 fi return 0 } # 先执行 pull,再执行 push echo "" echo "==========================================" echo "步骤 1: 执行 git pull" echo "==========================================" echo "" # 使用局部变量保存当前使用的密码 local current_password="$PASSWORD" local pull_success=0 # 首先尝试使用默认密码 pull echo "尝试使用默认密码执行: git pull $selected_remote $current_branch" if do_git_pull "$current_password" "$selected_remote" "$current_branch"; then pull_success=1 echo "" echo "✅ Pull 成功(使用默认密码)" else local pull_result=$? # 如果是认证失败,提示用户输入密码 if [ $pull_result -eq 1 ]; then echo "" echo "⚠️ 默认密码 pull 失败(可能是密码错误),请手动输入密码" echo "" read -sp "请输入 Git 密码: " user_password echo "" echo "" echo "使用您输入的密码重新执行: git pull $selected_remote $current_branch" if do_git_pull "$user_password" "$selected_remote" "$current_branch"; then pull_success=1 current_password="$user_password" echo "" echo "✅ Pull 成功(使用手动输入的密码)" else pull_result=$? if [ $pull_result -eq 2 ]; then # 合并冲突 echo "" echo "❌ Pull 失败:存在合并冲突,请手动解决后重试" return 1 else echo "" echo "❌ Pull 失败,请检查密码和网络连接" return 1 fi fi elif [ $pull_result -eq 2 ]; then # 合并冲突 echo "" echo "❌ Pull 失败:存在合并冲突,请手动解决后重试" return 1 else echo "" echo "❌ Pull 失败,请检查网络连接" return 1 fi fi # Pull 成功后执行 push echo "" echo "==========================================" echo "步骤 2: 执行 git push" echo "==========================================" echo "" echo "执行: git push $selected_remote $current_branch" local push_success=0 if do_git_push "$current_password" "$selected_remote" "$current_branch"; then push_success=1 else # push 失败,如果之前使用的是默认密码,尝试让用户输入 if [ "$current_password" = "Git@2018" ]; then echo "" echo "⚠️ 默认密码 push 失败,请手动输入密码" echo "" read -sp "请输入 Git 密码: " user_password echo "" echo "" echo "使用您输入的密码重新执行: git push $selected_remote $current_branch" if do_git_push "$user_password" "$selected_remote" "$current_branch"; then push_success=1 fi fi fi # 显示最终结果 echo "" echo "==========================================" echo "执行结果" echo "==========================================" if [ $pull_success -eq 1 ] && [ $push_success -eq 1 ]; then echo "✅ Pull: 成功" echo "✅ Push: 成功" echo "" echo "✅ 仓库 $repo_path 所有操作完成!" elif [ $pull_success -eq 1 ] && [ $push_success -eq 0 ]; then echo "✅ Pull: 成功" echo "❌ Push: 失败" echo "" echo "⚠️ 仓库 $repo_path Pull 成功,但 Push 失败,请检查密码和网络连接" else echo "❌ Pull: 失败" echo "❌ Push: 未执行" echo "" echo "❌ 仓库 $repo_path Pull 失败,Push 未执行" fi echo "==========================================" echo "" } # 主程序 echo "开始执行 Git 快速提交..." echo "" # 查找所有 Git 仓库 echo "正在查找 Git 仓库..." repos=($(find_git_repos "$SCRIPT_DIR")) if [ ${#repos[@]} -eq 0 ]; then echo "错误: 未找到任何 Git 仓库" exit 1 fi # 如果只有一个仓库,直接执行 if [ ${#repos[@]} -eq 1 ]; then echo "找到 1 个 Git 仓库:" echo " [1] ${repos[0]}" echo "" execute_git_operations "${repos[0]}" else # 多个仓库,让用户选择 echo "找到 ${#repos[@]} 个 Git 仓库:" echo "" for i in "${!repos[@]}"; do local_index=$((i + 1)) echo " [$local_index] ${repos[$i]}" done echo "" echo " [0] 全部执行" echo "" # 读取用户选择 while true; do read -p "请选择要操作的仓库编号 (0-${#repos[@]}): " choice # 检查输入是否为数字 if ! [[ "$choice" =~ ^[0-9]+$ ]]; then echo "错误: 请输入有效的数字" continue fi # 处理选择 if [ "$choice" -eq 0 ]; then # 全部执行 echo "" echo "将执行所有仓库的操作..." for repo in "${repos[@]}"; do execute_git_operations "$repo" done break elif [ "$choice" -ge 1 ] && [ "$choice" -le ${#repos[@]} ]; then # 执行选中的仓库 selected_repo="${repos[$((choice - 1))]}" execute_git_operations "$selected_repo" break else echo "错误: 请输入 0-${#repos[@]} 之间的数字" fi done fi echo "" echo "==========================================" echo "所有操作完成!" echo "=========================================="