前言
本篇博客记录git的杀手级功能,分支管理。
所谓分支,可以简单理解为一个并发的过程,最终可以合并到一条线上。可以简单理解为相同的时间做了多份的事情,效率加倍。
其次,有了分支的存在,我们可以在对主线分支不影响的操作下,创建新分支进行修改从而大大增加安全性,这在生产环境上十分实用。
当然,目前的介绍都是基于git的本地仓库下。
上一篇Git学习记录文章:
目录
一、理解Git分支
如何理解git下的分支呢?
可以发现,我们之前的操作就是建立在上面的情况下的。
HEAD指针默认指向master,而master指向就是我们初始化本地Git仓库时默认创建的一条分支的最新一条记录。 是的,master指向的也是一条分支,分支按照提交修改先后记录为Git对象,master里面保存着最新的一个记录的commit-id。
那么我们创建一个分支是怎么回事呢?
可以发现,我们有了分支管理后,可以在master目前的时间节点上,继承其git对象,并且创建独立于master的分支的dev分支。
dev里同样存储着dev分支的最新一次提交,和master互相独立,并不影响。而此时HEAD指向的哪个节点表示对应分支是否当前主机上的工作分支。
这里只是简单介绍以下分支管理的理解,后续我们结合操作,对Git分支管理进一步的加深理解。
二、分支操作
场景描述:在本地仓库的工作区中存在file1、file2、file3、ReadMe(存在两行内容)文件。并且均以提交被Git版本管理了起来。
1.创建分支
在当前的状态下,我们可以创建一个dev分支,和默认的master分支进行区分。
命令:git branch 分支名
根据分支名,创建对应分支。
在.git文件下的refs/heads下创建对应分支文件,内部保存当前时间点的git文件。
创建完分支后,我们可以对当前仓库的所有分支进行查看:
命令:git branch
对当前仓库下的所有分支进行查看。
*表示当前的工作的分支(即add、commit操作影响的分支)
对于.git文件中创建的dev文件也可以进行验证:
那么如何切换工作分支呢?
2.切换分支
命令:git checkout 分支名
切换到对应的分支下。
命令:git checkout -b 分支名
创建对应分支并且切换到此分支下
可以看到,此时我们以及将工作分支切换到dev下了。
现在我们对readme文件新增一行,表示是在dev目录下的工作。
此时进行add和commit操作托管到Git上管理后我们可以更容易的看到之前理解Git图中的内容。
可以看到,此时两个分支下的工作区的内容并不相同,我们仔细查看日志和对应master、dev中的内容即可理解:
此时的状态就如下图所示:
3.合并分支
我们就现在新增行的状态和master进行合并,让master分支上进行改变。所以我们需要切换到master状态下,然后对dev分支进行合并。
命令:git marge 要合并的分支名
会将对应分支合并到当前工作分支下。
注意:如果合并没有发生冲突,默认是以Fast-forward模式下进行。此状态下进行log图示查询时看不出时marge后的提交还是正常提交(区分不了)
命令:git marge --no-ff -m "此次marge信息" 分支名
会进行分支合并,以no Fast-forward的形式。
此时进行log图示操作可以区分marge还是常规commit。
对于第二个命令在后面合并冲突中进行演示,此处演示第一个进行合并。
此时我们可以用图示的方式查看此次合并的log:
命令:git log --graph --abbrev-commit
可以使用图示的方式查看提交的log,便于查看分支操作。
很明显,看不出是否进行了合并分支的marge操作。
此时的图示就是这样:
4.删除分支
可以发现,此时的dev实际上已经没有用了。(marge过的分支)
对于不需要的分支,我们可以对其进行删除。
命令:git branch -d 分支名
此时可以删除对应的分支。不可自己删自己
注意:删除成功建立在此分支marge过的前提,如果此分支创建出来没有经过marge操作(但是进行过commit操作)会被git保护起来。
命令:git branch -D 分支名
强制删除对应的分支。
此时应用于必须删除的分支,不管其是否进行marge了。
因为我们的dev分支已经marge过了,可以正常的进行删除:
此时就对dev进行了正常的删除,我们可以演示不正常删除的情况:
我们首先创建并且切换到dev分支下,修改了ReadMe的内容并且add和commit的了,但是并没有marge。
我们切换到master分支下,对dev分支使用-d普通删除会发现Git拒绝删除了,因为并没有合并。-D即可强制的进行删除。
三、合并冲突
在前一次我们演示合并的时候,发现合并并没有什么问题。
但是在实际的场景下,合并的时候很有可能出现问题:两条分支均做出过提交修改。此时Git并不知道需要保留哪个,需要程序员自己去做出决策。
并且此时合并不是以默认的fast-forward的模式进行,而是no ff(fast-forward)模式。
当前我们的ReadMe文件是这样的,我们创建个新分支dev,对里面的xxx修改为yyy并且提交,在master分支修改为zzz。
此时的状态如下图所示:
那么此时进行合并会发生冲突:
此时fast-forward模式失效,提示我们需要修复合并冲突,并且提交结果。这样才能算作此次合并成功。
我们可以先到ReadMe文件冲突处查看结果:
可以发现,红色区域表示是当前工作分支的上一次修改结果,蓝色区域表示是dev分支的上一次修改结果。此时Git不知道保留哪一部分,需要程序员自身做决定。
假设选择dev的部分,那么将其余多余删除并且commit后,此次的merge成功。
我们使用图示发现,此时显示可以区分marge操作,因为自动合并失败触发的是no-ff合并模式。
四、合并模式
通过上面的解决合并冲突我们已经可以了解到两种合并模式的区分了。
Fast-forward模式:
默认git merge合并选择的模式。如果双方分支不造成冲突可以直接合并,并且log图示中看不出与正常commit的区别。
No-Fast-forward模式:
当合并分支发生冲突时选择的模式(也可以进行手动git merge --no-ff -m "" 分支),此时解决完冲突后还需要提交(如果没有发生冲突直接一次提交成功),在log图示中可以查看到与普通commit的区别-merge。
注意:一般发生冲突的前提是两个需要合并的分支都进行了修改。
五、bug分支
现在存在这样的情况:
业务需要新增一个功能,于是你在master分支上新起了一个分支dev进行新增内容。
但是此时master分支上存在bug,需要立即切换到master上起dev_bug分支上修复bug。修复完后合并到master分支上。
最后在将dev新增功能合并到master上,并且解决冲突。
现在演示这种情况下该如何进行操作:
目前场景下假设dev分支给ReadMe新增行xxx plus。但是此时还没有提交。
但是此时需要紧急切换到master分支上进行修复yyy的bug(修改为xyz)。
此时可以发现,因为上一分支工作区修改的文件没有被Git管理的情况下所以被同步到了master分支,所以我们需要在上一分支对当前工作区修改的内容进行一个隔离。
Git为我们提供了这样的选项:可以将当前工作区的修改内容(相较于上一次修改),保存到stash中去,工作区回到上一次修改的状态。(实际上此时Git已经为这次修改创建了Git对象了)
工作区暂存stash
命令:git stash
可以将当前分支下的工作区内没有提交的修改暂存到stash(refs/stash)去,工作区恢复到上一次修改的状态(提交过的)。从而避免切换分支影响其他分支的工作区。
先切换到dev分支下,然后git stash。
可以看到,此时dev分支下的工作区内容变成和上次一样了,并且此时查看.git目录会发现在refs底下存在stash文件,内部就是保存的这次暂存的Git文件。
然后切换到master上工作区就不会受到影响,起dev_bug分支修复yyy的bug(修改为xyz)后使用no-ff模式进行合并。(不会发生冲突,因为只存在一个分支进行了修改)
此时解决bug成功后,我们再次切换到dev升级新内容,在pop之前,可以发现原本的dev分支下bug并没有解决,不用担心,在之后合并冲突的时候进行解决即可。
现在我们需要从之前stash中保存的修改信息恢复到工作区:
stash恢复工作区
命令:git stash pop
将stash内保存的修改内容恢复到工作区上。
可以看到,此时从stash中的升级代码确实恢复成功了。提交成功之后,但是现在我们想要和master合并肯定会出合并冲突问题。那么你选择在master上解决还是在dev分支上解决呢?
如果我们需要保持master上的代码稳定性,那么还是在dev上进行合并解决master原有的bug然后解决冲突问题。最后在合并到master上就不会出现问题。
目前就是在dev分支上进行解决冲突:
提交后便就在dev分支上解决了冲突,相当于在dev上完成测试并且解决了master之前的bug。此时在到master上合并新功能dev就不会出现任何冲突。
可以利用git log --graph --abbrev-commit 观察此次修复bug、新增功能历程:
过程可以如下图所示:
六、分支策略
在之后真正的合作环境下,对于线上环境采用master这条线,而日常开发环境就采用分支的条件。
因为线上环境需要稳定的代码,开发人员提交的代码需要经过测试验证,最终才能将稳定的代码提交到master分支上。