Git进阶:熟悉rebase变基
本文最后更新于:3 个月前
rebase和常用的合并方式merge有什么区别?除了简单的合并之外,rebase命令还能实现哪些骚操作?使用rebase命令需要注意什么?
Git进阶:熟悉rebase变基
一、rebase简介
git-rebase - Reapply commits on top of another base tip(译:在其它基础分支的头部重新应用commit修改)
在git中,有两种合并分支的方式:merge
和 rebase
,下面通过两张图来快速了解两种方式的差异:
1 |
|
1 |
|
从合并结果上来看:
rebase
之后得到一条串行的commit链merge
之后得到一条并行的commit链
从合并方式上来看:
rebase
将「修改分支的所有commit节点」 有序地移动到 「目标分支的HEAD节点」 之后。相当于把所有修改行为,在合入分支上重新播放一遍!(记住:虽然节点的数量一致,修改的内容也一致,但C4与C4’并不是同一个节点),也相当于将expriment
分支的祖先节点从C2
变为C3
,即base
发生改变,所以叫rebase
merge
在「目标分支的HEAD节点」之后创建一个 「合并节点」,将「修改分支」和「目标分支」同时指向「合并节点」
至此,你会发现:虽然rebase
和merge
在合并方式、合并的形式上都不同,但最终实现的效果是相同的。别急,下面即将介绍一些rebase
才特有的功能。
二、rebase用途
1. 跨域分支执行合并
这个标题可能有点抽象,看下面的两种图可能会更加清晰:
答案是使用 rebase --onto
命令:
1 |
|
以上命令的意思是:“取出 client
分支,找出它从 server
分支分歧之后的补丁, 然后把这些补丁在 master
分支上重放一遍,让 client
看起来像直接基于 master
修改一样”
注意:此时master
指针还停留在原地,需要将master
指针进行快进
1 |
|
2. 重写commit message
你可能会遇到过如下场景:在开发期间,先后提交了几条commit,例如:先后提交了“chore:初始化项目”
、“feat:添加Home组件”
、“feat:添加api接口逻辑”
。但是突然有一刻,你觉得第一条commit message不是很妥当,于是想要修改它,但是却被新的两条commit阻隔,让你非常犹豫,是否要将三条commit reset
然后全部重新commit一遍
此时,便可以使用git rebase -i
(-i
表示interactive
,交互式的)命令轻松实现,只修改指定的历史commit message
1 |
|
下面给出一个示例:
(1)第一步,mock出多次commit记录
(2)第二步,现在需要在不动HEAD^
、HEAD^2
这两次commit的情况下,修改HEAD~3
的commit message
1 |
|
输入命令后,终端会打开一个编辑器,如下
文首会按照commit的先后顺序,从上到下列出commit记录,此时将需要修改的commit,对应行首的pick
改成edit
(按下i
键,进入编辑模式)
1 |
|
然后(按下esc
退出编辑模式),保存退出(输入:wq
,并回车)
【扩展】如果在编辑该文件的过程中,失误导致了一些无法回撤的操作(eg:误删了一行),可以先:wq
保存退出,然后输入git rebase --abort
终止rebase过程,然后再开启一个新的rebase
(3)第三步,终端会弹出提示。
1 |
|
(键入i
进入编辑,将第一行的commit message,按需求进行修改。按下esc
键退出,输入:wq
,并回车保存退出)
注意:如果此时(即没有输入git rebase --continue
之前)再次输入git commit --amend
,将再次进入同一个commit记录中,可以反复多次进行修改!
(4)第四步,进入下一步
1 |
|
因为rebase过程只有一个edit
需求,所以点击下一步直接完成rebase过程。此时给出成功提示!
【扩展】
Q:如果要修改多条commit message怎么处理?
A:只需要将需要修改的commit记录前面的 pick
都改成 edit
即可!
3. 压缩历史commit
你可能遇到过这样的场景:
- 开发完组件A,提交了一条commit,
feat: 完成组件A开发
- 接着又完成了组件B,又提交了一条commit,
feat:完成组件B开发
- 然后又开发完了组件C,再次提交了一条
feat:完成组件C开发
提交完这些之后,你发现照这样的趋势下去,提交的commit条数可能会太多了,于是便想着把是否可以把这三条相似的commit合并压缩为一条,让提交更精简一点
答案是可以的,使用的正是rebase命令!
过程与上述「rebase重写commit message」基本相同,区别在于一条命令的差异(重写message用edit
、压缩用squash
)
下面继续用一个示例进行演示
(1)第一步,mock出多条commit记录
(2)第二步,输入git rebase -i HEAD~3
(3)第三步,修改命令,将需要压缩的commit记录,对应前面的pick
改成squash
1 |
|
(4)第四步,保存退出之后,紧接着会弹出另外一个编辑器,让你编辑压缩的 message
可以选择保留每次的commit message,最终所有message会合并到一条message中
可以删除不需要的message,只需要将对应的message删除即可
可以对message重新编辑
保存退出后,立即执行压缩,完成后弹出成功提示!
【扩展】
压缩提交还可以通过 git reset –soft 来完成!(Todo),参见:Git - 重置揭密 (git-scm.com)
1 |
|
三、使用rebase的注意事项
在普通的合并场景下,rebase
和merge
都能满足你的需求,区别点在于提交历史是更复杂(merge
)还是更简洁(rebase
),这是个主观和个性化的选择,没有对错之分,取决于你更倾向哪种!
但是请记住:不要对已经存在于第二个人电脑上的分支执行 rebase 操作!退一步来说,不要对已经被推送到远程仓库的分支执行rebase
操作(因为你不知道有没有第二个人拉取了这个分支)。因为rebase
实际上会删除掉一个现存的 commit 节点,然后再新建一个内容一模一样的 commit 节点,这会导致出现不一致的现象!
只讲理论太抽象,下面通过一个例子来了解:
参考链接
对于git初学者,和希望对git有进一步了解的朋友,强烈推荐Git - Book (git-scm.com),理论体系完整、而且有中文版易于阅读