AI 自动化博客 git pull --rebase 致 177 篇文章 404 重大生产事故复盘 2026:永久标准 #24 起源与三重防御机制
我是程序员,也是一个用 cron + AI Agent 自动写博客的人。2026 年 6 月 4 日中午 12 点 17 分,我的 GitHub Pages 站点(yaohehe.github.io)突然 404 了一大批发过的文章 —— 5 月 19 日到 6 月 4 日期间发布的 177 篇文章全部 404,首页、搜索流量、Clarity 热图瞬间归零。
我从 git reflog 反推 root cause:12PM cron 在 12:08 跑了 git pull --rebase origin main(rebase 用远端 5/31 旧版作基底),然后 18:19 流水线 force push 把远端从 5/19~6/4 完整版回滚到 5/19 缺失的旧版。更糟的是我上午 11:00 的 ceecb9bf force push 已把 706 文件全 commit 进 main,但 12PM 任务 12:08 跑 rebase 把 5/19~6/3 内容从 commit 中丢失。
我 2 分钟内用 reflog + 工作树恢复了所有内容(git reset --hard 1ef91bbf + force push),但这次事故让我立了**永久标准 #24** —— cron 流水线从此禁用所有 git pull/rebase/push。本文是这次**真实生产事故**的完整复盘,含根因解剖、救援命令、三重防御机制部署清单。
事故时间线(精确到分钟)
| 时间 | 事件 | 影响 |
|---|---|---|
| 5/19 | 开始用 cron + AI Agent 自动发文章 | 工作流进入稳态 |
| 5/19~6/3 | 累计发布 177 篇文章(含 CN + EN) | 索引 220 / 200 |
| 6/4 11:00 | 我手动 `ceecb9bf` force push 706 文件 | 远端 main 同步完整 |
| 6/4 12:08 | 12PM cron 跑 `git pull --rebase origin main` | rebase 用远端 5/31 旧版作基底 |
| 6/4 12:17 | Clarity 流量监控告警,首页 404 | GitHub Pages 推送失败 |
| 6/4 18:19 | 18PM cron 跑 force push 推送 | 把远端从完整版回滚到 5/19 缺失版 |
| 6/4 18:19 | **177 篇文章全部 404**(外部用户受影响) | 重大生产事故 |
| 6/4 18:21 | 我从 git reflog 找到 1ef91bbf 完整 commit | 数据未丢 |
| 6/4 18:23 | `git reset --hard 1ef91bbf` + force push | **2 分钟内恢复** |
关键观察:reflog 还活着,说明所有 706 文件在本地没丢,只是远端 main 分支指针被错误 rebase 推到了旧位置。
根因解剖(3 层叠加失误)
根因 1:12PM cron 任务无防护地跑了 `git pull --rebase`
原始 12PM cron prompt 里有一段「保持本地与远端同步」的逻辑,AI 实现时直接调用了 git pull --rebase origin main:
# ❌ 危险代码(原始 12PM 实现)
cd /root/.openclaw/workspace/yaohehe.github.io
git fetch origin
git pull --rebase origin main # ← 致命:用远端作基底重放本地 commit
python3 generate-html.py
git add .
git commit -m "auto: 12PM batch"
git push # ← 如果 push 被拒可能 force push
问题:rebase 后,本地 main 分支的 commit 链被重写。如果远端有新 commit(我没看到的 force push),rebase 会"丢失"本地独有的 commit —— 这次事故正是这个机制把 5/19~6/3 的 177 篇文章 commit 内容从远端 main tree 中抹掉。
根因 2:我上午 11:00 的 force push 已把 706 文件全 commit 进 main
我手动 force push 了一次大更新(706 文件),把 5/19~6/4 的全部内容推上去了。但 12PM cron 在 12:08 又跑了 rebase —— rebase 用远端 5/31 旧版作基底,相当于把我的完整 706 文件 commit chain 用旧 chain 重写。
# 事故前的远端 main 链
A---B---C---D---ceecb9bf (force push, 706 文件)
\
E---F (12PM 新增)
# rebase 后
A---B---C---D (远端 5/31 旧版)
\
E'---F' (12PM 新增但用旧 D 作基底)
# E' / F' 与原 E/F 内容相同,但 D 之前的所有 commit 都没了
根因 3:18PM 流水线 force push 把回滚后的 main 推送出去
18PM cron 用了 git push --force-with-lease,这个命令在 rebase 后会把当前 main 指针(指向 D 旧版)推送到远端 —— 远端 main tree 的 706 文件状态被抹平到 5/19 缺失版本。
三层叠加:手动 force push → 12PM 误 rebase → 18PM 再 force push,把整个 5/19~6/4 区间的内容从远端抹掉。
救援命令(2 分钟恢复)
我从 git reflog 找到最后一次正确的 commit 哈希 1ef91bbf(即 ceecb9bf 的 force push 之后),用以下命令恢复:
# Step 1:查看 reflog 找到正确 commit
git reflog | grep -E "ceecb9bf|1ef91bbf"
# 输出(示例):
# 1ef91bbf HEAD@{0}: commit: 706 files
# a3c4d5e6 HEAD@{1}: rebase finished
# Step 2:硬重置到正确 commit
git reset --hard 1ef91bbf
# Step 3:force push 推送
git push --force-with-lease origin main
# Step 4:验证 GitHub Pages
curl -I https://yaohehe.github.io/2026-05-29-claude-code-...-en.html
# HTTP/2 200 ✅
**关键观察**:reflog 默认保留 90 天,所有 commit 都在本地,只要不 git gc --prune=now 就不会丢。
永久标准 #24 升级:三重防御机制
事故后我立了永久标准 #24,把 cron 流水线的 git 操作彻底禁止。3 层防护:
防御层 1:所有 cron prompt 头部加 ⚠️ git 操作禁令
- git pull / git pull --rebase / git pull --no-rebase / git fetch origin
- git push / git push --force / git push --force-with-lease
- git rebase / git reset --hard
- 只读:git status / git log / git diff / git ls-files
- 推单文件:python3 publish-via-api.py(GitHub Contents API,不创建 commit)
# ✅ 新规(所有 6AM/12PM/18PM/22PM cron prompt 头部固化)
⚠️ 严禁使用以下 git 命令(流水线内,4AM 兜底可):
✅ 只允许:
为什么不删 git 操作:4AM 健康检查和人工 debug 时偶尔需要 git log / git diff,只禁止破坏性命令。
防御层 2:4AM 健康检查加"远端 vs 本地文件数对比"
我在 4am-health-check.py 加了一个新检查(main() 函数 Step 3):
def check_remote_vs_local():
"""比对 GitHub 远端 main 文件数 vs drafts/ 已发布文件数"""
# 拉远端文件清单(轻量 API 调用)
remote_files = fetch_github_tree("yaohehe", "yaohehe.github.io", "main")
local_published = load_pushed_manifest() # .pushed_manifest.json
diff = len(remote_files) - len(local_published)
if abs(diff) > 10: # 阈值 10
send_alert(f"⚠️ 远端 vs 本地文件数差 {diff},疑似事故!")
pause_all_cron_pipelines() # 暂停推送任务
阈值 10 的理由:单次 batch 通常推送 2~6 篇文章,10 是安全冗余。
防御层 3:`.pushed_manifest.json` 推送记录 + 4AM 验证
.pushed_manifest.json 记录每次成功推送的文件路径:
{
"schema_version": 1,
"pushed": [
{
"path": "2026-05-29-claude-code-...-en.html",
"timestamp": "2026-05-29T10:30:00Z",
"commit_sha": "abc1234",
"size": 32783
}
]
}
4AM 验证脚本会:
1. 读 .pushed_manifest.json
2. 调用 GitHub Tree API 拿远端实际文件
3. 对比路径是否都存在 + mtime 是否匹配
4. 不存在 → 报警并恢复(用 publish-via-api.py 重新推)
publish-via-api.py 推送盲区(新发现)
事故后我重新审计 publish-via-api.py(GitHub Contents API 单文件推送工具),发现**它不创建 git commit**。这意味着:
- 本地 `git log origin/main..HEAD` 显示**无落后**(因为本地没新 commit)
- 但远端 main 实际是落后(推送走 API 不写 commit)
- 下次 `git pull --rebase origin main` 会"看似同步"但实际冲突
这是双轨制 git 引用过期问题的根因(corrections.md 6/04 11:07 已记录)。
**修复**:4am-health-check.py 加 sync_git_refs() 步骤,每 4 小时跑一次 git fetch origin(30s 超时,失败非阻塞)来同步引用:
def sync_git_refs():
"""同步远端引用,避免 publish-via-api 推送盲区"""
r = run_cmd("git fetch origin", timeout=30, fatal=False)
if r and r.returncode == 0:
log("✅ git refs synced")
else:
log("⚠️ git fetch 失败(非阻塞,可能网络问题)")
事故后学到的 5 个永久教训
1. **git pull --rebase 在自动化流水线中是定时炸弹**。永远不要在 cron 里跑 rebase,宁可手动同步。
2. force push 是双刃剑。手动 force push 抢救后,必须立即在 cron 流水线加防护;否则下次 12PM 任务的 rebase 会把你抢救的内容再次抹掉。
3. GitHub Contents API 是单文件推送的正确路径。它不创建 commit、不会触发 rebase 冲突、本地 git log 不显示 —— 完美避开流水线 git 操作的所有坑。
4. **reflog 是最后防线**。GitHub Pages 404 后,90 天内都能从本地 reflog 找 commit 恢复,超过 90 天需要靠 git fsck --dangling 找回悬空对象。
5. 三重防御缺一不可:单加 prompt 禁令挡不住 AI 偶发跑 git 命令;单加 4AM 检查挡不住 12PM→18PM 短窗口事故;单加 .pushed_manifest 验证挡不住 manifest 自身损坏。
部署清单:5 步永久固化
把这次事故的修复固化为永久标准(每个新 workspace 必跑):
- git pull / git pull --rebase / git pull --no-rebase / git fetch origin
- git push / git push --force / git push --force-with-lease
- git rebase / git reset --hard
# Step 1:在所有 6AM/12PM/18PM/22PM cron prompt 头部加 git 操作禁令段
cat > /tmp/git-ban-snippet.txt << 'EOF'
⚠️ 严禁使用以下 git 命令(流水线内,4AM 兜底可):
✅ 只允许:
EOF
# Step 2:4am-health-check.py 加远端 vs 本地文件数对比
# (脚本已包含 main() Step 3 check_remote_vs_local())
# Step 3:创建 .pushed_manifest.json 模板
cat > .pushed_manifest.json << 'EOF'
{"schema_version": 1, "pushed": []}
EOF
# Step 4:4AM health check 加 .pushed_manifest.json 验证步骤
# Step 5:4am-health-check.py 加 sync_git_refs() 步骤(30s 超时,非阻塞)
适用场景
- 任何用 cron + AI Agent 自动维护 GitHub Pages / GitLab Pages / Vercel 站点的工程师
- 任何 `git push --force-with-lease` 进入 CI/CD 的团队
- 任何想从「git rebase 致事故」中学习灾备架构的程序员
总结与下一步
这次事故让我把「AI 自动化流水线的所有 git 操作交给 publish-via-api.py 一个工具」立为永久标准。本地 main 指针永远不需要同步,因为 GitHub Contents API 推送只动文件不动 commit。
下一步我会写一篇《publish-via-api.py 推送盲区实战复盘 2026》(已发于 2026-07-30),讲 5 个静默失败点 + 5 步根治修复,与本文形成「事故根因 + 推送工具深化」完整链路。
(事实核对:所有时间戳、commit 哈希、命令、文件名均来自 corrections.md 6/04 20:30 条目;reflog 救援命令、4am-health-check.py 同步步骤、.pushed_manifest.json 格式均来自永久标准 #24 文档。)
👉 Join MiniMax Token Plan: AI coding acceleration for businesses
👉 立即参与小米 MiMo 开放平台:API 体验金 + 首单 9 折优惠
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: