Git Flow 分支模型

目录

经典分支模型(Git Flow)

由 Vincent Driessen 提出的 Git Flow 模型,是管理 main(或 master)和 dev 分支的经典方案:

  • main 用于生产发布,保持稳定;
  • dev 用于日常开发,合并功能分支(feature/*);
  • 功能开发在 feature 分支进行,完成后合并回 dev
  • 预发布分支(release/*)用于测试,测试完成合并到 main 和 dev
  • 紧急修复分支(hotfix/*)从 main 拉出,修复后合并回 main 和 dev

    该模型清晰划分职责,减少冲突,适合中大型项目。

  • GitHub Flow

    适合快速迭代和持续部署的项目,只有一个主分支(main),所有功能分支直接从 main 拉出,完成后通过 Pull Request 合并回 main,保持 main 始终可发布状态。适合小团队或需要快速上线的项目。

分支 作用
main 生产环境分支(已上线)
dev 开发主线(功能集成、测试)
feature/* 功能开发分支,从 dev
release/* 发布准备分支,从 dev
hotfix/* 紧急修复分支,从 main

但是有一部分公司是这样的:【个人觉得超级坑】

项目场景 Git Flow 方式 你现在的方式 是否合理
正常开发 devfeature ❌ 你从 mainfeature ⚠️ 容易冲突
集成测试 feature 合并到 dev ✅ 一样 ✅ 合理
正式上线 dev 合并回 main ✅ 或 featuremain ✅ 合理
热修复生产问题 mainhotfix 分支 ❌ 当前无此逻辑 可补充

🚨 从慢分支切出一个 feature 分支,再合并到快分支,Git 是如何判断“要不要合并”“能不能合并” 的?是看提交指针,还是 diff?

🔥 Git 在合并时,不是简单对比提交指针,也不是简单对比 diff,而是会寻找“共同祖先(common ancestor)”,然后基于它做一个三方合并(three-way merge)。

🔍 详细说明

假设:

  • main(慢分支):只有 A → B 两个提交
  • dev(快分支):从 main 分出,但已有 A → B → C → D → E
  • 你从 main 切了 feat 分支,提交了 F → G

现在你要从 feat 合 PR 到 dev

🧠 Git 是怎么做的?

       A---B (main)
        \      
         F---G (feat)
        /
C---D---E (dev)

  1. Git 会找到这三个分支的 最近公共祖先(common ancestor):这里是 B
  2. 然后进行三方合并:B(祖先)、E(当前目标 dev)、G(你的分支 HEAD)
  3. Git 会计算:
    • dev 从 B → E 做了什么改动
    • feat 从 B → G 做了什么改动
    • 把这两个方向的改动 合并 到一起

✅ 所以 Git 合并看什么?

判断项 说明
✅ 共同祖先 找出你分支和目标分支的分叉点
✅ 各自的 diff 从分叉点开始的差异
❌ 提交指针不直接比较 两个分支的 HEAD 不一定要线性关系
✅ 是否有冲突 如果两边都改了同一行,就冲突

假设:

  • B 中某函数返回 "123"
  • dev 改成了 "dev-456"
  • 你 feat 改成了 "feat-789"

那合并时,就会冲突,因为从共同祖先 B 开始,两边都对这行做了不同的修改。

✅ 为什么你能合进去?

因为:

  • 你改的地方 dev 没碰过 → Git 三方合并自动通过
  • 哪怕是老分支,只要你改的代码段和 dev 没有冲突,Git 会智能合并
  • 不依赖时间先后,也不依赖谁快谁慢,而是看从共同祖先以来谁改了什么

✅ Git 合并靠的是三方合并:找共同祖先 + 比较两边 diff 合并内容,而不是简单看提交指针或谁快谁慢!

✅ 即使你的 feature 分支来自更“慢”的 main 分支,只要你和 dev 的改动没有在相同代码段重叠,Git 都能自动合并。

问题2:

  1. feat1 和 feat2 都是从 main 分出来的,然后都分别合入 dev;
  2. 合并后 dev 有了两条变更历史;
  3. feat1 发现 bug,要修复,要怎么做合并?Git 是怎么看这段合并链路的?
       A---B (main)
        |\       \
        | C1(feat1)\             C3 (bugfix)
        |   \       \           /
        |    \       \        /
        |     \       D (dev)
        |      \     /
        |       C2(feat2)
        |
        |
      common ancestor = B

初始状态:

  • C1:feat1 的改动
  • C2:feat2 的改动
  • D:dev 合并了 C1 和 C2

🔧 现在 feat1 要修复 bug:

✅ 方案一:继续在 feat1 分支上修

  1. git checkout feat1
  2. 写 bugfix(提交为 C3)
  3. git merge feat1 → dev

Git 的处理方式:

  • 会做一次三方合并:
    • 祖先 = dev 上一次合并 feat1 的版本(即 C1)
    • 当前 dev 最新 = D
    • 你的 feat1 最新 = C3

Git 能识别出:

“哦你只是继续往 feat1 分支上加了一个提交,那我只需要合并 C3 的 diff 就行。”

✅ 合并时不会乱套,也不会重复合并旧的提交。

✅ 方案二:新建 hotfix 分支(推荐)

适用于多人协作不希望回到原 feature 分支的情况

git checkout -b hotfix/feat1-bugfix D   #  dev 最新建出修复分支
# 修复代码提交 C3
git push origin hotfix/feat1-bugfix
#  PR 合并到 dev

这个方式的好处是:

  • 不污染原始的 feat1 分支(它已被归档、合并了)
  • 修复逻辑清晰,一条 commit 路就搞定了

✅ Git 是否会自动判断祖先指针?

是的,Git 合并永远使用 “共同祖先” + “目标分支 HEAD” + “当前分支 HEAD” 做三方合并

所以:

  • Git 会保留链路(commit 记录)
  • Git 并不只看最后提交的指针,而是完整计算变更路径
  • 合并记录也会清晰显示 “这个分支是从哪来的,做了哪些 diff”

✅ Git 是内容寻址 + 快照系统,不是完整复制代码

✅ 多个分支不会导致仓库体积线性增长

举例:

  • 你从 main 分出 20 个分支,每个分支只改了 3 个文件;
  • Git 仓库里最终只会多出:
    • 20 个“分支指针”
    • 每个改动的 diff(也叫快照对象)

🧠 Git 使用的是 增量存储(snapshot)+ 内容去重(hash),所以很多内容其实只是复用引用。

操作 是否会大幅增加仓库大小
创建分支 ❌ 几乎不增加
提交小代码改动 ✅ 增量增长,很小
改大文件、多图资源 ✅ 会增长,需优化

点赞一下

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