概念
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了 git 又学会了 SVN!
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
git 把我们之前每次提交的版本串成一条时间线,这条时间线就是一个分支。截止到目前只有一条时间线,在git里,这个分支叫主分支,即 master 分支。HEAD 严格来说不是指向提交,而是指向 master,master 才是指向提交的,所以,HEAD 指向的就是当前分支。
创建与合并分支
(1)一开始的时候,master 分支是一条线,git 用 master 指向最新的提交,再用 HEAD 指向 master,就能确定当前分支,以及当前分支的提交点。
每次提交,master 分支都会向前移动一步,这样,随着你不断提交,master 分支的线也越来越长。
(2)当我们创建新的分支,例如 dev 时,git 新建了一个指针叫 dev,指向 master 相同的提交,再把 HEAD 指向dev,就表示当前分支在dev上。
git 创建一个分支很快,因为除了增加一个 dev 指针,改变 HEAD 的指向,工作区的文件都没有任何变化。
(3)不过,从现在开始,对工作区的修改和提交就是针对 dev 分支了,比如新提交一次后,dev 指针往前移动一步,而 master 指针不变。
(4)假如我们在 dev 上的工作完成了,就可以把 de v合并到 master 上。gi t怎么合并呢?最简单的方法,就是直接把 master 指向 dev 的当前提交,就完成了合并。
git 合并分支也很快,就改改指针,工作区内容也不变。
(5)合并完分支后,甚至可以删除 dev 分支。删除 dev 分支就是把 dev 指针给删掉,删掉后,我们就剩下了一条 master 分支。
案例:
(1)执行如下命令可以查看当前有几个分支并且可以看到现在在哪个分支下工作。
$ git branch
(2)下面创建一个分支 dev 并切换到其上进行工作。
$ git checkout -b dev
(3)下面我们修改 code.txt 内容,在里面增加一行,并进行提交,创建版本。
(4)dev 分支的工作完成,我们就可以切换回 master 分支。
$ git checkout master
查看 code.txt,发现添加的内容没有了。因为那个提交是在 dev 分支上,而 master 分支此刻的提交点并没有变。
(5)现在,我们把dev分支的工作成果合并到master分支上。
$ git merge dev
git merge
命令用于合并指定分支到当前分支。合并后,再查看 code.txt 的内容,就可以看到,和 dev 分支的最新提交是完全一样的。
注意到上面的 Fast-forward 信息,Git 告诉我们,这次合并是“快进模式”,也就是直接把 master 指向 dev 的当前提交,所以合并速度非常快。
(6)合并完成后,就可以放心地删除 dev 分支了,删除后,查看 branch,就只剩下 master 分支了。
小结:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
解决冲突
合并分支往往也不是一帆风顺。
(1)再创建并切换到 dev。
$ git checkout -b dev
(2)修改 code.txt 内容,并进行提交。
(3)切换回 master 分支。
$ git checkout master
(4)在 master 的 code.txt 添加一行内容并进行提交。现在,master分支和dev分支各自都分别有新的提交,变成了这样:
这种情况下,git 无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
(5)执行如下命令尝试将 dev 分支合并到 master 分支上来。git告诉我们,code.txt 文件存在冲突,必须手动解决冲突后再提交。
(6)git status
也可以告诉我们冲突的文件。
(7)查看 code.txt 的内容。
(8)git 用 <<<<<<<,=======,>>>>>>> 标记出不同分支的内容,我们修改后保存。
(9)再提交,现在 master 分支和 dev 分支变成了下图所示:
(10)用带参数的git log
也可以看到分支的合并情况:
$ git log --graph --pretty=oneline
(11)最后工作完成,可以删除 dev 分支。
分支管理策略
通常,合并分支时,如果可能,git 会用 fast forward 模式,但是有些快速合并不能成功,而且合并时没有冲突,这个时候会合并之后并做一次新的提交。但这种模式下,删除分支后,会丢掉分支信息。
(1)创建切换到 dev 分支下。
$ git checkout -b dev
(2)新建一个文件 code3.txt,编辑内容后提交一个 commit。
(3)切换回 master 分支,编辑 code.txt 并进行一个提交。
(4)合并 dev 分支内容到 master 分支。
(5)出现如下提示,这是因为这次不能进行快速合并,所以 git 提示输入合并说明信息,输入之后合并内容之后 git 会自动创建一次新的提交。
(6)使用分支命令查看分支信息。
$ git log --pretty=oneline --graph
(7)最后就可以删除 dev 分支了。
$ git branch -d dev
如果要强制禁用 fast forward 模式,git 就会在 merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息。
(1)创建并切换到 dev 分支下。
(2)修改 code.txt 内容,并提交一个 commit。
(3)切换回 master 分支。
(4)准备合并 dev 分支,请注意 --no-ff 参数,表示禁用 Fast forward。
$ git merge --no-f -m '禁用fast-forward合并' dev
因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
(5)合并后,我们用git log
看看分支历史:
可以看到,不使用 Fast forward 模式,merge 后就像这样:
Bug 分支
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
(1)当你接到一个修复一个代号001的 bug 的任务时,很自然地,你想创建一个分支 bug-001 来修复它,但是,等等,当前正在 dev 上进行的工作还没有提交。并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
(2)git 还提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。
$ git stash
(3)首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支。
$ git checkout master
$ git checkout -b bug-001
(4)在bug-001我们修复了bug,然后提交。
(5)修复完成后,切换到 master 分支,并完成合并,最后删除 bug-001 分支。
(6)现在 bug-001 修复完成后,是时候接着回到 dev 分支干活了!发现工作区是干净的,刚才的工作现场到哪去了?用git stash list
命令查看。
$ git stash list
现场还在,git 把 stash把内容存在某个地方了,但是需要恢复一下。
$ git stash pop
小结:
修复bug时,我们会通过创建新的 bug 分支进行修复,然后合并,最后删除。
当手头工作没有完成时,先把工作现场git stash
一下,然后去修复 bug,修复后,再git stash pop
,恢复工作现场。