Git Rebase 详解及图解

2025-06-09

Git Rebase 详解及图解

大家好!如果你是 Git 新手,或者对它的使用还不太自信,那么我希望这篇文章能对你有所帮助。正如你所见,这篇文章内容冗长,但插图较少。我比较喜欢提供背景信息;这算是我的专长吧。

如果你觉得自己对 Git 足够熟悉,但只是不会 rebase,那么你可以放心地跳到理解这个关于分支的奇怪技巧

功劳应得

在我们开始之前,我想快速地介绍一下这本书,9 年前我第一次读到它时,它帮助我掌握了 Git 的概念和基础知识:

Travis Swicegood 撰写的 Git 实用指南

如果您购买了那本书,您就可以完全忘记这篇文章了!

还要感谢前端开发大师Dan Cortes审阅了这篇文章,并帮助我完善了细节。谢谢你,Dan!

序幕

你可能之前在使用 Git rebase 时有过特别糟糕的经历,那些可怕的合并冲突浪费了你的时间和精力。别担心,我在文章底部有一些让你的 rebase 生活更轻松的小技巧。

如果你在想,管他呢,直接用merge就行了,这样更方便,好吧……好吧。你说得对。直接用merge,别在意这篇文章了。你会没事的,你的团队和项目也会没事的,就算你不学变基也不会有什么可怕的事情发生。不过,我希望鼓励你多学习一些你工作中的基础工具——Git!

我在下面对此进行了更详细的阐述,但始终使用合并的一个问题是,它可能会导致更难追踪提交历史记录。拥有更简单的变更时间线可以大大减轻您将来的工作量。例如,如果您想隔离一组变更,那么如果这些提交没有与不相关的提交混在一起,就会容易得多,而使用合并而不是变基最终可能会造成这种情况。

术语表

这篇文章假设你已经有一些 Git 使用经验,并且很可能之前尝试过 rebase,之后却讨厌它,甚至连你自己都讨厌。即便如此,我还是在本文中使用了一些我认为值得定义的术语,以免让初学者感到困惑。

父分支
您输入时所在的分支

git branch my-branch-name
# or, if you want to switch to 
# your new branch at the same time:
git checkout -b my-branch-name
Enter fullscreen mode Exit fullscreen mode

这是您“重新定位”和“合并”的分支,因为它与您的子分支有关

子分支
您正在处理的分支,您希望与某个父分支(几乎总是您最初起源的分支)的新变化保持同步

拉取请求
是源代码控制 Web 应用程序的常见 UI 功能,团队使用它来允许其他开发人员在将你的工作合并到父分支之前对其进行审查

代码审查
您的团队会审查您打算通过拉取请求合并的代码

SHA、提交 SHA 或 SHA-1
是每次提交生成的 40 个字符的唯一哈希字符串(例如:f4f78b319c308600eab015a5d6529add21660dc1)。它实际上是创建哈希的算法的名称,但开发人员通常将字符串本身称为 SHA。大多数服务在显示它们时通常会将其缩短为前七个字符(例如:f4f78b3),因为这在单个项目中始终足够唯一。如果您需要在某些 Git 命令中引用提交 SHA,则可以使用缩短的版本,Git 不会对此感到惊讶。

head 给
定分支、标签或其他类似 Git 引用对象的最新 SHA/提交点

HEAD
当前活动的头 - 因此 Git 存储库有多个头,但有一个 HEAD

合并冲突
当您和团队成员对同一行代码做出不同的更改时,Git 将很难自动解决这些更改。Rebase 将停止并要求您手动解决冲突后再继续。

部署
一个团队特定的流程,用于将所需版本的代码发布到远程服务器环境,例如暂存(由内部员工测试)或生产(由实际客户看到)

请请求我定义帖子中使用的任何其他术语,我会添加它们!

我为什么要重新定基?

我很想在评论中看到更多的理由,但根据我的经验,重新定基就是将分支中的提交保持在一起,并将其放在提交历史记录的顶部,以便进行拉取请求/代码审查。

如果您在更适合进行 rebase 的情况下进行合并,则会在提交历史记录中创建这些错误的合并点。例如,当您回顾提交列表时,您可能无法轻松地从已批准的拉取请求中仅找出合并点。

