编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 业务思考

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

    • README
    • 版本控制的诞生:从一场灾难说起
    • 单人工作流:在 Git 的时光机里自由穿梭
    • 分支:Git 的灵魂——像开平行宇宙一样开发
      • 一、两线作战
      • 二、分支本质
        • 2.1 哲学问题
        • 2.2 四十字节指针
        • 2.3 时间岔路
      • 三、日常操作
        • 3.1 创建切换
        • 3.2 切换须知
        • 3.3 删除分支
        • 3.4 命名惯例
      • 四、合并分支
        • 先准备实验环境
        • 4.1 快进合并
        • 4.2 三方合并
        • 4.3 推荐操作
      • 五、合并冲突
        • 5.1 冲突原因
        • 5.2 制造冲突
        • 5.3 冲突模样
        • 5.4 解决策略
        • 5.5 完成合并
        • 5.6 放弃合并
      • 六、变基操作
        • 6.1 历史混乱
        • 6.2 工作原理
        • 6.3 黄金法则
      • 七、交互变基
        • 7.1 整理分支
        • 7.2 四种操作
        • 7.3 操作速查表
      • 八、终极对比
      • 九、综合实战
        • 场景设定
        • 实战开始
        • 时间线回顾
      • 十、本章回顾
        • 📎 本章涉及的命令速查
    • 远程协作:把你的代码推到全世界
    • Git特种作战:stash、cherry-pick、bisect三件套
    • 团队工作流实战:从一个人能打到一队人能战
    • Git故障排除:遇到报错不再慌的急诊手册
    • Git 场景速查地图:遇到问题对号入座
    • 常见操作实践:从理论到实战的最后一步
  • 技术模版

  • 技术规范

  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 八股文

  • 技术规划

  • 技术
  • Git应用
杨充
2025-06-06
目录

分支:Git 的灵魂——像开平行宇宙一样开发

# 第3章 · 分支本质

# 一、两线作战

下午 3 点,小李正在做一个「用户积分系统」。他把代码改得满目疮痍——user.js 改了 200 行,points.js 是全新的文件,数据库表结构也重构了一半。还没 commit。现在整个项目处于一种「拆了一半、跑不起来」的状态。

突然,Leader 在群里 @ 他:

"线上登录崩了,所有用户都登不进去。紧急!你 15 分钟内搞定。"

小李愣住了。他的大脑飞速运转:

  1. 先把现在的改动 commit 了? → 不行,代码跑不起来,commit 一个 broken commit 太丢人。
  2. 把现在的改动全丢掉? → 不行,改了一下午,丢了等于白干。
  3. 把现在的项目复制一份? → 两个目录来回切?上次这么干,复制了 3GB 的 node_modules,硬盘差点冒烟。

他卡住了。手里正在做的功能不能丢,线上 bug 必须要修。两条时间线在同一个工作区里打架。

这不是小李一个人的困境。 任何一个程序员,只要同时做两件事,都会遇到这个问题。好消息是:Git 从设计的第一天起,就把这个问题变成了它的核心卖点。


# 二、分支本质

# 2.1 哲学问题

你正在读一本书的第 100 页。现在你想做一个实验:

「如果第 50 页的主角做了不同的选择,后面的故事会怎么发展?」

你怎么办?你不会撕了后半本重新写。你会在第 50 页夹一个书签,然后拿一本空白的笔记本,从第 50 页开始「写自己的版本」。想回到原故事线?把笔记本合上,翻到第 100 页继续读。

这个书签,就是 Git 里的分支。 它只是一个「你现在读到哪一页了」的标记。

# 2.2 四十字节指针

打开终端,亲眼看看:

mkdir branch-lab && cd branch-lab
git init

# 创建第一个 commit
echo "第1章" > story.txt
git add . && git commit -m "第1章"

echo "第2章" >> story.txt
git add . && git commit -m "第2章"

