🏆【Git 实战专题】你知道 git rebase 如何让代码提交变得清晰明白吗?
什么是 rebase
在介绍 rebase 之前,需要提前说明,rebase 是一个非常强大的功能,也正是因为它的强大,所以使用起来也有一定的隐患,一旦使用不好可能会对团队的代码造成非常大的问题。所以在使用前一定要了解其中的原理并小心谨慎的使用。
rebase 的功能可以简要的概述为对某一条线性提交分支上的记录进行编辑、删除、复制、粘贴、合并。合理使用 rebase 可以使团队的提交记录变的非常干净整洁。
注意:如非迫不得已不要通过 rebase 对任何已经提交到公共主干分支上的提交记录进行 commit!
使用 rebase 替代 merge 进行主干/开发分支合并
假设我们在开发的时候同时拉出来两个分支并行开发,然后有一条已经合并到主干分支 main
了,像这样:
这个时候我们想要把主干分支 main
的代码再合并到当前开发分支 test-1
,然后再进行提交,如果使用 merge 会是这样:
如果当前分支有很多的子分支,到最后合并的时候就会出现像一开始那张图一样爆炸的情况。
而如果使用 git rebase main
(或者 git pull --rebase origin main
/ git pull -r origin master
)合并 main
分支,提交记录就会变成这样:
这样清晰明了。
注:使用 rebase 合并主干分支时,当前分支所做的修改永远会被认为是最新的,所以会导致在提交记录上就算
test-1
分支一开始的提交时间在test-2
之前,但是 rebase 之后 commit 记录也是最新的,这也是 rebase 的初衷之一,更清晰的显示出来在什么时候提交到主干的什么功能(因为按照提交时间来看,test-1
分支的功能是在test-2
分支功能合并到主干main
分支后添加的)。⚠️ 正是因为它改变了历史,所以我们更应该谨慎使用 。
当然,如果该分支是测试环境或者预发环境分支,则一般需要使用git merge
来合并。因为在这两个分支上会存在多个分支但是并没有合并到主干的修改。
再看一种情况:
如图所示,test-1-1
分支其实是从 test-1
分支的 commit-1-3
节点拉取出来的,而此时 test-1
分支并没有合并到主干 main
分支,但是 main
分支上的功能要提前合并到test-1-1
分支上,合并完成之后将 test-1-1
与 test-1
独立,变成两个互不相干的分支,这种情况下可以使用 git rebase --onto main test-1 test-1-1
可以看到这种情况下 test-1-1
与 test-1
已经互相独立互不相干了,而且 test-1-1
的记录线已经跟 main
在同一条线上了 *( *不过在正常开发情况下应该避免这种情况发生,因为test-1-1
中已经包含了一部分的 test-1
变更,所以当 test-1
分支再合并主干的时候,可能会有许多冲突 ) 。
我们在使用 git rebase
或 git pull --rebase
合并的时候可能会有许多冲突
在发生冲突时,
git rebase
将停止在第一个有问题提交,并在树上留下冲突标记。我们可以使用git diff
定位标记(<<<<<<),并进行编辑以解决冲突。对于编辑的每个文件,需要告诉 Git 冲突已解决,通常使用
git add <filename>
手动解决冲突并更新到对应的索引,之后,我们可以手动进行下一步进程
git rebase --continue
我们也可以手动撤销 rebase
git rebase --abort
4. 使用 rebase 编辑 commit
rebase 最经典的功能,就是编辑 commit。比如在 pull requests(gitlab 为 merge request)中需要修改代码的时候,需要把修改的内容合并到已经提交的 commit 上。
这里我们可以使用 git rebase -i [startpoint] [endpoint]
来编辑提交记录,其中-i
的意思是--interactive
交互的意思,即弹出交互式页面供使用者编辑。[startpoint]
和[endpoint]
指的是编辑的开始和结束节点(不包含[startpoint]
节点,范围逻辑是[startpoint] < commits <= [endpoint]
),如果不指定[endpoint]
则结束节点会指向HEAD
所对应的节点(一般使用的时候也不指定结束节点...)。
举个栗子:
此时我们想编辑test-1
分支的最新三条历史记录,我们可以使用前面所说的
git rebase -i 39ce617
或者是 git rebase -i HEAD~3
,我们会看到如下页面:
指令说明:
在该页面点击i
键进入编辑页面,即可在指令模块编辑,编辑完成后点击esc
然后输入:wq
保存并退出进入下一步操作。
一般来说,除默认的pick
之外,最常用的就是reword
、edit
、squash
、fixup
这几个命令了,这里就只详细的讲解以上命令。
4.1 reword
如果我们想要修改 commit-1-5
这条 commit 的提交信息,只需在该条之前写明 r
或 reword
方法:
保存退出之后进入 commit 编辑页面:
修改并保存之后,在提交记录里可以看到本地 commit-1-5
记录已经被修改成了 commit-1-5-reword
,如果后面还有分支存在修改,则使用 git rebase --continue
命令进行之后的修改,待所有 commit 都修改完成之后再使用 git push -f
或git push --force
强制推送代码覆盖远端后,远端的提交记录就变成了:
可以看到提交记录已经被修改了。
4.2 edit
使用该方法之后,我们就可以修改想要修改的内容,修改完并执行 git add
之后,就可以调用
git commit --amend
来修改 commit 的记录内容,这里可以看到 edit 之后修改的文件是 README.md,
然后修改 commit 方式跟 reword
一致,待所有 commit 都修改完成之后,使用 git push -f
强推至远程便可以实现记录内容的编辑修改了。
4.3 squash
字面意思,使用 squash
是将记录压缩到一起,一般用于一个大功能开发完成后合并提交到主干分支上使用,这里我们可以用 vue 的提交记录举例:
可以看的出来 fix(types): async Component types (#11906)
记录下合并了许多子记录,这样也可以清晰的表明一个迭代的功能及开发提交记录。
4.4 fixup
fixup
也是将代码进行保留并合并到上一条提交记录,唯一的区别就在于 fixup
的记录被删除,此方法适用于我们在 MR 时候的 comment 修改。
注:如果我们在 rebase 的过程中因为一些原因退出了终端中断了 rebase,我们可以回来继续使用git rebase --edit-todo
进行继续编辑。
版权声明: 本文为 InfoQ 作者【李浩宇/Alex】的原创文章。
原文链接:【http://xie.infoq.cn/article/5d060ce545ac140978893cc30】。文章转载请联系作者。
评论