accounts/scripts/git-prune-branches.sh
Haitao Pan 3349b3ffd3 feat(release): add branch-prune script & update download manifest
- Add scripts/git-prune-branches.sh (one-shot cleanup: keep main/release branches)
- Update public/dl-index/manifest.json
- Prepare for release/v0.2.0 (Public Download SVC)
2025-09-16 16:38:07 +08:00

127 lines
3.1 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
# === Config ===
REMOTE="${REMOTE:-origin}"
KEEP_BRANCHES=("main" "release/mvp" "release/v0.1.0")
DRY_RUN=0
ASSUME_YES=0
usage() {
cat <<EOF
Usage: $(basename "$0") [options]
Options:
-r <remote> 指定远程名(默认: origin
-n 仅演示要删除哪些分支dry-run
-y 跳过确认,直接执行
-k <branch> 额外保留一个分支(可重复多次)
-h 显示帮助
Env:
REMOTE=origin|... 也可用环境变量指定远程名
EOF
}
# --- Parse args ---
while getopts ":r:nyk:h" opt; do
case $opt in
r) REMOTE="$OPTARG" ;;
n) DRY_RUN=1 ;;
y) ASSUME_YES=1 ;;
k) KEEP_BRANCHES+=("$OPTARG") ;;
h) usage; exit 0 ;;
\?) echo "Invalid option: -$OPTARG" >&2; usage; exit 1 ;;
:) echo "Option -$OPTARG requires an argument." >&2; usage; exit 1 ;;
esac
done
# --- Preconditions ---
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "❌ 当前目录不是 Git 仓库"; exit 1; }
echo "➡️ 远程: $REMOTE"
echo "➡️ 保留分支: ${KEEP_BRANCHES[*]}"
# --- Helpers ---
is_kept() {
local b="$1"
for k in "${KEEP_BRANCHES[@]}"; do
[[ "$b" == "$k" ]] && return 0
done
return 1
}
join_by_newline() { printf '%s\n' "$@"; }
CURRENT_BRANCH="$(git symbolic-ref --quiet --short HEAD || echo "")"
# --- Refresh remote refs ---
git fetch -p "$REMOTE" >/dev/null
# --- Compute deletion sets ---
mapfile -t LOCAL_BRANCHES < <(git for-each-ref --format='%(refname:short)' refs/heads)
mapfile -t REMOTE_BRANCHES < <(
git for-each-ref --format='%(refname:short)' "refs/remotes/$REMOTE" \
| sed "s#^$REMOTE/##" \
| sort -u
)
LOCAL_TO_DELETE=()
for b in "${LOCAL_BRANCHES[@]}"; do
if ! is_kept "$b"; then
if [[ -n "$CURRENT_BRANCH" && "$b" == "$CURRENT_BRANCH" ]]; then
echo "⚠️ 跳过本地当前检出分支: $b"
continue
fi
LOCAL_TO_DELETE+=("$b")
fi
done
REMOTE_TO_DELETE=()
for b in "${REMOTE_BRANCHES[@]}"; do
is_kept "$b" || REMOTE_TO_DELETE+=("$b")
done
echo
echo "📌 将删除的本地分支 (${#LOCAL_TO_DELETE[@]}):"
((${#LOCAL_TO_DELETE[@]})) && join_by_newline "${LOCAL_TO_DELETE[@]}" || echo "(无)"
echo
echo "📌 将删除的远程分支 (${#REMOTE_TO_DELETE[@]}):"
((${#REMOTE_TO_DELETE[@]})) && join_by_newline "${REMOTE_TO_DELETE[@]}" || echo "(无)"
if (( DRY_RUN )); then
echo
echo "✅ dry-run 模式:未做任何更改。"
exit 0
fi
if (( ! ASSUME_YES )); then
echo
read -r -p "❓确认删除以上分支吗?(y/N) " ans
[[ "${ans:-N}" =~ ^[Yy]$ ]] || { echo "已取消。"; exit 0; }
fi
# --- Delete locals ---
if ((${#LOCAL_TO_DELETE[@]})); then
for b in "${LOCAL_TO_DELETE[@]}"; do
echo "🗑 删除本地分支: $b"
git branch -D "$b"
done
else
echo " 无本地分支需要删除。"
fi
# --- Delete remotes ---
if ((${#REMOTE_TO_DELETE[@]})); then
for b in "${REMOTE_TO_DELETE[@]}"; do
echo "🗑 删除远程分支: $REMOTE/$b"
git push "$REMOTE" --delete "$b"
done
else
echo " 无远程分支需要删除。"
fi
echo "✅ 完成。"