# 分支到底是什么?
cat .git/refs/heads/master
# 输出类似:8f3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a
#           ↑ 就是一个 40 字符的 hash!

这就是分支的全部内容:一个写着 commit hash 的文件。 创建分支只是写一个 40 字符的文件——毫秒级。这也是为什么 Git 的分支既快又廉价。

.git/refs/heads/
  ├── master   →  "8f3a2b1c4d5e..."
  └── dev      →  "7e2d1c0b9a8f..."

现在你理解了第 1 章那句话:「分支只是一个可以移动的指针。」

# 2.3 时间岔路

# 当前历史:
#   a1b2c3d 第2章  ← master 指向这里
#   b1c2d3e 第1章

# 创建新分支:就像在第1章和第2章之间放了一个分岔路口
git branch feature/魔法世界

# 看看分支列表
git branch
# 输出:
#   feature/魔法世界
# * master              ← * 表示你当前在哪个分支

# 切换到新分支
git switch feature/魔法世界
# 或 git checkout feature/魔法世界(旧版命令)

git log --oneline
#   a1b2c3d 第2章  ← 两个分支目前指向同一个 commit
#   b1c2d3e 第1章

# 在新分支上写内容
echo "第3章(魔法世界线):主角获得了魔法" >> story.txt
git add . && git commit -m "第3章:发现魔法"

# 切回 master
git switch master

# 在 master 上写完全不同的内容
echo "第3章(现实世界线):主角考上了大学" >> story.txt
git add . && git commit -m "第3章:考上大学"

# 现在看全貌
git log --oneline --graph --all

输出:

* ccccccc 第3章:考上大学                      ← master
| * ddddddd 第3章:发现魔法                     ← feature/魔法世界
|/
* a1b2c3d 第2章                                 ← 分岔点
* b1c2d3e 第1章

同一天下午 3 点,你的项目分裂成了两个平行宇宙。 一个世界里主角考上了大学,另一个世界里主角成为了魔法师。而这一切,只花了几个 40 字节的指针。


# 三、日常操作

# 3.1 创建切换

git switch -c feature/用户积分    # 创建 + 切换(推荐)
# 等价旧命令:git checkout -b feature/用户积分

# 3.2 切换须知

# 切换前,工作区必须是干净的(或者用 stash,第 6 章讲)
git status
# 如果有未 commit 的修改,Git 会阻止切换

# 查看所有分支(带最新 commit)
git branch -v

# 查看哪些分支已经合并到当前分支
git branch --merged
# 这些可以安全删除

# 查看尚未合并的分支
git branch --no-merged
# 删之前想一想

# 3.3 删除分支

git branch -d feature/魔法世界    # 安全删除(已合并的)
git branch -D feature/魔法世界    # 强制删除(没合并也删)

# 3.4 命名惯例

好的文件名让人一眼看懂,好的分支名也一样:

feature/用户积分          # 新功能
hotfix/登录崩溃           # 紧急修复
release/v1.2.0           # 发布准备
bugfix/分页失效           # bug 修复
experiment/新算法测试     # 实验性分支

# 四、合并分支

分支分开了,总得合回来。Git 合并有两种截然不同的方式。

# 先准备实验环境

mkdir merge-lab && cd merge-lab && git init

echo "const app = (() => {" > app.js
echo "  return { name: 'My App', version: '1.0' };" >> app.js
echo "})();" >> app.js
git add . && git commit -m "init: 应用骨架"

# 从 master 开出 feature 分支
git switch -c feature/添加问候

echo "function greet() { return 'Hello!'; }" >> app.js
git add . && git commit -m "feat: 添加问候函数"

# 切回 master,做点别的
git switch master
echo "function version() { return '1.0'; }" >> app.js
git add . && git commit -m "feat: 添加版本函数"

现在的历史:

* eeeeeee feat: 添加版本函数          ← master
| * fffffff feat: 添加问候函数         ← feature/添加问候
|/
* aaaaaaa init: 应用骨架

