理解分支
本章开始介绍Git的杀手锏之一:"分支"
具体情景
分支就像玄幻电影里的"分身术"。当你正在电脑前努力学习C++的时候,你的分身正在努力学习JAVA。
分身和你之间互不影响。
不过,在某一天,你将分身合并了,于是最终你即学会了C++,又学会了JAVA

分支的本质
-
我们一系列的提交会形成一条时间线,这条时间线就是一个分支
-
默认情况下只有一条时间线,即主分支(master)
HEAD指针
-
HEAD严格来说不是指向提交,而是指向当前所在分支
-
master才是指向提交的(准确来说,是主分支提交)
随着不断提交,master分支的时间线越来越长,而HEAD 始终指向当前分支。

基础分支操作
创建分支
Git支持我们查看或者创建其它分支,这里我们来创建第一个自己的分支dev
Shell
# 查看当前本地所有分支
$ git branch
* master
# 新建分支dev
$ git branch dev
# 再次查看分支
$ git branch
dev
* master
补充 :
*表示当前HEAD指向的分支,即当前工作分支
另外,还可以通过目录结构发现新的dev分支
Shell
# 查看分支引用文件
$ ls .git/refs/heads/
dev master
# 查看目录结构
$ tree .git/refs/heads/
.git/refs/heads/
|-- dev
`-- master
# 查看分支指向的提交
$ cat .git/refs/heads/*
81b7158d55c34e495558198fcc3edd5f26e728e4
81b7158d55c34e495558198fcc3edd5f26e728e4
发现目前dev 和master 指向同一个修改。验证HEAD 目前是指向master的
Shell
$ cat .git/HEAD
ref: refs/heads/master
综上,HEAD 指向当前分支,而master指向具体的提交

补充 :HEAD 不仅可以指向
master分支,还可以指向其他分支 ,而HEAD所指向的分支就是当前工作的分支。
切换分支
那如何切换到dev分支下进行开发呢?使用git checkout命令即可完成切换
Shell
# 切换到指定分支
git checkout dev
# 创建并切换到新分支(一步完成)
git checkout -b dev1
注意:
git checkout dev与git checkout -- file不同,后者用于撤销工作区修改
操作示例
Shell
# 切换到dev分支
$ git checkout dev
Switched to branch 'dev'
# 切换分支后,HEAD指向改变
$ git branch
* dev
master
$ cat .git/HEAD
ref: refs/heads/dev

接下来,在dev分支下修改ReadMe文件,新增一行内容,并进行一次提交操作
Shell
$ vim ReadMe
$ cat ReadMe
test for new branch "dev" # 新增内容
$ git add .
$ git commit -m "insert(+) 1 in ReadMe for dev branch"
[dev 9663d75] insert(+) 1 in ReadMe for dev branch
1 file changed, 1 insertion(+)
现在,dev分支的工作完成,我们就可以切换回master分支:
Shell
$ git checkout master
Switched to branch 'master'
$ cat ReadMe
发现ReadMe 文件中新增内容不见了!为什么会出现这种现象呢?我们来看看dev 和master指向,发现两者指向的提交是不一样的
Shell
$ cat .git/refs/heads/dev
9663d756fda23eb65c99fa002a87c575a9eceef1
$ cat .git/refs/heads/master
81b7158d55c34e495558198fcc3edd5f26e728e4
看到这里就能明白,因为我们是在dev分支上提交的,而master分支此刻的提交点并没有变,此时的状态如下所示:

当切换到master分支时,HEAD就指向了master,当然看不到提交了!
合并分支
git merge [目标分支]命令用于合并指定分支到当前分支。为了在master分支上看到dev分支提交的内容,就需要将dev分支合并到master分支。
Shell
# 将dev分支合并到当前分支
git merge dev
合并时可能出现两种模式:
-
Fast-forward(快进模式):直接移动指针,合并速度最快(默认模式)
-
no-ff(非快进模式):创建新的合并提交,保留分支历史
补充 :Fast-forward就是直接把master 指向
dev分支的当前提交,所以合并速度非常快。当然也不是每次合并都能Fast-foward
操作示例
Shell
# 查看当前分支
$ git branch
* dev
master
# 切换到master分支
$ git checkout master
Switched to branch 'master'
# 合并dev分支
$ git merge dev
Updating 81b7158..9663d75
Fast-forward
ReadMe | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
# 查看合并后的文件
$ cat ReadMe
test for new branch "dev" # 新增内容

删除分支
合并完成后,dev分支对我们来说就没用了,可以删除掉git branch -d [目标分支]
Shell
$ git branch -d dev
Deleted branch dev (was 9663d75).
**注意:**不能删除当前所在的分支(master肯定也不能删啊,走火入魔了这是!?)

建议:因为创建、合并和删除分支非常快,所以Git鼓励使用分支完成某个任务,合并后再删除分支,这和直接在master分支上工作效果是一样的,但过程更安全。
分支合并策略
Fast-forward 快进模式(默认)
当目标分支是当前分支的直接上游时,Git会默认使用快进模式,直接将指针移动到目标分支的最新提交。
Shell
# 快速合并示例
git merge dev
# 输出:Fast-forward

缺点:删除分支后无法从历史中看出合并信息。
no-ff 非快进模式(推荐)
使用--no-ff参数强制创建合并提交,保留完整分支历史。
Shell
# 非快速合并示例
git merge --no-ff dev -m "merge dev with no-ff"
# 输出:

优点:分支历史清晰,能看出合并点
操作示例
Shell
# 创建并切换到新分支
$ git checkout -b dev2
Switched to a new branch 'dev2'
# 修改文件并提交
$ vim ReadMe
$ cat ReadMe
test for new branch "dev2" # dev2 新增内容
$ git add .
$ git commit -m "insert(+) 1 in ReadMe for dev2 branch"
[dev2 5724f11] insert(+) 1 in ReadMe for dev2 branch
1 file changed, 2 insertions(+), 1 deletion(-)
# 切回master分支并使用no-ff模式合并
$ git checkout master
$ git merge --no-ff dev2 -m "merge dev2 with no-ff"
Merge made by the 'recursive' strategy.
ReadMe | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
注意 :
--no-ff参数,表示禁用Fast-forward模式。禁用Fast-forward模式后合并会创建一个新的commit,所以需要加上-m参数添加描述。
合并后,用git log --graph --pretty=oneline --abbrev-commit查看分支历史
Shell
$ git log --graph --pretty=oneline --abbrev-commit
* da1e876 merge dev2 with no-ff
|\
| * 5724f11 insert(+) 1 in ReadMe for dev2 branch
|/
可以看到,不使用Fast-forward模式,merge后就像这样

处理合并冲突
当不同分支对同一文件的同一部分进行了不同修改时,就会发生合并冲突。
冲突示例
Shell
# 创建并切换到dev1分支
$ git checkout -b dev1
Switched to a new branch 'dev1'
$ git branch
* dev1
master
在dev1分支下修改ReadMe文件,并进行一次提交
Shell
$ vim ReadMe
$ cat ReadMe
test for new branch "dev1" # dev1新增内容
$ git add .
$ git commit -m "modify ReadMe for dev1"
[dev1 19895ee] modify ReadMe for dev1
1 file changed, 1 insertions(+), 1 deletion(-)
切换回master分支,再对ReadMe文件进行修改
Shell
$ git checkout master
Switched to branch 'master'
$ vim ReadMe
$ cat ReadMe
test for new branch "master" # master新增内容
$ git add .
$ git commit -m "modify ReadMe for master"
[master df5d186] modify ReadMe for master
1 file changed, 1 insertions(+), 1 deletion(-)
现在,master分支和dev1分支各自都分别有了新的提交,变成了这样

这种情况下,Git只能试图把各自的修改合并起来,但这种合并就可能会有冲突
Shell
$ git merge dev1
Auto-merging ReadMe
CONFLICT (content): Merge conflict in ReadMe
Automatic merge failed; fix conflicts and then commit the result.
冲突标记
发现ReadMe文件有冲突后,可以直接查看文件内容。Git会用<<<<<<<,=======,>>>>>>>来标记出不同分支的冲突内容
Shell
<<<<<< HEAD
当前分支的内容
=======
合并分支的内容
>>>>>>> dev1
操作示例
Shell
$ cat ReadMe
<<<<<<< HEAD
test for new branch "master" # master新增内容
=======
test for new branch "dev1" # dev1新增内容
>>>>>>> dev1
解决冲突
此时我们必须要手动调整冲突代码,并需要再次提交修正后的结果!!!(再次提交很重要,切勿忘记)
Shell
# 1. 编辑文件解决冲突
vim ReadMe
# 2. 标记冲突已解决
git add .
# 3. 提交合并
git commit -m "解决合并冲突"
操作示例
Shell
# 编辑文件解决冲突
$ vim ReadMe
# 查看解决后的内容
$ cat ReadMe
version3
test for new branch "dev"
test for new branch "dev1" # 保留下来的内容
# 提交解决结果
$ git add .
$ git commit -m "merge ReadMe"
[master aa31f2e] merge ReadMe
到这里冲突就解决完成,此时的状态就变成了

查看合并历史
Shell
$ git log --graph --abbrev-commit
* commit aa31f2e
|\ Merge: df5d186 19895ee
| | Author: HY <1234567890@qq.com>
| | Date: Sun Nov 30 19:24:50 2025 +0800
| |
| | merge ReadMe
| |
| * commit 19895ee
| | Author: HY <1234567890@qq.com>
| | Date: Sun Nov 30 19:07:47 2025 +0800
| |
| | modify ReadMe for dev1
| |
* | commit df5d186
|/ Author: HY <1234567890@qq.com>
| Date: Sun Nov 30 19:16:48 2025 +0800
|
| modify ReadMe for master
最后不要忘记删除dev1分支
Shell
$ git branch
dev1
* master
$ git branch -d dev1
Deleted branch dev1 (was 19895ee).
分支管理策略
开发基本原则
1、master分支(主分支)
-
仅用于发布稳定版本,保持代码库稳定
-
禁止直接在此分支上进行开发工作
2、dev分支(开发分支)
-
作为集成开发环境,所有功能在此合并
-
团队成员在此协作,进行功能集成测试
3、功能分支(Feature分支)
-
每个新功能的开发,都要在dev分支的基础上,创建独立分支
-
命名规范:
feature/功能名称(如feature/user-login)
4、修复分支(Hotfix分支)
-
用于紧急bug修复的临时分支
-
命名规范:
hotfix/问题描述(如hotfix/login-bug)
团队协作模型


实用分支技巧
存储工作现场
当需要切换分支,但由于当前工作未完成无法提交时,可以用git stash命令存储更改
Shell
# 1、暂存当前工作(包括未提交的修改)
git stash
# 2、查看所有暂存记录
git stash list
# 输出示例:stash@{0}: On dev2: 正在进行的功能开发
# 3、恢复最近一次暂存(并删除暂存记录)
git stash pop
# 4、恢复指定暂存(不删除记录)
git stash apply stash@{0}
# 5、删除指定暂存记录
git stash drop stash@{0}
# 6、清空所有暂存记录
git stash clear
操作示例
Shell
# 1、当前在dev2分支开发中,突然发现master有bug
$ git status
# On branch dev2
# Changes not staged for commit:
# modified: ReadMe
# 2、暂存当前工作
$ git stash
Saved working directory and index state WIP on dev2: 5724f11 insert(+) 1 in ReadMe
# 3、工作区已干净,可切换分支
$ git status
# On branch dev2
nothing to commit, working directory clean
# 4、创建并切换到修复分支
$ git checkout master
$ git checkout -b fix_bug
Switched to a new branch 'fix_bug'
$ vim ReadMe
$ cat ReadMe
This is a bug
# 5、修复bug并提交
$ vim ReadMe
$ cat ReadMe
Having fixed the bug
$ git add ReadMe
$ git commit -m "fix a bug"
[fix_bug f85271f] fix a bug
1 file changed, 2 insertions(+), 1 deletion(-)
# 6、合并修复到master分支,并删除修复分支
$ git checkout master
Switched to branch 'master'
$ git merge fix_bug --no-ff -m "merge fix_bug branch"
Merge made by the 'recursive' strategy.
ReadMe | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
$ git branch -d fix_bug
Deleted branch fix_bug (was f85271f).
# 回到dev2分支恢复工作
$ git checkout dev2
Switched to branch 'dev2'
$ git stash pop
# On branch dev2
# Changes not staged for commit:
# modified: ReadMe
Dropped refs/stash@{0} (b12a006d2e3b2d4966fc70b43cfe00207ded9d8a)

分支合并技巧
为避免在master分支上直接解决冲突,推荐以下操作流程
Shell
# 1、在功能分支上更新主分支最新代码
git merge master
# 2、解决冲突并提交
vim ReadMe
git add .
git commit -m "合并master并解决冲突"
# 3、切换回master,合并功能分支
git checkout master
git merge feature-branch
# 4、删除已完成的功能分支
git branch -d feature-branch



操作示例
Shell
# 在dev2分支开发功能
$ git checkout dev2
Switched to branch 'dev2'
# 合并master最新代码到dev2,发生冲突
$ git merge master --no-ff -m "合并master到dev2"
Auto-merging ReadMe
CONFLICT (content): Merge conflict in ReadMe
Automatic merge failed; fix conflicts and then commit the result.
# 查看冲突文件
$ cat ReadMe
<<<<<<< HEAD
I have coded ... # dev2的代码
=======
Having fixed the bug # master的修复
>>>>>>> master
# 手动解决冲突
$ vim ReadMe
I have coded ......
Having fixed the bug
# 提交解决结果
$ git add .
$ git commit -m "merge master"
[dev2 9e3bf59] merge master
[HY@VM-24-13-centos gitcode]$ git status
# On branch dev2
nothing to commit, working directory clean
# 切换回master合并(此时应该无冲突)
$ git checkout master
Switched to branch 'master'
$ git merge dev2 --no-ff -m "merge dev2"
Merge made by the 'recursive' strategy.
ReadMe | 1 +
1 file changed, 1 insertion(+)
# 删除已合并的分支
$ git branch -d dev2
Deleted branch dev2 (was 9e3bf59).
强制删除分支
在开发新功能时,通常会在feature分支上进行。但如果功能开发中途被取消,该分支未合并就需要删除。此时使用git branch -d会失败,因为分支未合并。
操作示例
直接使用传统删除分支的方法不行,可以使用-D参数进行强制删除:
Shell
# 常规删除失败(分支未合并)
git branch -d dev3
error: The branch 'dev3' is not fully merged.
# 强制删除成功
$ git branch -D dev3
Deleted branch dev3 (was 70248ce).
$ git branch
* master
小结
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,二年你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作
并且Git无论创建、切换和删除分支,Git在1秒之内就能完成!无论你的版本库是一个文件还是1万个文件