变基功能可以保持提交的逻辑顺序,并且在将两个分支合并到主分支时,不会将团队成员的提交与自己的提交混在一起。这使得跟踪项目功能和代码变更的历史记录变得更加容易。

我什么时候应该重新定基?

基本上,每当父分支有新的更改,而你的子分支尚未合并回来时,就需要进行变基。根据团队规模和活跃程度,你可能需要每天或每天多次进行变基。你变基的频率越高,遇到合并冲突的频率就越低,你处理自创建分支以来已经过时的代码的可能性也就越小。

最终,您可以相信自己会随着时间的推移而感受到这一点,并培养自己的直觉。

我什么时候应该合并?

它帮助我将合并和变基理解为对修改进行不同方向的控制。合并是从子分支到父分支,或者子分支合并父分支。变基是从父分支到子分支,或者子分支变基父分支(或完全不同的分支!)上的新提交。

了解关于分支的这个奇怪技巧

我认为,首先要帮助你更好地理解变基操作的是:在 Git 中,分支只是一些文本文件,用于记录哪个提交的 SHA 值是该分支的最新提交。Git在进行变基操作时,会使用这个技巧,正如你可能之前看到的,来“回退”你的分支,并从一个新的点“重放”它的提交。

Git rebase 会让您感觉好像是从父分支的最新提交分支,而不是您首次创建分支时的原始提交。您正在更改分支的基准提交,或者说,重新定位您的分支。

哦天哪,伙计,这比我预期的要多得多,给我看看吧!

好的。现在你位于主分支(main-branch)的最新位置。

这里我们在历史记录顶部看到 3 个提交,分别标记为 A、B、C,并且“主分支”指向提交 C

3 个彩色点,分别标记为 A、B、C,沿水平线连接,并且

然后使用以下命令创建分支

git checkout -b new-branch
Enter fullscreen mode Exit fullscreen mode

你会看到 Git 也将新分支指向提交 C

与之前相同的图像

随着时间的推移,你向新分支 D 和 E 添加了两个提交

与之前的图像类似,但现在有一个分支,由一条与其他提交点类似的线连接,正在下降到主分支线以下(仍然在 C 处表示),显示标记为 D 和 E 的提交点,并且

当你这样做的时候,也许你回到了主分支,拉取并发现了两个新的提交。

> git checkout main-branch
> git pull
# ... new commits arrive!
Enter fullscreen mode Exit fullscreen mode

这不是检测主分支变化的唯一方法,但我现在不想让你分心。嘘。嘿。孩子。过来

与上图类似,但现在主枝多了两个点,分别标记为 F 和 G,表示主枝位于 G。新枝仍显示在下方的 E 处

现在到了关键时刻。你再次 checkout new-branch 并运行 rebase 命令。

> git checkout new-branch
> git rebase main-branch
Enter fullscreen mode Exit fullscreen mode

...Git 在幕后执行以下操作(或多或少,这是故意过度简化的)

Git 从每个分支的头部向后查找每个提交,直到找到两个分支之间的第一个共享点(因此箭头显示提交的关系,额外的红色箭头显示搜索)

Git 创建一个隐藏的临时分支并将其指向 C,您可能在终端中看到过“倒带”

与上图相同,但带有箭头,用于显示对提交的搜索,以及提交 C 处临时隐藏分支的指示器。其他分支仍然指示在 G 和 E 处。该图还显示了 Git rebase 命令,标记为

现在 Git 知道了新分支中所有缺失的提交,它会将临​​时分支指向主分支的头部,即 G

已标记

接下来,Git 从提交 G 中“重放”新分支的提交,添加提交 D2 和 E2

主分支现在有两个分支,分别来自提交 C 和 G。新分支仍然指示从 C 到提交 E;临时隐藏分支源自提交 G,显示提交 D2 和 E2。标记为

最后,因为我们奇迹般地没有合并冲突