# 4.1 快进合并

当 master 没有自己的新 commit 时(即 master 直接是 feature 的祖先),Git 只需要把 master 指针「快进」到 feature 的位置。

# 先模拟一个"纯净"场景:master 没有新提交,feature 有
git switch master
git reset --hard aaaaaaa   # 退回到 init

git switch -c feature/简单功能
echo "const DEBUG = true;" >> app.js
git add . && git commit -m "feat: 添加调试开关"

git switch master
git merge feature/简单功能

# 输出:Fast-forward
# 没有新的 merge commit!master 直接移到了 feature 的位置

Fast-forward 的结果:历史是一条直线,没有合并痕迹。 干净,但你看不出「这里曾经有个分支」。

# 4.2 三方合并

回到之前的场景——master 和 feature 各自有独立的提交:

git switch master
git merge feature/添加问候

这次 Git 会弹出一个编辑器让你写 merge commit message。因为它不能简单地「快进」——master 和 feature 分叉后各自前行了,Git 需要把两个分支的改动综合起来。

git log --oneline --graph

# 输出:
# *   ggggggg Merge branch 'feature/添加问候'   ← 新的合并 commit
# |\
# | * fffffff feat: 添加问候函数
# * | eeeeeee feat: 添加版本函数
# |/
# * aaaaaaa init: 应用骨架

这个结果比 Fast-forward 多了一个「合并提交」。它完整记录了:branch 什么时候创建的、在它上面做了什么、什么时候合回来的。

# 4.3 推荐操作

git merge --no-ff feature/简单功能

即使可以快进,也强制生成一个 merge commit。好处:以后看历史,一眼就知道「这里曾经有个 feature 分支」。团队协作强烈推荐。


# 五、合并冲突

# 5.1 冲突原因

冲突的本质很简单:Git 很聪明,但没那么聪明。

  • ✅ Git 能自动合并:两个分支改了不同的文件,或同一文件的不同行。
  • ❌ Git 不能自动合并:两个分支改了同一文件的同一行。Git 不知道该保留谁的,只能举手投降:"你来决定。"

# 5.2 制造冲突

mkdir conflict-lab && cd conflict-lab && git init

echo "let config = {" > config.js
echo "  theme: 'light'," >> config.js
echo "  lang: 'zh'" >> config.js
echo "};" >> config.js
git add . && git commit -m "init: 基础配置"

# 创建两个分支,各自改同一行
git switch -c feature/暗黑模式
# 把 theme 改成 dark
sed -i '' "s/theme: 'light'/theme: 'dark'/" config.js
git add . && git commit -m "feat: 默认暗黑模式"

git switch master
# master 上把 theme 改成 auto
sed -i '' "s/theme: 'light'/theme: 'auto'/" config.js
git add . && git commit -m "feat: 自适应主题"

现在试试合并:

git merge feature/暗黑模式

输出:

Auto-merging config.js
CONFLICT (content): Merge conflict in config.js
Automatic merge failed; fix conflicts and then commit the result.

别慌! 这不是错误,这只是 Git 在说「你来决定」。

# 5.3 冲突模样

cat config.js
let config = {
<<<<<<< HEAD
  theme: 'auto',          ← master 的版本(HEAD 是你当前所在分支)
=======
  theme: 'dark',          ← feature/暗黑模式 的版本
>>>>>>> feature/暗黑模式
  lang: 'zh'
};

解读:

  • <<<<<<< HEAD 到 ======= 之间:当前分支(master)的内容
  • ======= 到 >>>>>>> feature/暗黑模式 之间:被合并分支的内容
  • 你的任务:把这段混乱的标记,改成一个"综合了两边优点"的最终版本。

# 5.4 解决策略

策略 A:保留 master 的

# 手动编辑 config.js,删掉冲突标记,只保留 theme: 'auto'

编辑 config.js:

let config = {
  theme: 'auto',
  lang: 'zh'
};

策略 B:保留 feature 的

