Git分支管理技巧-使用rebase让你的分支笔直生长

2018-04-10 15:49:00
邱simple
转贴:
简书
236

发现问题

之前写过一篇博客 《30分钟教你轻松使用Git做代码管理》,其中有提到本地提交代码的一些步骤:

  1. 如果新参与一个项目,首先需要本地clone远程仓库。之后会有一个master(若不特殊说明,均指代本地master)和远端的master(以下称为remote/master)是关联的,即remote/master是本地master的up-stream。


    从远程仓库进行克隆
  2. 做开发时,要新建一个分支,如dev1,作为你的开发分支。开发完新特性后,将工作区(work tree)的修改提交至暂存区(index),然后commit,此时会在dev1分支上生成一个新的commit单号。



    提交你的修改
  3. 切换回master分支,然后使用pull或者fetch+merge命令与remote/master同步一下(此时可能会有来自其他开发者的提交),再合并(merge)dev1分支的,如果有冲突,则解决冲突。最后推送代码到远端仓库。



    更新本地主分支

    合并dev1分支,推送
    合并dev1分支,推送

    这是我刚开始实习时,常用的一个开发,推送代码的流程。久而久之,我发现了几个问题:

  • 在master分支上解决冲突,可能会有些风险。比如一不注意,冲突没有正确解决,导致本地修改与别人的修改混在一起,本来稳定的与远程保持同步的master分支被你改混乱了,且没有其他的备份了。

  • 在master分支上merge开发分支,如果master分支上有dev1没有的commit单号,则会产生一个携带merge信息的提交单。这个commit你也要推送到远端。企业开发一般会有一个评审分支,主管会对commit单号进行review,然后submit合并进remote/master分支中。merge信息的提交单也会让reviewer做一次review,然而没什么必要的。

  • 一个人提交时,会有一个merge commit,那么10个人提交,就会有10个merge commit,此时分支树看起来会很混乱。零零散散的merge commit信息穿插在你的特性commit之间。

因此,我考虑使用另外一种本地分支管理策略,来改善这样的问题。

使用rebase命令。

先简要说下rebase命令的作用。

比如在dev1分支上,你提交了2个单,c1和c2。然后你在dev1分支上将master分支rebase到当前分支git rebase master。此时,如果master分支已经与remote/master做了同步,更新了2个来自其他人的提交,c3和c4。


场景描述

rebase会做如下操作:
  1. 把dev1分支上的c1和c2“拆”下来,并临时保存成c1'和c2'。git里将其称为patch
  2. 将master分支上更新的提交c3和c4合并进dev1分支上。


    rebase操作,拆分提交
    rebase操作,拆分提交
  3. 将c1'和c2',再按顺序接在c3和c4的提交后面,如果没有冲突,则rebase成功。此时c1'和c2'虽然和c1和c2的修改完全一样,但却已经不是原来的提交了,commit id已经变化了。
    连接patch

    此时dev1分支包含master分支的所有commit,并且超前了两个commit。如果你现在切换至master分支,并执行git merge dev1操作,由于没有不同于dev1的提交,merge操作就不会产生merge commit了。此时推送代码,也只会有两个commit。同时,master分支树笔直前进,分支很清晰地展示一个个提交。并且,上述的更新和提交代码的过程,是在dev1分支上修改冲突的,相对来说会比在master分支上修改更安全,如果不小心改混了,也能通过切换回master分支来找到稳定代码。
    在master分支上合并dev1分支

    基于上述内容,可以使用如下流程来提交代码:

  1. 在dev1分支上进行开发,然后commit提交,在dev1分支上生成一个提交单。
  2. 切换到master分支,与remote/master分支同步。
  3. 切换回dev1分支,将master分支rebase到dev1分支上,如果有冲突,修改冲突。 rebase操作的冲突修改与merge不一样,修改完冲突后,保存进index,然后直接git rebase --continue即可,不同再多做一次提交。
  4. 切换回master分支,合并dev1分支,此时合并会非常顺畅。然后push。

rebase的风险

之前提到,rebase会将当前分支的新提交拆下来,保存成patch,然后合并进其他分支新的commit,最后将patch接进当前分支。这是rebase对多条分支的操作。对于单条分支,rebase还能够合并多个commit单号,将多个提交合并成一个提交。

git rebase -i [commit id]命令能够合并(整改)commit id之前的所有commit单。加上-i选项能够提供一个交互界面,分阶段修改commit信息并rebase。

但这里就会出现一个问题:如果你合并多个单号时,一不小心合并多了,将别人的提交也合并了,此时你本地的commit history和远程仓库的commit history不一样了,无论你如何push,都无法推送你的代码了。如果你并不记得rebase之前的HEAD指向的commit的commit ID的话,git reflog都救不了你。

tips: 你可以push时带上-f参数,强制覆盖远程commit history,你这样做估计会被打,因为覆盖之后,团队的其他人的本地commit history就与远程的不一样了,都无法推送了。

因此,请保证仅仅对自己私有的提交单进行rebase操作,对于已经合并进远程仓库的历史提交单,不要使用rebase操作合并commit单。


发表评论
评论通过审核后显示。