Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 其他内容 / Other Topics

事故复盘 / Incident Reviews

Git 复盘 / Git Reviews

1. Git 添加 Gitee 远程仓库操作记录 / Adding a Gitee Remote Repository with Git

2. 分支事故复盘:基线分支选错,以及如何用 Cherry-pick 补救 / Branch Incident Review and Recovery with Cherry-Pick

本页目录

分支事故复盘:基线分支选错,以及如何用 Cherry-pick 补救 / Branch Incident Review and Recovery with Cherry-Pick ​

1. 这次事故是什么 ​

这次我们在推进 MapDocument 文档化重构时,后面才发现当前工作分支并不是基于预期的 main 最新 UI 基线,而是从另一个已经偏离的分支演化过来的。

这带来了一个非常典型但也非常危险的问题:

  • 我们以为自己是在“重构后端存储结构”
  • 实际上工作区同时混入了“旧 UI 状态”和“新数据结构改造”
  • 最终出现的现象不是单一 bug,而是“基线不一致导致的系统性错位”

最直接的表现就是:

  • 前端 UI 和 main 上记忆中的版本不一致
  • 一些之前存在的交互能力消失了,比如侧边栏收缩、Graph 缩放/拖拽、Profile 入口等
  • 很难一眼判断“到底是这次重构改坏了”,还是“本来这个分支就不是正确的 UI 基线”

所以这不是普通的小冲突,而是一次典型的“分支基线事故”。

2. 根因是什么 ​

根因并不复杂,但后果很大:

根因 1:起始分支不是正确基线 ​

这次重构最开始所在的分支,不是从真正想要合入的 main 最新状态切出来的,而是从另一个存在 UI 偏差的分支继续开发。

这意味着:

  • 数据结构重构本身是对的
  • 但是承载它的前端基线是错的
  • 于是越往后做,越容易把“基线差异”误判成“本次改动引入的回归”

根因 2:改动跨度很大,难以整体搬运 ​

这次改动不是单点修复,而是跨越多层:

  • Prisma schema
  • 后端 repository/service
  • shared contracts
  • frontend map/progress 数据流
  • 本地种子数据
  • 若干 UI 恢复补丁

如果直接整分支合并,很容易把错误 UI 基线也一起带进来。

根因 3:冲突不是语法冲突,而是语义冲突 ​

真正麻烦的不是“文件冲突了”,而是“两个分支分别代表两种不同阶段的系统状态”:

  • 一边是正确方向的数据结构重构
  • 一边是更接近目标的 main UI 基线

所以不能机械地 merge,必须有意识地筛选“哪些提交值得搬运,哪些状态不该被带过去”。

3. 为什么当时不能直接 merge ​

如果直接把旧工作分支 merge 到新的 main 基线分支里,会有几个风险:

  • 把旧分支里不想要的 UI 状态也带过来
  • 很难保证历史整洁
  • 容易把本来局部的补丁放大成全局冲突
  • 后面很难解释这次 PR 到底真正改了什么

更关键的是,直接 merge 以后,提交历史里会混入大量“和目标无关的基线差异”。这样哪怕最后能跑,也会让后续排查变得非常痛苦。

4. 为什么选择 Cherry-pick ​

我们最后采用的核心补救策略是:

  1. 回到真正的 main 基线
  2. 新建一个干净分支
  3. 把上午在错误基线分支上做对的那些提交,一个个“摘”过来
  4. 在新的基线上解决冲突和 UI 回归

这就是 git cherry-pick 最适合的场景:

  • 你不想整条分支一起搬过来
  • 你只想保留其中一部分“有价值的提交”
  • 你希望最终的新分支建立在正确基线上

换句话说,cherry-pick 不是为了炫技,而是为了把“正确的改动”从“错误的基线”里救出来。

5. 我们是怎么做的 ​

第一步:切回 main,建立真正的目标分支 ​

先回到 main,然后从 main 新建这次真正要交付的分支:

powershell
git switch main
git switch -c map-document-refactor-mainbase
1
2

这一步的意义非常大:

  • 从这里开始,新的工作只建立在 main 之上
  • 后面任何冲突,都是“重构内容 vs main 基线”的冲突
  • 不再混入之前错误分支的整体状态

第二步:梳理原分支里真正想保留的提交 ​

我们并不是把整个旧分支照搬,而是挑出这次真正想保留的提交。

这次最终搬运过来的提交链大致如下:

text
1000d84 docs: add map storage refactor design spec
f2c16b8 feat: define document-centered map contracts
efd7342 refactor: move prisma schema to map documents
b3933fd feat: add map document validation and projection scripts
da070cf refactor: move backend services to document-based maps
256e666 refactor: consume document-based maps in frontend
34c1d33 fix: align progress validation with document payload
47fba02 chore: remove legacy file-based map artifacts
f3188ff refactor: remove legacy map compatibility types
81d2784 refactor: remove legacy node api route
b5b21ac docs: document map authoring workflow
1639102 fix: fallback to canonical map document when db is empty
0efbbfb fix: seed a usable default map document for local development
c860763 fix: restore profile entry in the main header
bea34b3 fix: stabilize main-based document map branch
3810458 fix: restore local auth and cors flow
89a35b8 fix: resolve prisma schema merge remnants
7f9f807 fix: restore mainbase graph interactions and sidebar
ed33aba fix: restore graph dragging and edge visibility
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