let config = {
  theme: 'dark',
  lang: 'zh'
};

策略 C:融合两边的(最常用的真实场景)

let config = {
  theme: 'auto',       // 默认自动检测,但我们给暗黑模式选项
  darkMode: true,      // 新增暗黑模式开关
  lang: 'zh'
};

# 5.5 完成合并

# 解决完冲突后
git add config.js           # 标记「冲突已解决」
git status
# 输出:
# All conflicts fixed but you are still merging.
#   (use "git commit" to conclude merge)

git commit -m "merge: 合并暗黑模式,保留自适应主题并添加暗黑开关"
# 冲突解决完成!

# 5.6 放弃合并

git merge --abort
# 一键回到合并前的干净状态。毫无痕迹,就像没操作过。

💡 心态转变:不要怕冲突。冲突不是 Git 的 bug,是 Git 的诚实——它在告诉你「这件事需要人工判断」。害怕冲突 → 不分支 → 所有代码混在一条线上 → 更混乱。拥抱冲突 → 频繁分支 → 每次合并都小且可控。


# 六、变基操作

# 6.1 历史混乱

想象你维护一个功能分支两周,每天都有别人往 master 合新代码:

*   merge #14
|\
| * feature commit #3
* | master commit #14
| * feature commit #2
* | master commit #13
| * feature commit #1
|/

三个月后,没人能看懂这张蜘蛛网。rebase 的使命就是把这个蜘蛛网捋成一条直线。

# 6.2 工作原理

rebase 的本质:把你分支上的 commit "搬"到目标分支的最新 commit 之后,然后一个个重新应用。

mkdir rebase-lab && cd rebase-lab && git init

echo "第一版" > code.txt
git add . && git commit -m "v1"

git switch -c feature

echo "功能A" >> code.txt
git add . && git commit -m "feat: A"

echo "功能B" >> code.txt
git add . && git commit -m "feat: B"

git switch master
# 模拟别人在 master 上推了新代码
echo "线上修复" >> code.txt
git add . && git commit -m "hotfix: 紧急修复"

当前历史(使用 git log --oneline --graph --all):

* hhhhhhh hotfix: 紧急修复              ← master
| * ggggggg feat: B                     ← feature
| * fffffff feat: A
|/
* aaaaaaa v1

开始 rebase:

git switch feature
git rebase master

Git 做的三件事:

  1. 🚚 把 feature 上的 A 和 B 「拆下来」(暂时保存)
  2. 📍 把 feature 的指针移到 master 的最新点
  3. 📥 把 A 和 B 按照顺序「重新安装」上去

结果:

* ggggggg' feat: B                      ← feature(重新应用,hash 变了)
* fffffff' feat: A                      ← 重新应用
* hhhhhhh hotfix: 紧急修复              ← master
* aaaaaaa v1

历史变成了一条直线——和线性开发一模一样,看不出曾经分叉过。

# 6.3 黄金法则

绝不对已经 push 到公共仓库的 commit 执行 rebase。

原因很简单:rebase 会改写 commit hash。如果你改了已经 push 的 commit,别人的本地仓库就会和你产生不可调和的冲突。团队协作中,破坏公共历史的代价远大于历史不整洁的代价。

✅ 私有分支上 rebase → 整理好再 push     → 完美
❌ 公共分支上 rebase → 别人的仓库全乱    → 灾难

# 七、交互变基

你做了一个 feature,提交了 5 次——这 5 次里有「修 typo」「加注释」「真正功能」「临时 debug」「去掉 debug」。推给团队的时候,应该是一个干净、专业的提交列表,而不是你的草稿本。

# 7.1 整理分支

mkdir rebase-i-lab && cd rebase-i-lab && git init

echo "const user = {};" > user.js
git add . && git commit -m "init"

# 模拟混乱的开发过程
echo "function login() {}" >> user.js
git add . && git commit -m "开始写登录"

