git_menu
Git 一键操作管理菜单
一键脚本
bash <(curl -sL https://gitee.com/meimolihan/cmdbox/raw/master/sh/git_menu.sh)
bash <(curl -sL ../sh/git_menu.sh)
效果预览
补充说明
该脚本将 11 个 git_* 一键操作脚本整合为一个交互式菜单,无需记忆脚本名称和参数,通过编号选择即可完成 Git 操作。
功能特点
- 聚合 11 个 Git 操作脚本,覆盖分支管理、标签管理、克隆、推送、拉取、SSH 密钥配置等常用场景
- 选择后自动提示输入必要参数(仓库地址、工作目录、目标协议等)
- 平台选择与协议选择使用子菜单,操作直观
- 确认执行前显示完整命令,防止误操作
- 执行完毕后自动返回菜单,方便连续操作
服务清单
| 编号 | 功能 | 对应脚本 | 参数说明 |
|---|---|---|---|
| 1 | Git 分支管理 | git_branch | 无参数,直接进入交互菜单 |
| 2 | SSH 密钥配置 | git_ssh_key | 选择平台 gitee/github/gitlab |
| 3 | Git 标签管理 | git_tag | 无参数,直接进入交互菜单 |
| 4 | 克隆远程仓库 | git_clone | 输入仓库 URL |
| 5 | 拉取项目更新 | git_pull | 输入工作目录(默认当前目录) |
| 6 | 推送项目更新 | git_push | 输入工作目录(默认当前目录) |
| 7 | 批量推送所有仓库 | git_all_push | 输入目录、提交信息、排除目录 |
| 8 | 批量拉取所有仓库 | git_all_pull | 输入目录、排除目录 |
| 9 | 初始化新项目 | git_new_project | 输入仓库主页地址 |
| 10 | 应用 .gitignore 规则 | git_apply_ignore | 无参数,一键执行 |
| 11 | 切换远程协议 | git_url_switch | 选择协议 ssh/https 并输入目录 |
交互流程
启动菜单
↓
显示菜单 + 退出选项
↓
输入编号选择功能
↓
无参数功能 → 直接确认执行
有参数功能 → 输入必要参数(目录/地址/平台等)
↓
确认执行(显示完整命令)
↓
执行远程一键脚本
↓
返回菜单或退出
脚本源码
#!/bin/bash
set -euo pipefail
list_color_init() {
export gl_hui=$'\033[38;5;59m'
export gl_hong=$'\033[38;5;9m'
export gl_lv=$'\033[38;5;10m'
export gl_huang=$'\033[38;5;11m'
export gl_lan=$'\033[38;5;32m'
export gl_bai=$'\033[38;5;15m'
export gl_zi=$'\033[38;5;13m'
export gl_bufan=$'\033[38;5;14m'
export reset=$'\033[0m'
}
list_color_init
log_info() { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok() { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn() { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }
sleep_fractional() {
local seconds=$1
if sleep "$seconds" 2>/dev/null; then return 0; fi
if command -v perl >/dev/null 2>&1; then perl -e "select(undef, undef, undef, $seconds)"; return 0; fi
if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
if command -v python >/dev/null 2>&1; then python -c "import time; time.sleep($seconds)"; return 0; fi
local int_seconds=$(echo "$seconds" | awk '{print int($1+0.999)}')
sleep "$int_seconds"
}
handle_invalid_input() {
echo -ne "\r\033[K${gl_huang}无效的输入,请重新输入! ${gl_zi} 1 ${gl_huang} 秒后返回"
sleep_fractional 1
echo -ne "\r\033[K${gl_lv}无效的输入,请重新输入! ${gl_zi}0${gl_lv} 秒后返回"
sleep_fractional 0.5
echo -ne "\r\033[K"
return 2
}
break_end() {
echo -e "${gl_lv}操作完成${gl_bai}"
echo -e "${gl_bai}按任意键继续 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
read -r -n 1 -s -r -p ""
echo ""
clear
}
exit_script() {
echo ""
echo -ne "${gl_hong}感谢使用,再见!${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.5
echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.6
clear
exit 0
}
cancel_return() {
local menu_name="${1:-上一级选单}"
echo -ne "${gl_lv}即将返回 ${gl_huang}${menu_name}${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.5
echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.6
echo ""
clear
}
REMOTE_BASE="https://gitee.com/meimolihan/cmdbox/raw/master/sh"
SERVICES=(
"git_branch_menu|Git 分支管理|none"
"git_ssh_key|SSH 密钥配置|platform"
"git_tag_menu|Git 标签管理|none"
"git_clone|克隆远程仓库|url"
"git_pull|拉取项目更新|dir"
"git_push|推送项目更新|dir"
"git_all_push|批量推送所有仓库|batch_push"
"git_all_pull|批量拉取所有仓库|batch_pull"
"git_new_project|初始化新项目|url"
"git_apply_ignore|应用 .gitignore 规则|none"
"git_url_switch|切换远程协议|protocol"
)
SERVICE_COUNT=${#SERVICES[@]}
show_menu() {
clear
echo -e "${gl_zi}>>> Git 一键操作菜单${reset}"
echo -e "${gl_bufan}─────────────────────────────────────────────────────${reset}"
echo -e "${gl_bufan}1. ${gl_bai}Git 分支管理 ${gl_bufan}2. ${gl_bai}SSH 密钥配置"
echo -e "${gl_bufan}3. ${gl_bai}Git 标签管理 ${gl_bufan}4. ${gl_bai}克隆远程仓库"
echo -e "${gl_bufan}5. ${gl_bai}拉取项目更新 ${gl_bufan}6. ${gl_bai}推送项目更新"
echo -e "${gl_bufan}7. ${gl_bai}批量推送所有仓库 ${gl_bufan}8. ${gl_bai}批量拉取所有仓库"
echo -e "${gl_bufan}9. ${gl_bai}初始化新项目 ${gl_bufan}10. ${gl_bai}应用 .gitignore"
echo -e "${gl_bufan}11. ${gl_bai}切换远程协议"
echo -e "${gl_bufan}─────────────────────────────────────────────────────${reset}"
echo -e "${gl_huang}0. ${gl_bai}返回上一级选单 ${gl_hong}00. ${gl_bai}退出脚本"
}
confirm_and_run() {
local script_name="$1"
local desc="$2"
local param_type="$3"
echo -e "${gl_zi}>>> ${desc}${reset}"
echo -e "${gl_bufan}─────────────────────────────────────────────────────${reset}"
# echo -e "${gl_bai} 脚本: ${gl_lv}${script_name}.sh${reset}"
read -r -e -p "$(echo -e "${gl_bai}请输入工作目录 (回车默认 ${gl_lv}.${gl_bai}): ")" work_dir
work_dir="${work_dir:-.}"
local base="mkdir -p \"$work_dir\" && cd \"$work_dir\""
case "$param_type" in
url)
read -r -e -p "$(echo -e "${gl_bai}请输入仓库地址: ")" repo_url
if [ -z "$repo_url" ]; then
log_error "地址不能为空"
sleep 1
return
fi
local preamble; preamble=$(printf '%s && set -- "%s"' "$base" "$repo_url")
;;
dir)
local preamble; preamble=$(printf '%s && set -- "%s"' "$base" "$work_dir")
;;
protocol)
while true; do
echo -e "${gl_bai}请选择目标协议:${reset}"
echo -e "${gl_lv} 1${gl_bai}) SSH"
echo -e "${gl_lv} 2${gl_bai}) HTTPS"
read -r -e -p "$(echo -e "${gl_bai}请输入编号 (${gl_lv}1-2${gl_bai}): ")" proto_choice
case "$proto_choice" in
1) local proto="ssh"; break ;;
2) local proto="https"; break ;;
*) handle_invalid_input ;;
esac
done
local preamble; preamble=$(printf '%s && set -- "%s" "%s"' "$base" "$proto" "$work_dir")
;;
batch_push)
read -r -e -p "$(echo -e "${gl_bai}提交信息 (回车默认 ${gl_huang}update${gl_bai}): ")" commit_msg
commit_msg="${commit_msg:-update}"
read -r -e -p "$(echo -e "${gl_bai}排除目录 (空格分隔, 留空无排除): ")" exclude_dirs
local preamble; preamble=$(printf '%s && set -- "%s" "%s" "%s"' "$base" "$work_dir" "$commit_msg" "$exclude_dirs")
;;
batch_pull)
read -r -e -p "$(echo -e "${gl_bai}排除目录 (空格分隔, 留空无排除): ")" exclude_dirs
local preamble; preamble=$(printf '%s && set -- "%s" "%s"' "$base" "$work_dir" "$exclude_dirs")
;;
*)
local preamble="$base"
;;
esac
echo ""
echo -e "${gl_bufan}─────────────────────────────────────────────────────${reset}"
read -r -e -p "$(echo -e "${gl_bai}确认执行 ${gl_huang}${desc} ${gl_bai}吗? (${gl_lv}Y${gl_bai}/${gl_hong}n${gl_bai}): ")" confirm
confirm="${confirm:-y}"
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
cancel_return
return
fi
echo ""
local tmp_script
tmp_script=$(mktemp)
printf '%s\n' "$preamble" > "$tmp_script"
printf '\n' >> "$tmp_script"
if ! curl -sL "${REMOTE_BASE}/${script_name}.sh" >> "$tmp_script"; then
log_error "下载脚本失败"
rm -f "$tmp_script"
sleep 1
return
fi
bash "$tmp_script"
rm -f "$tmp_script"
}
main() {
while true; do
show_menu
echo -e "${gl_bufan}─────────────────────────────────────────────────────${reset}"
read -r -e -p "$(echo -e "${gl_bai}请输入你的选择: ")" choice
case "${choice}" in
0)
cancel_return "上一级选单"
break
;;
00 | 000 | 0000)
exit_script
;;
*)
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
handle_invalid_input
continue
fi
local idx=$((choice - 1))
if (( idx < 0 || idx >= SERVICE_COUNT )); then
handle_invalid_input
continue
fi
local IFS_BAK="$IFS"; IFS='|'
local parts=(${SERVICES[$idx]})
IFS="$IFS_BAK"
clear
confirm_and_run "${parts[0]}" "${parts[1]}" "${parts[2]}"
;;
esac
done
}
main "$@"
创建本地脚本
new_script="new_test.sh"
cat > "$new_script" << 'EOF'
# 粘贴脚本源码
EOF
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"