#!/usr/bin/env bash # ===================================================================== # 一条命令把本机提交推到所有远端(CNB 主仓 + 自建 Gitea 代码辅仓) # # 为什么需要「先 fetch 再 rebase」: # 线上写作后台(editor-api 容器,post.usj.cc)发布文章时是**直接往 main 推**的。 # 所以本机在「上次 fetch 之后」只要后台发过文章,本机 main 就与远端分叉 —— # 此时直接 push 会被 rejected(fetch first)。 # 这不是异常,是**日常**:2026-10-06 第一次实测就撞上了。 # 所以推送前先把自己的提交 rebase 到远端之上,保持线性历史。 # # 安全边界: # · 只 rebase **本机未推送的**提交,不外加强推 # · 有冲突就**停在 rebase 中途**交给人工处理(`git rebase --abort` 可放弃) # · 工作区不干净时 git 自己会拒绝 rebase,不会吞掉改动 # · 任一远端推送失败**不改判另一个** —— 主仓失败时辅仓照样推, # 备份的意义正在于「主仓出问题时它那儿还有」;但退出码以第一次失败为准 # # 用法: # git pushall (别名指向本脚本,等价于直接 bash scripts/pushall.sh) # bash scripts/pushall.sh # ===================================================================== set -uo pipefail cd "$(git rev-parse --show-toplevel)" || exit 1 REMOTES=("origin") git remote | grep -qx gitea && REMOTES+=("gitea") echo "1/3 同步远端 (origin)…" git fetch origin --prune 2>&1 | sed 's/^/ /' || { echo "!! fetch 失败,中止"; exit 1; } if git rev-parse --verify --quiet origin/main >/dev/null; then behind=$(git rev-list --count HEAD..origin/main) if [ "$behind" -gt 0 ]; then echo " 远端领先 $behind 笔(大概是后台发的文章)→ rebase 到其之上" if ! git rebase origin/main 2>&1 | sed 's/^/ /'; then echo "!! rebase 冲突 —— 已停在 rebase 中途,请处理:" echo " git status 看冲突文件" echo " git rebase --continue 解决后继续" echo " git rebase --abort 放弃本次 rebase(回到原状)" exit 1 fi else echo " 本机已是最新" fi fi echo "2/3 推送 → ${REMOTES[*]}" ec=0 for r in "${REMOTES[@]}"; do # set -o pipefail 在,所以管道退出码 = git push 的(sed 不会掩盖它) if ! git push "$r" main 2>&1 | sed "s/^/ [$r] /"; then # ★ 这里不能写 ec=$? —— `!` 之后的 $? 是取反后的结果(0),不是 git 的退出码 [ "$ec" -eq 0 ] && ec=1 fi done if [ "$ec" -eq 0 ]; then echo "3/3 ✓ 全部远端同步完成" for r in "${REMOTES[@]}"; do echo " $r = $(git ls-remote "$r" refs/heads/main 2>/dev/null | cut -c1-8)" done else echo "3/3 ✗ 有远端推送失败(退出码 $ec)" fi exit "$ec"