Git rebase 集成分支之间的更改

本文将深入讨论 git rebase 命令。这里将介绍常见的 rebase 用例和常见的问题。

rebase 是一个比较实用的程序,专门集成从一个分支到另一个分支的更改。另一个集成分支变更的实用程序是 git merge。合并始终是向前移动的更改记录。或者,rebase具有强大的历史重写功能。rebase本身有两种主要模式:“手动”和“交互”模式。我们将在下面更详细地介绍不同的 rebase基础模式。


什么是 git rebase ?

Rebase 是将一系列提交移动或组合到新的基础提交的过程。 在功能分支工作流的上下文中,rebase 是最有用且易于可视化的。 可以将一般过程表示如下:

git rebase 全新的提交
git rebase 全新的提交

从内容的角度来看,rebase 是将分支的基础从一个提交更改为另一个提交,使其看起来好像是从不同的提交创建了分支。 在内部,Git 通过创建新的提交并将它们应用到指定的基础上来实现这一点。 尽管分支看起来相同,但它由全新的提交组成,理解这一点非常重要。


用法

重新定位的主要原因是维护线性项目历史记录。 例如,考虑这样一种情况,自开始在 feature 分支上工作以来,main 分支已经取得进展。 我们希望获得 feature 分支中 main 分支的最新更新,但希望保持分支的历史记录是干净的,以便看起来好像一直在处理最新的 main 分支。 这为以后将我们的 feature 分支干净地合并回 main 分支提供了好处。 为什么我们要保持“干净的历史”? 在执行 Git 操作以调查回归的引入时,拥有干净历史记录的好处变得切实可行。 更真实的场景是:

  1. 在 main 分支中发现了一个bug。 运行中的功能现在已损坏。
  2. 开发人员使用 git log 检查主分支的历史记录,因为开发人员可以快速推断项目的历史记录。
  3. 开发人员无法使用 git log 识别错误是何时引入的,因此开发人员执行 git bisect。
  4. 因为 Git 历史记录是干净的,所以git bisect在寻找回归时有一组精致的提交进行比较。开发人员很快找到引入错误的提交,并能够相应地采取行动。

我们有两个选项可以将 feature 分支集成到 main 分支中:直接合并或rebase之后合并。 前者将导致 三路合并和合并提交,而后者为 Fast-Forword 合并和完美的线性历史。 下图演示了在 main 分支上的rebase 是如何进行 Fast-Forward 合并的。

git rebase fast forward 合并
git rebase fast forward 合并

rebase 是将上游更改集成到本地仓库的常用方法。 每次想查看项目进展情况时,使用 Git 合并拉入上游更改会导致多余的合并提交。 另一方面,rebase 就像说,“我想根据每个人已经做过的事情来做我的改变。”

警告 - 正如我们之前所讨论的那样,一旦提交被推送到公共仓库,你就不应该重新设置提交。 rebase 会用新的提交替换旧的提交,看起来你的项目历史的那部分突然消失了。所以不要使用 git rebase 改变公共历史


Git Rebase 标准模式 与 Git Rebase 交互模式

git rebase 后面跟上 -i--interactive 选项时,启动交互模式。后面如果没有任何选线,则是标准模式。在这两种情况下,我们创建一个单独的功能分支。

$ git checkout -b feature_branch main
# 编辑 hello.txt 文件
$ git commit -a -m "Adds new feature" 

标准模式下的 Git rebase 将自动获取当前工作分支中的提交并将它们应用到传递分支的头部。

git rebase <base>

使用 -i 选项运行 git rebase 开始一个交互式 rebase 会话。 不是盲目地将所有提交移到新的基础上,交互式 rebase 让我们有机会在此过程中更改单个提交。 这使你可以通过删除、拆分和更改现有的一系列提交来清理历史记录。

git rebase --interactive <base>

这将打开一个编辑器,我们可以在其中为每个要rebase的提交输入命令(如下所述)。 这些命令决定了如何将单个提交转移到新的基础上。 还可以重新排序提交列表以更改提交本身的顺序。

pick 2231360 some old commit
pick ee2adc2 Adds new feature

# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

rebase 的风险

使用 git Rebase 时要考虑的一个问题是,在 rebase 工作流程中合并冲突可能会变得更加频繁。如果有一个长期存在的分支偏离了主分支,就会发生这种情况。最终,我们将希望针对 main 进行 rebase,那时它可能包含许多新提交,我们的分支更改可能会与之冲突。这可以通过频繁地根据 main 分支重新建立分支并进行更频繁的提交来轻松解决。

一个更严重的 rebase 警告是交互式历史重写丢失的提交。以交互模式运行 rebase 并执行诸如 squash 或 drop 之类的子命令将从分支的即时日志中删除提交。乍一看,这看起来好像提交已经永久消失了。使用 git reflog 可以恢复这些提交,并且可以撤消整个 rebase。

git Rebase 本身并不是很危险。当执行历史重写交互式 rebase 并强制将结果推送到其他用户共享的远程分支时,会出现真正的危险情况。这是一种应该避免的模式,因为它有能力在其他远程用户拉动时覆盖他们的工作。


git rebase 总结

在本文中,我们介绍了 git rebase 的使用。 我们讨论了基本和高级用例以及更高级的示例。 一些关键的讨论点是:

  • git rebase 标准模式与交互模式
  • git rebase 配置选项
  • git rebase 丢失的提交

我们使用其他工具(如 git refloggit fetchgit push)查看 git rebase 的使用情况。

查看笔记

扫码一下
查看教程更方便