# Git教程 - 4 分支

# 1 工作树

git 在每次提交代码的时候,都会创建一个节点,通过一个一个节点来记录代码的状态。

举个例子:

我们进行了4次提交,那么就会创建4个节点(下面的节点名称是我自己起的),每一提交就会在链式结构的后面追加节点。

每次的提交的信息都保存在节点中。

在实际的开发中,这些节点会构成一个树状结构,会存在分叉,可能会形成如下的结构,叫工作树。

所以我们之前提交完代码提示 working tree clean

只是默认情况下只会存在一个分支,就是 master 分支。

# 2 创建分支

为什么需要多个分支呢?

如果我们只有一个分支,那么现在线上发布的版本存在bug,但是当前的分支上,已经开发了很多的新功能,而且没有完成测试,那怎么办呢?

其实正常的情况,应该是 master 分支始终是一个随时可以发布的,是线上版本的分支,而我们还维护一个分支叫 develop 分支,在这个分支上开发最新的功能,如果此时有线上发布的版本有bug,那么我们就切换到 master 分支上进行修改,然后发布 master 分支上的代码。新功能开发测试完成,再将 develop分支合并到 master 分支。

所以我们可以创建多个分支来管理代码,每个分支之间是相互独立的(相当于平行宇宙),在一个分支上修改代码不会影响其他分支。

注意,这里简化了,后面再讲 Git flow 工作流。


我们可以使用如下指令进行分支的查看、创建、删除。

git branch							# 查看有哪些分支
git branch 分支名				# 创建分支
git branch -d 分支名			# 删除分支
1
2
3

举个例子:

我们也可以在 VSCode 的终端执行指令,这样修改和提交代码更方便一些。

# 4 切换分支

在 VSCode 窗口最下面下的状态栏,显示的分支名称为 main 表示当前在 main 分支上。

main分支就是master分支,因为 master 这个词在过去被视为与奴隶制度有关联的词汇(主从master和 slave),跟种族歧视有关系,不够政治正确,于是改名了,简直日了狗,那个,那个,那个怎么说呢,简直吃饱了撑的。

所以在下面 master分支 就是 main 分支,main 分支就是 master 分支 。


重新再次创建 develop 分支。

我们可以使用 git branch 命令查看分支,当前在什么分支,就会在前面显示 *

现在使用如下指令切换分支:

git switch 分支名			# 切换分支
1

举个栗子:切换到 develop 分支:

可以看到切换到了 develop 分支上。


git switch 是 git 新版本才有的命令(从 2.23 开始),在旧版本中使用 git checkout

使用 git switch 的地方可以使用 git checkout 替代。

# 5 创建并切换分支

我们也可以使用如下指令,创建分支的同时切换到该分支上:

git switch -c 分支名			# 创建并切换分支
# 或使用checkout
git checkout -b develop
1
2
3

举个栗子:重新创建并切换到 develop 分支

# 6 分支的简单使用

比如我们的 master 分支已经发布到了生产环境,准备开发新功能,这个时候,我们开发新功能需要在 develop 分支上开发,不能在master分支,因为如果只有一个master分支,我们在上面添加了新代码,生产环境又突然发现bug,那就没办法了。

正常的操作是:master分支用于生产环境,开发新功能在develop分支,此时发现生产环境有bug,我们需要切换到master分支进行修改,修改完成,发布master分支即可。

但是其实我们切换到 master 分支修改bug的时候,还需要重新创建一个 bug 分支,在bug分支上修改bug,修改完成,再将bug分支的代码合并到 master 分支,然后再发布master分支。

为什么这样做呢?

其实这也是保持 master 分支随时是一个可以发布的状态,如果我们在master分支修改代码,代码修改到一半,此时的 master 分支就不是一个可以运行的状态了。而且如果我们在 master 分支上正在修改 bug1,修改到一半,发现了bug2 更严重,现在要先修改bug2,那么修改bug1的代码怎么办,难道删掉吗?而我们修改bug,先创建bug1分支,此时要修改bug2,我们重新切换到 master 分支,再创建 bug2 分支来修改bug2即可。


下面简单演示一下:

例如,我们现在项目下有一个 1.txt,经历了四次编辑,每次增加了一个bug

这是第一次写的bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4

提交的记录如下:

此时的工作树是这样的:

这里的 master 和 HEAD 是什么呢?

master 就是指 master 分支,master 分支指向 C4,表示当前 master 分支上的最新提交是 C4 节点。

HEAD 表示当前看到的代码在哪里,HEAD 也指向 C4,表示看到的就是C4 时候的代码。如果我们将 HEAD 指向 C3,就可以看到 C3 时候的代码,也就是看到历时版本了,待会再讲。


然后,我们准备开发新功能,创建 develop 分支,并切换到 develop 分支:

git switch -c develop
1

此时的工作树会变成如下:

此时创建了 develop 分支,develop 分支也是指向 C4 节点的。


然后我们修改 1.txt,注意,现在我们是在 develop 分支修改。

修改 1.txt 为如下:

这是第一次写的bug
develop分支开发第一个新功能
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5

然后暂存并提交,这次提交称它为 C5 节点。

使用 Git Graph 看到的提交记录如下:

此时的工作树会变成这样:

此时 develop 分支在 C5 节点,master 分支还在C4 节点,因为现在我们看到的代码是 C5 的,所以此时 HEAD 指向了 C5。

HEAD 就像是眼睛,看到哪里的代码就是指向哪里。

此时使用 git log 命令,也可以看到 (HEAD -> develop)


此时发现 master 分支有 bug 了,但是不能在master 分支修改,需要创建 bug 分支修改。