echo "function logout() {}" >> user.js
git add . && git commit -m "加一个登出 忘了写"

# 写错了
echo "asdfasdf" >> user.js
git add . && git commit -m "tmp: debug日志 等下删"

# 修复
# 删除 debug 日志
sed -i '' '/asdfasdf/d' user.js
git add . && git commit -m "修一下"

echo "function validate() {}" >> user.js
git add . && git commit -m "feat: 添加输入校验"

现在的历史:

e9e9e9e feat: 添加输入校验
d8d8d8d 修一下
c7c7c7c tmp: debug日志 等下删     ← 不该存在
b6b6b6b 加一个登出 忘了写           ← message 太随意
a5a5a5a 开始写登录
0000000 init

用交互式 rebase 整理:

git rebase -i HEAD~5

弹出编辑器:

pick a5a5a5a 开始写登录
pick b6b6b6b 加一个登出 忘了写
pick c7c7c7c tmp: debug日志 等下删
pick d8d8d8d 修一下
pick e9e9e9e feat: 添加输入校验

# 7.2 四种操作

修改为:

pick a5a5a5a 开始写登录
squash b6b6b6b 加一个登出 忘了写    ← squash:合并到上一个 commit
drop c7c7c7c tmp: debug日志 等下删   ← drop:直接删除
squash d8d8d8d 修一下               ← 也合并进去
reword e9e9e9e feat: 添加输入校验    ← reword:改 message

保存退出后,Git 会让你编辑合并后的 commit message:

# 第一个合并后的 commit
feat: 实现用户登录和登出功能     ← 新的、专业的 message

# 第二个
feat: 添加输入校验

最终结果:

git log --oneline
# 输出:
# xxxxxxx feat: 添加输入校验
# yyyyyyy feat: 实现用户登录和登出功能
# 0000000 init

5 个凌乱的 commit → 2 个干净的 commit。推给团队时,就像你一次性就写对了。

# 7.3 操作速查表

操作 含义 什么时候用
pick 保留这个 commit 不变 默认
reword 改 commit message message 写得不好
squash 合并到上一个 commit,保留 message 把多个小 commit 合成一个
fixup 合并到上一个 commit,丢弃 message 把「修 typo」合并到「真正功能」
drop 删除这个 commit 临时 debug commit 删掉
edit 停下来让你手动改这个 commit 的内容 只想改其中几个文件

# 八、终极对比

维度 merge rebase
历史形态 保留真实分叉记录 线性历史,看起来像没分过叉
commit hash 不变 重新生成(变了)
冲突解决方式 一次性解决全部冲突 每个 commit 依次解决冲突
适合场景 公共分支合并、PR 合并 整理私有分支、同步上游
可追溯性 保留了「何时分支的」 丢失了分支信息
安全性 安全 遵循黄金法则才安全

一句话建议:日常往团队分支合代码用 merge,整理自己的分支历史用 rebase。


# 九、综合实战

# 场景设定

你是小李,公司电商平台要开发「优惠券系统」。同时,线上报了一个紧急 bug:商品详情页的图片全部 404。

# 实战开始

# ==================== Phase 1:初始化项目 ====================
mkdir ecommerce && cd ecommerce && git init

echo 'const app = {
  name: "电商平台",
  version: "1.0.0"
};' > app.js

echo 'const products = [
  { id: 1, name: "iPhone", price: 6999 },
  { id: 2, name: "MacBook", price: 12999 }
];' > products.js

echo 'function getProductImage(id) {
  return `/images/products/${id}.jpg`;
}' > images.js

git add . && git commit -m "init: 电商平台基础骨架"


# ==================== Phase 2:开始开发优惠券功能 ====================
git switch -c feature/优惠券系统

echo '
// ===== 优惠券模块 =====
const coupons = [];

function createCoupon(code, discount, minAmount) {
  coupons.push({ code, discount, minAmount });
  return coupons[coupons.length - 1];
}

