Welcome to 9ilk's Code World

(๑•́ ₃ •̀๑) 个人主页: 9ilk
(๑•́ ₃ •̀๑) 文章专栏: Git
本篇博客我们来介绍Git的一个重要功能之一 ---- 分支。我们将讲解关于分支的各种操作,以及如何帮助我们进行开发。
🏠 理解分支

假设你处于一个平行宇宙,主时间线里你按部就班的学者C++,OS,另一个时间线的里学着Java,到达某一个时间点时两个时空合并,此时相当于你在一段时间掌握的技能增加了,这里平行时空2相当于是在当前分支基础上创建一个新分支 ,到达某一个时间点,我们再将两个分支合并。
head与master
在版本回退里,每次提交,Git都会把他们串成一条时间线, 这条时间线 可以理解为是一个分支 。截止到目前,我们只有一条时间线,在Git里**,这个分支叫主分支,即master分支**。
再来理解一下,HEAD 指针严格来说不是指向提交,而是指向master ,master才是指向提交 的**,** 所以HEAD(默认)指向的是当前分支。

每次提交,master分支都会向前移动一步,随着你的不断提交,master分支的线也越来越长,而HEAD只要一直指向master分支即可指向当前分支。通过查看当前的版本库,我们也能清晰的观察:拿到master分支,我们就能通过parent依次拿到该分支下之前的一个个提交。

🏠 创建、切换、合并分支
当我们创建本地仓库时,Git会自动给我们创建master分支 ,如果我们想查看本地有哪些分支 ,我们可以使用 git branch 命令

- *****表示当前正在master分支工作。
- HEAD不仅可以指向master分支,也可以指向其他分支,其中被HEAD指向的分支就是我们当前的工作分支。
创建分支
bash
git branch + 分支名
创建完一个新的分支之后,我们可以使用git branch验证一下是否创建了新分支:

还可以在.git目录树上验证:

由于我们创建dev分支是基于master分支创建的,所以创建出来之后会指向最近的一次提交:

时间线图如下:

切换分支
我们之前说HEAD所指向分支为当前工作分支,那如何切换当前工作分支 呢?我们可以使用命令git checkout + 分支名
bash
git checkout + 分支名
使用git branch操作验证下是否切换成功:

切换后的时间线如图:

合并分支
由于当前dev分支只是刚基于master分支创建,合并没什么意义,我们对dev分支做一次提交
修改ReadMe文件:

两板斧提交:
bash
zhuang@VM-8-14-ubuntu:~/gitcode$ git add ReadMe
zhuang@VM-8-14-ubuntu:~/gitcode$ git commit -m "md ReadMe"
[dev 3165a96] md ReadMe
1 file changed, 2 insertions(+), 1 deletion(-)
zhuang@VM-8-14-ubuntu:~/gitcode$ git status
On branch dev
nothing to commit, working tree clean
此时我们切换到master分支观察变化:

此时我们切换到dev分支:

为什么切换到master分支下我们没有观察到变化,而在dev分支上内容还在呢?
这是因为我们是在dev分支上提交的 ,而master分支此刻的提交点没有变,即没有在master分支上master进行提交,时间线不会延长,此时状态如图:

当切换到master分支时,HEAD指向了master,此时自然看不到提交。
此时如果我们想让我们之前修改的信息能在master分支上能查看到,此时需要合并两个分支, 但是注意master合并dev,需要先切换到master,再合并
bash
git checkout + 要合并到的分支
git merge + 要合并的分支
此时我们就能在master分支上查看到我们之前修改的信息了:

- Fast-forward表示快进模式(快速提交),直接把master的指向指向dev的最新提交,合并起来很快
合并后的时间线图如下:

合并之后如果我们在master分支下再进行一次提交会发生什么?

合并之后并不是指dev分支等同于master分支了,而是将dev时间线的各个版本与master分支进行合并,往后他们各自还是独立的:

🏠 删除分支
将master分支和dev分支合并完之后,dev的使命基本结束了,此时我们需要把它删除,那怎么删除一个分支呢?
bash
git branch -d + 要删除的分支名
注意:要删除一个分支时,需要先切换到其他分支,否则会报错

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全 。

🏠 合并冲突
合并分支不是说能随便合并的,合并时也可能出现冲突:

假设master分支上已经有aaa on branch的提交,我们模拟剩下的内容:
(1)在dev分支上提交 ccc on branch
bash
zhuang@VM-8-14-ubuntu:~/gitcode$ git checkout -b dev1
Switched to a new branch 'dev1'
补充选项 :git checkout的-b 选项帮我们完成两件事:先帮我们创建新分支,再切换到新分支上
bash
zhuang@VM-8-14-ubuntu:~/gitcode$ vim ReadMe
zhuang@VM-8-14-ubuntu:~/gitcode$ git add ReadMe
zhuang@VM-8-14-ubuntu:~/gitcode$ git commit -m "bb ReadMe"
[dev1 700c7b6] bb ReadMe
1 file changed, 1 insertion(+), 1 deletion(-)
(2)在master分支上提交ccc on branch
bash
zhuang@VM-8-14-ubuntu:~/gitcode$ git checkout master
Switched to branch 'master'
zhuang@VM-8-14-ubuntu:~/gitcode$ vim ReadMe
zhuang@VM-8-14-ubuntu:~/gitcode$ git add ReadMe
zhuang@VM-8-14-ubuntu:~/gitcode$ git commit -m "cc ReadMe"
[master a5c1371] cc ReadMe
1 file changed, 1 insertion(+), 1 deletion(-)
(3**)合并分支**

