为什么要学会Rebase,而不总是用Merge

包括我在内,我想很多人第一次接触 Git 时,大多只使用 git merge 合并分支,对 git rebase 既感陌生又好奇。

即使偶尔用过,往往也只是照着教程执行命令,对其工作机制和适用场景并不真正理解。

因此,本文不打算泛泛而谈 rebase 的所有参数,而是聚焦于它最核心的本质——重新应用提交,重写分支的基础。

我们将通过一个典型场景,深入剖析 git rebase 背后的逻辑,理解它为何能让 Git 历史变得更加简洁有序。

场景再现

初始场景

起初情况是这样的,你从主分支创建了新的特性分支,心里期待着开发完成后将自己开发的新功能合并到主分支上。

此时的分支结构像下面这样:

C---D (feature)

/

A---B (master)

突发意外

正当你全神贯注的进行开发时,有人告诉你主分支上修复了一个紧急BUG。

听了这个消息,你眉头一紧,因为此时的分支结构变成了这样:

C---D (feature)

/

A---B---E (master)

你发现你现在正基于旧版本B进行开发,如果继续开发,以后合并的时候可能会有很多冲突,最后积重难返就麻烦了,所以要尽快解决这个问题。

解决方案

方案一

拉取最新的主分支代码,然后先与主分支进行一次合并,再在合并的基础上重新创建分支进行开发,分支结构如下:

C---D (feature [old])

/ \

A---B---E---M (master)

\

F (feature [new])

方案二

方案一可以解决你的问题,但显然这会让分支结构变得复杂,显得不够优雅。

此时,git rebase就是你的方案二,它不仅可以让你实现与主分支进行同步的目的,还会让你的分支结构保持简洁清晰。

使用git rebase后你的分支结构将变成下面的样子:

C'---D' (feature)

/

A---B---E (master)

这个样子是不是看起来就像是直接基于最新的 master 开发的?

Rebase的本质:时间线重构

当我们在 feature 分支执行 git rebase master 时发生了什么呢?

Git 会找到 feature 和 master 的共同祖先(这里是提交 B)临时保存 feature 的差异(C 和 D 的改动)将 feature 分支的**起点(base)**从 B 迁移到 E,这正是“变基”(rebase)的核心含义。在 E 之后重新应用 C 和 D 的改动,生成新提交 C' 和 D'

这样操作后,分支结构就变成了方案二中的结构:

C'---D' (feature)

/

A---B---E (master)

C和D被复制为C’和D’,并且基于主分支最新的提交E进行重新应用

关键变化

提交历史被重写:原来的 C 和 D 会被遗弃(最终被 Git 垃圾回收),但会生成新的 C' 和 D'线性历史:现在 feature 分支的历史看起来像是直接基于最新的 master 开发的提交哈希值改变:因为父提交变了,所以C' 和 D' 的哈希值与原来的 C、D 不同

与Merge的对比

操作命令历史结构特点Mergegit merge master保留分叉,生成合并节点历史完整,适合公共分支Rebasegit rebase master线性历史,无合并提交整洁清晰,适合本地整理实操演示:变基工作流

创建特性分支

git checkout -b feature

开发过程中主分支更新

# 在主分支获取更新

git checkout master

git pull

变基同步最新代码

git checkout feature

git rebase master

处理可能出现的冲突

# 手动解决冲突文件

git add resolved_file

git rebase --continue

git会逐个处理feature上的每个分支(例子中的C和D),所以第4步可能需要重复多次。

写在最后

git rebase 不是一个神秘高深的命令,它的本质就是将你写过的提交,转移到一个新的基础之上。

掌握它,能帮你构建出更加清晰、可读性更强的提交历史。

当然,我们需要注意,对于已经共享到远程的分支,随意使用 rebase 可能会带来混乱。

希望这篇文章能让你对 git rebase 有更深的理解。

如果它帮你理清了思路,欢迎点赞或转发给还在纠结 merge 和 rebase 的朋友们 😊

本文首发于微信公众号《Linux在秋名山》,欢迎大家关注~