function applyCoupon(code, orderAmount) {
  const coupon = coupons.find(c => c.code === code);
  if (!coupon) return { valid: false, reason: "优惠券不存在" };
  if (orderAmount < coupon.minAmount) {
    return { valid: false, reason: `订单金额需满${coupon.minAmount}` };
  }
  return { valid: true, discountedAmount: orderAmount - coupon.discount };
}
' > coupon.js

git add . && git commit -m "feat: 优惠券创建与应用逻辑"

# 继续开发...
echo '
function listCoupons() {
  return coupons.map(c => ({
    ...c,
    status: c.expired ? "已过期" : "可用"
  }));
}
' >> coupon.js

git add . && git commit -m "feat: 优惠券列表查询"

# 还没开发完,优惠券的过期判断和前端页面都还没写
# 代码处于"半成品"状态


# ==================== Phase 3:紧急!线上图片 404 ====================
# Leader 紧急通知:所有商品图片 404!
# 小李检查 images.js:

cat images.js
# 发现路径写错了——应该是 /static/images/products/ 而不是 /images/products/

# 但你现在在 feature 分支上,优惠券写到一半,不能提交...

# 🎯 第一步:保存现场
git stash                    # 暂时保存工作现场(第 6 章细讲)
git status                   # 确认:working tree clean

# 🎯 第二步:切到 master,创建 hotfix 分支
git switch master
git switch -c hotfix/图片路径修复

# 🎯 第三步:修复 bug
# 把路径修正
sed -i '' 's|/images/products/|/static/images/products/|g' images.js
cat images.js  # 确认修改

git add images.js
git commit -m "hotfix: 修复商品图片路径 404 问题"

# 🎯 第四步:合回 master
git switch master
git merge --no-ff hotfix/图片路径修复 -m "merge: 合并图片路径 hotfix"

# 发布!线上恢复了!
git log --oneline --graph
# 输出:
# *   merge: 合并图片路径 hotfix
# |\
# | * hotfix: 修复商品图片路径 404 问题
# |/
# * init: 电商平台基础骨架


# ==================== Phase 4:回到优惠券开发 ====================
git switch feature/优惠券系统
git stash pop                 # 恢复现场!

# 继续开发...
echo '
function checkExpiry(coupon) {
  return new Date() > new Date(coupon.expireDate);
}
' >> coupon.js

git add . && git commit -m "feat: 优惠券过期校验"


# ==================== Phase 5:同步 master 的最新代码(含 hotfix) ====================
# 你开发了两天,master 上已经合了那个 hotfix
# 需要把 master 的修复同步到你这边

git merge master

# 如果没有冲突(优惠券和图片路径是不同文件),直接成功
# 如果有冲突 → 按照 5.3 节的方法解决

git log --oneline --graph
# 输出:
# *   merge: 合并 master 到 feature/优惠券系统
# |\
# | *   merge: 合并图片路径 hotfix         ← master
# | |\
# | | * hotfix: 修复商品图片路径 404 问题
# | |/
# | * init: 电商平台基础骨架
# * | feat: 优惠券过期校验                  ← feature
# * | feat: 优惠券列表查询
# * | feat: 优惠券创建与应用逻辑
# |/
# * init: 电商平台基础骨架


# ==================== Phase 6:整理 feature 分支的 commit ====================
# 推给团队之前,把 3 个 commit 整理成 1 个

git rebase -i HEAD~3

# 把后两个改成 squash:
# pick aaaaaaa feat: 优惠券创建与应用逻辑
# squash bbbbbbb feat: 优惠券列表查询
# squash ccccccc feat: 优惠券过期校验

git log --oneline
# 输出:
# xxxxxxx feat: 实现完整优惠券系统(创建、查询、过期校验)
# ...


# ==================== Phase 7:提 PR,合并到 master ====================
git push origin feature/优惠券系统
# 去 GitHub 提 Pull Request → Code Review → Merge

