上周,我们团队经历了一场"史诗级"的 Git 合并冲突。
起因很简单:同事在 B 分支(开发下个大版本)对整个项目做了大规模目录结构调整------移动文件、删除旧模块、重命名包路径。与此同时,A 分支(当前迭代)正在紧张开发新功能,并已成功上线。
当 A 分支准备合入 B 分支时,灾难发生了:
- 初级灾难-代码"消失"了:A 分支修改的文件,在 B 分支中已被移到新目录,Git 无法识别这是"同一个文件",导致所有修改被丢弃;
- 进阶级灾难-双重修改冲突:不仅仅文件被移动了目录,A 和 B 都改了同一个文件,但 Git根本却无法告诉你哪些逻辑该保留;
- 终极灾难-方法级覆盖:如果只是修改了不同的方法还好,住需要用最新的方法覆盖旧的提交,更糟的是,两人改了同一个方法的不同业务逻辑,必须A、B两分支的开发者一起人工逐行比对需求,重新梳理实现。
整个合并过程耗时两天,还引入了几个低级 bug。
问题不在技术,而在协作流程。
为什么 Git 搞不定这种重构?
很多人以为 Git 能智能识别"文件移动",但事实是:
Git 并不真正跟踪"文件移动"------它只通过内容相似度猜测是否为重命名。
当你同时做两件事:
- 大量移动/重命名文件;
- 其他分支在原文件上持续开发;
Git 就会"失明":它认为旧文件被删除了,新文件是全新创建的。于是,你的修改"凭空消失"。
正确姿势:如何安全地做大型结构调整?
✅ 原则一:重构先行,开发后置
最理想的做法(也是你提到的):
在任何功能开发开始前,先完成结构性调整,并形成一个干净的"新基线"分支。
text
main (v1.0 上线)
│
└── dev-base (仅做目录重构,无业务改动)
│
├── feature/A (基于新结构开发)
└── feature/B (基于新结构开发)
这样,A 和 B 的所有代码都写在新目录下,后续合并零冲突。
💡 关键点 :这个
dev-base分支必须只包含纯结构调整,不能混入任何业务逻辑变更。否则,后续 diff 和回滚都会变得复杂。
✅ 原则二:如果来不及?那就"渐进式迁移"
现实中,往往等不到"完美时机"。那怎么办?
不要直接删除旧文件!采用"双写 + 废弃"策略:
- B 分支重构时,保留旧目录结构;
- 在新位置拷贝旧文件,并标记为"新版";
- 所有新功能在新目录开发;
- 旧文件中的方法,用
@Deprecated标注,并加注释:"请迁移到 XXX 新路径"; - 等 A 分支上线并合入后,再统一删除旧文件。
这样做的好处:
- A 分支仍可正常提交到旧路径;
- 合并时,Git 能清晰看到"旧文件有修改"+"新文件存在";
- 开发者只需将 A 的修改手动迁移到新文件一次,而非在冲突中迷失。
🛠️ 工具建议:用 IDE 的"Move Class"功能(如 IntelliJ 的 Refactor → Move),它会自动更新引用,并生成 Git 可识别的 rename 记录,提高合并成功率。
✅ 原则三:沟通 > 技术
再好的流程,也抵不过信息不同步。
- 一旦有人提出"要做大重构",立即拉通所有并行开发团队;
- 明确告知:未来 X 周内,主干将冻结业务开发,优先完成结构调整;
- 或者,约定"重构窗口期",在此期间暂停向公共分支提交功能代码。
技术债可以慢慢还,但协作债会瞬间爆炸。
重构不是一个人的事
大型项目结构调整,本质是一次"架构演进",而不是"个人炫技"。
它需要:
- 提前规划(何时做、怎么做);
- 分阶段落地(先迁移,再废弃);
- 全员共识(谁受影响、怎么配合)。
下次当你想"整理一下混乱的目录"时,请记住:
你动的不是文件,而是整个团队的协作节奏。
欢迎转发给正在"痛苦合并"的同事,也许能帮他们少熬一个通宵。
💡 感谢你看完这篇内容,这是我自己在工作学习中遇到的case,做一些简单的究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索。