Git学习笔记(二)分支管理与合并及Bug分支
一、分支管理
1、什么是分支
分支就相当于我们看科幻片里的平行宇宙,如果两个平行宇宙互不干扰,那铁定是啥事儿没有。不过,在某个时间点,两个平行宇宙合并了呢?假如两个宇宙中都有你的影子,
合并之后相当于你们两个合二为一,所有的技能都合并在一个人的身上!
分支在实际中有什么用呢?
假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
2、分支的创建与合并
我们已经清楚的知道版本的每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点(只要不回滚,一定是在最新的版本上),每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长,如图所示!
创建
1)当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前在dev分支上;不过有一点需要特别注意:从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev分支的版本就会更新一次(git log 可以查看到更新日志),而master分支是不会改变的。
创建分支: $ git branch dev #创建dev分支 $ git checkout dev #切换到dev分支 $ git checkout -b dev //git checkout命令加上-b参数表示创建并切换,相当于上边两条命令
注意:创建的时候,一定要注意所在分支的位置!分支的创建相当于是在当分支的最新版本为起始生成的一个旁支,这个分支上最基础的版本一定是这个起点(可以理解为克隆了一份放到了其他的旁支上)!同时你现在也应该甚至必须知道,分支之间的关系是平行的互不干扰!!!也就是说你在当前分支更新了一次版本只是这个分支的时间线往前走了一步,而另一个分支的版本是丝毫不变的!
2)用git branch命令查看当前分支,git branch命令会列出所有分支,当前分支前面会标一个*号。
$ git branch * dev master
PS:我们也可以通过git status 查看当前工作区所在的分支【代码中第二行】,会有相应的提示!!!再或者是在当前git的窗口下,$提示符上一行(当前仓库的绝对路径最后会有当前分支的标记,括号内)
3)用git checkout命令切换分支,如下代码,切换到master分支:
$ git checkout master 用 git branch命令查看当前分支 $ git branch dev * master
既然学会了创建分支和切换,我们可以尝试着在dev分支上做一次版本更新!在dev版本的工作区仓库下,我们可以创建一个readme2.txt文件,在这个文件中随意添加点儿内容,保存。然后我们通过命令提交到版本库。
$ git add . $ git commit -m "branch test" $ git status //看下工作区是否干净!
所有工作完成,我们切换到master分支 命令:$ git checkout master此时你会惊奇的发现,工作区内我刚刚新创建的readme2.txt文件不见了!心里甚是惶恐!怎么办?怎么
办?是不是我的git仓库瓦特了?!
别急,咱再切换到dev分支去看看,到底是怎么回事!命令:$ git checkout dev,切换过来之后,你发现工作区新建的readme2.txt文件有神奇的出现了!!!这时候你肯定会想这是为什么呢?咱们一开始提到过的平行分支的概念!git就是这么玩儿的!分支之间是互不干扰的!!!在dev分支上你爱怎么玩儿就怎么玩儿,对我主分支是没有丝毫影响。这就相当于是上线的业务分支和新开发的程序分支一样,业务线分支上的程序在正常运转,而你在开发这个分支上恶搞,能影响到业务线吗?
合并
当我们在dev上完成了我们的开发任务,我们就想着怎么把上线的业务更新到最新版本呢?这时候就需要把dev分支上的版本合并到master分支上。那Git怎么合并呢?这时候你是否还记得,上面咱们说的主分支master分支,这时候我们就需要回到主分支上,然后使用命令对dev分支进行合并!这样就相当于是完成了合并操作,把dev的版本更新到master版本上!
$ git checkout master //切换到master分支 $ git branch //查看当前所在分支 $ git merge dev //git merge 分支名 命令用于合并指定分支到当前分支。 查看分支合并情况: $ git log --graph $ git log --graph --pretty=oneline --abbrev-commit //详细
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。我们实战一下--no-ff 方式的git merge:
创建分支及切换更新版本操作此处省略;
当切换到master分支下,准备合并dev分支的时候,请注意--no-ff参数,表示禁用Fast forward:命令如下:$ git merge --no-ff -m "merge with no-ff" dev 因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
合并后,我们用git log看看分支历史:此处要敲代码了(哥)!我们会发现不使用Fast forward模式,merge合并后给出的合并方式(因为多了一步 commit)。
合并分支的风风雨雨 -----> 冲突问题:
时刻记住一句话,合并分支往往不是一帆风顺的。无冲突那肯定是万事大吉,什么事都不用做!高兴的喝茶就好!如果合并问题的过程中出现冲突,那我们就需要手动解决冲突后再提交!我们可以通过 git status 查看发生冲突的文件。然后进入文件,我们会发现,git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们根据 git 给出的标记,将两段内容中重复的部分手动去掉,然后把两段内容整合!最后去除标记符号,保存!然后手动提交到仓库(分支)。
举例如下:
第一种,无冲突发生的情况:如果你在master分支上创建一个新的分支(dev),然后在该仓库下添加了某个文件(如test.txt)并更新到分支,当合并时由于master分支的库中没有这个文件(test.txt),那么合并时就不会出现冲突问题!
第二种,有冲突发生的情况:如果你在master分支上创建一个新的分支(dev),正在为某个文件(如:plugins.py)添加新功能,这时有人跟你说这个文件(plugins.py)中有bug,你又在master分支基础上重新建立了一个bug分支,用于修改这个bug当改好测试正常之后,合并到主分支master上,然后你切换到dev分支下plugins.py文件继续完善你的功能,当你测试正常合并的时候,git报错说你plugins.py文件的出现冲突了!为什么呢?
就因为你在plugins.py文件改了一次bug,更新了主线master分支版本,你开发分支dev操作的时候也是修改的这个文件,当合并时,git发现同一个文件下,开发分支dev与主分支master之间的版本差一代,他对不上了,所以就引发出现冲突这个问题!
出现这个问题不用慌张,这不是你的代码除了问题,而是你的版本出现差异了!只需要找到对应的文件,按照文件内部git给出的表记把内容整合在一起,然后再手动提交一次,就万事大吉!
删除
合并完分支后,甚至可以删除dev分支。当然删除操作要慎重删掉临时创建而不常用的分支(当然,我们需要的话还是可以在创建的!),这样我们就剩下了主分支master.
git branch -d dev //只删除完成合并之后的dev分支 git branch -D dev //强制删除dev分支, 场景:dev分支上开发新更新的某个版本,还未合并到主分支,而项目发展出现变动你开发的东西不再需要,通过-d的方法删除这个分支git会报错的,此时就需要强制删除!
二、关于Bug我们不得不说的那点事儿
bug一直伴随在程序员的身边,如胶似漆比媳妇儿都黏人。哪怕是你考虑的再周全,你也不可能是上帝把所有要出现的问题全部解决!更何况你还不是上帝呢!所以说,在业务上线之后,你正在为这个业务编写新功能时,出现bug了怎么办呢?宝宝不慌,Git给了我们两种解决方案!
方案一:(stash,工作正在进行,新内容还没提交)
当你正在进行的工作还没法提交【不是效率问题,而是时间预估】,而bug必须在2小时内解决,总不能把现在写好的东西先保存到别的地儿然后再恢复到原来的版本吧,这样一个小时都过去了,你还怎么如期完成修复bug的问题?幸好,Git还提供了一个stash功能,可以把当前工作现场临时“储藏”起来,等以后恢复现场后再继续工作:
临时隐藏工作区:(常见的场景是,当我们需要临时处理其它事情【如:解决BUG】时而不得不暂时中断目前的主要开发任务) //dev分支下 $ git stash //隐藏目前工作区 $ git status //查看工作区,就是干净的(除非有没有被Git管理的文件)因此可以放心地创建分支来修复bug。 //切换到master分支,创建分支 $ git checkout master //切换到需要建立临时分支的分支,比如:master $ git checkout -b issue-101 //建立并切换到临时分支,issue-101 用于修复bug //修复完bug,提交 $ git add file //添加到暂存区 $ git commit -m "fix bug 101" //提交到临时分支仓库 //切换到master分支,合并 $ git checkout master //回到master分支 $ git merge --no-ff -m "merged bug fix 101" issue-101 //合并临时分支,这里禁用fast foward模式,保留提交记录以便日后可查或恢复 //删除分支 $ git branch -d issue-101 //删除临时分支 //到此bug修复完成,切换到dev分支! 查看被隐藏的工作区信息: $ git stash list 恢复工作区: $ git stash pop //相当于以下两条命令 $ git stash apply //恢复工作区 $ git stash drop //清除隐藏区
在dev分支下,我们执行 git status 命令看到工作区是干净的,原先我们隐藏的文件去哪里了?我们可以使用 git stash list 查看!发现工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了:
恢复之后,再使用 git stash list 查看就应该看不到任何文件了!此处要注意一点:可以多次把工作区临时隐藏存放,恢复的时候,就需要使用命令:git stash list 查看,然后利用命令:git stash apply stash名 指定恢复文件!
方案二:(推荐,当然是你当前dev分支已经提交保存了)
利用分支,在业务线的基础上创建一个bug分支,解决完bug问题之后合并分支,没冲突就过,有冲突就解决冲突再提交!问题解决!
三、分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样(盗用廖神的图片):