分支与合并
理解分支指针本质,会用 branch/checkout/merge 与解决冲突
- 来源
- 补充
- 纠错
标记说明:【来源】来自上传资料 · 【补充】课程新编 · 【纠错】按勘误表修正 · 【更新】过时内容已现代化 · 【待确认】无法可靠还原
完成标准(本章)
- 📖 已阅读:滚动 ≥ 80% 且有效阅读 ≥ 120 秒
- ✏️ 已练习:小练习正确率 ≥ 60%
- 📝 已通过测验:分数 ≥ 60 分
- 🛠️ 已掌握还需完成实践任务
0.3.3 分支与合并
本章来源:分支指针本质、branch/checkout(-b)/switch、merge 快进与三路合并、冲突标记、rebase 概念与危险提示来自《第一阶段讲义》1.4 节【来源】,经重新组织表述;1.4 节目录中分支名 feature 被误写为 featureh,见【纠错】err-02-0-3-3;命令清单、冲突实验与全部练习为新编【补充】。平台适用性 universal。
① 学习目标
- 说清分支的本质是"指向某次提交的指针",以及 HEAD 指向当前分支的含义;
- 用
git branch/git checkout(-b)/git switch创建与切换分支; - 区分快进合并(Fast-forward)与三路合并,说出各自的发生条件与结果差异;
- 制造并解决一次真实冲突:识别
<<<<<<<=======>>>>>>>标记、手动编辑、git add、git commit; - 说出 rebase 与 merge 的本质区别,并知道为什么不能对已推送的公共分支执行 rebase(概念级,不深练)。
② 前置知识
- 必选:0.3.2(提交循环、status/log/diff 的读写)。
③ 核心概念【来源】
| 概念 | 说明 | 要点 |
|---|---|---|
| 分支 = 指针 | 分支名是 .git/refs/heads/ 下的一个引用,指向某次提交 | 建分支只是写一个指针,几乎零成本,所以"随手开分支"是正确习惯 |
| HEAD | 指向"当前所在分支"的特殊指针 | git checkout/git switch 切换分支就是移动 HEAD |
git branch / git checkout(-b) / git switch | 建分支 / 切换分支 | 一步到位:git checkout -b dev 或新版 git switch -c dev;checkout 一个命令兼两职是历史原因 |
git merge | 合并分支 | 快进:目标分支分叉后无新提交,指针直接前进、无新提交;三路:两边都有新提交,以共同祖先为基准三方比较,生成合并提交 |
| 冲突 | 两边改了同一文件的同一区域 | git 无法自动合并,文件里留下 <<<<<<< HEAD / ======= / >>>>>>> 分支名 标记,人工决定最终内容 |
| rebase(概念) | 把本分支提交"搬"到目标分支最新提交之后依次重放 | 历史成直线但提交哈希全变(改写历史);只对本地未推送分支使用,公共分支一律 merge |
④ 通俗解释【补充】
- 分支像书签:每张书签插在书页(提交)上。建分支=多插一张书签;提交=从这张书签往后翻新的一页;切分支=把手指(HEAD)挪到另一张书签。
- 快进合并:你出差期间搭档没动书稿,你回来直接把他的新页接上——书签往前一插就行。
- 三路合并与冲突:两个人都改了同一段文字。如果改的是不同段落,编辑(git)自动把两处改动都合进去;如果改的是同一段,编辑不敢替你决定,就把两人的版本都摆出来、画好分隔线(冲突标记),等你拍板。
- rebase:把你写的几页拆下来,重新装订到对方最新一页后面——看着整齐,但页码(哈希)全变了。自己还没发出去的稿子可以这么整理;已经发给别人的稿子再重排页码,别人手里的旧稿就全对不上了。
⑤ 示例代码
代码示例与验证记录
- examples/git-branch-merge-commands.txtGit 分支与合并命令清单(建分支→快进合并→制造冲突→解决冲突)✓ 已实测(git 2.53.0.windows.2 / Windows 11, 2026-08-15)编译:
按文件内顺序在终端逐行执行(在 $env:TEMP 临时目录实验)适用环境:LinuxWindows(MinGW)macOS展开预期输出(实测)
Switched to branch 'dev' [dev adc8713] feat: add line 2 on dev Updating 4fe5e13..adc8713 Fast-forward (快进:main 直接前进到 dev) notes.txt | 1 + 1 file changed, 1 insertion(+) [main 86cd4a4] fix: main edits line 2 [dev a3a546a] feat: dev edits line 2 CONFLICT (content): Merge conflict in notes.txt Automatic merge failed; fix conflicts and then commit the result. <<<<<<< HEAD 第 2 行:主线修改 ======= 第 2 行:dev 修改 >>>>>>> dev [main aa8e72c] merge: resolve conflict between main and dev * aa8e72c merge: resolve conflict between main and dev |\ | * a3a546a feat: dev edits line 2 * | 86cd4a4 fix: main edits line 2 |/ * adc8713 feat: add line 2 on dev * 4fe5e13 init: notes.txt
差异说明:哈希值为本机实测(每次不同);冲突标记段为真实 git 输出;LF/CRLF 警告是 Windows 正常提示;git 2.53.0 / Windows 11 本机实测
完整源码见 /code 代码示例页
⑥ 编译与运行方法
本章无编译步骤:命令在终端逐行执行。请把实验放在 $env:TEMP 下的临时目录,不要在任何真实项目仓库里练习冲突。冲突是正常协作流程而不是事故:报 CONFLICT 后先打开文件读懂标记,再动手改。
⑦ 常见错误
| 症状 | 原因 | 解决 |
|---|---|---|
| 【纠错】资料目录里的分支名 featureh | 1.4 节目录 OCR/排版笔误 | 正确拼写为 feature(见 err-02-0-3-3) |
git merge dev 报 CONFLICT 就慌了 | 冲突是多人协作的正常环节 | 打开文件看 <<<<<<< ======= >>>>>>> 标记,手动改好 → git add → git commit |
冲突文件里残留 <<<<<<< 标记就提交了 | 解决不彻底 | 提交前自查:git grep "<<<<<<<" 应无输出 |
| 切分支被拒(Your local changes would be overwritten) | 工作区有未提交改动 | 先 commit 或(了解)git stash 暂存改动再切分支 |
| 直接在 main 上开发功能 | 没养成分支习惯 | 开发用 feature/dev 分支,验证后再合并回 main |
| 对已推送的公共分支执行 rebase | 改写历史导致他人仓库错乱 | 公共分支只用 merge;rebase 限本地未推送分支(本章概念级要求) |
⑧ 小练习
小练习
学习自测:提交后才显示答案与解析(前端判分,不作为正式考试)ex-0-3-3-1.Git 中分支的本质是什么?(单选)
◌ 未作答ex-0-3-3-2.git merge dev 时提示 Fast-forward,说明发生了什么?(单选)
◌ 未作答ex-0-3-3-3.git merge dev 之后,文件里出现了 <<<<<<< HEAD 到 >>>>>>> dev 的标记,原因是?(单选)
◌ 未作答
⑨ 章节测验
章节测验
⑩ 实战任务
实践任务
dev 分支改代码合并回 main 并解决一次真实冲突
在本地练习仓库里:建立 dev 分支并在其上修改文件提交;合并回 main(先体验快进合并); 再让 main 与 dev 分别修改同一行制造真实冲突,手动解决后完成合并, 用 git log --graph 确认合并提交与分支结构。
输入与输出
无程序输入输出。交付物:本地仓库中 git log --oneline --graph 输出包含一个 合并提交(两个父提交),且冲突文件最终内容正确、无残留冲突标记。
功能要求
- 使用本地练习仓库(新建或沿用 0.3.2 的仓库;不创建任何远程仓库)
- git branch dev 建立 dev 分支并在其上提交至少 1 次改动
- 合并回 main 并观察 Fast-forward 提示
- 在 main 与 dev 分别修改同一文件的同一行并各自提交,制造真实冲突
- 手动解决冲突(删除全部标记、保留想要的最终内容),git add 后 git commit 完成合并
- 用 git log --oneline --graph --all 查看合并后的分支结构
限制条件
- 实验只用本地仓库,不创建任何远程仓库
- 冲突解决只靠手动编辑 + add + commit,不使用任何 GUI 的"一键解决"
验收步骤(自检清单 0/6)
验收标准
- git log --oneline --graph 输出包含一个合并提交(验收步骤 6)
- 冲突文件最终内容正确且 grep 不到 <<<<<<< 标记(验收步骤 5)
- 快进合并与冲突合并各经历一次(验收步骤 2/3)
常见失败原因
- 冲突文件残留 <<<<<<< 标记就提交
- 解决冲突时把对方有效改动整体删除
- merge 报 CONFLICT 后不知所措(应打开文件看标记,解决后再 add + commit)
可选扩展
- 用 git merge --abort 体验放弃一次冲突合并
- 用 git branch -d dev 在合并后删除本地分支
完成必要清单后才能计入"已完成实践"(学习状态自动推导,不提供一键完成)
⑪ 面试问题
面试问题
git merge 的快进合并(Fast-forward)和三路合并有什么区别?分别在什么情况下发生?高频C/C++ · medium
要点:快进:分叉后目标分支没有新提交,指针直接前进,不产生新提交;三路:两边都有新提交,git 以共同祖先为基准三方比较生成合并提交;若三方修改冲突则报 CONFLICT 留人工解决。
快进合并:从分叉点之后目标分支(如 main)没有任何新提交,合并时 git 只需把 main 指针 前进到被合并分支(如 dev)的最新提交,历史保持一条直线,不产生新提交对象。 三路合并:两边在分叉后都有新提交,git 取"共同祖先(base)+ main + dev"三份内容比较: 只有一边改动的部分自动采用改动方;两边改了同一文件的同一区域时无法自动合并, 生成冲突标记(<<<<<<< HEAD / ======= / >>>>>>> dev)留在文件里,由人工决定最终内容, 解决后 git add + git commit 生成一个有"两个父提交"的合并提交。 面试加分点:能说出 git merge --no-ff 可以强制禁用快进(生成合并提交保留分支历史), 以及"冲突是正常协作流程,不是事故"。
追问:- 追问:怎么强制产生合并提交而不是快进?(--no-ff,及其使用场景)
评分要点:- 两种合并的发生条件
- 三路合并的 base 概念
- 冲突标记与合并提交两个父提交
merge 出现冲突后,完整的解决流程是什么?有哪些常见坑?高频C/C++ · medium
要点:打开冲突文件,理解 <<<<<<< ======= >>>>>>> 三段标记的含义 → 手动编辑保留最终内容并删除全部标记 → git add 标记已解决 → git commit 完成合并;坑:残留标记、解决不彻底、丢弃对方有效改动。
完整流程:① git status 找到冲突文件(Unmerged paths);② 打开文件,<<<<<<< HEAD 到 ======= 之间是当前分支内容,======= 到 >>>>>>> dev 之间是对方分支内容;③ 手动编辑成 想要的最终内容(可能两边各取一部分),删除全部三组标记;④ git add 冲突文件告诉 git "已解决";⑤ git commit 完成合并(合并提交有两个父提交)。常见坑:提交前残留 <<<<<<< 标记(可用 git grep '<<<<<<<' 自查);解决时只删标记不改内容导致逻辑错误; 把对方有效改动整体删掉;冲突解决到一半又去 merge 别的分支。工具提示: git merge --abort 可以在解决前放弃本次合并回到干净状态。
追问:- 追问:git merge --abort 什么时候用?
- 追问:冲突解决错了还能找回吗?(reflog/再次合并)
评分要点:- 三段标记含义
- add 表示"已解决"的语义
- 残留标记与 --abort 两个坑/工具
git rebase 和 git merge 的区别是什么?为什么说 rebase 有危险、哪些场景不能 rebase?高频C/C++ · medium
要点:merge 保留分叉历史并生成合并提交;rebase 把本分支的提交搬到目标分支最新提交之后重放,历史成直线。危险在于改写提交(哈希变化);已推送的公共分支不能 rebase。
区别:merge 是"合并两条线",保留分叉与合并结构,历史呈树状;rebase 是"把本分支的 提交取下来、放到目标分支最新提交之后依次重放",得到一条直线历史,提交哈希全部改变 (父提交变了,哈希必然变)。危险根源就在"改写历史":如果被 rebase 的分支已经 push 给他人,别人仓库里的旧提交与你的新哈希对不上,强行同步会制造重复提交甚至需要 force push,破坏他人工作。安全用法:只对"自己本地、尚未推送"的功能分支做 rebase 整理历史;公共分支(main/dev)一律用 merge。加分:能说出 interactive rebase (git rebase -i)可以压缩/重排提交消息——同样只在本地分支做。
追问:- 追问:为什么 rebase 后提交的哈希会变?
- 追问:git pull --rebase 与 git pull 的区别?
评分要点:- 历史形状差异(树状 vs 直线)
- 哈希改变与改写历史的危险
- 公共分支禁 rebase 的边界
⑫ 延伸阅读
- 《Pro Git》第 3 章(分支简介/分支的新建与合并/分支管理);
- 本地手册:
git help merge/git help rebase; - 下一章预告:0.3.4 远程仓库 Gitee——把本地分支推到远端,和搭档真正协作起来。
迁移训练(migration training)
把本章技能迁移到 Linux 与 Mac:
| 环节 | Windows | Linux | Mac |
|---|---|---|---|
| 全部 git 命令 | 一致 | 一致 | 一致 |
| 文件创建/改写 | Set-Content -Encoding UTF8 | echo / printf 重定向 | 同 Linux |
| 换行符 | CRLF | LF | LF |
| 冲突标记内容 | 与内容编码一致 | 同左 | 同左 |
不变的:分支指针模型、merge 语义、冲突标记与解决流程。要改的:文件创建命令与换行符习惯;跨 Windows/Linux 协作时换行符差异可能让冲突表现为"整行都不同",团队统一 .gitattributes 可根治(0.3.5 展开)。
内容来源映射
| 内容部分 | 资料 | 位置 | 标记 | 说明 |
|---|---|---|---|---|
| 分支指针本质、branch/checkout(-b)/switch、merge 快进与三路合并、冲突标记、rebase 概念与危险提示 | 第一阶段讲义 | 1.4 节 | 【来源】 | 正文在原资料基础上重新组织表述,未大段复制原文 |
| 1.4 节目录中分支名 feature 被误写为 featureh | 第一阶段讲义 | 1.4 节目录 | 【纠错】 | 目录 OCR/排版笔误,正确拼写为 feature;对应 corrections.yaml err-02-0-3-3(02 映射表记录) |
| 分支与合并命令清单、本机冲突实验输出、通俗解释 | 无 | 【补充】 | 命令清单在 $env:TEMP 临时仓库真实执行(git 2.53.0 / Windows 11),expected_output 为实测值(含真实冲突标记) | |
| 小练习 / 章节测验 / 实践任务 / 面试问题 / 延伸阅读 | 无 | 【补充】 | 原资料该章无成体系练习,全部新编 |
本章勘误与更新记录(【纠错】/【更新】)
err-02-0-3-3 · 技术错误 · 出处 1.4 节目录
原文:资料目录中把 feature 分支误写为 featureh
正确:正确拼写为 feature(featureh 为笔误)
原因:第一阶段讲义 1.4 节目录 OCR/排版笔误,02 映射表记录