Git 多分支开发实践:彻底搞懂 Master、Dev、Feature、Hotfix
很多人会用 Git,但真正进入团队开发后,经常会遇到这些问题:
- 为什么不能直接在 Master 上开发?
- Dev 分支到底是干什么的?
- Feature 分支什么时候创建?
- Merge 和 Rebase 到底有什么区别?
- IDEA 右键菜单里的各种操作对应什么 Git 命令?
本文通过一个完整项目案例,带你彻底理解 Git 多分支开发模型。
先看全局:一个项目是如何流转的
在开始之前,先记住下面这张图。
发布
│
▼
┌──────────────┐ ┌─────────────────┐
│ feature/* │─────────▶│ dev │
│ 功能开发 │ Merge │ 测试集成区 │
└──────────────┘ └─────────────────┘
▲
│
│ Merge
│
▼
┌─────────────────┐
│ master │
│ 线上版本 │
└─────────────────┘
同步代码:
master ───────▶ dev
线上修复:
master ───────▶ hotfix/* ───────▶ master
实际上:feature/* → dev → master,就已经覆盖了绝大多数团队开发场景。
为什么不要直接在 Master 上开发
很多新手项目是这样的:
master:A ── B ── C ── D ── E ── F
所有内容都往 Master 提交: Agent核心功能; Bug修复;实验代码
看起来很简单。
但实际上:
登录功能写崩 → 项目无法发布
实验代码提交 → 影响所有开发
线上Bug出现 → 无法快速定位版本
多人同时开发 → 冲突不断
因此实际项目都会引入分支隔离。核心思想:
开发归开发
测试归测试
线上归线上
互不影响。
各个分支到底负责什么
最推荐的小团队模型:
feature/* ---→ dev ---→ master
↑
hotfix/*
职责如下:
| 分支 | 作用 |
|---|---|
| master | 永远保持可发布 |
| dev | 团队开发主线 |
| feature/* | 新功能开发 |
| hotfix/* | 紧急线上修复 |
理解成公司里的不同环境:
feature/* → 开发环境
dev → 测试环境
master → 生产环境
就非常容易理解。
一次完整开发流程
假设现在要开发 Agent 功能。整个流程其实只有四步。
Step1:从 Dev 创建feature功能分支
当前状态:
master ─────── A ───── B
dev ─────── A ───── B
创建:
git checkout dev
git checkout -b feature/agent
结果:
master A ───── B
dev A ───── B
feature/agent A ───── B
Step2:在 Feature 分支开发
所有代码都提交到功能分支。
feature/agent: A ───── B ───── C ───── D ───── E
期间不断:git add .; git commit; git push, 此时:
master A ───── B
dev A ───── B
feature/agent A ───── B ───── C ───── D ───── E
不会影响任何人。
Step3:合并回 Dev
功能开发完成。
执行:
git checkout dev
git merge feature/agent
结果:
master: A ───── B
dev: A ───── B ───── C ───── D ───── E
此时开始测试验证。
Step4:发布到 Master
测试通过后执行:
git checkout master
git merge dev
结果:
master:A ───── B ───── C ───── D ───── E
dev: A ───── B ───── C ───── D ───── E
正式上线。整个生命周期:
feature/agent
└────────→ dev
└────────→ master
建议在这个新章节里扩写以下 3种团队最常见的真实合并场景:
如何同步别人代码
这是最容易搞混、也最容易踩坑的部分。
场景假设:你基于master分出了 dev 分支,你在 dev 上写了一个提交 C。此时,同事把他的代码 D 优先推到了 master。此时:
master:A ───── B ───── D (同事的最新提交)
dev :A ───── B ───── C (你的本地提交)
此时你的 dev 分支落后了,想要把 master 上的新代码 D 同步进来,有两种方案。
方案一:Merge(合并)
核心逻辑:把 master 的新代码“拉”过来,和你的代码强行揉在一起,生成一个全新的“合并提交”。
执行
git checkout dev
git merge master
结果:
master:A ───── B ───── D
╲
dev :A ───── B ───── C ───── M (新生成的Merge合并提交)
特点
- 绝对安全:纯粹的增量操作,完全不影响、不修改任何已经存在的历史提交。
- 冲突一次搞定:哪怕
C和D有一万个冲突,也只需要在生成M提交时一次性解决。 - 缺点:如果天天都这么干,Git 历史树会充斥着无数个无意义的
Merge branch...节点,变成复杂的“麻花辫”。
方案二:Rebase(变基)
核心逻辑:把你的本地提交 C 先“拔出来”放在一边。把 dev 的地基直接换成 master 的最新状态 D,然后再把 C 重新“贴”上去(变成 C')。
执行
git checkout dev
git rebase master
结果:
master:A ───── B ───── D
╲
dev :A ───── B ───── D ───── C' (你的提交被重新应用在了 D 后面)
特点
- 历史极度干净:强制抹平分支分叉,提交记录是一条完美的直线,像是一个人串行开发出来的一样。
- 冲突需要耐心:如果是多笔提交进行 Rebase,Git 会按照提交顺序逐个应用,你可能需要反复、多次解决同一个地方的冲突。
- 黄金铁律:只能在自己的私有分支上进行! 如果你把已经推送到远端、别人也在用的公共分支拿来 Rebase,会导致同事的代码全部报废。
Merge 和 Rebase 怎么选
| 对比维度 | git merge | git rebase |
|---|---|---|
| 适合场景 | 公共分支、多人协作分支(如 main、develop) |
个人本地分支、特性(feature)分支 |
| 历史呈现 | 真实但复杂。保留完整的分支图表与交叉痕迹 | 干净且线性。抹平分叉,历史是一条直线 |
| 冲突处理 | 一次性解决。所有冲突在最后的 Merge 提交中统一解决 | 逐步解决。按提交顺序逐个解决,可能需要多次重复操作 |
| 安全性 | 极高。纯增量操作,任何时候都不会改写过去的历史 | 有风险。重写历史 Hash,严禁在公共分支上执行 |
📌 终极一句话口诀:
对外(公共分支/同步主干)用 Merge 尊重历史;对内(本地私有/提交 PR 前)用 Rebase 净化历史。
如何把代码合入主干(Push)
在本地把代码开发完毕,并且同步了别人的最新代码后,接下来就要把你的 dev 分支正式合回 master 主干。
合并前:
master:A ───── B ───── D
dev :A ───── B ───── D ───── C'
在正规团队中,绝对不允许在本地直接 git merge。你必须走云端流程:
Pull Request / Merge Request
- 推送分支:
git push origin dev。 - 发起申请:登录 GitHub/GitLab 网页,点击 Create Pull Request (PR),申请将
dev合入master。 - 代码评审:邀请团队大佬进行 Code Review。
- 网页端一键合并:大佬通过后,在点击网页上的
Merge按钮前,通常会有以下两种技术选项(由项目负责人根据团队规范选择):
Fast-Forward(普通/快进合并)
合并后(Fast-Forward):
master:A ───── B ───── D ───── C'
dev :A ───── B ───── D ───── C'
- 特点:主干指针直接“快进”指向你的最新提交,历史是一条直线。但如果删除了
dev分支,后续就看不出这段代码曾是在独立分支上开发的。
Squash and merge (压缩合并)
一般推荐这个。
合并后(Squash):
master:A ───── B ───── D ───── C(你在dev上的所有碎提交被揉成了一个完美的C)
dev :A ───── B ───── D ───── C1 ─── C2 ─── C3
- 特点:把你在
dev上的所有零碎提交(修错字、测Bug)打包压缩成一个干净完美的提交贴在master后面,主干历史极度整洁。
线上 Bug 如何修复(Hotfix)
假设线上登录功能突然崩溃。
很多新人会这样做:
dev
↓
修复
↓
master
这是错误的。
因为:
dev 上可能还有一堆没开发完的功能
正确流程:
master ─────→ hotfix/login-crash
│
│ 修复
▼
hotfix完成
│
▼
master ─────────────┘
│
▼
dev
完整链路:
master
│
└────→ hotfix/login-crash
│
└────→ master
│
└────→ dev
这样:
- 线上立即恢复
- 未完成功能不受影响
- 开发节奏不会被打乱
个人项目最佳实践
对于个人项目。没必要照搬 GitFlow。
直接:feature/* ─────→ dev ─────→ master就够了。
例如:
feature/agent
feature/template
feature/search
全部开发完成后:
feature/*
└────→ dev
└────→ master
足以覆盖:独立开发 ;2~5人团队
IDEA 右键菜单速查
很多人天天看 JetBrains 系列的 IDEA / Android Studio / Pycharm菜单。
却不知道对应什么 Git 操作。以下是每个操作的含义解析:
此时:
- 当前所在分支:fix/...
- 鼠标右键点击的(选中分支):developer
| 操作名称 | 核心含义 | 具体作用与应用场景 | 对应 Git 命令 / 说明 |
|---|---|---|---|
| Checkout | 切换分支 | 切换到选中的 developer 分支,将工作区文件恢复到该分支状态。 | git checkout developer 或 git switch developer |
| New Branch from... | 从选中分支建新分支 | 以 developer 分支为起点,创建一个全新的本地分支并自动切换过去。 | git checkout -b <新分支名> developer 或 git switch -c <新分支名> developer |
| Checkout and Rebase onto 'fix/...' | 切换并变基 (选中分支移动) | 先切换到 developer 分支,然后将 developer 分支上的新提交变基到 fix/... 之上。 👉 场景:修复完成,准备把主干代码挪到修复代码后面,保持线性历史。 | git checkout developer git rebase fix/... |
| Rebase 'fix/...' onto 'developer' | 变基到选中分支 (当前分支移动) | 保持在当前 fix/... 分支,直接将 fix/... 上的新提交变基到 developer 之上。 👉 场景:开发中途,去拉取主干的最新代码并解决冲突。 | git rebase developer (此时人已在 fix/... 分支) |
| Merge 'developer' into 'fix/...' | 将选中分支合并到当前 | 传统的 Git 合并。将 developer 的代码合并进当前 fix/... 分支,通常会产生一个合并节点(Merge Commit)。 | git merge developer (此时人已在 fix/... 分支) |
| Compare with... | 与该分支对比 | 对比当前 fix/... 分支与选中的 developer 分支之间的代码和提交差异。 | git diff fix/.....developer |
| Show Diff with Working Tree | 与当前工作区对比 | 查看选中的 developer 分支与你当前本地工作区(未提交的修改)之间的差异。 | git diff developer |
| Update | 更新分支 | 从远程仓库拉取该分支的最新代码。若非当前分支,Git 只会在后台更新本地指针,不触发合并。 | git fetch origin developer:developer |
| Push... | 推送分支 | 将本地的 developer 分支提交推送到远程服务器(如 GitHub / GitLab)。 | git push origin developer |
| Rename... | 重命名分支 | 修改 developer 分支的名字。 | git branch -m developer <新名称> |
| Delete | 删除分支 | 删除本地的 developer 分支。如果包含未合并的代码,系统会报错拦截。 | git branch -d developer (强删使用 -D) |
重点说下这两个 Rebase(此时,“当前分支” = fix/...,“选中分支” = developer):
在 Git 术语中,Rebase A onto B 的意思是:“把 A 挪到 B 的后面”(A 的历史改变,B 保持不动)。因此:
-
Checkout and Rebase onto 'fix/...'(将“选中分支”变基到“当前分支”)
实际动作:Git 先帮你切换到 developer(选中分支),然后把 developer 上特有的新提交,切下来挪到 fix/...(当前分支)的最新提交后面。
谁的历史变了:developer 分支。 -
Rebase 'fix/...' onto 'developer'(将“当前分支”变基到“选中分支”)
实际动作:不切换分支,直接把当前所在的 fix/...(当前分支)上的新提交,切下来挪到 developer(选中分支)的最新提交后面。
谁的历史变了:fix/... 分支。
总结
如果只能记住一张图。
新功能开发:
feature/*
└────────→ dev
└────────→ master
同步主分支:
master ───────────────→ dev
线上修复:
master ───→ hotfix/* ───→ master ───→ dev
一句话总结:Feature负责开发;Dev负责集成测试; Master负责线上发布; Hotfix负责紧急救火
最常用的命令其实也就五个:
Checkout → 切换分支
New Branch → 创建分支
Compare → 查看差异
Rebase → 同步代码
Merge → 合并代码

浙公网安备 33010602011771号