一、前言
对于git rebase
一直不太了解,这几天想着提高下git提交质量,就发现了这个好用的指令,顺便记录一下,好加深记忆
贴出官方文档以便大家进一步学习 Git
二、rebase是作用
rebase
官方解释为变基,可以理解为移动你的分支根节点,维护一个更好的提交记录。rebase
把你当前最新分支与其他分支合并时候,会把其他分支的提交记录放在我们当前分支时间线最开始的位置。也就是说,会把我们的提交记录整合在公共分支的后面。- 简单来讲,合并本地其他分支 为了不产生多余的分叉,及合并记录时可以使用
rebase
。 - 接下来我们看一下
rebase
有哪些应用场景及使用技巧与merge
的差异。
三、rebase与merge的差异
rebase
会把你当前分支的commit
放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。merge
会把公共分支和你当前的commit
合并在一起,形成一个新的commit
提交。
四、应用场景
-
你刚入公司,技术leader让你以
master
分支为基础,拉出一个分支进行开发需求。此时master
分支提交记录为a、b
。你在dev
分支分别commit
了2次记录为e、f
,有其他同事在master
分支提交了两次记录为c、d
这个时候你要合并master分支代码。 -
当前分支状态节点如下图所示:
五、使用git merge 进行合并操作
5.1、结论
- 如上图所示
merge
会把两个分支合并在一起,形成一个新的commit
提交记录。- 我们发现
coomit
提交记录是把合并的分支记录放到我们当前dev
分支记录的后面。- 并且
coomit
提交记录会产生分叉
。
七、使用git rebase 进行合并操作
$ git rebase master // 合并master分支代码$ git log --graph --oneline // 查看log点线图// 符号解释:* 表示一个commit, 注意不要管*在哪一条主线上 | 表示分支前进 / 表示分叉 \ 表示合入
7.1、结论
- 首先,我们发现目前
dev
分支上面的提交记录为abcdef
并没有像merge
一样产生新的提交记录- 其次
rebase
master
分支到dev
分支,dev
分支的历史记录会添加在master
分支的后面。- 如图所示,历史记录成一条线,非常整洁,最后并没有像使
merge
一样提交记录产生分叉
。
八、rebase的使用业务场景
- 认识到了rebase,让我们来看看有哪些实战场景可以参考使用。
8.1 场景一
- 经典场景,优化本地提交记录,使其减少分叉。
- 和上面👆那个经典案例一样,这里就不做重复描述了😁
8.2 场景二
- 连续性冲突
- 此时你从master分支拉出一个dev分支来对以v1版本为基础a功能进行需求更改,由于项目经理分功能时,让你的同事也对master分支中的a功能中的某个公共页面a也进行了整改,过一会你同事改完,提交x版本并合并push到了master远程分支上面。此时我们在dev分支完成一次开发提交了v2版本,产品经理过来说,需求有变更,要再做修改,然后我们又以v2的基础上在做修改,并提交为v3版本。过一会测试又提了一个需求建议。我们接着以当前dev分支v3版本为基础做好了整改并commit一个v4版本。到此我们本地假设对页面a修改了三次。同事修改了一次,并push到了远程master上面。那么我们对master分支进行合并到时候就会产生冲突。
- 简单来讲就是。远程分支 master 对文件a进行了1次 commit ,而别的分支dev对文件A进行了3次commit,但是本地分支dev提交的n次 commit都与master分支的1次commit有冲突,
8.2.1 使用 git rebase 解决冲突
$ git fetch // 更新本地存储的远程仓库的分支引用
$ git rebase origin/master // 拉去远程分支master中的代码与当前分支合并且变基
// 此时我们会产生第一次冲突,为当前dev分支版本v2中的a页面与远程分支master中的a页面冲突。解决后,根据提示进行
$ git add .
$ git rebase continue // 继续进行合并
// 此时我们会产生第二次冲突,为当前dev分支版本v3中的a页面与远程分支master中的a页面冲突。解决后,根据提示进行
$ git add .
$ git rebase continue // 继续进行合并
// 此时我们会产生第三次冲突,为当前dev分支版本v4中的a页面与远程分支master中的a页面冲突。解决后,根据提示进行
$ git add .
$ git rebase continue // 继续进行合并
// 至此我们使用 rebase 变基完成 可以根据产品需求push到远程dev分支
$ git log --graph --oneline // 查看log点线图
// 符号解释:
* 表示一个commit, 注意不要管*在哪一条主线上
| 表示分支前进
/ 表示分叉
\ 表示合入
8.3 结论
1、不会因为像使用
merge
时合代码时遇到冲突产生新的提交记录2、用 merge 只需要解决一次冲突即,简单粗暴,而用
rebase
的时候 ,需要依次解决每次的冲突,才可以提交。3、使用
rebase
提交记录不会分叉,一条线干净整洁4、冲突解决完之后,使用
git add
来标记冲突已解决,最后执行git rebase --continue
继续。如果中间遇到某个补丁不需要应用,可以用下面命令忽略:git rebase --skip
5、如果想回到
rebase
执行之前的状态,可以执行:git rebase --abort
8.4 场景三
- 你开发的一个需求产品反复更改,导致你的commit记录多次重复功能点
- 在日常开发中,难免有重复的commit提交记录.这时候我们想优化一下提交记录该如何做呢
- 当前分支状态节点如下图所示:
使用git rebase 进行commit记录合并操作
$ git rebase -i HEAD~x // i(interactive)交互,HEAD~x 代表要合并到距离HEAD最近的几个历史提交,如 HEAD~3就是历史的前3个提交.$ git log --graph --oneline // 查看log点线图// 符号解释:* 表示一个commit, 注意不要管*在哪一条主线上 | 表示分支前进 / 表示分叉 \ 表示合入
- 这里我们使用git rebase -i HEAD~3,此时我们会出现如下界面,我们进行简单设置
此时我们会产生一条新的提交记录,并选择填写提交相关信息,并删减掉合并掉的提交记录
- 到此我们的合并已结束。
8.4.1 结论
- 合并了冗余的提交记录,并产生了一条新的提交记录
- 使提交记录看起来更整洁,也方便同事查阅
九、使用过程中可能出现的问题
当你在自己的分支A上完成开发,并执行
git rebase develop
后,本地分支A的历史已经被重写,此时尝试推送(git push
)到远程仓库时,可能会遇到需要先拉取(pull
)的情况。以下是详细解释及解决方
原因分析
-
历史冲突:
-
假设你之前已将分支A推送到远程仓库,此时远程分支A指向某个提交(如
commit-old
)。 -
执行
rebase develop
后,本地分支A的提交历史被重写,生成新的提交(如commit-new
)。 -
此时本地分支与远程分支的历史分叉(尽管内容可能相同,但提交的哈希值不同),Git会检测到冲突。
-
-
Git的安全机制:
Git默认拒绝直接推送分叉的历史,提示你需要先拉取远程变更(pull
),以避免覆盖他人提交。但由于分支A只有你使用,这实际是本地与远程的历史不一致导致的“假冲突”。
解决方案
1. 强制推送(推荐)
如果你确认远程分支A没有他人提交,且需要保持线性历史,使用强制推送覆盖远程分支:
bash
复制
git push --force-with-lease origin A
-
--force-with-lease
:安全强制推送,确保远程分支未被他人修改过。
2. 拉取并合并(不推荐)
若执行常规的git pull
,Git会尝试合并本地与远程的历史,导致以下问题:
-
生成一个无意义的合并提交,破坏
rebase
后的线性历史。 -
可能引入冲突,需手动解决,增加复杂性。
git pull origin A # 不推荐,除非你明确需要合并
根本原因总结
-
Rebase重写历史:
rebase
会修改提交的父节点和哈希值,导致本地与远程历史不一致。 -
Git的保守策略:Git默认阻止直接推送分叉的历史,提示
pull
以合并变更,但这对个人分支来说通常是多余的。
最佳实践
-
推送前Rebase:在开始新功能前,先拉取主分支最新代码(
git pull --rebase origin develop
),减少后续冲突。这步确保develop 最新的拉取到本地是最新,后边进行本地rebase才能将自己最新的合并到devleop的最前面 -
及时强制推送:若分支仅自己使用,
rebase
后直接强制推送,保持远程与本地一致。 -
避免共享分支的Rebase:多人协作的分支不要使用
rebase
,以免影响他人工作。
通过理解Git的历史管理机制,合理选择rebase
和push --force
,可以高效维护代码库的整洁性
9.1 执行完上面做作后,通过在web 发起merge request 到develop-多出一条分支线
这宗情况会产生自己分分支线从rebase 分支来在合进develop 的一条分支线
9.2 通过 --ff-only
合入,这种情况始终只有一条线
会检查是否可以快进合并。由于你已通过 rebase 将 A
分支对齐到 develop
最新提交,此时一定可以快进
--ff-only 会检查是否可以快进合并。由于你已通过 rebase 将 A 分支对齐到 develop 最新提交,此时一定可以快进git checkout develop
git merge --ff-only A
具体操作步骤
1. 保存工作进度
在开始操作之前,确保你 A
分支上的工作已经完成并且所有修改都已经提交。如果你有未提交的修改,可以使用以下命令保存:
bash
git add .
git commit -m "保存当前工作进度"
2. 切换到 develop
分支并拉取最新代码
bash
git checkout develop
git pull origin develop
此操作会让你切换到 develop
分支,并且从远程仓库拉取最新的代码。
3. 切换回 A
分支并进行 rebase
bash
git checkout A
git rebase develop
这里,git rebase develop
会把 A
分支上的提交移到 develop
分支的最新提交之后。若在 rebase
期间碰到冲突,你需要手动解决这些冲突,然后使用以下命令继续 rebase
:
bash
git add .
git rebase --continue
4. 切换回 develop
分支并进行快进合并
bash
git checkout develop
git merge --ff-only A
--ff-only
参数会保证只有在 develop
分支可以快进合并到 A
分支时才进行合并,也就是确保提交历史是一条直线。要是 develop
分支无法快进合并,就意味着之前的 rebase
操作可能有误,你需要重新检查。
5. 将更新后的 develop
分支推送到远程仓库
bash
git push origin develop
完整操作流程示例
bash
# 保存工作进度
git add .
git commit -m "保存当前工作进度"# 切换到 develop 分支并拉取最新代码
git checkout develop
git pull origin develop# 切换回 A 分支并进行 rebase
git checkout A
git rebase develop# 解决可能出现的冲突
# ...
# git add .
# git rebase --continue# 切换回 develop 分支并进行快进合并
git checkout develop
git merge --ff-only A# 将更新后的 develop 分支推送到远程仓库
git push origin developgit log --graph --oneline
关键点总结
-
先更新
develop
分支:确保本地develop
分支与远端同步。 -
用
rebase
对齐提交历史:将A
分支的提交“移动”到develop
最新提交之后。 -
快进合并:
git merge --ff-only
保证提交历史线性。 -
解决冲突:在 rebase 过程中解决冲突,而不是在合并时。
十、总结
- 我们发现分享
rebase
全文都是围绕 优化分支提交记录 来举例子介绍该命令,我个人觉得这也就是该命令的核心之处。 - 在学习
rebase
之前我日常使用的基本都是merge
导致commit
记录过于混乱
参考文献
-
# 从git log 点线图(graph)看merge和rebase操作
-
# 从git rebase的常见冲突及解决办法