#!/usr/bin/env bash # ═══════════════════════════════════════════════════════════════ # git-workflow.sh — ChildHood 项目 Git 工作流自动化 # # 基于 develop 长期开发分支模型。 # 所有 agent 在 develop 上工作,迭代结束时通过 PR 合并回 main。 # # 用法: bash .claude/scripts/git-workflow.sh [args...] # # 命令: # ready 确保在 develop 上且最新 # commit "" 格式化提交 # push 推送 develop 到远程 # submit 一键 commit + push(从 Issue 拉取信息) # release [milestone-name] 创建 develop→main 的 PR # post-release 合并后同步 main 和 develop # catchup 将 develop 合并最新 main 的内容 # status 显示当前分支和状态 # ═══════════════════════════════════════════════════════════════ set -euo pipefail # ─── 配置 ─── DEVELOP_BRANCH="develop" MAIN_BRANCH="main" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" GITEA_PM_CLI="node $PROJECT_ROOT/.claude/skills/gitea-pm/gitea.js" GITEA_COLLAB_CLI="node $PROJECT_ROOT/.claude/skills/gitea-collab/gitea.js" # ─── 颜色 ─── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color info() { echo -e "${BLUE}ℹ️ $*${NC}"; } ok() { echo -e "${GREEN}✅ $*${NC}"; } warn() { echo -e "${YELLOW}⚠️ $*${NC}"; } fail() { echo -e "${RED}❌ $*${NC}"; exit 1; } # ─── 当前分支 ─── current_branch() { git rev-parse --abbrev-ref HEAD; } # ─── 检查是否有未提交的修改 ─── has_changes() { [ -n "$(git status --porcelain)" ]; } # ─── 检测 UE5 二进制文件 ─── has_ue5_binaries() { git diff --cached --name-only 2>/dev/null | grep -qE '\.(uasset|umap)$' } # ═══════════════════════════════════════════════════════════════ # 命令实现 # ═══════════════════════════════════════════════════════════════ cmd_ready() { info "准备工作环境..." local branch branch=$(current_branch) # 如果不在 develop 上,切换过去 if [ "$branch" != "$DEVELOP_BRANCH" ]; then if has_changes; then warn "有未提交的修改,暂存中..." git stash push --include-untracked -m "auto-stash-$(date +%s)" fi git checkout "$DEVELOP_BRANCH" 2>/dev/null || { # 本地没有 develop 分支,从 main 创建 info "本地没有 $DEVELOP_BRANCH 分支,从 $MAIN_BRANCH 创建..." git checkout "$MAIN_BRANCH" git pull origin "$MAIN_BRANCH" git checkout -b "$DEVELOP_BRANCH" } fi # 拉取最新 develop if git remote get-url origin &>/dev/null; then info "拉取远程 $DEVELOP_BRANCH..." git pull origin "$DEVELOP_BRANCH" 2>/dev/null || { # 远程没有 develop,首次推送 warn "远程没有 $DEVELOP_BRANCH 分支,将首次推送" } fi local commit_hash commit_hash=$(git rev-parse --short HEAD) ok "已在 $DEVELOP_BRANCH 分支上 (${commit_hash})" } cmd_commit() { local type="${1:-}" local scope="${2:-}" local message="${3:-}" if [ -z "$type" ] || [ -z "$scope" ] || [ -z "$message" ]; then fail "用法: git-workflow.sh commit \"\"\n 示例: git-workflow.sh commit feat player \"实现角色跳跃\"" fi # 校验分支 if [ "$(current_branch)" != "$DEVELOP_BRANCH" ]; then warn "当前不在 $DEVELOP_BRANCH 分支上 (当前: $(current_branch))" read -rp "继续提交? [y/N] " confirm [[ "$confirm" != [yY] ]] && { info "已取消"; return; } fi # 暂存所有修改 if ! has_changes; then warn "没有检测到修改" return fi git add -A # UE5 二进制文件检测 if has_ue5_binaries; then warn "检测到 .uasset/.umap 文件,请确保 commit message 清楚描述了变更内容" fi # 格式化 commit message local commit_msg="${type}(${scope}): ${message}" # subject ≤ 50 字符检查(仅消息部分) local msg_len=${#commit_msg} if [ "$msg_len" -gt 72 ]; then warn "提交消息较长 ($msg_len 字符),建议精简" fi git commit -m "$commit_msg" ok "已提交: $commit_msg" } cmd_push() { local branch branch=$(current_branch) info "推送 $branch 到远程..." # 设置上游分支(首次) if ! git rev-parse --abbrev-ref "@{upstream}" &>/dev/null; then git push -u origin "$branch" else git push origin "$branch" fi ok "已推送 $branch 到远程" } cmd_submit() { local issue_number="${1:-}" if [ -z "$issue_number" ]; then fail "用法: git-workflow.sh submit " fi info "提交工作 (Issue #$issue_number)..." # 从 Issue 拉取标题作为 commit 参考 local issue_title="" issue_title=$($GITEA_COLLAB_CLI --role dev issue get "$issue_number" --json 2>/dev/null | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"//') || true # 提交 if has_changes; then if [ -n "$issue_title" ]; then info "Issue 标题: $issue_title" fi warn "有未提交的修改,请先运行: git-workflow.sh commit \"\"" return fi # 推送 cmd_push ok "工作已提交到 $DEVELOP_BRANCH (关联 Issue #$issue_number)" } cmd_release() { local milestone_name="${1:-}" info "准备发布 PR ($DEVELOP_BRANCH → $MAIN_BRANCH)..." # 确保在 develop 上 if [ "$(current_branch)" != "$DEVELOP_BRANCH" ]; then fail "请先切换到 $DEVELOP_BRANCH 分支" fi # 确保本地最新 git pull origin "$DEVELOP_BRANCH" # 获取里程碑名称 if [ -z "$milestone_name" ]; then # 尝试从当前里程碑获取 milestone_name=$($GITEA_PM_CLI --role pm milestone list --state open --json 2>/dev/null | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"//') || "迭代发布" if [ -z "$milestone_name" ]; then milestone_name="迭代发布" fi fi local pr_title="Merge develop → main: ${milestone_name}" # 构造 PR body(汇总已关闭的 Issue) local pr_body="## 发布合并\n\n将 \`${DEVELOP_BRANCH}\` 分支的成果合并到 \`${MAIN_BRANCH}\`。\n\n### 里程碑\n${milestone_name}" # 创建 PR info "创建 PR: $pr_title" local pr_result pr_result=$($GITEA_PM_CLI --role pm pr create \ --base "$MAIN_BRANCH" \ --head "$DEVELOP_BRANCH" \ --title "$pr_title" \ --body "$(echo -e "$pr_body")" \ --json 2>&1) || { fail "创建 PR 失败: $pr_result" } local pr_number pr_number=$(echo "$pr_result" | grep -o '"number":[0-9]*' | head -1 | sed 's/"number"://') || true if [ -n "$pr_number" ]; then ok "PR #$pr_number 已创建: $pr_title" info "请在 Gitea 审查并合并: http://micecola.top:8418/starmen1265/ChildHood/pulls/$pr_number" else ok "PR 已创建,请查看 Gitea" fi } cmd_post_release() { info "合并后同步..." # 同步 main info "同步 $MAIN_BRANCH..." git checkout "$MAIN_BRANCH" git pull origin "$MAIN_BRANCH" ok "$MAIN_BRANCH 已更新" # 切回 develop 并合并 main(保持 develop 包含 merge commit) info "同步 $DEVELOP_BRANCH..." git checkout "$DEVELOP_BRANCH" git merge "$MAIN_BRANCH" --no-edit git push origin "$DEVELOP_BRANCH" ok "$DEVELOP_BRANCH 已同步(包含 $MAIN_BRANCH 的 merge commit)" local commit_hash commit_hash=$(git rev-parse --short HEAD) ok "完成,当前在 $DEVELOP_BRANCH (${commit_hash})" } cmd_catchup() { info "将 develop 跟上最新的 $MAIN_BRANCH..." if [ "$(current_branch)" != "$DEVELOP_BRANCH" ]; then fail "请先切换到 $DEVELOP_BRANCH 分支" fi git fetch origin git merge "origin/$MAIN_BRANCH" --no-edit ok "$DEVELOP_BRANCH 已合并最新 $MAIN_BRANCH" } cmd_status() { local branch branch=$(current_branch) local commit_hash commit_hash=$(git rev-parse --short HEAD) local changed changed=$(git status --porcelain | wc -l | tr -d ' ') echo -e "${BLUE}═══ Git 工作流状态 ═══${NC}" echo " 当前分支: ${branch} (${commit_hash})" if [ "$branch" = "$DEVELOP_BRANCH" ]; then echo -e " ${GREEN}✓ 在 develop 开发分支上${NC}" elif [ "$branch" = "$MAIN_BRANCH" ]; then echo -e " ${YELLOW}⚠ 在 main 分支上,请切换到 develop 开始工作${NC}" else echo -e " ${YELLOW}⚠ 在非标准分支上: $branch${NC}" fi echo " 未提交修改: ${changed} 个文件" if [ "$changed" -gt 0 ]; then echo "" git status --short fi # 远程同步状态 if git rev-parse "@{upstream}" &>/dev/null; then local ahead behind ahead=$(git rev-list --count "@{upstream}..HEAD" 2>/dev/null || echo "0") behind=$(git rev-list --count "HEAD..@{upstream}" 2>/dev/null || echo "0") echo " 远程状态: ahead ${ahead}, behind ${behind}" fi } # ═══════════════════════════════════════════════════════════════ # 入口 # ═══════════════════════════════════════════════════════════════ case "${1:-}" in ready) cmd_ready ;; commit) shift; cmd_commit "$@" ;; push) cmd_push ;; submit) shift; cmd_submit "$@" ;; release) shift; cmd_release "$@" ;; post-release) cmd_post_release ;; catchup) cmd_catchup ;; status) cmd_status ;; *) echo "git-workflow.sh — ChildHood 项目 Git 工作流自动化" echo "" echo "用法: bash .claude/scripts/git-workflow.sh [args...]" echo "" echo "命令:" echo " ready 确保在 develop 上且最新" echo " commit \"\" 格式化提交" echo " push 推送 develop 到远程" echo " submit 推送并关联 Issue" echo " release [milestone-name] 创建 develop→main PR" echo " post-release 合并后同步 main 和 develop" echo " catchup 将 develop 合并最新 main" echo " status 显示当前分支和状态" echo "" echo "工作流模型: develop 长期开发分支 → 迭代完成时 PR 合并回 main" ;; esac