在Git中,原则上使用merge或者rebase都可以解决分支冲突的问题,比如:1
2
3      D (dev)
     /
A---B---C (master)
当master和dev存在冲突时,在dev上进行git merge master或者git rebase master都可以解决冲突。这两种方式的作用机制不同,结果也不一样。
使用merge
当使用merge时,git merge master会首先把master上的commit都搬进dev里,然后创建一个新的commit(M)以记录如何化解冲突:1
2
3      D---M (dev)
     /   /
A---B---C (master)
M的特点是具有两个parent。注意这时的dev分支就包括了master上的所有信息,包括commit message。这可能并不是一件好事,比如如果有人在commit message里@了某人或者某issue,那当你把处理了冲突的分支push上去的时候就又@了一遍。
当解决了dev和master的冲突以后,把dev合并进master就可以fast-forward了:1
2
3      D---M (dev, master)
     /   /
A---B---C
使用rebase
使用rebase时,D会直接被接到C上变成D':1
2
3      D   D' (dev)
     /   /
A---B---C (master)
这时从master去合并dev分支也可以fast-forward,最后的结果看起来会更漂亮一些,因为通过修改历史的方式曾经的冲突都被隐藏了起来,不存在有两个parent的commit:1
2
3      D   D' (dev, master)
     /   /
A---B---C
如果考虑到最后Git会把D这个commit垃圾回收,最终的状态是:1
A---B---C---D'(dev, master)
可以说是非常理想了。因此在比较简单的冲突的情形下,使用rebase是更好的方案,这种方案下master的历史非常清晰,易于管理。
复杂情况最好用merge
但是当分支冲突情况比较复杂时,rebase并不能妥善地解决冲突问题,而应该使用merge,比如下面这种情况,dev1和master有冲突,dev2基于dev1开发,与master也有冲突:1
2
3
4
5        F (dev2)
       /
      D (dev1)
     /
A---B---C (master)
在这种情形下,假如将dev1分支rebase到master上,得到的状态是:1
2
3
4
5
6
7        F (dev2)
       /
      D
     /   
A---B---C (master)
         \
          D' (dev1)
其中D'是rebase之后的D。这时如果我们只关心dev1和master之间的冲突,那么问题已经解决了。然而比较让人头疼的是dev2和master之间的冲突问题并没有解决,如果要将dev2和master合并,就需要把dev1和master之间的冲突再重新处理一遍,这显然是低效的。
如果使用merge方案,dev1分支和master分支合并后的状态是:1
2
3
4
5        F (dev2)
       /
      D---M (dev1)
     /   /
A---B---C (master)
这时直接将dev2和master合并起来还是会有冲突,但是dev2可以直接rebase到dev1上:1
2
3
4
5        F   F' (dev2)
       /   /
      D---M (dev1)
     /   /
A---B---C (master)
通过这种方式,dev2可以重用dev1处理与master冲突的commit。这时再将dev2合并进master就可以fast-forward:1
2
3
4
5        F   F' (dev2, master)
       /   /
      D---M (dev1)
     /   /
A---B---C
总结
Git中的rebase和merge命令都可以用于解决冲突。对于简单的情形,比如冲突分支只由一人开发是线性分支,出于简化master历史的考虑应该优先使用rebase。而当冲突比较复杂时,则应该回避修改历史,使用merge化解冲突。