# 时间线回顾

        3:00 PM              3:05 PM              3:20 PM              3:30 PM
    ──────┼──────────────────┼────────────────────┼────────────────────┼──────→
           │                  │                    │                    │
    接到线上 bug 通知     git stash 保存现场    切 hotfix 修 bug     bug 修复完成
    优惠券写到一半        创建 hotfix 分支       改一行代码          合回 master
                                                    发布!
    
        3:35 PM              之后的两天              两天后
    ──────┼──────────────────┼────────────────────┼──────→
           │                  │                    │
    git stash pop 回现场   继续开发优惠券       rebase 整理历史
    继续写优惠券           把 master 合并进来    提 PR 等待审查

如果没有分支,小李只有两个选择:要么丢掉优惠券的半成品去修 bug,要么硬着头皮把半成品提交上去。无论哪种都是灾难。有了分支,两条时间线互不干扰,各自干净。


# 十、本章回顾

我学会了什么 一句话总结
分支的本质 一个 40 字节的指针文件,指向某个 commit
创建/切换分支 git switch -c <name> 创建并切换
Fast-forward 合并 没有分叉时,master 指针直接快进
三方合并 分叉后各自有提交,生成 merge commit
冲突的本质 同一行被两个分支改了,Git 不知道用哪个
解决冲突 编辑文件 → 删除标记 → git add → git commit
rebase 的原理 把 commit 拆下来,搬到目标分支最新点,重新安装
rebase 黄金法则 绝不对已 push 的公共 commit 做 rebase
交互式 rebase rebase -i:squash / drop / reword / fixup
merge vs rebase 公共分支用 merge,私有分支用 rebase

🎯 核心心法:分支是 Git 最强大的武器。不要怕分支,不要怕冲突。分支多 = 隔离好 = 出问题影响面小 = 你敢放心试。 频繁开分支、及时合分支、定期清理分支,是高级工程师的基本操作。


🏃 下一章预告:现在你一个人能玩转多条时间线了。下一步,连接世界——远程仓库 & 多人协作。你会学到:push / pull / fetch 的区别、为什么 fetch + merge 比 pull 更安全、以及 Pull Request 到底是怎么回事。


# 📎 本章涉及的命令速查

# === 分支操作 ===
git branch                          # 查看所有本地分支
git branch -v                       # 带最新 commit 信息
git branch <name>                   # 创建分支
git switch -c <name>                # 创建 + 切换(推荐)
git switch <name>                   # 切换分支
git branch -d <name>                # 安全删除(已合并)
git branch -D <name>                # 强制删除
git branch --merged                 # 查看已合并的分支
git branch --no-merged              # 查看未合并的分支

# === 合并 ===
git merge <branch>                  # 标准合并
git merge --no-ff <branch>          # 推荐:保留分支痕迹
git merge --abort                   # 放弃正在进行的合并

# === 变基 ===
git rebase <target-branch>          # 把当前分支 rebase 到目标分支
git rebase --abort                  # 放弃正在进行的 rebase
git rebase -i HEAD~N                # 交互式整理最近 N 个 commit

# === rebase -i 操作 ===
# pick    → 保留这个 commit
# reword  → 改 commit message
# squash  → 合并到上一个 commit(保留 message)
# fixup   → 合并到上一个 commit(丢弃 message)
# drop    → 删除这个 commit
# edit    → 停下来手改 commit 内容

# === 底层探索 ===
cat .git/refs/heads/<branch-name>   # 看分支指向哪个 commit
cat .git/HEAD                       # 看当前在哪个分支
#Git
上次更新: 2026/06/28, 17:55:19
单人工作流:在 Git 的时光机里自由穿梭
远程协作:把你的代码推到全世界

← 单人工作流:在 Git 的时光机里自由穿梭 远程协作:把你的代码推到全世界→

最近更新
01
科学方法实践论法
06-28
02
辩证思维矛盾论法
06-28
03
毛选中的调查观念
06-28
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式