Git 双向合并原理详解

核心问题

Q1: develop 分支是基于 main 创建的,还能把 main 上修复的合并到 develop 吗? A1: ✅ 完全可以! 分支创建后就各自独立了。

Q2: 把 main 同步到 develop 后,发布时又要把 develop 合并到 main,会重复吗? A2: ✅ 不会重复! Git 会自动去重。


一、分支的本质:独立发展

分支创建后是独立的

虽然 develop 是基于 main 创建的,但创建后两个分支就各自独立发展了

  1. 时刻 1:创建 develop 分支
  2. main ──●──●──●
  3. (创建分支)
  4. develop (此时 develop main 指向同一个提交)
  5. 时刻 2:各自发展(已经独立!)
  6. main ──●──●──●──●(hotfix) main 继续前进
  7. develop ●──●──●──● develop 也在前进
  8. (新功能开发)
  9. 此时两个分支已经不同了:
  10. - main hotfix
  11. - develop 有新功能
  12. - 互相都不知道对方的修改

Git 分支的技术原理

  1. 分支只是一个"指针",指向某个提交
  2. 创建时:
  3. main commit #5
  4. develop commit #5 (基于 main 创建,指向同一个提交)
  5. 一个月后:
  6. main commit #6 (修复了 bug)
  7. develop commit #8 (开发了新功能)
  8. 两个指针已经指向不同位置了!

为什么需要 merge main → develop?

  1. main ──●──●──●──●(修复支付bug)
  2. develop ●──●──●──●──●(新功能)
  3. 缺少支付bug修复!
  4. 如果不 merge main develop
  5. develop ──●──●──●──●──● (没有bug修复)
  6. (几个月后合并到main)
  7. main ──●──●──●──●──● (bug修复被覆盖,bug重现!❌)

二、双向合并完整流程

时间线演示

  1. 1个月:hotfix main develop(同步修复)
  2. main ──●──●(hotfix)
  3. merge
  4. develop ──●──●──●──●
  5. 4个月:develop main(发布新功能)
  6. main ──●──●──────●(发布v2.0)
  7. merge
  8. develop ──●──●──●──●

详细操作流程

  1. # === 第1天:创建 develop ===
  2. git checkout main
  3. git checkout -b develop
  4. git push -u origin develop
  5. # main 和 develop 指向同一个位置
  6. # === 第1-30天:develop 开发新功能 ===
  7. git checkout develop
  8. git commit -m "feat: 新功能A"
  9. git commit -m "feat: 新功能B"
  10. git push
  11. # === 第45天:main 修复线上 bug ===
  12. git checkout main
  13. git pull
  14. # 修复代码...
  15. git add .
  16. git commit -m "fix: 修复支付回调问题"
  17. git tag v1.0.1
  18. git push origin main --tags
  19. # === 第46天:❗关键 - 同步 hotfix 到 develop(main → develop)===
  20. git checkout develop
  21. git merge main # ← 第一次合并:main 到 develop
  22. git push
  23. # develop 现在有:新功能A + 新功能B + 支付bug修复 ✅
  24. # === 第60-90天:develop 继续开发 ===
  25. git checkout develop
  26. git commit -m "feat: 新功能C"
  27. git commit -m "feat: 新功能D"
  28. git push
  29. # === 第120天:发布新版本(develop → main)===
  30. git checkout main
  31. git pull
  32. git merge develop # ← 第二次合并:develop 到 main
  33. git tag v2.0.0
  34. git push origin main --tags
  35. # main 现在有:新功能A + B + C + D + 支付bug修复 ✅

