rebase介绍
一、背景
远程仓库有oh4w-dev
和oh4k-dev
两个分支,oh4k-dev
是基于oh4w-dev
开发到80%的代码新拉的分支;此后两条分支同步开发,当oh4k-dev
开发完成,oh4w-dev
还在开发阶段,oh4k-dev
需要拉取到oh4w-dev
自分出oh4k-dev
后的最新提交。
二、git pull VS git rebase
有两种方法,git pull
和git rebase
。
git pull
git pull
其实是git fetch
和git merge
的组合
当执行git pull
时,相当于执行了git fetch
从远程仓库拉取最新代码、git merge
将拉取的最新代码和本地代码合并
使用git pull
,虽然在存在冲突时,会多出无意义的一条提交记录“Merge…to…”,但能清楚地知道当前分支上谁合了代码以及合代码的时间先后顺序
git rebase
git rebase
则是将当前分支的提交历史,建立在指定分支上的和当前分支的公共祖先节点的最新提交上(不论提交的时间先后顺序)
三、选择git rebase的理由
目前的情况使用git rebase
更加合适,因为oh4w-dev
仍然在开发过程中,即使使用git pull
合并一次代码,下次oh4k-dev
再次拉取oh4w-dev
最新代码时,上次合并过的冲突还需要重复处理;而使用git rebase
合并一次提交历史,oh4w-dev
和oh4k-dev
的提交记录的公共祖先节点已经更新了,无需再处理一遍上次处理过的冲突
四、使用git rebase遇到的问题
问题一
本地仓库中,与远程分支oh4w-dev
和oh4k-dev
对应的分支分别是local-oh4w-dev
和local-oh4k-dev
站在local-oh4k-dev
分支使用git rebase local-oh4w-dev
拉取local-oh4w-dev
的最新提交历史,很幸运没有冲突,但在执行git push --force-with-lease
进行强制提交时终端报错
分析了一下原因,是因为这个项目在git push
提交修改到Gerrit远程仓库时,每次提交都需要携带Change-Id
,Change-Id
是Gerrit等代码审查工具用来跟踪提交的唯一标识符,如果Gerrit判断推送的变基后的提交历史中包含已经被合并到主分支或已经关闭的修改(即存在相同的Change-Id),则会导致提交被拒绝
还有一点,推送的远程分支是Gerrit中的refs/for/<branch>
,这是一个特殊的引用命名空间,用于接受代码审查的推送,Gerrit不会像传统分支那样检查其状态;而git push --force-with-lease
的核心逻辑是检查远程分支的当前状态是否与本地引用的远程分支状态一致,所以git push --force-with-lease
对refs/for/oh4k-dev
是无效的,与普通的git push
几乎相同
解决方法
如果Change-Id
冲突,可以重新生成新的Change-Id
:
-
如果是单个commit记录
1.使用git commit --amend
修改提交记录
2.删除旧的Change-Id
3.esc
退出编辑模式
4.输入:wq
保存
5.Git会自动生成一个新的Change-Id
6.git push origin local-oh4k-dev:refs/for/oh4k-dev
将本地的变更推送到远程 -
如果有多条提交记录需要修复,可以使用交互式变基
1.使用git rebase -i HEAD~n
选择最新的n条commit记录进入编辑模式
2.输入i
开启编辑操作
3.将需要修改的提交记录的pick
改为edit
4.
esc
退出编辑模式
5.输入:wq
保存
6.在每一条提交记录处停留,用处理单个提交记录的方式处理
7.每处理完一条,执行git rebase --continue
,直到变基结束
8.git push origin local-oh4k-dev:refs/for/oh4k-dev
将本地的变更推送到远程
问题二
变基结束,将本地合并后的提交历史上传到远程,意味着,之前在local-oh4k-dev
上提交过的修改,需要重新提交一次,可能会存在冲突
解决方法
1.使用git rebase -i HEAD~n
启动交互式变基
2.修改第一条提交之外的状态为fixup(自动合并修改但丢弃提交信息)或squash(自动合并修改且允许修改提交信息)
3.如果产生冲突,解决完冲突后git add .
,git rebase --continue
继续变基
4.变基结束git commit --amend
生成新的Change-Id
5.最后git push origin local-oh4k-dev:refs/for/oh4k-dev
推送本地变更
问题三
本地的提交历史由于进行了rebase已经改变,而git push
不会覆盖远程仓库的提交历史,只是新增了刚刚push上去的新的变更,所以本地的提交历史和远程仓库还是不一致的,可以通过git reset --soft origin/oh4k-dev
,将两者进行同步
思考
这样一条一条提交记录去删除,记录较少的情况下还好处理,一旦数据量过大,很浪费时间,看看可以不可以一键删除?