Git 分支管理深度实战:从理解分支到企业分支策略

🌈 say-fall:个人主页 🚀 专栏:《手把手教你学会C++》 | 《系统深入Linux操作系统》 | 《数据结构与算法》 | 《小游戏与项目》 💪 格言:做好你自己,才能吸引更多人,与他们共赢,这才是最好的成长方式。
📝 前言
如果让你给 Git 的功能排个"惊艳榜",分支管理一定稳坐第一。
但不少新手一提"分支"就头大:HEAD 是什么?fast-forward 是啥?为什么合并会冲突?git stash 又是干啥的?
别慌。本篇就是来"治"这个的。我们从分支的本质 讲起,把分支的"前世今生"画成时间线,让你彻底看懂 HEAD、分支指针、合并提交到底是什么;然后手把手 教你创建、切换、合并、删除分支;再深入到合并冲突 的真实解决过程;最后讲 Bug 分支 、stash 暂存 、feature / release / hotfix 三大经典分支策略,以及企业级开发模型的引子(详细模型我们留到第 5 篇专题展开)。
🎯 本节目标 :让你彻底玩转 Git 分支管理。学完之后,你能像资深开发者一样用分支隔离功能开发、用 stash 保护未完成代码、用冲突解决流程搞定团队合并 ,并对企业级分支模型有清晰的整体认知。
通过本文,你将掌握:
| 技能 | 应用场景 |
|---|---|
| 从时间线角度理解分支本质 | 看懂 Git 内部指针机制 |
| 熟练创建/切换/合并/删除分支 | 日常开发必备 |
| 掌握 fast-forward 与 --no-ff 模式合并 | 写出清晰的提交历史 |
| 解决真实合并冲突 | 团队协作必踩的坑 |
| 用 git stash 暂存未完成工作 | 紧急修复 Bug 不慌 |
| 理解 feature / release / hotfix 三大分支策略 | 进阶团队协作 |
📌 前置知识: 熟练掌握第 1 篇的本地仓库操作(init、add、commit、log、reset、checkout)。本篇命令同样在
user@localhost虚拟环境下演示。
文章目录
- [Git 分支管理深度实战:从理解分支到企业分支策略](#Git 分支管理深度实战:从理解分支到企业分支策略)
-
- [📝 前言](#📝 前言)
- [一、🔍 分支是什么:从时间线讲起](#一、🔍 分支是什么:从时间线讲起)
-
- [1.1 一个生活中的类比](#1.1 一个生活中的类比)
- [1.2 Git 的分支本质](#1.2 Git 的分支本质)
- [1.3 创建分支后,时间线长这样](#1.3 创建分支后,时间线长这样)
- [1.4 HEAD 到底是什么](#1.4 HEAD 到底是什么)
- [1.5 为什么 Git 分支"几乎零成本"](#1.5 为什么 Git 分支"几乎零成本")
- [二、🌱 分支的创建与切换](#二、🌱 分支的创建与切换)
-
- [2.1 查看当前所有分支](#2.1 查看当前所有分支)
- [2.2 创建分支](#2.2 创建分支)
- [2.3 切换分支](#2.3 切换分支)
- [2.4 创建并切换(最常用)](#2.4 创建并切换(最常用))
- [2.5 在指定 commit 上创建分支](#2.5 在指定 commit 上创建分支)
- [2.6 删除分支](#2.6 删除分支)
- [2.7 实战演练](#2.7 实战演练)
- [三、🔀 分支合并:fast-forward 模式](#三、🔀 分支合并:fast-forward 模式)
-
- [3.1 什么是 fast-forward](#3.1 什么是 fast-forward)
- [3.2 实际例子](#3.2 实际例子)
- [3.3 什么时候不会 fast-forward](#3.3 什么时候不会 fast-forward)
- [四、🌳 --no-ff 模式合并:保留分支历史](#四、🌳 --no-ff 模式合并:保留分支历史)
-
- [4.1 为什么需要 --no-ff](#4.1 为什么需要 --no-ff)
- [4.2 怎么用 --no-ff](#4.2 怎么用 --no-ff)
- [4.3 什么时候用 --no-ff](#4.3 什么时候用 --no-ff)
- [4.4 查看分支图:`git log --graph`](#4.4 查看分支图:
git log --graph)
- [五、💥 合并冲突:原理与解决](#五、💥 合并冲突:原理与解决)
-
- [5.1 什么情况下会冲突](#5.1 什么情况下会冲突)
- [5.2 看冲突文件](#5.2 看冲突文件)
- [5.3 解决冲突](#5.3 解决冲突)
- [5.4 验证冲突解决完没](#5.4 验证冲突解决完没)
- [5.5 取消合并](#5.5 取消合并)
- [5.6 用工具解决冲突](#5.6 用工具解决冲突)
- [5.7 实战示例:完整的冲突解决流程](#5.7 实战示例:完整的冲突解决流程)
- [六、🐛 Bug 分支与 stash 暂存](#六、🐛 Bug 分支与 stash 暂存)
-
- [6.1 场景重现](#6.1 场景重现)
- [6.2 git stash 暂存](#6.2 git stash 暂存)
- [6.3 stash 常用命令](#6.3 stash 常用命令)
- [6.4 多个 stash 的管理](#6.4 多个 stash 的管理)
- [6.5 暂存未追踪的文件](#6.5 暂存未追踪的文件)
- [6.6 stash 实战工作流](#6.6 stash 实战工作流)
- [七、📋 分支管理策略:feature / release / hotfix](#七、📋 分支管理策略:feature / release / hotfix)
-
- [7.1 三大经典分支](#7.1 三大经典分支)
- [7.2 feature(功能分支)](#7.2 feature(功能分支))
- [7.3 release(预发布分支)](#7.3 release(预发布分支))
- [7.4 hotfix(紧急修复分支)](#7.4 hotfix(紧急修复分支))
- [7.5 分支命名规范](#7.5 分支命名规范)
- [八、🏢 企业级开发模型:引子](#八、🏢 企业级开发模型:引子)
- [九、📌 分支管理实操:完整工作流演示](#九、📌 分支管理实操:完整工作流演示)
- [十、❓ 本节常见问题解答](#十、❓ 本节常见问题解答)
-
- [1️⃣ 为什么有人的 Git 终端有颜色?](#1️⃣ 为什么有人的 Git 终端有颜色?)
- [2️⃣ 什么是 `--no-ff` 模式合并?](#2️⃣ 什么是
--no-ff模式合并?) - [3️⃣ `git stash pop` 多个 stash 会怎样?](#3️⃣
git stash pop多个 stash 会怎样?) - [4️⃣ `git stash` 和 `git stash push` 有什么区别?](#4️⃣
git stash和git stash push有什么区别?) - [5️⃣ 怎么判断合并冲突有没有解决完?](#5️⃣ 怎么判断合并冲突有没有解决完?)
- [十一、🎯 实战练习:分支管理"五连击"](#十一、🎯 实战练习:分支管理"五连击")
- [十二、📌 关键命令速查表](#十二、📌 关键命令速查表)
- [十三、🤔 几个思考题](#十三、🤔 几个思考题)
-
- [1️⃣ fast-forward 合并和 --no-ff 合并的本质区别是什么?](#1️⃣ fast-forward 合并和 --no-ff 合并的本质区别是什么?)
- [2️⃣ stash 后忘了 stash 列表里有什么,怎么办?](#2️⃣ stash 后忘了 stash 列表里有什么,怎么办?)
- [3️⃣ 删除分支前,怎么确认这个分支的工作已经合并了?](#3️⃣ 删除分支前,怎么确认这个分支的工作已经合并了?)
- [4️⃣ 合并冲突解决到一半,想放弃怎么办?](#4️⃣ 合并冲突解决到一半,想放弃怎么办?)
- [5️⃣ 怎么删除远程已不存在的远程跟踪分支?](#5️⃣ 怎么删除远程已不存在的远程跟踪分支?)
- 写在最后
一、🔍 分支是什么:从时间线讲起
要玩转分支,先得搞清楚分支到底是什么。
1.1 一个生活中的类比
想象你正在写一本书,主线版本 写到第 5 章。某天你想试试一个疯狂的想法------把第 3 章结尾反转到完全不同的方向。
最笨的办法:把整本书复制一份,在副本上改。改得不好,主线还在;改得好,再合并回主线。
Git 的"分支"就是这个副本机制 ------而且它是几乎零成本的。
1.2 Git 的分支本质
很多人以为 Git 分支是"复制整个项目目录"。错!
Git 的分支只是一个指向某个 commit 的可移动指针。
💡 Git 仓库的初始分支叫
master(现在很多项目改用main),它指向你最新一次的提交。每提交一次,master 就往前移动一步。
看图就懂:
时间线(master 一直往前走)
C0 ── C1 ── C2 ── C3 ← master 指向最新
↑
(HEAD)
- 每一个
C0、C1... 是一次提交 master是一个指针,永远指向当前分支的最新提交HEAD是一个特殊指针,指向"你当前所在的分支"
1.3 创建分支后,时间线长这样
当你在 C2 处创建了一个叫 dev 的分支:
时间线(master 和 dev 分叉)
master
↓
C0 ── C1 ── C2 ── C3
↓
dev
实际工作流通常是这样的:
master
↓
C0 ── C1 ── C2 ── C3 ── C5 ← master 继续往前走
\ ↑
\ (合并点)
C2'─ C4 ──────┘
↑
dev
- 在
C2处创建dev分支 dev分支独立提交了C4master分支独立提交了C5- 最后把
dev合并回master
1.4 HEAD 到底是什么
HEAD 是一个指针文件 ,记录在 .git/HEAD 里。它指向当前所在的分支。
bash
# 查看 HEAD 的指向
cat .git/HEAD
# 输出:ref: refs/heads/master
这表示:当前所在分支是 master,HEAD 实际指向的是 master 这个指针。
💡 切换分支时,HEAD 的指向也会改变。比如
git checkout dev后,.git/HEAD就会变成ref: refs/heads/dev。
1.5 为什么 Git 分支"几乎零成本"
别的版本控制系统(SVN、CVS)创建分支要把所有文件复制一份,耗时耗空间。
Git 呢?只创建一个新指针:
bash
# 在当前提交上创建一个叫 dev 的分支
git branch dev
# 实际就是创建了 .git/refs/heads/dev 文件
# 文件内容:当前 commit 的 SHA-1
cat .git/refs/heads/dev
# 输出:abc1234567890abcdef1234567890abcdef123456
创建完没有切换到 dev,要切换还需要 git checkout dev。
💡 创建分支用 41 字节(commit id 的 SHA-1 长度)就够了。这就是为什么 Git 在 Linux 这种千万行代码项目里也能毫秒级创建分支。
二、🌱 分支的创建与切换
理论讲够了,开始动手。
2.1 查看当前所有分支
bash
git branch
# 输出:
# * master
# 带 * 号的是当前所在分支
加 -a 看所有分支(包括远程分支):
bash
git branch -a
2.2 创建分支
bash
# 创建一个叫 dev 的分支(基于当前所在分支)
git branch dev
# 验证
git branch
# 输出:
# dev
# * master
# (dev 已存在,但 HEAD 还在 master)
2.3 切换分支
git checkout 是切换分支的老牌命令(Git 2.23 之前唯一方式):
bash
git checkout dev
# 验证
git branch
# 输出:
# * dev
# master
# (* 号跳到了 dev)
⚠️ 注意:切换分支前要保证当前分支的工作区是干净的(没有未提交的改动),否则 Git 会拒绝切换,或者把改动"带"到新分支。
2.4 创建并切换(最常用)
99% 的场景下,你要的是"创建新分支 + 立即切过去"。一条命令搞定:
bash
# 老语法
git checkout -b dev
# 新语法(Git 2.23+,更清晰)
git switch -c dev
输出:
Switched to a new branch 'dev'
💡
git switch是 Git 2.23 引入的新命令,专门做"切换"这件事(git checkout身兼多职容易混淆)。switch让命令语义更清晰。新项目建议用switch。
2.5 在指定 commit 上创建分支
bash
# 基于某个 commit id 创建分支
git branch <branch-name> <commit-id>
# 基于远程分支创建本地分支
git branch <local-branch> origin/<remote-branch>
2.6 删除分支
bash
# 删除已合并的分支(-d 是 --delete 的简写)
git branch -d dev
# 强制删除(即使没合并也删,慎用!)
git branch -D dev
⚠️
-D大写是强制删除,可能丢失未合并的提交。永远不要在你不熟悉的分支上用-D。
2.7 实战演练
bash
# 1. 看现在在哪
git branch
# * master
# 2. 创建并切到 dev
git checkout -b dev
# Switched to a new branch 'dev'
# 3. 在 dev 上做改动
echo "这是 dev 分支的修改" >> ReadMe
git add ReadMe
git commit -m "dev: 添加 dev 分支说明"
# 4. 切回 master
git checkout master
# Switched to branch 'master'
# 5. 看 ReadMe 的内容
cat ReadMe
# (dev 分支的修改不见了!因为它在 dev 分支上)
# 6. 把 dev 合并回 master
git merge dev
# 输出(fast-forward 模式):
# Updating abc1234..def5678
# Fast-forward
# ReadMe | 1 +
# 1 file changed, 1 insertion(+)
# 7. dev 分支可以删了
git branch -d dev
到这里你已经玩过分支的"创建 → 切换 → 提交 → 合并 → 删除"全流程。下一节我们深入看合并模式。
三、🔀 分支合并:fast-forward 模式
合并分支用 git merge 命令。但合并方式分两种:fast-forward(快进)和 --no-ff(非快进)。
3.1 什么是 fast-forward
当 master 分支在 dev 创建后没有任何新的提交 ,dev 分支顺着 master 走,合并时直接"快进"指针到 dev 最新 commit。不会产生新的合并提交。
时间线:
合并前:
master
↓
C0 ── C1 ── C2 ── C3
\
C2'─ C4
↑
dev
合并后(fast-forward):
master, dev
↓
C0 ── C1 ── C2 ── C3 ── C4
实际命令:
bash
# 在 master 上执行
git merge dev
# 输出:
# Updating abc1234..def5678
# Fast-forward
# ReadMe | 1 +
# 1 file changed, 1 insertion(+)
注意 "Fast-forward" 这个词------它告诉我们这次是直接移动 master 指针 到 C4,没有产生新 commit。
💡 fast-forward 是 Git 合并的"最优解"------历史是一条直线,没有多余的合并节点,干净利落。
3.2 实际例子
bash
# 准备工作
mkdir -p /home/user/test/merge-test
cd /home/user/test/merge-test
git init
echo "v1" > ReadMe
git add .
git commit -m "v1"
# 创建 dev 分支
git checkout -b dev
echo "v2 in dev" >> ReadMe
git add .
git commit -m "v2 in dev"
# 切回 master,合并
git checkout master
git merge dev
# Fast-forward
# ReadMe | 1 +
# 1 file changed, 1 insertion(+)
# 查看日志
git log --pretty=oneline
# def5678 (HEAD -> master, dev) v2 in dev
# cff9d1e v1
# 注意:master 和 dev 都指向同一个 commit
3.3 什么时候不会 fast-forward
当 master 在 dev 之后又有新提交时,无法 fast-forward。看图:
合并前:
C0 ── C1 ── C2 ── C3 ← master
\
C2'─ C4
↑
dev
这种结构没办法直接把 master 移到 C4(因为会丢掉 C3)。Git 会做三方合并 ,产生一个新的合并提交:
合并后(三方合并):
C0 ── C1 ── C2 ── C3 ── C5 (merge commit) ← master
\ /
C2'─ C4 ───────
↑
dev
💡 三方合并需要你写合并提交信息(Git 会弹出编辑器)。可以加
-m "xxx"直接写。
四、🌳 --no-ff 模式合并:保留分支历史
虽然 fast-forward 看起来很完美,但它有个小缺点 :分不清哪些提交是哪个分支做的。
4.1 为什么需要 --no-ff
看下面这个历史:
Fast-forward 后的历史(看不出来分叉过):
C0 ── C1 ── C2 ── C3 ── C4
我完全看不出"哪些提交是在 dev 分支上做的"。功能分支被"抹平"了。
而用 --no-ff:
--no-ff 合并后的历史(能看出分叉):
C0 ── C1 ── C2 ── C3 ── C5 (merge commit)
\ /
C2'─ C4 ───────
C4 是在 dev 分支上做的,C5 是合并提交------一目了然。
4.2 怎么用 --no-ff
bash
# 强制使用非 fast-forward 合并
git merge --no-ff -m "合并 dev 分支" dev
输出:
Merge made by the 'recursive' strategy.
ReadMe | 1 +
1 file changed, 1 insertion(+)
注意:这次是 "Merge made by the 'recursive' strategy",不是 Fast-forward ,产生了新的 merge commit。
4.3 什么时候用 --no-ff
| 场景 | 推荐模式 |
|---|---|
| 个人小项目 / 临时分支 | fast-forward(默认) |
| 团队协作的功能分支 | --no-ff(强烈推荐) |
| 上线分支 / 长期维护分支 | --no-ff |
| 简单的本地实验 | fast-forward |
💡 企业开发中,功能分支合并几乎都用 --no-ff。这样历史里能清晰看到"哪些功能是哪个分支做的",对后期维护和代码审查帮助极大。
4.4 查看分支图:git log --graph
看分支历史结构的最强工具:
bash
# 图形化显示
git log --graph --pretty=oneline --abbrev-commit
# 加上 --all 看所有分支
git log --graph --pretty=oneline --abbrev-commit --all
输出类似:
* c123456 (HEAD -> master) Merge branch 'dev'
|\
| * d789abc (dev) v2 in dev
|/
* a456789 v1
这个图清晰显示:master 和 dev 在 v1 后分叉,dev 提交了 v2,master 又合并了 dev。
💡 建议把这条命令设个别名:
bashgit config --global alias.lg "log --graph --pretty=oneline --abbrev-commit --all"以后直接
git lg就能看漂亮的分支图。
五、💥 合并冲突:原理与解决
合并最让人头疼的就是冲突(Conflict)。但理解了原理,解决起来其实很简单。
5.1 什么情况下会冲突
当两个分支修改了同一个文件的同一行,Git 没办法自动判断保留谁的内容,就会触发冲突。
举个例子:
bash
# 1. 在 master 上改 ReadMe
git checkout master
echo "hello master" > ReadMe
git add .
git commit -m "master: 改 ReadMe"
# 2. 切到 dev 改同一行
git checkout dev
echo "hello dev" > ReadMe
git add .
git commit -m "dev: 改 ReadMe"
# 3. 切回 master 合并 dev
git checkout master
git merge dev
# 输出:
# Auto-merging ReadMe
# CONFLICT (content): Merge conflict in ReadMe
# Automatic merge failed; fix conflicts and then commit the result.
出现 CONFLICT 就是冲突了。
5.2 看冲突文件
打开冲突文件(这里是 ReadMe),你会看到这样的"奇怪"内容:
<<<<<<< HEAD
hello master
=======
hello dev
>>>>>>> dev
这段标记的含义:
<<<<<<< HEAD:当前分支(master)的内容=======:分隔符>>>>>>> dev:要合并过来的分支(dev)的内容
5.3 解决冲突
三步走:
① 手动编辑文件
保留你想要的内容(或者两个都保留,或合并成新的内容):
# 编辑后:
hello master and dev
② 标记解决冲突
bash
git add ReadMe
③ 完成合并
bash
git commit -m "解决 ReadMe 合并冲突"
💡
git add不是"添加文件",在合并场景下它的意思是"我已解决冲突,标记这个文件为已解决"。
5.4 验证冲突解决完没
git status 会告诉你还有没有未解决的冲突:
bash
git status
# 输出(未解决):
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# both modified: ReadMe
#
# 输出(已解决):
# Changes to be committed:
# modified: ReadMe
看到 "Unmerged paths" 就是没解决完。
5.5 取消合并
如果冲突搞砸了想放弃合并:
bash
git merge --abort
这条命令会回退到合并前的状态,相当于"撤销合并"。
5.6 用工具解决冲突
手动改文件太累?很多 IDE/编辑器有冲突解决工具:
- VS Code:内置冲突解决器,左边选 ours / theirs,右边实时预览
- IntelliJ IDEA:三栏布局(base / ours / theirs)
- Beyond Compare:专业的 diff 工具
- Meld:免费跨平台 diff 工具
💡 团队规模大了,强烈建议团队成员用同一种冲突解决工具,避免风格不一致。
5.7 实战示例:完整的冲突解决流程
bash
# 假设冲突发生
git merge dev
# CONFLICT 提示
# 1. 看哪些文件冲突
git status
# Unmerged paths: ReadMe
# 2. 编辑 ReadMe(保留两个版本)
vim ReadMe
# 改成你想要的内容,删除 <<<<<<< ======= >>>>>>> 标记
# 3. 标记解决
git add ReadMe
# 4. 查状态(应该没有 Unmerged paths 了)
git status
# Changes to be committed: ReadMe
# 5. 完成合并
git commit -m "merge dev: 解决 ReadMe 冲突"
# 6. 验证
git log --graph --pretty=oneline --abbrev-commit
# * c123456 (HEAD -> master) merge dev: 解决 ReadMe 冲突
# |\
# | * d789abc (dev) dev: 改 ReadMe
# |/
# * a456789 master: 改 ReadMe
完美!冲突解决后,分支历史清晰可读。
六、🐛 Bug 分支与 stash 暂存
实际开发中,正在写一个功能写到一半,突然发现线上有 Bug 要立刻修------这种场景太常见了。怎么办?
6.1 场景重现
bash
# 1. 你正在 dev 分支开发一个"用户登录"功能,写到一半
git checkout -b dev
echo "登录功能代码" > login.py
git add login.py
git commit -m "feat: 登录功能 - 1"
echo "登录功能代码 - 2" >> login.py
# (注意:这里没 commit,因为还没写完!)
现在老板突然说:线上付款功能有 Bug,立刻修!
你不能:
- 直接
git checkout master切分支(会带着未提交的文件) git commit把没写完的代码提交了(污染历史)
正确做法:用 git stash 暂存起来。
6.2 git stash 暂存
bash
# 把当前工作区和暂存区的改动"打包"起来
git stash
# 输出:
# Saved working directory and index state WIP on dev: abc1234 feat: 登录功能 - 1
💡
stash本质是把改动保存到一个栈结构里,工作区瞬间变成"上次 commit 的干净状态"。
现在你可以放心切换分支修 Bug 了:
bash
# 2. 切到主分支修 Bug
git checkout master
# 3. 创建 bugfix 分支
git checkout -b bugfix-001
# 4. 修复 Bug 并提交
echo "付款 Bug 修复" > pay.py
git add pay.py
git commit -m "fix: 修复付款 Bug"
# 5. 切回 master 合并 bugfix
git checkout master
git merge --no-ff -m "merge bugfix-001" bugfix-001
# 6. 删除 bugfix 分支
git branch -d bugfix-001
修完 Bug,回到 dev 分支继续写你的登录功能:
bash
# 7. 切回 dev 分支
git checkout dev
# 8. 把暂存的改动"恢复"出来
git stash pop
💡
git stash pop会把最新的 stash 恢复 到工作区,并从 stash 栈里删除。
6.3 stash 常用命令
bash
# 暂存当前改动(不带消息)
git stash
# 暂存并加消息(推荐,方便查找)
git stash push -m "修复登录功能写到一半"
# 查看所有 stash
git stash list
# 恢复最新 stash(不删除 stash)
git stash apply
# 恢复最新 stash(删除 stash,等价于 apply + drop)
git stash pop
# 恢复指定 stash
git stash apply stash@{0} # 恢复第 1 个
git stash apply stash@{1} # 恢复第 2 个
# 删除指定 stash
git stash drop stash@{0}
# 清空所有 stash
git stash clear
6.4 多个 stash 的管理
bash
git stash push -m "改了一半的登录功能"
git stash push -m "性能优化草稿"
git stash push -m "UI 重构中"
git stash list
# stash@{0}: On dev: UI 重构中
# stash@{1}: On dev: 性能优化草稿
# stash@{2}: On dev: 改了一半的登录功能
stash 是个栈(LIFO:后进先出):
bash
git stash pop # 恢复 stash@{0}(UI 重构中)
git stash pop # 恢复 stash@{1}(性能优化草稿)
git stash pop # 恢复 stash@{2}(改了一半的登录功能)
6.5 暂存未追踪的文件
默认 git stash 不暂存未追踪的文件(新创建但没 add 的):
bash
echo "新文件" > new.txt # 创建新文件,没 add
git stash
# 暂存的是已追踪文件的改动,new.txt 不会暂存
想暂存未追踪文件,加 -u:
bash
git stash -u
# 或
git stash push -u -m "包含新文件"
6.6 stash 实战工作流
bash
# 1. 当前在 dev 分支,工作区有未提交的改动
git status
# modified: login.py
# 2. 老板紧急派活,先把当前改动暂存
git stash push -m "登录功能 - 未完成"
# 3. 切到 hotfix 分支修紧急 Bug
git checkout master
git checkout -b hotfix
# ... 修 Bug、提交、合并、删除分支 ...
# 4. 回到 dev 分支
git checkout dev
# 5. 恢复之前的改动
git stash pop
💡 这是企业里每天都在发生的工作流。
git stash就是你的"工作区保险箱"。
七、📋 分支管理策略:feature / release / hotfix
前面我们学的是"操作",现在讲"策略"------一个团队应该怎么用分支。
7.1 三大经典分支
| 分支类型 | 作用 | 来源 | 归宿 | 生命周期 |
|---|---|---|---|---|
| feature | 开发新功能 | develop | develop | 功能完成即删 |
| release | 发布前的稳定分支 | develop | master + develop | 发布完即删 |
| hotfix | 紧急修复线上 Bug | master | master + develop | 修完即删 |
7.2 feature(功能分支)
场景:开发一个新功能。
bash
# 从 develop 创建 feature 分支
git checkout develop
git checkout -b feature/user-login
# 开发...
git add .
git commit -m "feat: 用户登录"
# 完成后合并回 develop
git checkout develop
git merge --no-ff -m "merge feature/user-login" feature/user-login
# 删除 feature 分支
git branch -d feature/user-login
💡 feature 分支通常一个人用(避免冲突),完成后合并回 develop。
7.3 release(预发布分支)
场景:要发版了,做最后的测试和 Bug 修复。
bash
# 从 develop 创建 release 分支
git checkout develop
git checkout -b release/v1.0.0
# 测试 + 修小 Bug
git commit -m "fix: 修复测试发现的 Bug"
# 测试通过,发布到 master
git checkout master
git merge --no-ff -m "release v1.0.0" release/v1.0.0
git tag v1.0.0 # 打 tag 标记版本
# 同步回 develop
git checkout develop
git merge --no-ff -m "merge release/v1.0.0 back to develop" release/v1.0.0
# 删除 release 分支
git branch -d release/v1.0.0
💡 为什么要同步回 develop?因为 release 分支可能修了 Bug,develop 也要拿到这些修复。
7.4 hotfix(紧急修复分支)
场景:线上版本出问题了,要立刻修。
bash
# 从 master 创建 hotfix 分支
git checkout master
git checkout -b hotfix/pay-bug
# 修复 Bug
git add .
git commit -m "fix: 紧急修复付款 Bug"
# 修复完成,合并到 master
git checkout master
git merge --no-ff -m "hotfix pay-bug" hotfix/pay-bug
git tag v1.0.1 # 升级版本号
# 同步到 develop
git checkout develop
git merge --no-ff -m "merge hotfix/pay-bug" hotfix/pay-bug
# 删除 hotfix 分支
git branch -d hotfix/pay-bug
💡 hotfix 只修紧急的、能快速解决的 Bug。复杂问题应该走 feature 分支完整修复。
7.5 分支命名规范
好命名 = 好维护:
| 类型 | 命名示例 |
|---|---|
| 功能分支 | feature/user-login、feature/shopping-cart |
| 预发布分支 | release/v1.0.0、release/2024-q3 |
| 紧急修复 | hotfix/pay-bug、hotfix/crash-2024-09 |
| 个人开发 | dev/<your-name>/xxx |
八、🏢 企业级开发模型:引子
你以为 feature / release / hotfix 就是全部了?不,这是经典 Git Flow 模型的核心。
一个完整的企业级开发模型远不止这三个分支。它涉及:
- 长期分支 vs 短期分支的搭配
- 环境对应(开发 / 测试 / 预发布 / 正式)
- 角色分工(开发 / 测试 / 运维 / 技术经理)
- 发版节奏(每周 / 每月 / 紧急)
- 回滚机制(出问题了怎么秒级回滚)
🎯 本节限于篇幅 只讲 feature / release / hotfix 三大基础策略的单独使用 。完整的企业级开发模型 (Git Flow / GitHub Flow / GitLab Flow)将在第 5 篇《企业级 Git 开发模型:从小作坊到工业级规范》 里专题展开。
到那里我们会看到:
- 一个 10 人团队怎么用 5 种分支协作
- 不同公司为什么选不同的模型
- 大厂的真实发版流程长什么样
- 怎么把 Git 用成"生产线"而不是"备份工具"
💡 学完第 5 篇,你才真正具备"上手真实企业项目"的 Git 能力。第 1-3 篇是基础,第 4 篇讲多人协作,第 5 篇是把所有知识串成完整的工程实践。
九、📌 分支管理实操:完整工作流演示
把这一篇的所有知识点串起来,做一个完整的"个人开发工作流"演示。
任务:在 master 上开发"登录功能",用 dev 隔离,用 stash 应对紧急情况。
bash
# 1. 准备工作
mkdir -p /home/user/test/branch-workflow
cd /home/user/test/branch-workflow
git init
echo "v1" > app.py
git add .
git commit -m "init: 项目初始化"
# 2. 创建并切到 dev 分支
git checkout -b dev
# 3. 在 dev 上开发登录功能
echo "def login(): pass" > login.py
git add login.py
git commit -m "feat(login): 添加登录函数"
# 4. 突然老板让修付款 Bug
# 把当前未提交的改动暂存(假设我们正在改 login.py)
echo "def login_v2(): pass" >> login.py
git stash push -m "login v2 写到一半"
# 5. 切到 master 修付款 Bug
git checkout master
git checkout -b hotfix/pay-bug
echo "def pay(): fix" > pay.py
git add pay.py
git commit -m "hotfix(pay): 修复付款 Bug"
# 6. 合并回 master 并打 tag
git checkout master
git merge --no-ff -m "merge hotfix/pay-bug" hotfix/pay-bug
git tag v1.0.1
git branch -d hotfix/pay-bug
# 7. 回到 dev 继续开发
git checkout dev
git stash pop # 恢复 login v2 的改动
# 8. 完成登录功能
git add login.py
git commit -m "feat(login): 完善登录功能"
# 9. 合并到 master
git checkout master
git merge --no-ff -m "merge dev" dev
git tag v1.1.0
# 10. 删除 dev 分支
git branch -d dev
# 11. 看完整历史
git log --graph --pretty=oneline --abbrev-commit --all
# * d789abc (HEAD -> master, tag: v1.1.0) merge dev
# |\
# | * c456def (tag: v1.0.1) hotfix(pay): 修复付款 Bug
# |/
# * b123456 init: 项目初始化
跑一遍这个流程,你对分支的"创建 → 切换 → 暂存 → 合并 → 删除 → 标签"就有完整感觉了。
十、❓ 本节常见问题解答
挑了 5 个跟分支管理最相关的问题,逐一解答。
1️⃣ 为什么有人的 Git 终端有颜色?
答:配置颜色显示。
Git 默认在某些终端会显示颜色帮助区分。要开启/关闭:
bash
# 开启颜色(推荐)
git config --global color.ui auto
# 关闭颜色
git config --global color.ui false
auto 模式:输出到终端时显示颜色,输出到文件或管道时不显示,最智能。
💡 颜色不是"花里胡哨",是减少误操作的关键 。比如红色的
deleted比黑白文字警告更醒目。
2️⃣ 什么是 --no-ff 模式合并?
答:保留分支历史的合并方式。
git merge --no-ff 即使能 fast-forward,也强制创建合并提交,让历史能看出"哪些提交是哪个分支做的"。
bash
# 不管能不能 fast-forward,都产生 merge commit
git merge --no-ff -m "merge feature/login" feature/login
💡 团队开发的功能分支强烈建议用
--no-ff。这样 PR 评审、回溯功能、查 Bug 都很方便。
3️⃣ git stash pop 多个 stash 会怎样?
答:按 LIFO(后进先出)顺序恢复。
git stash 把当前工作区"打包"暂存起来,git stash pop 把最新的 stash 恢复出来。多个 stash 会按栈结构组织:
bash
git stash # 暂存当前
git stash # 暂存第二个
git stash list # 查看所有 stash
git stash pop # 恢复最新(即第二个)
git stash pop # 恢复最早(即第一个)
💡 stash 不是长期存储方案 。stash 默认可能 30 天后被清理。重要的代码一定要 commit 提交,不能依赖 stash。
4️⃣ git stash 和 git stash push 有什么区别?
答:几乎没有区别。
git stash 是 git stash push 的简写(Git 2.13+ 引入)。git stash push 更明确,支持更多参数:
bash
# 等价
git stash
git stash push
# 推 stash 时附加信息
git stash push -m "修复登录bug"
💡 推荐用
git stash push -m "说明",给自己和别人留个上下文。
5️⃣ 怎么判断合并冲突有没有解决完?
答:用 git status 看是否有 unmerged 标记。
冲突时 git status 会显示:
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: file.txt
解决完冲突后 git add 标记解决,再 git status 就看不到 Unmerged paths 了。
💡 解决冲突后一定要仔细看 diff!两个分支的修改可能都"看起来对",但合并后逻辑可能错。提交前用 IDE 走读一遍。
十一、🎯 实战练习:分支管理"五连击"
来练 5 个连续动作,体验真实开发场景。
任务:
- 创建一个叫
git-branch-practice的仓库,提交一个app.py(内容任意) - 创建
feature/pay分支,加一个pay.py函数并提交 - 切回 master,创建
feature/login分支 - 在
feature/login改app.py(加一行 import),切回 master 也改app.py(加一行注释) - 把
feature/login合并到 master,解决冲突 ,合并feature/pay验证 fast-forward - 删除所有临时分支,用
git log --graph看历史
参考答案:
bash
# 1. 初始化
mkdir -p /home/user/test/git-branch-practice
cd /home/user/test/git-branch-practice
git init
echo "print('hello')" > app.py
git add .
git commit -m "init: app.py"
# 2. feature/pay
git checkout -b feature/pay
echo "def pay(): pass" > pay.py
git add pay.py
git commit -m "feat(pay): 添加支付函数"
# 3. feature/login
git checkout master
git checkout -b feature/login
echo "import os" >> app.py
git add app.py
git commit -m "feat(login): 添加 os 导入"
# 4. 回到 master 也改 app.py
git checkout master
echo "# 入口文件" >> app.py
git add app.py
git commit -m "docs: 添加 app.py 注释"
# 5. 合并 feature/login(会有冲突)
git merge feature/login
# CONFLICT 提示
# 编辑 app.py 解决冲突(保留两边的修改):
# print('hello')
# # 入口文件
# import os
git add app.py
git commit -m "merge feature/login: 解决冲突"
# 6. 合并 feature/pay(fast-forward)
git merge feature/pay
# Fast-forward
# pay.py | 1 +
# 1 file changed, 1 insertion(+)
# 7. 删除分支
git branch -d feature/login
git branch -d feature/pay
# 8. 看历史
git log --graph --pretty=oneline --abbrev-commit --all
跑完这五步,你对分支管理就有"肌肉记忆"了。
十二、📌 关键命令速查表
| 命令 | 作用 |
|---|---|
git branch |
查看本地分支 |
git branch -a |
查看所有分支(含远程) |
git branch <name> |
创建分支 |
git branch -d <name> |
删除已合并的分支 |
git branch -D <name> |
强制删除分支 |
git checkout <name> |
切换分支(老语法) |
git checkout -b <name> |
创建并切换分支 |
git switch <name> |
切换分支(新语法) |
git switch -c <name> |
创建并切换分支 |
git merge <name> |
合并指定分支到当前分支 |
git merge --no-ff <name> |
非 fast-forward 合并 |
git merge --abort |
取消合并 |
git stash |
暂存当前改动 |
git stash push -m "..." |
暂存并加消息 |
git stash list |
查看所有 stash |
git stash pop |
恢复并删除最新 stash |
git stash apply |
恢复但不删除 |
git stash drop |
删除指定 stash |
git tag <name> |
打 tag |
git log --graph --all |
图形化看分支历史 |
十三、🤔 几个思考题
学完本文,来试试回答这些问题:
1️⃣ fast-forward 合并和 --no-ff 合并的本质区别是什么?
答:是否产生新的 merge commit。
- fast-forward :当被合并的分支是当前分支的直接祖先时,Git 直接把当前分支指针移动到被合并分支的最新 commit,不产生新 commit。
- --no-ff :强制产生一个新的 merge commit,把两条分支的历史连接起来。
选择建议:功能分支合并用 --no-ff (保留分支历史),简单同步用 fast-forward(保持历史干净)。
💡 看
git log --graph时,fast-forward 是一条直线,--no-ff 是一个"分叉再合并"的 Y 字形。
2️⃣ stash 后忘了 stash 列表里有什么,怎么办?
答:用 git stash list + git stash show。
bash
# 1. 看所有 stash
git stash list
# stash@{0}: On dev: 改了一半的登录功能
# stash@{1}: On dev: 性能优化草稿
# 2. 看某个 stash 的内容(默认只看文件改动概览)
git stash show stash@{0}
# 3. 看完整 diff
git stash show -p stash@{0}
如果 stash 内容太多忘了是什么,用 git stash show -p 看 diff 。看到不认识的代码,别乱 pop,先开新分支验证。
💡 stash 不是 commit,没有 message 也能存 。养成
git stash push -m "说明"的习惯,一个月后看也认得。
3️⃣ 删除分支前,怎么确认这个分支的工作已经合并了?
答:先看分支列表,再看 merged 列表。
bash
# 1. 看所有分支
git branch -a
# 2. 看哪些分支已合并到当前分支
git branch --merged
# 3. 看哪些分支**未**合并
git branch --no-merged
--no-merged 列出的分支千万别 -d 删 ,要用 -D 强制删(且必须确认分支上的工作不要了)。
💡 长期不清理的"僵尸分支"会让
git branch列表很乱。定期(比如每两周)review 一次 merged / no-merged 列表。
4️⃣ 合并冲突解决到一半,想放弃怎么办?
答:git merge --abort(合并中)或 git rebase --abort(rebase 中)。
bash
# 合并冲突中放弃合并
git merge --abort
# rebase 冲突中放弃
git rebase --abort
执行后,工作区会恢复到合并/rebase 之前的状态,相当于"撤销这次操作"。
💡 比
--abort更狠的是git reset --hard HEAD,但那会丢失所有未提交的改动 。--abort是更安全的选择。
5️⃣ 怎么删除远程已不存在的远程跟踪分支?
答:git remote prune origin。
bash
# 删除远程已删除但本地还残留的远程跟踪分支
git remote prune origin
# 或者用 fetch 时自动清理
git fetch --prune origin
# 简写
git fetch -p
这条命令会同步远程状态,把本地那些"远程已经删了但本地还留着"的远程跟踪分支清理掉。
💡 远程分支积累多了
git branch -a会很难看。每个工作日开始前 fetch -p 一下是好习惯。
写在最后
到这里,《Git 分支管理深度实战》就告一段落了 🎉
我们从分支的本质 (可移动指针)讲起,到创建/切换/合并/删除分支的实操,深入了 fast-forward 与 --no-ff 两种合并模式 ,再到合并冲突的真实解决过程 ,最后讲了 stash 暂存 和 feature / release / hotfix 三大经典分支策略。
📝 下一篇我们要讲远程操作。你会学到:怎么把本地仓库推到 Gitee/GitHub、怎么从远程拉取、远程跟踪分支是什么、怎么解决推送冲突。学完之后,你就能真正和团队"异地协作"了。
分支是 Git 最强大的特性,也是面试必考、工作必用的核心技能。多练、多合并、多冲突,是练出真功夫的唯一方法。
我们下一篇见!
✅ 本节完...
📝 作者:say-fall | 编辑:say-fall | 🌟 原创不易,如果对你有帮助,记得 👍 点赞 + ⭐ 收藏 哦!