我们打开ReadMe文件查看冲突内容,Git会用<<<<<<<,=======, >>>>>>>来标记出不同分支的冲突内容:

此时我们需要自己决策,手动调整冲突代码,并需要再次提交修正后的结果!


此时的时间线图如下:

如果我们想查看分支的合并情况,也可以使用git log命令带上 --graph --abrev-commit选项

🏠 合并模式
Fast-forward 模式

这里我们创建新分支dev2,提交一次之后与master分支进行合并,我们git log看看结果:

在这种Fast forward 模式下,删除分支之后 ,查看分支历史时,会丢掉分支信息,看不出来最新提交到底时merge进来的还是正常提交的。

no-ff模式
在合并冲突部分,我们也看到通过解决冲突问题,会再进⾏⼀次新的提交,得到的最终状态为:

那么这就不是 Fast forward 模式了,而是no-ff模式 ,这样的好处是,**从分⽀历史上就可以看出分⽀信息。**例如我们现在已经删除了在合并冲突部分创建的 dev1 分⽀,但依旧能看到master其实是由其他分⽀合并得到。
Git**⽀持我们强制禁⽤ Fast forward 模式(带上--no-ff选项)** ,那么就会在merge时⽣成⼀个新的 commit ,这样,从分⽀历史上就可以看出分⽀信息 :
在dev2分支上提交:

使用no-ff模式,此时需要一次新的提交,需要带上-m选项:

用git log查看:

此时时间线图如下:
🏠 分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先 ,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢**?干活都在dev分支上** ,也就是说,dev分支是不稳定的 ,到某个时候,比如1.0版本发布
时,再把dev分支合并到master上 ,在master分支发布1.0版本;(类似王者荣耀更新版本);你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。所以,团队合作的分支看起来就像这样:

🏠 Bug分支
线上环境不是百分百稳定 ,master主分支也可能有bug,我们应该单独创建一个 专门修复master分支bug的分支再合并 ,不能直接在master分支上修改bug有可能改出一个更大bug
我们要模拟的场景如下:

(1)dev2分支正在开发(I am coding)

(2) 暂存dev2分支工作区内容
由于dev2分支开发时只是对工作区进行修改并未提交所以会影响master,此时我们可以使用git stash命令先将工作区内容暂存起来

此时我们发现在.git目录下多了个stash分区:
注意 git stash命令只能存储之前被追踪管理过的文件内容

(3) 创建bug分支修改分支
假设bug为:

创建fix_bug分支修改bug提交之后然后与master分支合并:

(4) 修复完bug切回dev2分支继续开发
git stash list展示stash里存了什么
git stash pop将stash内容放出来
我们将内容恢复然后在dev2分支上进行提交

此时我们注意到,bug修复的内容在dev2分支上没有显示,这是因为master分支目前最新的提交,是要领先于新建dev2时基于的master分支的提交的。

我们最终目的是让master合并dev2分支,但是这样是有风险的,这是因为在合并分支时可能会有冲突 ,而代码冲突需要我们手动解决(在master分支上解决) ,我们无法保证对于冲突问题可以正确地一次性解决,实际开发中,代码冲突不只一行两行那么简单,解决过程出错难免,导致错误的代码合并到master分支上。此时状态为:

解决这个问题的一个好的建议就是:最好在自己的分支上(dev2)合并下master,再让master去合并dev2 ,这样做的目的是有冲突可以在本地分支解决并进行测试,而不影响master。此时的状态为:


演示:
dev2合并master

再将dev2合并到master

最后删除fix_bug和dev2
bash
zhuang@VM-8-14-ubuntu:~/gitcode$ git branch
dev2
fix_bug
* master
zhuang@VM-8-14-ubuntu:~/gitcode$ git branch -d dev2 fix_bug
Deleted branch dev2 (was 1714a31).
Deleted branch fix_bug (was dffd15d).
🏠 强制删除分支

我们之前git branch -d删除分支的前提是merge,git认为只要你创建分支不能随便删,此时我们确定该分支可以删,只需改选项-D表示强行删除
bash
git branch -D 分支名

总结分支相关操作:
创建分支 : git branch
切换分支 : git checout
创建 + 切换分支 : git checkout -b
合并分支 : git merge
删除分支 : git branch -d
查看分支情况 : git log --graph --abrev-commit
no-ff模式合并 : git merge --no-ff -m "message"