第4章:Git分支与标签
本章目标
1. 什么是分支
分支(Branch) 可以理解为一条独立的开发时间线。你可以在分支 A 上开发新功能,同时保持主分支 B 的稳定,最后再把两条线合并起来。
Git 的分支与其他工具不同,它本质上只是一个指向提交的指针(存一个 40 位的哈希值,只占几十个字节),所以创建和切换分支是"瞬间"完成的。
main
↓
A ─── B ─── C
\\
D ─── E ← dev 分支
↑
dev
- main 指针指向提交 C
- dev 指针指向提交 E
- HEAD 指向"你当前所在的分支"(上图如果 HEAD → main,说明你在 main 分支上工作)
1.1 主分支叫什么?
- 早期 Git 默认主分支叫 master
- 2020 年起,GitHub 新建仓库默认叫 main;Gitee 的默认分支通常是 master(可在仓库设置中修改)
- Git 2.28+ 可用 git config –global init.defaultBranch main 设置本地默认分支名(见 [[第1章:Git入门与环境搭建]])
2. 分支基本操作
2.1 查看分支
git branch # 本地分支(当前分支前有 * 号)
git branch -v # 显示每个分支最后一次提交
git branch -a # 本地 + 远程分支(remote 开头的是远程分支)
git branch -vv # 显示分支与远程的追踪关系
git branch –merged # 已合并到当前分支的分支(可安全删除)
git branch –no-merged # 未合并的分支
2.2 创建分支
git branch dev # 基于当前提交创建 dev 分支(不切换)
git branch dev <commit> # 基于指定提交创建
git switch -c dev # 创建并立即切换(推荐)
git checkout -b dev # 同上(旧写法)
git branch dev origin/dev # 基于远程分支创建本地分支
[!tip] switch vs checkout
git switch 是 Git 2.23 引入的专用切换分支命令,语义更清晰;git checkout 一个命令身兼数职(切分支、恢复文件),容易混淆。建议新操作习惯用 switch / restore。
2.3 切换分支
git switch dev # 切换到 dev 分支
git switch – # 切回上一个分支
git switch -c dev # 创建并切换
git checkout dev # 旧写法
[!warning] 切换分支前
工作区有未提交的改动时:
- 如果改动与目标分支不冲突,Git 会把改动带过去(容易懵)
- 如果冲突,Git 拒绝切换并要求你先 commit 或 stash
建议:切换分支前保证工作区干净(git status 无输出),或先 git stash。
2.4 重命名与删除分支
git branch -m newname # 重命名当前分支
git branch -m old new # 重命名指定分支
git branch -d dev # 删除已合并的分支(安全)
git branch -D dev # 强制删除未合并分支(危险)
git push origin –delete dev # 删除远程分支
[!warning] 删分支后悔了?
只要分支上的提交曾经存在过,可以先 git reflog 找到对应提交,然后 git branch 名字 <commit> 复活。
3. 合并分支:git merge
合并 = 把另一个分支的改动并入当前分支。注意方向:先切到"接收方"分支,再合并"提供方"。
# 把 dev 合并到 main:先切到 main,再合并 dev
git switch main
git merge dev
3.1 快进合并(Fast-forward)
如果从共同祖先到目标分支是"一条直线"(main 没有新提交),Git 只需把指针向前移动,这叫快进合并。
合并前:
A ─── B (main)
\\
C ─── D (dev)
git switch main
git merge dev
合并后:
A ─── B ─── C ─── D (main, dev) ← main 指针直接前移,无新提交
想禁止快进、强制产生一个合并提交(保留分支痕迹):
git merge –no-ff dev
合并后(–no-ff):
A ─── B ────────── M (main)
\\ /
C ─── D (dev)
3.2 三方合并(3-way merge)
如果两边都有新提交,Git 会找到共同祖先,把两边的改动合并,并自动生成一个合并提交:
合并前:
C ─── D (dev)
/
A ─── B
\\
E ─── F (main)
合并后:
C ─── D (dev)
/ \\
A ─── B M (main) ← 合并提交
\\ /
E ─── F
git merge dev # 自动合并,弹出提交信息编辑器
git merge dev -m "合并dev分支" # 指定合并提交信息
git merge –abort # 合并出问题?放弃本次合并,回到合并前状态
3.3 冲突及其解决(重点)
当两个分支修改了同一文件的同一位置,Git 无法自动决定用谁,就会产生冲突:
Auto-merging hello.c
CONFLICT (content): Merge conflict in hello.c
Automatic merge failed; fix conflicts and then commit the result.
此时文件内容变成:
<<<<<<< HEAD
printf("hello from main");
=======
printf("hello from dev");
>>>>>>> dev
含义:
- <<<<<<< HEAD 到 ======= 之间:当前分支的内容
- ======= 到 >>>>>>> dev 之间:要合入的 dev 分支的内容
解决步骤:
# 1. 查看哪些文件冲突
git status
# 输出:both modified: hello.c
# 2. 打开文件,手动编辑成最终想要的内容(删掉 <<<<<<< ======= >>>>>>> 这些标记行)
# 例如改成:printf("hello from main and dev");
# 3. 标记为已解决(把文件加入暂存区)
git add hello.c
# 4. 完成合并提交
git commit # 直接提交即可(信息已预填)
# 如果中途想放弃合并:
git merge –abort
[!tip] 减少冲突的技巧
- 勤 pull:经常同步远程改动,冲突窗口更小
- 小步提交:一次提交只做一件事
- 分工清晰:避免多人长期同时改同一个文件
- 冲突工具:命令行手改难受时,用 git mergetool 或 TortoiseGit 的三方合并工具(见 [[第7章:TortoiseGit功能详解]])
4. 变基:git rebase
rebase = 把当前分支的提交"搬"到另一个分支的最新提交之后,形成一条干净的直线历史。
git switch dev
git rebase main
变基前:
C ─── D (dev)
/
A ─── B ─── E ─── F (main)
变基后(dev 的 C、D 被复制为 C'、D' 接到 F 后面):
A ─── B ─── E ─── F (main)
\\
C' ─── D' (dev)
4.1 merge 与 rebase 对比
| 历史形状 | 分叉的(保留真实轨迹) | 直线的(被整理过) |
| 是否改写提交 | 否 | 是(生成新哈希) |
| 冲突解决次数 | 一次 | 可能每个提交都要解决 |
| 使用场景 | 公共分支合并 | 私人分支同步主分支 |
[!warning] rebase 的黄金法则
不要对已经推送到远程的公共分支执行 rebase! 因为它改写了提交历史,会让其他人的仓库与你的对不上。rebase 自己的私有分支则没问题。
4.2 交互式变基(整理提交)
git rebase -i HEAD~3 # 编辑最近 3 个提交
编辑器会列出:
pick f7f3f6d 修复了第一个问题
pick 310154e 修复了第二个问题
pick a5f4a0d 改正了错别字
把 pick 改成其他指令(想调整提交顺序时,直接上下移动行即可):
| pick | 保留该提交 |
| reword | 修改提交信息 |
| edit | 停下来修改该提交内容 |
| squash | 合并到上一个提交,合并提交信息 |
| fixup | 合并到上一个提交,丢弃本条信息 |
| drop | 删除该提交 |
| exec | 在该位置执行一条自定义命令(进阶) |
[!example] 常用场景
把最近 3 个"改bug、再改bug、还改bug"的碎提交压成一个干净的提交:git rebase -i HEAD~3,后两条前面改成 squash 或 fixup。
4.3 rebase 遇到冲突
# 解决掉冲突文件后:
git add <文件>
git rebase –continue
# 想放弃变基,回到变基前:
git rebase –abort
# 跳过当前这个提交:
git rebase –skip
4.4 等效的 pull –rebase
拉取远程更新时,如果本地有提交,默认会产生合并提交。想用变基方式保持直线:
git pull –rebase
git config –global pull.rebase true # 永久设为默认行为
5. 标签:git tag
标签用于给某个提交起一个"永久性的名字",典型用途是标记版本号(v1.0、v2.1.3)。分支会移动,标签不会。
5.1 创建标签
git tag v1.0 # 轻量标签(只是个指针)
git tag -a v1.0 -m "1.0 正式版发布" # 附注标签(含作者、日期、说明,推荐)
git tag -a v1.0 9fceb02 # 给历史提交补打标签
5.2 查看与检出
git tag # 列出全部标签
git tag -l "v1.*" # 按模式过滤
git show v1.0 # 查看标签详情
git switch -c hotfix v1.0 # 基于标签创建分支
5.3 推送与删除标签
标签不会随 git push 自动上传,要单独推:
git push origin v1.0 # 推送单个标签
git push origin –tags # 推送所有本地标签
git tag -d v1.0 # 删除本地标签
git push origin –delete v1.0 # 删除远程标签
6. 常见分支模型
6.1 Git Flow(经典模型)
main ───●──────────────●────────●─── 只放正式发布
\\ / /
release ●──●──●───● / 发布准备
\\ /
develop ───●────●────●────●────●────●─── 日常开发主干
\\ / \\ /
feature ●───● ●────● 功能分支
| main | 只包含已发布版本,每个提交打标签 |
| develop | 开发主干,功能都先合到这里 |
| feature/* | 每个新功能一个分支,完成后合回 develop |
| release/* | 发布准备(修 bug、改版本号) |
| hotfix/* | 线上紧急修复,从 main 拉出,修完合回 main + develop |
适合版本发布节奏明确的中大型项目,流程偏重。
6.2 GitHub Flow(轻量模型)
main ───●────●─────────●────●───
\\ \\ / /
feature ●────●─────● / 从 main 拉功能分支
\\ /
●──────● 合并回 main 后可立即部署
- 只有 main 一条长期分支
- 任何改动都从 main 拉分支 → 提交 → 发起 Pull Request → 评审 → 合并
- 合并即可部署,适合持续交付的 Web 项目
6.3 适合个人 / 小团队的做法
- 单人项目:直接一个 main 分支 + 定期打标签即可
- 2-5 人:main + feature 分支 + PR 评审
- 养成习惯:任何新功能、修复都开新分支,开发完合并回 main
7. 本章小结
- 分支本质是指向提交的可移动指针,创建/切换极快
- 合并方向:先切到接收方,再 git merge 提供方
- 冲突=同一位置被两边修改;解决=手动编辑 → git add → git commit
- rebase 让历史变直线,但别动公共分支
- 标签用于标记版本;git push –tags 才会同步到远程
- 小团队推荐 GitHub Flow:main + 功能分支 + 合并
网硕互联帮助中心






评论前必须登录!
注册