版本演进图

  1. 详细版本演进:
  2. 1天(初始状态):
  3. ●(A)
  4. ├─ main
  5. └─ develop
  6. 30天(develop 开发新功能):
  7. ●(A)──●(B)──●(C) develop (新功能AB)
  8. └─ main
  9. 45天(main 修复 bug):
  10. ●(A)──●(B)──●(C) develop
  11. └───●(D) main (hotfix)
  12. 46天(main develop 同步 hotfix):
  13. ●(A)──●(B)──●(C)──●(E) develop (合并了hotfix D)
  14. └───●(D)─────────┘ main
  15. 90天(develop 继续开发):
  16. ●(A)──●(B)──●(C)──●(E)──●(F)──●(G) develop (新功能CD)
  17. └───●(D) main
  18. 120天(develop main 发布 v2.0):
  19. ●(A)──●(B)──●(C)──●(E)──●(F)──●(G) develop
  20. └───●(D)─────────────────────────┘ main (v2.0)

三、Git 智能去重原理

会不会重复应用 hotfix?

担心:hotfix 会不会被合并两次导致重复?

答案:❌ 不会! Git 使用 DAG(有向无环图)管理提交历史。

技术原理

  1. 每个提交都有唯一的 SHA-1 标识
  2. commit D (hotfix):
  3. - SHA: abc123def456...
  4. - 内容:修复支付bug
  5. 第一次 mergemain develop,第46天):
  6. - develop 分支包含了 commit abc123
  7. 第二次 mergedevelop main,第120天):
  8. - Git 检查:abc123 已经在两个分支的共同祖先里了
  9. - 结果:只合并新的提交(FG),不重复 abc123

