在使用git进行代码版本控制时,经常会遇到分支合并的情况,而merge和rebase都能够实现分支的合并,那么什么时候用merge什么时候用rebase呢。
Branch分支
git中的分支可以理解为是一条条的开发线路,这些线路可能会有重叠的部分,也可能有自己独有的部分,而且不同的线路还可以合并到一起。实质上git中的分支是指向了某个commit的指针,创建分支相当是创建了一个指针,指向某个commit,分支的切换实质上是指针的切换,git中的指针主要有Branch指针(分支指针)和Head指针。
Branch指针
如上图所示,dev分支和feature分支实质上就是分支指针,它们分别指向了各自分支中最新的提交。
Head指针
一个项目中可能会有多个分支,但当前使用的只能是一个分支,Head指针可以理解为是一种标记,它指向了当前正在使用的分支指针。当使用git checkout
或者git switch
命令切换分支时实际上就是切换Head指针的指向,将Head指针指向了Branch指针。如下图所示,Head指针指向了dev分支指针,即表示当前正在使用的分支是dev分支。注:一种例外情况,当使用
git checkout
切换到当前分支的某次commit上时,Head指针的指向将从Branch指针移动到此commit上,并且在内部创建一个匿名分支,这种情况下Head指针处于一种游离状态。开发者在此匿名分支上进行修改之后,可选择保存或者丢弃修改,如果保存修改并进行了commit,那么可以使用git checkout -b [New Branch Name] 命令创建一个新的分支(Branch指针)。
merge命令
如下图所示,假设有一个开发分支dev,以及从dev分支拉取的特性分支feature,然后在feature分支上进行新功能的开发并有了两次提交D、E。如果要将feature分支上的内容merge到dev分支,考虑到此时dev分支可能没有新的提交也可能有新的提交,就会存在两种情况,这两种情况的merge分别称为快进式合并(Fast-forward merge)和三路合并(Three-way merge)。
快进式合并
如上图所示,如果dev分支没有新的提交,那么feature分支和dev分支其实是在一条链上的,只需要dev分支指针指向到feature分支指针上即可,如下图所示。
三方合并
如图所示,如果dev分支有了新的提交F和G,此时两个分支所做的修改会生成一个新的提交E,然后dev分支将会移动到该提交上。具体来说,就是分别将feature分支的提交D、E以及dev分支的提交F、G,和两个分支的共同祖先C,这三方进行合并并生成一次新的提交H。另外,因为dev分支和feature分支有可能会对同一文件进行了修改,所以三路合并过程中git可能会提示冲突,需要开发者解除冲突后才可完成合并。
注:如果设置
git merge
时使用了--no-ff
(non fast-forward)参数,那么即使满足快进式合并的条件,也会以类似三路合并的方式生成一次新的提交完成merge操作。
rebase命令
如上图左图所示,有dev分支和feature两个分支,它们有共同的祖先C,并且在此基础上还都有两次新的提交,如上节所属,使用merge命令进行合并,将会产生一次新的提交。如使用以下rebase命令进行合并:
git checkout dev
git rebase feature
其中,feature称为基分支,dev称为待变基分支,当执行 rebase 操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面。
结合上图解释,就是在执行rebase操作时,git会先将待变基分支dev上的新提交F、G提取出来暂存,然后dev分支指针会指向feature分支的最新提交E上,接着暂存的提交F、G会接在E后面,最后dev分支指针移动到G上,完成rebase操作。其中要注意的是,虽然rebase操作后端提交F、G和之前的提交内容是一样的,但是它们的commit id是不同的,因为它们实质上是用同样的内容进行了行的提交,而且在rebase过程中也可能会存在冲突。
git pull的过程
通常使用git pull
命令将本地代码与远端的代码同步,执行git pull
命令实际上会先执行git fetch
,再执行git merge
命令,其中,git fetch
表示将远程仓库的所有文件拉取到本地的远程仓库,然后git merge
将本地远程仓库的文件与本地仓库中的文件进行合并。但是当使用git pull --rebase
命令拉取代码时,在fetch
操作之后,git将会运行git rebase
命令去合并代码。
总结
- 下游分支更新上游分支内容的时候根据情况使用merge或rebase
- 上游分支(master)合并下游分支内容的时候使用merge
- 更新当前正在使用分支的内容时使用rebase
merge和rebase虽都能完成分支的合并,但由于其合并原理的不同,拥有不同的使用场景。git merge
适合在公共分支上使用,用以将其它分支合并到公共分支,由于merge操作会新生成一个commit提交,因此会留下清晰的合并记录便于追溯合并情况。git rebase
适合个人分支(只自己一个人提交)。日常开发过程中,个人分支代码需要和公共分支代码保持一致,定期合并公共分支代码到个人分支,这种场景很适合rebase,可以形成线性提交记录,更加直观。
sdf