#!/usr/bin/env bash # tools/sync-upstream.sh # # 同步上游 (Wei-Shaw/sub2api) 最新代码到我们 Gitea fork 的工作流脚本。 # # 设计目标: # 1. 在 Gitea 上维护一个纯净的上游镜像分支(默认 ora_main),永远 = upstream/main HEAD # 2. 永远不直接合并到 main —— 创建临时分支让人工 review/preview 后手动合并 # 3. 合并前打印冲突预检,让人提前知道改动范围 # # 使用方式: # ./tools/sync-upstream.sh # 仅刷新 gitea/ora_main 镜像 # ./tools/sync-upstream.sh --try-merge # 刷新 + 创建临时分支并启动合并(停在 --no-commit) # # --try-merge 模式的完整步骤: # 1. fetch upstream + force-with-lease 推到 gitea/ora_main # 2. 预检:列出预计会冲突的文件 # 3. 从最新的 main 切出临时分支 merge/upstream-YYYY-MM-DD-HHMM # 4. 在临时分支启动 git merge --no-commit --no-ff upstream/main(停在合并状态) # 5. 打印 next-steps 引导后续 typecheck/build/preview/commit/main 合并 # # 自定义环境变量(可覆盖默认): # UPSTREAM_REMOTE 远程名(默认 upstream,应指向 GitHub Wei-Shaw/sub2api) # GITEA_REMOTE 远程名(默认 gitea,自建) # MIRROR_BRANCH 镜像分支名(默认 ora_main) # # 安全注意: # - 强制推送只发生在镜像分支上(force-with-lease,比 force 安全),不会动 main # - 主分支 main 完全由人工接手最后一步 git merge --ff-only + git push set -euo pipefail UPSTREAM_REMOTE="${UPSTREAM_REMOTE:-upstream}" GITEA_REMOTE="${GITEA_REMOTE:-gitea}" MIRROR_BRANCH="${MIRROR_BRANCH:-ora_main}" TEMP_BRANCH_PREFIX="merge/upstream-" # ------------------------------------------------------------------ # 前置检查 # ------------------------------------------------------------------ if ! git rev-parse --git-dir >/dev/null 2>&1; then echo "Error: 当前目录不是 git 仓库" >&2 exit 1 fi if ! git remote get-url "$UPSTREAM_REMOTE" >/dev/null 2>&1; then echo "Error: remote '$UPSTREAM_REMOTE' 未配置" >&2 echo " 添加方式: git remote add $UPSTREAM_REMOTE https://github.com/Wei-Shaw/sub2api.git" >&2 exit 1 fi if ! git remote get-url "$GITEA_REMOTE" >/dev/null 2>&1; then echo "Error: remote '$GITEA_REMOTE' 未配置" >&2 exit 1 fi # ------------------------------------------------------------------ # 1. 拉最新 upstream # ------------------------------------------------------------------ echo "[1/3] Fetching $UPSTREAM_REMOTE/main..." git fetch "$UPSTREAM_REMOTE" main --tags # ------------------------------------------------------------------ # 2. 刷新 Gitea 镜像分支 # ------------------------------------------------------------------ UPSTREAM_HEAD=$(git rev-parse --short "$UPSTREAM_REMOTE/main") echo "[2/3] Refreshing $GITEA_REMOTE/$MIRROR_BRANCH to $UPSTREAM_REMOTE/main ($UPSTREAM_HEAD)..." # force-with-lease:远端如果在我们 fetch 之后被别人改过会拒绝推送(比 --force 安全) git push --force-with-lease "$GITEA_REMOTE" \ "refs/remotes/$UPSTREAM_REMOTE/main:refs/heads/$MIRROR_BRANCH" # ------------------------------------------------------------------ # 3. 状态报告 # ------------------------------------------------------------------ BASE=$(git merge-base main "$UPSTREAM_REMOTE/main") NEW_COMMITS=$(git rev-list --count "$BASE..$UPSTREAM_REMOTE/main") echo "[3/3] Status:" echo " upstream/main HEAD : $UPSTREAM_HEAD" echo " $GITEA_REMOTE/$MIRROR_BRANCH : refreshed" echo " 距上次合并到 main 的新 commit : $NEW_COMMITS" # 没有 --try-merge 就到此为止 if [[ "${1:-}" != "--try-merge" ]]; then echo "" echo "完成。要尝试合并到临时分支:./tools/sync-upstream.sh --try-merge" exit 0 fi # ------------------------------------------------------------------ # --try-merge 模式 # ------------------------------------------------------------------ if [[ "$NEW_COMMITS" -eq 0 ]]; then echo "" echo "上游无新 commit,跳过合并。" exit 0 fi # 预检:列冲突文件 echo "" echo "[Pre-flight] 检测预计会冲突的文件..." CONFLICT_LIST="" # 优先用 merge-tree --name-only(git >= 2.38) if git merge-tree --name-only --no-messages main "$UPSTREAM_REMOTE/main" >/tmp/.sync-conflicts 2>/dev/null && [[ -s /tmp/.sync-conflicts ]]; then CONFLICT_LIST=$(cat /tmp/.sync-conflicts) else # Fallback:求两边都改过的文件作为「潜在冲突」候选(不一定真冲突,但是个高准确率的提示) CONFLICT_LIST=$(comm -12 \ <(git diff --name-only "$BASE" "$UPSTREAM_REMOTE/main" | sort) \ <(git diff --name-only "$BASE" main | sort)) fi rm -f /tmp/.sync-conflicts if [[ -n "$CONFLICT_LIST" ]]; then echo " 以下文件预计需要手动解冲突 (或 auto-merge 后核对):" echo "$CONFLICT_LIST" | sed 's/^/ /' else echo " 无冲突预测,auto-merge 应该干净通过。" fi # 工作树检查 if [[ -n "$(git status --porcelain)" ]]; then echo "" echo "Error: 工作树非干净,请先 commit 或 stash" >&2 exit 1 fi # 同步本地 main 到远端 echo "" echo "[Branch] 同步本地 main 到 $GITEA_REMOTE/main..." git fetch "$GITEA_REMOTE" main >/dev/null 2>&1 git checkout main >/dev/null if ! git merge --ff-only "$GITEA_REMOTE/main" >/dev/null 2>&1; then echo " Warning: 本地 main 与 $GITEA_REMOTE/main 不同步(非 fast-forward)。继续基于本地 main。" >&2 fi # 创建临时分支 TIMESTAMP=$(date +%Y-%m-%d-%H%M) TEMP_BRANCH="${TEMP_BRANCH_PREFIX}${TIMESTAMP}" echo "[Branch] 从 main 创建临时分支 $TEMP_BRANCH..." git checkout -b "$TEMP_BRANCH" # 启动合并(不 commit,让人工解冲突) echo "[Merge] 启动 git merge --no-commit --no-ff $UPSTREAM_REMOTE/main..." # || true 因为冲突时 git merge 退出码非 0 git merge --no-commit --no-ff "$UPSTREAM_REMOTE/main" || true # Next-steps 引导 cat < # 全部解完后: git commit -m "Merge upstream/main ($UPSTREAM_HEAD) into $TEMP_BRANCH" 2. 验证: cd frontend && pnpm run typecheck && pnpm run build cd backend && go build ./cmd/server 3. 浏览器实测: cd frontend && pnpm run preview # 重点回归我们改过的页面:/ /pricing /docs /login /register 4. 测试通过后合并到 main: git checkout main git merge --ff-only $TEMP_BRANCH git push $GITEA_REMOTE main 5. 清理临时分支: git branch -d $TEMP_BRANCH 放弃合并: git merge --abort git checkout main git branch -D $TEMP_BRANCH EOF