分支并非Git特有,其他的诸如SVN的版本控制系统都有,但是创建和切换的速度实在不好恭维,因此逐渐的分支变成了摆设,无用武之地。Git则不同,不论是创建还是切换分支,那速度简直是杠杠的,不少人评价Git的分支管理简直是门艺术。既然Git的分支被夸得天上有地上无的,我们是不是得好好的认识下呢。
什么是分支?
就像是树木,有一个主干,主干可以生长出很多枝干,每个枝干还可以生出大量的小分支。Git的分支也是类似的情况,我们初始化仓库的时候Git会生成一个分支master,我们可以在此基础上创建大量的分支,分支上还可以创建分支。当然Git的分支跟树木的分枝还是有些区别,首先Git各分支没有主次之分,如果你不想要master分支也可以删除,对于树木的主干我们可不能这么任性,不过一般情况下我们默认使用master作为主分支;其次,Git的分支可以合并,树木的分枝可没有get这么高的技能。
为什么要使用分支?
为了让你的项目更加有条理。默认情况下Git只有唯一分支master,当然我们可以在这个分支上进行所有操作,不论是开发,测试还是修复BUG。但是随着提交次数的日益增多,master的提交树会越来越长,我们想要找到需要的版本也会越发困难。终于到了某一天,我们发现居然无法在这么多提交找到我们需要的版本,那将会是一件多么悲催的事情。因此我们需要分支来为我们的各种提交操作分分类,开发操作在开发分支上进行,修复BUG的操作在修复BUG的分支上进行,各分支互不干扰,互不影响,当一个分支任务完成后,可以合并到其他分支,最终形成一个完整的项目。
分支管理策略
使用分支是毋庸置疑的,平时开发的时候应该使用哪些分支呢?
主要分支
master:默认的主分支,该分支上不应该进行开发操作,只保存能够对外发布的版本。
develop:开发分支,所有的开发操作都在该分支上进行。
辅助分支
feature:功能分支,当有一个新功能需要开发的时候,可以从develop分支上新建一个功能分支,该功能都在这个分支上进行开发。
hotfixes:修补分支,也可以成为BUG分支,程序出现BUG在所难免,修复BUG我们应该在BUG分支上进行。
release:预发行分支,当程序开发到一定程度,没有新功能要加入的时候,就可以进入预发行状态,此时只需要对小BUG进行修复后就可以成为发行版本。
主要分支(master和develop)一般是我们开发的时候必备的,而辅助分支则看项目需求,上述列举的几个辅助分支都是我们开发的时候应该常会用到的,至于是否需要其他的辅助分支,可以根据自身项目情况酌情添加。
如何使用分支?
创建分支
git branch <branch-name>
在Git中创建一个分支非常容易,只需要输入“git branch 分支名”即可。
重命名分支
git branch -m <old-branch-name> <new-branch-name>
-m之后紧跟的是现有的分支名称,最后才是新的分支名称,两者位置不要弄错了。
切换分支
git checkout <branch-name>
对于git checkout相信大家不会陌生,当初在“管理修改”的教程中有使用过,不过当初checkout的作用是撤销文件的修改状态,参数中特意加了--,如果没了--,就变成了今天我们要讲的切换分支的命令了。
创建并切换分支
git checkout -b <branch-name>
想要创建分支后就立刻切换,那就尝试下上面的命令吧。
合并分支(快速合并)
git merge <branch-name>
当某个分支的任务完成后,我们就可以把它合并到其他分支。默认的合并方式为快速合并(Fast-forward)。
分支冲突
如果合并分支和被合并分支对于同一文件进行了不同的修改,就会产生分支冲突,从而导致合并失败。
Git会提示你哪些文件产生了合并冲突(例如上图中的Merge conflict in dev就说明冲突出现在dev文件中),我们找到该文件进行修改。在该文件中会出现<<<<<<< HEAD,=======,>>>>>>> dev等字样,其中<<<<<<< HEAD和======之间的部分就是当前版本对于冲突文件做出的修改,=======和>>>>>>> dev,就是要合并的分支对于冲突文件做出的修改,我们需要做的就是结合实际情况对该文件做出修改,完成后git add和git commit即可。<<<<<<< HEAD,=======,>>>>>>> dev这些内容只是为了方便我们知道哪些版本对文件做了哪些修改,没有任何实际意义,可以没有顾忌的删除。
合并分支(非快速合并)
刚才我们尝试过了快速合并,但是快速合并有一个缺点,就是没有说明提交是否来自分支,例如我们在dev进行了一个提交,然后合并到master中,此时你通过git log能够看出来那些提交是来自dev吗?完全跟在master上提交没有任何区别,如果此时我们删除了dev,我们完全找不到dev分支存在过的痕迹。
在使用git log的时候,我们看到了两个新的参数,--graph,--abbrev-commit。使用--graph参数之后可以看到开头多出一些 ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。使用--abbrev-commit之后,原本40个字符的commit id被缩短到了7个字符。
如果想要避免这种情况,我们可以加上--no-ff参数,这个参数是禁止使用快速合并,顺便加个-m,为此次合并做个说明。
git merge --no-ff -m "commit info" <branch-name>
使用--no-ff进行合并之后我们再来看看git log,果然有了一些变化。我们可以清楚的看到commit的记录有了分支,证明有些提交操作是在分支上进行的。
删除分支(普通删除)
git branch -d <branch-name>
当合并完成了,如果分支不在有价值,我们可以通过-d参数进行删除。有一点需要说明,当你有多条分支的时候,例如master,dev,feature,hotfixes四条分支,只有dev分支合并了feature,其他分支没有合并,那么只能在dev分支下使用-d进行删除操作,其他分支(master和hotfixes分支)进行删除操作会报错。
删除分支(强制删除)
git branch -D <branch-name>
如果某个分支还未开发完成,但是不具备开发价值,我们就需要在不合并状态下删除该分支,但是如果不合并,使用-d就无法进行删除操作。此时-D就派上用场了,它可以在分支没有被合并的情况下强制删除分支。
查看分支
git branch //查看本地分支
git branch -r //查看远程分支
git branch -a //查看所有分支(本地+远程)
团队协作
如果开发需要多人协作,本地分支就不能满足要求了,我们需要借助远程分支。
拉取远程分支
当远程仓库里有出现我们本地没有的分支,我们就需要从远程库中拉取(假设我们已经使用git remote add添加了远程仓库osc-git)。
状况一:本地未建dev分支
git fetch osc-git dev
git branch --track dev osc-git/dev
上述两个命令做了四件事,一拉取远程分支dev到本地,二建立本地分支dev,三合并远程分支dev到本地分支dev,四本地分支dev和远程分支dev建立关系。
状况二:本地建立了分支dev
git fetch osc-git dev
git checkout dev
git merge osc-git/dev
git branch --set-upstream-to osc-git/dev
上述四个命令同样做了四件事,一拉取远程分支dev到本地,二切换到本地分支dev,三合并远程分支dev到本地分支dev,四本地分支dev和远程分支dev建立关系。
注意事项
--track和--set-upstream-to参数中的osc-git/dev是已经被git fetch拉取到本地的远程分支,如果该远程分支没有拉取到本地,使用的时候会报错,因此使用前应该使用git fetch拉取对应远程分支到本地。
推送远程分支
git push osc-git dev
在远程仓库一节我们已经推送过master分支,其他分支的推送也是一样。
删除远程分支
删除本地分支我们使用-d(或者-D)就行,但是本地分支删除了,远程分支还在,我们还想删除远程分支怎么办呢?很简单,使用如下命令即可。
git branch osc-git :dev
或者
git branch osc-git --delete dev