
前言
Git的分支管理是Git最难的部分,且是其重中之重,关于 Git的分支管理,最好移步廖雪峰老师,我这里主要是做一个学习的记录,可能某些地方表述有缺失或者误解。
含义
我们每一次的提交都是一个版本,都对应一个版本号,而一个分支就是将这些版本串在一起的一条时间线,多个分支就相当于从某个版本结点开始出现了另一条时间线,在另一条时间线里可能会做着与原时间线不同的事,它也可能在未来的某个结点与原时间线合并。
从具体应用上来讲,我们默认有一个master分支,通常这个分支存放的是已经具备完整功能的主代码,当我们需要开发一个新功能时,我们会新建一个分支feature来进行开发,在功能开发完毕后合并到原分支当中。
创建和合并分支
在默认情况下,我们有一个master分支,master会指向自己的最新提交版本,而我们的HEAD则是指向master,来间接指向对应版本。
当我们创建一个新的分支dev,此时dev会指向与master指向相同的版本,当我们切换到dev时,则是把HEAD指向dev。
此时如果我们进行版本提交,就会在dev上进行版本的调整,而master是不会受到影响的。
当我们在dev上完成的工作,想把版本合并到master上,就是将master指向与dev指向相同的版本。
在合并结束后,我们还可以删除dev节点,此时master节点也不会受影响。
具体实现: 我们可以利用switch来进行分支的创建
|
|
此时我们就创建了dev分支,并且显示我们已经切换到了该分支。
- git switch命令加上
-c
参数表示创建并切换分支 - 我们可以通过
git banch <branch>
来创建分支,但此时不会切换,还会在master节点上,我们需要git switch <branch>
来切换分支 switch
命令是git新版本出现的,在旧版本下,我们可以通过git checkout <branch>
来切换分支
创建完分支后,我们可以通过git branch
来查看所有分支,*
代表当前所处分支
|
|
现在我们在新的分支上做一些修改,并进行提交:
|
|
完成之后,我们将分支切换回master,就会发现内容变回去了
|
|
切回到master分支上,现在我们将dev上的内容合并到master上:
|
|
我们再次查看内容,发现master的内容与dev一致了。同时,输出信息告诉我们这次合并是Fast-forward
“快进模式”,也就是直接把master指向dev的当前提交。
我们查看一下log,可以看到里面也有了dev分支当中的提交记录:
|
|
合并完成后,我们就可以删除掉dev分支了:
|
|
«««< HEAD
冲突
前面创建和合并分支我们都是在最简单的情况下进行的,但是在多人合作的情况下有时就会发生修改冲突。
比如说我现在仓库里的test.txt,在master上它内容为:
|
|
然后我在dev分支下修改并提交为:
|
|
此时如果合并,就是前面的快速合并,也就是最简单的情况,但如果我的master又更改且提交了:
|
|
此时如果我们用master去合并dev,就会发现它无法合并:
|
|
Git告诉我们test.txt中发生了合并冲突,让我们修复再提交结果,此时我们查看一下status,也发现它有无法合并的部分:
|
|
我们现在直接打开test.txt:
|
|
Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们只需要将其中的内容修改为同一个就行:
|
|
然后再添加和提交:
|
|
这样就解决了冲突:
同时我们在log下也可以发现master已经成功合并dev:
|
|
或者用git log --graph
命令可以看到分支合并图
|
|
所以当我们的合并遇到冲突时,我们需要先解决冲突,然后再提交,完成最终合并。
禁用Fast forward
模式
在我们前面的情况下,我们都是Fast forward
模式,在这个模式下,当我们删除合并后的分支后,我们就会丢失原本分支的信息,不知道这个合并的分支是什么,比如master和并dev后,在log中,我们能看到哪个版本是dev下提交的,但是如果我们在这个模式下删除后,这个消息就会消失。
如果我们要保留分支信息,需要禁用这个Fast forward
模式,如果要禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息
我们先来创建一个dev分支,并修改提交test.txt中的内容:
|
|
现在我们用master合并dev,并禁用Fast forward
模式:
|
|
此时我们打开log:
|
|
可以看到,不使用Fast forward模式,merge后就像这样,master直接接在了dev的后头,而不是指向和dev同一个版本上
此时我们再来删除dev,就可以发现里面还有dev分支的信息
分支策略
在我们正式开发时,我们会有几个原则来管理分支
- master分支应该是稳定分支的,也就是仅用来发布新版本的,平时不能在上面干活
- 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本,当我们组内有多人时,每个人都会有自己的dev分支来独立开发,在完成之后进行合并
- 2个人都修改了同一个文件的内容才会引发冲突,而不是因为合并了2个不同的分支会引发冲突。事实上,在共同开发过程中,由于大家进行的不是相同的开发任务,只要约定某些公用的文件不去提交本地的修改,大多数情况下合并都不会有冲突。
Bug 分支
在我们日常开发中bug是肯定会遇见的,而在git中,我们可以通过新建一个临时的bug分支来修复bug,修复完毕后,将分支合并回去,再删除bug分支。
现在举一个具体情况,我们正在开一个dev分支,突然收到一个任务,在master上有一个bug,我们需要新建一个issue-666的分支来修复它。但是我们的dev上还有没提交的内容,因为还没完成它,所以不想直接commit,这个时候我们可以先把工作现场隐藏起来,等完成任务后在恢复现场:
|
|
这个时候,我们发现我们修改的内容全部不见了,status里面也是干净的,而且我们现在可以正常切换分支。
现在我们就可以放心地修复bug了,首先我们要确定的就是从哪个分支上创建bug分支,因为是dev是从master上分支来的,同样dev也会有master上的bug,这个稍后再提,现在我们决定从master分支上新建bug分支:
|
|
假定我们的bug出现在test.txt的文本内容上,修改完成之后,我们进行提交,并切回master分支,合并bug分支并删除:
|
|
完成bug的修复后,我们现在该回到dev分支上干活了,可切换回去后发现,原先的内容还没恢复,我们可以先看看stash下的内容:
|
|
Git把stash内容存在某个地方了,需要恢复一下,有两个办法:
- 用
git stash apply stash@{0}
恢复,但是恢复后,stash内容并不删除,需要用git stash drop stash@{0}
来删除 - 用
git stash pop
,恢复的同时把stash内容也删了
|
|
现在再用git stash list
就看不见内容了。
前面提到了dev分支是从master中分出来的,也存在master的bug,我们也需要修复dev上的bug,但不需要重复前面的操作,只需要将bug分支所做的提交复制到dev上即可,并不是把整个master分支merge过来。
对于这个问题,我们可以使用cherry-pick
,把特定分支的一个提交复制到当前分支:
|
|
Git自动给dev分支做了一次提交,注意这次提交的commit是与原先的bug提交的版本号不同,因为它们只是做了相同的提交操作,但不是一个提交。
同样的如果说你的dev修改了原先的bug文件,这时候进行提交会出现合并冲突,你需要按照前面的操作先解决冲突再提交。
因为master和dev同有一个bug,既然可以在master分支上修复bug后,在dev分支上可以“重放”这个修复过程,那么直接在dev分支上修复bug,然后在master分支上“重放”也是可以的,不过仍然需要git stash命令保存现场,才能从dev分支切换到master分支
删除未合并分支
当我们在一个dev分支开发时,突然被告知这个功能不再需要了,让我们把这个分支删除,此时master尚未合并该分支,如果我们执意要删除当前分支会发生什么:
|
|
Git就会告知我们该分支没有完成合并,如果我们强制删除需要用大写的D:
|
|
小结
以上便是git中分支的常用操作,再来简单梳理一遍
git branch <branch>
用于创建一个分支,git switch -c <branch>
用于创建并切换到该分支,git switch <branch>
用于切换到指定分支git branch
用于查询分支,git branch -d <branch>
用于删除分支,git branch -D <branch>
用于强制删除分支git merge <branch>
用于合并分支,git merge --no-ff -m "xxx" <branch>
用于在非Fast Forward
模式下合并并记录合并信息git stash
用于隐藏工作现场去处理其他分支,git stash pop
用于恢复上一个隐藏的现场并丢弃,git stash apply stash@{0}
用于恢复指定现场,git stash drop stash@{0}
用于丢弃指定现场- 当发生合并冲突时, 修改冲突的地方并重新提交, 即可解决冲突
- 但有bug需要修复时, 先隐藏当前分支现场, 然后去特定分支新建bug分支, 修复并合并bug分支, 在将bug分支的提交复制到其他工作分支
git cherry-pick xxxx