所以首先切换到 master 分支

git switch master			# 看你的主分支是main还是master
1

此时的工作树会变成这样:

因为现在切换到了 master 分支,master 分支在 C4,所以此时看到的是 C4 的代码,所以 HEAD 也指向了 C4;

此时发现在 develop 分支上添加的 1.txt 的内容不见了,因为添加的内容在 C5,内容变成了:

这是第一次写的bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4

然后在 master 分支创建 bug1 分支,并切换到 bug1 分支进行修改bug。

git switch -c bug1
1

现在是有三个分支了:main、develop、bug1


修改 1.txt,此时是在 bug1 分支修改。修改内容为如下:

修改第一个bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4

然后暂存并提交,这次提交称它为 C6 节点。

此时的提交记录如下:

此时的工作树如下:

因为现在看到的是 C6 状态的代码,所以 HEAD 指向 C6。


然后发现bug1没有修改好,需要再次修改,然后又继续在bug1分支修改并暂存提交。

1.txt 修改为如下:

修改第一个bug
再次修改第一个bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5

此时的提交记录如下:

此时的工作树如下:

我们此时在bug1分支上提交了两次代码。

# 7 合并分支

现在代码修改完成了,怎么发布修改的代码呢?

因为我们是在主分支(master)上发布代码,所以需要将bug1分支上修改的内容合并到 master 分支。

所以现在要合并分支。

将 bug1 分支合并到 master 分支,需要先切换到 master 分支。

git switch main				# main 就是 master,看你本地的主分支的名称
1

此时的 master 分支的内容变成了 C4,1.txt 内容为:

这是第一次写的bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4

然后将 bug1 分支合并 到 main 分支,使用如下指令:

git merge bug1
1

可以看到执行结果显示 Fast-forward,表示快速合并。

此时成功的 bug1分支修改的内容合并到 master 分支了, 1.txt 内容变为:

修改第一个bug
再次修改第一个bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5

此时的工作树如下:

此时 master 、 bug1 、HEAD 都指向了 C7,看到的就是 C7 的代码。

什么是 Fast-forward 快速合并呢?

就是master分支的节点都在bug1分支节点的前面,合并的时候,master分支的指针沿着节点从C4移动到C7即可。

快速合并后,代码是已经 commit 了,合并后不用 commit。


合并完成,bug1 分支没用了,可以删除bug1 分支了。

git branch -d bug1
1

此时的工作树如下:

通过 Git Graph 也能看到树的结构。

# 8 处理冲突

好了,我们现在再次切换到 develop 分支,然后再次修改 develop 分支,修改完成,暂存提交 1.txt。

develop 分支的 1.txt 内容如下:

这是第一次写的bug
develop分支开发第一个新功能
这是第二次写的bug
develop分支开发第二个新功能
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5
6

此时的提交记录如下:

Git Graph 显示的时候,哪个分支是最新的提交,该分支是直线显示。

我自己重新画一下,工作树结构如下:

现在 develop 分支开发完成了,要将代码合并到 master 分支上。

和刚才的操作一样,要先切换到 master 分支,然后将 develop 分支合并到 master分支。

git switch main
git merge develop
1
2

可以看到,出问题了,没有快速合并,因为 C7 和 C8 不能通过移动指针就可以到达了。

日志显示 1.txt自动合并失败,需要处理冲突才能提交。这是因为 1.txt, master 分支在 C4 的基础上进行修改,develop 也是在 C4 的基础上进行修改,现在要合并,就出现冲突了。

合并前,master 分支上的 1.txt 内容是:

修改第一个bug
再次修改第一个bug
这是第二次写的bug
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5

develop 分支上的 1.txt 内容是:

这是第一次写的bug
develop分支开发第一个新功能
这是第二次写的bug
develop分支开发第二个新功能
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5
6

上面刚才执行了 git merge develop,此时打开 1.txt,变为了:

<<<<<<< HEAD
修改第一个bug
再次修改第一个bug
=======
这是第一次写的bug
develop分支开发第一个新功能
>>>>>>> develop
这是第二次写的bug
develop分支开发第二个新功能
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5
6
7
8
9
10
11

这是什么鬼?

莫慌!这里的:

<<<<<<< HEAD
=======
1
2

这中间的内容表示的是 master 分支的修改,因为刚才是在 master 分支,HEAD 和 master 指向的都是 C7 节点。

=======
>>>>>>> develop
1
2

这中间的内容表示的是 develop 分支的修改。

就是上面这两块区域的代码发生冲突。


现在需要手动修改 1.txt,可以随意修改,修改为自己想要的结果就好了。例如我修改完成的 1.txt 内容如下:

修改第一个bug
再次修改第一个bug
develop分支开发第一个新功能
这是第二次写的bug
develop分支开发第二个新功能
这是第三次写的bug
这是第四次写的bug
1
2
3
4
5
6
7

合并冲突完成,就可以将文件加入暂存,提交到仓库即可。

如果有多个文件冲突,就需要处理多个文件的冲突,冲突都处理完成,再提交。

上面合并的时候,是自动合并失败,有时候虽然不是快速合并,但是自动合并成功了,也就是虽然是多个分支修改了代码,但是修改代码的地方不在一个文件或不在一个文件的地方,自动合并是可能成功的,那么就不需要处理冲突了,直接提交就可以。

此时的工作树变为:

master 、develop 分支和 HEAD 都指向了 C9 。


使用 VSCode,也可以使用可视化的操作进行暂存和提交,提交的时候,甚至帮你写好了合并的日志(当然可以随便改)。