这些提交在新的 main 基线上,最终形成了我们现在这条可提 PR 的分支历史。

第三步:按顺序执行 cherry-pick ​

cherry-pick 的关键原则是:尽量按原本的逻辑顺序来摘取提交。

例如:

powershell
git cherry-pick 1000d84 f2c16b8 efd7342 b3933fd da070cf 256e666
1

再逐步继续摘后面的修复提交。

为什么强调“顺序”:

  • contract 先到,后端和前端才能接
  • schema 先到,repository/service 才有落点
  • 数据流稳定后,UI 修复才有意义

如果顺序乱了,冲突会明显增多。

6. Cherry-pick 过程中遇到了什么问题 ​

问题 1:shared/contract.ts 冲突 ​

在早期 cherry-pick feat: define document-centered map contracts 时,就遇到了 shared/contract.ts 冲突。

这是很合理的,因为:

  • main 基线里这个文件有自己的状态
  • 我们要摘过来的提交又在重构 shared contract

处理原则不是“随便选一边”,而是:

  • 保留 document-first 架构必须要的 contract
  • 同时不要误删 main 上仍然需要的部分

冲突解决后,再继续:

powershell
git add shared/contract.ts shared/map-document.ts test/frontendToBackendTest/map-document.contract.test.ts
git cherry-pick --continue
1
2

问题 2:Prisma schema 残留冲突标记 ​

后面虽然主链路能跑,但工作区里还残留了 schema.prisma 的冲突标记。

这个问题危险在于:

  • 它未必立刻阻塞 TS build
  • 但会污染数据库 schema 真相
  • 合并前如果不清掉,就是明显事故遗留

所以我们专门做了一次清理,把 backend/prisma/schema.prisma 整理成干净的 document-first 版本,并单独作为修复处理。

问题 3:基于 main 的 UI 回归 ​

即便数据结构已经搬运成功,UI 仍然出现了回归:

  • Profile 入口丢失
  • Graph 交互不完整
  • 连线不明显
  • 侧边栏和主页面布局与记忆中的 main 不一致

这里说明一个非常重要的事实:

cherry-pick 解决的是“正确搬运提交”的问题,它不自动保证最终用户体验等价。

所以我们后面又补了多次 fix 提交,专门修基于 main 的 UI 可用性。

7. 这次 Cherry-pick 的本质是什么 ​

这次 cherry-pick 的本质,可以概括成一句话:

我们不是在复制一个分支,而是在重建一条正确历史。

也就是说:

  • 不是把旧分支原样搬过来
  • 而是把“真正有价值的提交”重新安放到正确基线之上

这就是为什么最后这条分支虽然经历了事故,但仍然能保持比较清晰的原子提交历史。

8. 这次事故带来的教训 ​

教训 1:任何大重构开始前,先确认基线分支 ​

至少要先确认三件事:

powershell
git branch --show-current
git log --oneline --decorate -20
git diff main...HEAD --stat
1
2
3

确认点包括:

  • 当前分支到底从哪里来的
  • 当前分支和 main 差了什么
  • 你记忆中的 UI/功能是否真的存在于这个基线上

教训 2:大改动不要等到最后才验证 UI 基线 ​

这次就是后面才意识到:

  • “怎么现在 UI 和 main 不一样?”

如果在更早阶段就和 main 对照一次,事故会小很多。

教训 3:原子提交很重要 ​

如果上午的改动全都混成一坨,后面几乎不可能精准 cherry-pick。

正因为我们有相对明确的提交切分:

  • contract
  • schema
  • backend services
  • frontend consumption
  • validation
  • recovery fixes

所以补救才有操作空间。

教训 4:正确的数据结构改造,仍然可能被错误基线拖累 ​

这次最值得庆幸的是:

  • 数据结构方向是对的
  • MapDocument / projection / progress 这条路线没有错

出问题的是“承载这次重构的分支基线”,而不是重构目标本身。

9. 这次补救后留下了什么正面结果 ​

虽然过程很折腾,但最终结果其实是好的:

  • MapDocument 成为真相源
  • MySQL 直接存地图文档 JSON
  • projection 成为派生结构
  • progress 独立为文档型用户状态
  • 分支最终重新建立在 main 基线上
  • 原子提交历史被保住了,后面可以 rebase and merge 到 main

这意味着我们不是“勉强把事故盖过去了”,而是把正确架构真正救回到了正确基线上。

10. 如果以后再遇到类似情况,推荐处理流程 ​

推荐流程如下:

  1. 先停止继续在错误基线上叠加改动
  2. 回到正确基线,比如 main
  3. 新建干净补救分支
  4. 先梳理哪些提交是真正想保留的
  5. 按依赖顺序执行 cherry-pick
  6. 每次冲突都按“最终目标架构”解决,而不是机械选 theirs/ours
  7. 每搬完一批就 build/test
  8. 最后再修 UI 回归和残留问题

11. 一句话总结 ​

这次事故的本质不是“代码写错了”,而是:

在错误的基线分支上,做了正确的重构。

而 cherry-pick 的价值在于:

它让我们可以把这些正确的重构,从错误的历史里精确地救出来。

最后更新于:

Pager
上一篇1. Git 添加 Gitee 远程仓库操作记录 / Adding a Gitee Remote Repository with Git

持续记录,持续成长

Copyright © Tidenflow