diff --git a/tools/sync-upstream.sh b/tools/sync-upstream.sh new file mode 100755 index 00000000..13d3d8c0 --- /dev/null +++ b/tools/sync-upstream.sh @@ -0,0 +1,191 @@ +#!/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