rebase
⇒ 将一系列节点移动到目标分支的最后 ⇒ 也叫变基
rebase
vs cherry-pick
rebase
和 cherry-pick
的操作角度是不一样的
cherry-pick
是把别的分支提交合并到当前分支rebase
是把自己提交合并到目标分支
基本原理
初始分支结构为
此时在dev
分支执行 git rebase main

- 复制
c2
和c3
生成内容完全一致,但sha-1
不一致的节点 dev
分支移动到了复制后的c3'
c2
和c3
没有使用,被称之为悬挂提交
,再下次 git 自动执行git gc
时会被自动移除
此时看起来就像是 dev分支是基于 main分支的最新提交来开发的
这样的好处是
- 整个开发分支线变成线性的了,相对简单
- 在
main
分支执行git merge dev
的时候就变成了 快进合并
这也是rebase
和 merge
的最大区别
merge
如果不是快进会产生新的提交节点rebase
是让节点尽可能的线性,使merge
可以快进合并
其中 c3'
是和c3
节点内容完全一致的节点,之所以不复用之前的节点是因为,节点的parent
发生了改变,即改变了历史
在变基的过程中,是需要变基节点依次进行变基操作
c2
执行变基,将c2
相对于c1
的变动作用于c5
,并依次为基础生成c2'
c3
执行变基,将c3
相对于c2
的变动作用于c2'
,并依次为基础生成c3'
- 如果有其余节点指向类似操作
所以其本质也是依次执行三路合并
- 第一次 ⇒ 三路为
c1
、c2
、c5
- 第二次 ⇒ 三路为
c3
、c2
、c2'
- 依次类推
既然是三路合并,就可能会产生冲突,解决方法如下
-
解决冲突:用编辑器(比如 vi)修改冲突文件,保留需要的改动,然后用以下命令继续:
shellgit add . git rebase --continue
-
忽略冲突::
shellgit rebase --skip
意思是冲突了,就将当前提交视为空提交,直接跳过,不合并 ⇒ 不推荐
-
终止变基:不想继续变基,恢复到变基前的状态:
shellgit rebase --abort
此时会和数据库的事务一样,依次回滚,将之前的变基操作都还原
rebase
vs merge
并列分支
单人维护分支推荐使用rebase
,假设分支结构如下
shell
A---B---C---D (main)
\ \
\ H---I (fix/log)
\
E---F---G (fix/login)
此时在 fix/login
分支执行 git rebase main
,再切到 main
执行git merge fix/login
shell
A---B---C---D---E'---F'---G' (main)
\
H---I (fix/log)
此时别的分支不会受到任何影响,使用rebase
还可以让分支合并是线性的,而merge
合并后会生成新节点
例如在 main
分支直接执行 git merge fix/login
,此时将会生成一个新合并节点 M
shell
A---B---C---D----------M (main)
\ \ /
\ E---F---G-/ (fix/login)
\
H---I (fix/log)
分支上的分支(依赖分支)
shell
A---B---C---D (main)
\
E---F---G
\
X---Y
此时就需要团队协商确认,大家统一使用merge
还是 rebase
假设初始分支如下
shell
A---B---C---D (main)
\
E
张三从 E
开始继续开发,生成F
和 G
shell
A---B---C---D (main)
\
E---F---G (fix/zs)
但此时李四,从E
开了一个新分支进行开发, 结构变成了
shell
A---B---C---D (main)
\
E---F---G (fix/zs)
\
X---Y (fix/ls)
此时如果在 fix/zs
执行 git rebase main
对于张三而言,分支就变成了
shell
A---B---C---D---E'---F'---G' (main)
此时张三进行了更新推荐,李四执行了git pull
此时对于李四而言,她的分支变成了
shell
A---B---C---D---E'---F'---G' (main)
\
E---F---G
\
X---Y (fix/ls)
原分支不变,E'
、F'
、G'
会新增提交
如果此时在 fix/ls
执行 git rebase main
-
因为
E
、F
、G
和E'
、F'
、G'
内容一致所以
E
、F
、G
会被抛弃,直接使用E'
、F'
、G'
所以
E
、F
、G
也被称之为空提交
最终分支结构如下
shell
A---B---C---D---E'---F'---G' (main)
\
X---Y (fix/ls)
此时并不会有很大的问题
但如果此时 李四在main
执行的git merge fix/ls
, 分支结构就变成了
shell
A---B---C---D---E'---F'---G'---M (main)
\ \ /
E---F---G \ /
\ X---Y (fix/ls)
\ /
--------------------
此时很明显 存在很多重复的 parent
节点
所以一般情况下,推荐分支的开发模式是 git flow模型
+ git rebase
-
只
rebase
单人维护开发的分支 -
如果一个分支是多人一起维护的,就不能随意使用
rebase
- 此时要么都用
merge
,要么都用rebase
- 不应该混用
merge
和rebase
- 此时要么都用