Git 合并算法

  1. Git 三方合并(Three-way Merge):
  2. 1. 找到共同祖先(merge base
  3. 2. 比较两个分支相对于祖先的变化
  4. 3. 自动合并不冲突的部分
  5. 4. 标记冲突的部分需要手动解决
  6. 120天合并时:
  7. - 共同祖先:commit E(第46天的合并点)
  8. - main 相对于 E:没有新提交
  9. - develop 相对于 E:有 FG 两个新提交
  10. - 结果:只把 FG 合并到 mainD 不会重复

四、实际代码演示

完整模拟流程

  1. # ========== 初始化项目 ==========
  2. mkdir git-merge-demo
  3. cd git-merge-demo
  4. git init
  5. # 创建初始提交
  6. echo "v1.0" > version.txt
  7. git add .
  8. git commit -m "v1.0 初始版本"
  9. # ========== 创建 develop 分支 ==========
  10. git checkout -b develop
  11. # ========== develop 开发新功能 ==========
  12. echo "feature1" >> version.txt
  13. git add .
  14. git commit -m "feat: 功能1"
  15. echo "feature2" >> version.txt
  16. git add .
  17. git commit -m "feat: 功能2"
  18. # ========== main 修复线上 bug ==========
  19. git checkout main
  20. echo "hotfix1" >> version.txt
  21. git add .
  22. git commit -m "fix: 修复支付bug"
  23. # 查看此时 main 的内容
  24. cat version.txt
  25. # 输出:
  26. # v1.0
  27. # hotfix1
  28. # ========== 同步 hotfix 到 develop(main → develop)==========
  29. git checkout develop
  30. git merge main -m "merge: 同步 hotfix 到 develop"
  31. # 查看 develop 的内容
  32. cat version.txt
  33. # 输出:
  34. # v1.0
  35. # feature1
  36. # feature2
  37. # hotfix1 ← hotfix 已同步 ✅
  38. # ========== develop 继续开发 ==========
  39. echo "feature3" >> version.txt
  40. git add .
  41. git commit -m "feat: 功能3"
  42. # ========== 发布:develop → main ==========
  43. git checkout main
  44. git merge develop -m "release: v2.0 发布新功能"
  45. # 查看最终 main 的内容
  46. cat version.txt
  47. # 输出:
  48. # v1.0
  49. # hotfix1 ← 只有一次,不重复!✅
  50. # feature1
  51. # feature2
  52. # feature3
  53. # ========== 查看提交历史 ==========
  54. git log --oneline --graph --all
  55. # 可以看到完整的合并历史,hotfix 只出现一次

验证不会重复

  1. # 检查 hotfix 提交的 SHA
  2. git log --oneline | grep "修复支付bug"
  3. # abc123 fix: 修复支付bug
  4. # 检查这个 SHA 在历史中出现几次
  5. git log --all --oneline | grep abc123
  6. # abc123 fix: 修复支付bug ← 只出现一次!✅
  7. # 查看文件修改历史
  8. git log --all --oneline -- version.txt
  9. # 可以看到 hotfix 相关的修改只被应用了一次

五、可能遇到的冲突

什么时候会冲突?

  1. # 场景:main 和 develop 修改了同一行代码
  2. # main 修复:
  3. git checkout main
  4. echo "price = 100" > config.txt # 修改价格为100
  5. git commit -m "fix: 修复价格"
  6. # develop 开发:
  7. git checkout develop
  8. echo "price = 200" > config.txt # 新功能设置价格为200
  9. git commit -m "feat: 新价格体系"
  10. # 同步时会冲突:
  11. git merge main
  12. # Auto-merging config.txt
  13. # CONFLICT (content): Merge conflict in config.txt

如何解决冲突

  1. # 1. 查看冲突文件
  2. cat config.txt
  3. # <<<<<<< HEAD
  4. # price = 200
  5. # =======
  6. # price = 100
  7. # >>>>>>> main
  8. # 2. 手动编辑解决冲突
  9. vim config.txt
  10. # 决定保留哪个值,或者合并两者
  11. # 3. 标记为已解决
  12. git add config.txt
  13. git commit -m "merge: 解决价格配置冲突"
  14. # 4. 继续推送
  15. git push

避免冲突的最佳实践

  1. 及时同步:main 有 hotfix 后尽快合并到 develop
  2. 模块化:不同功能修改不同文件
  3. 沟通协调:团队知道谁在改什么
  4. 使用工具:IDE 的合并工具(Android Studio、VSCode)

六、核心概念总结

分支关系表

概念 说明 示例
分支创建 只是复制了那一刻的”起点” develop 基于 main 的 commit A 创建
独立发展 创建后各走各的路,互不影响 main 改 D,develop 改 B、C
merge 合并 可以双向合并,谁先创建无所谓 main → develop,develop → main
main → develop 把 main 的修复同步到 develop 避免新版本重现旧 bug
develop → main 把 develop 的新功能发布到 main 正式发布新版本
Git 去重 使用 SHA-1 标识,自动去重 hotfix 不会被应用两次

双向合并流程图

  1. 完整生命周期:
  2. 创建 1次合并 2次合并
  3. develop (同步hotfix) (发布新功能)
  4. main ──●── develop main ──→ develop develop ──→ main
  5. (分支) hotfix修复 新功能发布

关键检查清单

阶段 操作 检查项
创建 develop git checkout -b develop ✅ 基于最新的 main
开发新功能 在 develop 提交 ✅ 只在 develop 开发
修复线上 bug 在 main 提交 hotfix ✅ 基于 main 修复
同步 hotfix git merge main (在 develop) ✅ 必须执行!
发布新版本 git merge develop (在 main) ✅ 充分测试后

七、类比理解

平行宇宙类比

  1. 就像两个平行宇宙:
  2. 宇宙 A (main):
  3. - 2024.1.1 创建
  4. - 2024.3.15 修复了时空裂缝 (hotfix)
  5. 宇宙 B (develop):
  6. - 2024.1.1 从宇宙 A 分裂出来
  7. - 2024.2.1 发明了传送门 (feature1)
  8. - 2024.3.1 发明了时间机器 (feature2)
  9. 虽然宇宙 B 是从宇宙 A 分裂出来的,
  10. 但分裂后各自发展,互不影响。
  11. 第一次 merge (main develop):
  12. - 打通虫洞,把宇宙 A "时空裂缝修复"同步到宇宙 B
  13. - 现在宇宙 B 有:传送门 + 时间机器 + 修复的时空
  14. 第二次 merge (develop main):
  15. - 把宇宙 B 的发明带回宇宙 A
  16. - 现在宇宙 A 有:时空裂缝修复 + 传送门 + 时间机器
  17. - 时空裂缝修复不会重复,Git 知道它已经在两个宇宙的历史中了

八、常见问题 FAQ

Q1: 为什么不能只在一个分支开发?

  1. 只用 main 分支的问题:
  2. - 新功能未完成就不能修复线上 bug
  3. - 无法隔离"稳定版本""开发版本"
  4. - 团队协作混乱
  5. 使用 main + develop 的好处:
  6. - main 永远是稳定的线上版本
  7. - develop 可以自由开发,不影响线上
  8. - 清晰的版本管理

Q2: 能不能跳过”同步 hotfix 到 develop”?

  1. # ❌ 不同步的后果:
  2. 45天:
  3. main ──●──●(修复支付bug)
  4. develop ──●──●──●(新功能)
  5. 120天发布:
  6. git checkout main
  7. git merge develop
  8. # 结果:develop 的旧代码覆盖了 main 的修复
  9. # 线上 bug 重现!❌
  10. # ✅ 正确做法:必须同步
  11. 46天:
  12. git checkout develop
  13. git merge main # 同步修复
  14. 120天发布:
  15. git checkout main
  16. git merge develop
  17. # 结果:既有新功能,也有 bug 修复 ✅

Q3: merge 和 rebase 的区别?

操作 效果 历史 推荐场景
merge 保留完整历史 有合并节点 跨分支合并(main ↔ develop)
rebase 线性历史 改写提交 本地整理提交
  1. # merge(推荐用于 main 和 develop)
  2. git checkout develop
  3. git merge main
  4. # 优点:保留完整历史,安全
  5. # 缺点:历史图复杂
  6. # rebase(不推荐用于已推送的分支)
  7. git checkout develop
  8. git rebase main
  9. # 优点:历史线性,干净
  10. # 缺点:改写历史,容易出错

Q4: 如果忘记同步 hotfix 怎么办?

  1. # 第120天准备发布时才发现忘记同步了
  2. # 方案1:现在补上(推荐)
  3. git checkout develop
  4. git merge main
  5. git push
  6. # 然后再发布
  7. git checkout main
  8. git merge develop
  9. # 方案2:cherry-pick 特定提交
  10. git checkout develop
  11. git cherry-pick <hotfix-commit-sha>

九、实战建议

针对 GemmyClients_v_2025 项目

  1. # 分支规划
  2. main # 线上版本(v1.0.x)
  3. develop # 重构开发(v2.0.0)
  4. # Hotfix 流程(线上出现支付bug)
  5. git checkout main
  6. # 修复...
  7. git commit -m "fix: 修复微信支付回调异常"
  8. git tag v1.0.1
  9. git push origin main --tags
  10. # ⚠️ 立即同步到 develop
  11. git checkout develop
  12. git merge main
  13. git push
  14. # 继续开发新功能
  15. git commit -m "refactor: 重构订单系统"

最佳实践

  1. 及时同步:main 有 hotfix 后 24 小时内同步到 develop
  2. 测试验证:每次合并后运行测试
  3. 提交规范:使用语义化提交(feat/fix/refactor)
  4. Tag 管理:每次发布打 tag(v1.0.1, v2.0.0)
  5. 冲突解决:遇到冲突先理解再解决,不确定就问

十、总结

核心要点

要点 说明
分支独立 develop 虽基于 main 创建,但创建后各自发展
双向合并 main → develop(同步修复),develop → main(发布新功能)
不会重复 Git 使用 SHA-1 去重,hotfix 只应用一次
必须同步 任何在 main 上的修复都必须同步到 develop
安全可靠 使用了几十年的成熟方案

记忆口诀

  1. 1. develop 基于 main,创建后独立
  2. 2. main bug,必须同步 develop
  3. 3. develop 新功能,几个月后合并 main
  4. 4. Git 很智能,自动去重不重复
  5. 5. 遇到冲突,手动解决别慌张