Git 中整合来自不同分支的修改有两种方式:git merge
和git rebase
。本文主要介绍rebase
的3种使用场景:
场景1: 使用rebase
合并分支--整合分叉的提交历史
使用交互式的 rebase
场景2: 压缩提交历史
场景3: 修改多个提交信息
1 使用 rebase 合并分支
1.1 场景描述
我们在feature
分支开发完新的功能后,通常会通过merge
操作将代码合并到master
分支,这样会产生一条分叉。这时可以通过变基使得提交历史更加整洁,看上去就像是串行的一样,提交历史是一条直线没有分叉。
1.2 操作命令
首先在自己的feature
分支里进行开发,当开发完成时你需要先将你的代码变基到master
上,然后再向master
分支提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。具体操作如下:
#1变基到mastergitcheckoutfeaturegitrebasemaster#2回到master分支,进行一次快进合并。gitcheckoutmastergitmergefeature
1.3 原理图解
假设在你的项目上master
分支的提交为C0、C1、C2、C3
,基于master
的C2
节点拉出了一个feature
分支,并产生了新的提交C4
,如下图:
16-1
整个变基操作的过程如下图所示,首先找到这两个分支(即当前分支feature
、变基操作的目标基底分支master
) 的最近共同祖先C2
,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件, 然后将当前分支(feature
)指向目标基底C3
, 最后以此将之前另存为临时文件的修改依序应用,此时feature
指向C4'
,如下图:
最后回到master
分支,进行一次快进合并,完成后feature
和master
分支均指向C4'
,如下图:
16-3
至此合并分支操作完成,master
的提交历史是一条干净的、没有分叉的直线。
2 使用 交互式的 rebase
通过给git rebase
增加-i
选项来交互式地运行变基。 一般被用于将feature
分支并入master
分支之前,清理混乱的提交历史。
2.1 压缩提交历史
2.1.1 场景描述
觉得commit
太小太散,将多个commit
压缩成一个单独的提交。
2.1.2 操作命令
假设将feature
分支当前最近的3个提交变为一个提交,操作如下:
#1.执行交互式rebasegitrebase-iHEAD~3#或使用如下命令gitrebase-idbcc8dd#dbcc8dd为与HEAD~3对应的commitid#2.进入编辑器,将16.md-2、16.md-3的pick改为squash,代表想要把它们跟上一个commit(16.md-1)合并pickfe08362feat:update16.md-1squash72a7faefeat:update16.md-2squashf606aecfeat:update16.md-3#3.第2步保存并退出编辑器后看到如下画面feat:addfile16.md#删除原本的commitmessage,改为自己想要的提交信息#Pleaseenterthecommitmessageforyourchanges.Linesstarting#with'#'willbeignored,andanemptymessageabortsthecommit.##Date:WedAug320:00:18+0800##interactiverebaseinprogress;ontodbcc8dd#Lastcommandsdone(3commandsdone):#squash72a7faefeat:update16.md-2#squashf606aecfeat:update16.md-3#Nocommandsremaining.#Youarecurrentlyrebasingbranch'master'on'dbcc8dd'.#4.操作完成,验证结果gitlog
注意上述第2步中的反序显示。 交互式变基给你一个它将会运行的脚本。 它将会从你在命令行中指定的提交(HEAD~3
)开始,从上到下的依次重演每一个提交引入的修改。 它将最旧的而不是最新的列在上面,因为那会是第一个将要重演的。
2.2 修改多个提交信息
2.2.1 场景描述
修改在提交历史中的提交信息。
2.2.2 操作命令
例如要修改feature
分支最近三次的提交中的任意一个提交信息。
#1.执行交互式rebasegitrebase-iHEAD~3#2.进入编辑器,将要被修改信息的提交由pick改为reword,代表要修改16.md-1的提交信息rewordfe08362feat:update16.md-1pick72a7faefeat:update16.md-2pickf606aecfeat:update16.md-3#3.第2步保存并退出编辑器后看到如下画面修改后的提交信息feat:update16.md-1#删除原本的commitmessage,改为自己想要的提交信息#Pleaseenterthecommitmessageforyourchanges.Linesstarting#with'#'willbeignored,andanemptymessageabortsthecommit.##Date:ThuAug1816:34:55+0800##interactiverebaseinprogress;ontodbcc8dd#...#4.操作完成,验证修改结果gitlog
2.3 其它的一些操作
除了2.1、2.2的使用场景外,交互式的rebase
还可以调整commit顺序、删除 commit等,操作过程与2.1、2.2大同小异,具体可参考如下指令:
#Commands:#p,pick<commit>=usecommit#r,reword<commit>=usecommit,buteditthecommitmessage#e,edit<commit>=usecommit,butstopforamending#s,squash<commit>=usecommit,butmeldintopreviouscommit#f,fixup<commit>=like"squash",butdiscardthiscommit'slogmessage#x,exec<command>=runcommand(therestoftheline)usingshell#b,break=stophere(continuerebaselaterwith'gitrebase--continue')#d,drop<commit>=removecommit#l,label<label>=labelcurrentHEADwithaname#t,reset<label>=resetHEADtoalabel#m,merge[-C<commit>|-c<commit>]<label>[#<oneline>]#.createamergecommitusingtheoriginalmergecommit's#.message(ortheoneline,ifnooriginalmergecommitwas#.specified).Use-c<commit>torewordthecommitmessage.
3 注意事项
使用 rebase 得遵守一条准则:
如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
换句话说,因为rebase
会变更历史记录,所以最好是在push
之前就做好rebase
,不然就是确保要这个branch
只有你自己在修改,否则你也rebase
、他也rebase
,最后只会把历史记录搞得一团糟。
4 参考资料
Git 官方文档
送 PR 前,使用 Git rebase 來整理你的 commit 吧!