“来吧,请帮我学习如何做那部分!”......别担心,很快就会到来(-:

Git 丢弃临时分支并将新分支指向 E2

概括

在本文中,我们简单介绍了 Git rebase 在典型开发场景中的工作原理。实际上,rebase 的用法和功能远不止我这里介绍的这些,但我希望本文能为那些经验有限甚至毫无经验的人提供一个基本的框架。我们总共涵盖了以下内容:

  • 为什么要使用 rebase 而不是 merge? 我个人的经验法则是:merge 是从子分支到父分支,而 rebase 则将父分支的更改移动到子分支历史的底部。

  • 分支只是 Git 用来了解分支头位置的隐藏文本文件

  • 一目了然,Git 在 rebase 期间在后台执行的操作,它如何“倒回”到两个分支之间第一个共享的提交,以及如何从父分支的新头部“重放”子分支的提交

Rebase 工作流程提示

我知道有些人可能想了解更多关于处理合并冲突的知识,我计划在后续文章中制作一个截屏视频。在此之前,我根据自己的工作流程总结了一些小技巧,希望能帮助大家避免陷入合并冲突的泥潭:

需要考虑的 .gitconfig 设置

如果您使用 VSCode,可以将其设置为合并冲突编辑器。其他优秀的 GUI 编辑器也支持更简单的合并冲突 UI,因此我很乐意听听其他评论者如何配置其他编辑器:

假设您确实使用 VSCode 并安装了“code”命令行工具,您可以将这些设置添加到“~/.gitconfig”文件中:

[merge]
tool = vscode
[mergetool "vscode"]
cmd = code --wait $MERGED
[mergetool]
keepBackup = false
view raw .gitconfig hosted with ❤ by GitHub
[merge]
tool = vscode
[mergetool "vscode"]
cmd = code --wait $MERGED
[mergetool]
keepBackup = false
view raw .gitconfig hosted with ❤ by GitHub

关于保留备份的最后一点,是我个人的偏好,其他人可能不认同。我之所以选择它,是因为在我多年的开发工作中,我从未需要过备份,而且我厌倦了每次都清理备份。

另一种方法是将“.orig”添加到项目的“.gitignore”文件中:

.orig
view raw .gitignore hosted with ❤ by GitHub
.orig
view raw .gitignore hosted with ❤ by GitHub

考虑默认将所有分支设置为使用 rebase 进行“拉取”:

[branch]
autosetuprebase = always
view raw .gitconfig hosted with ❤ by GitHub
[branch]
autosetuprebase = always
view raw .gitconfig hosted with ❤ by GitHub

当您处于 rebase 地狱中时,您是否厌倦了一遍又一遍地重复相同的合并冲突?

使用 Git 的Re use Re recording Re解决方案设置:

[rerere]
enabled = true
view raw .gitconfig hosted with ❤ by GitHub
[rerere]
enabled = true
view raw .gitconfig hosted with ❤ by GitHub

对于大多数曾经尝试过但讨厌 rebasing 的人来说,[rerere]可能是第一救星。

经常重新定基- 您等待重新定基分支以适应新变化的时间越长,发生大量合并冲突的可能性就越高。

考虑使用交互式 rebase 压缩你的提交- 当你运行

git rebase -i main-branch
Enter fullscreen mode Exit fullscreen mode

...“-i”选项表示交互。它会以有序列表的形式显示你分支上的所有提交(最早的提交显示在最上面)。

屏幕将为每次提交提供不同选项的说明,默认是“选择”提交,这意味着将其保留用于“重播”部分。

另一个选择是将其与上级分支“挤压”。如果你的分支非常老旧,并且有大量提交,那么将其挤压成一个提交可能会很有帮助,这样合并冲突只会发生一次(因为只有一个大型提交被重放)。不过,你最好先和你的团队讨论一下这个问题。

--

好吧,我只能说这么多了。你觉得怎么样?这些对你有帮助吗?我还需要澄清或解释什么吗?各位代码老手,我是不是说得有点离题了?或者我可以调整一下信息,让它更准确一些?欢迎大家提出任何评论、问题、疑虑和反馈,感谢你们今天抽出时间。祝你编程愉快!

鏂囩珷鏉ユ簮锛�https://dev.to/joemsak/git-rebase-explained-and-eventually-illusterated-5hlb
PREV
超级生产力:如何培养对时间跟踪和任务管理的兴趣
NEXT
使用 VSCode 调试器可视化闭包更多视频