Git rebase 集成分支之间的更改
本文将深入讨论 git rebase
命令。这里将介绍常见的 rebase 用例和常见的问题。
rebase 是一个比较实用的程序,专门集成从一个分支到另一个分支的更改。另一个集成分支变更的实用程序是 git merge。合并始终是向前移动的更改记录。或者,rebase具有强大的历史重写功能。rebase本身有两种主要模式:“手动”和“交互”模式。我们将在下面更详细地介绍不同的 rebase基础模式。
什么是 git rebase ?
Rebase 是将一系列提交移动或组合到新的基础提交的过程。 在功能分支工作流的上下文中,rebase 是最有用且易于可视化的。 可以将一般过程表示如下:
从内容的角度来看,rebase 是将分支的基础从一个提交更改为另一个提交,使其看起来好像是从不同的提交创建了分支。 在内部,Git 通过创建新的提交并将它们应用到指定的基础上来实现这一点。 尽管分支看起来相同,但它由全新的提交组成,理解这一点非常重要。
用法
重新定位的主要原因是维护线性项目历史记录。 例如,考虑这样一种情况,自开始在 feature 分支上工作以来,main 分支已经取得进展。 我们希望获得 feature 分支中 main 分支的最新更新,但希望保持分支的历史记录是干净的,以便看起来好像一直在处理最新的 main 分支。 这为以后将我们的 feature 分支干净地合并回 main 分支提供了好处。 为什么我们要保持“干净的历史”? 在执行 Git 操作以调查回归的引入时,拥有干净历史记录的好处变得切实可行。 更真实的场景是:
- 在 main 分支中发现了一个bug。 运行中的功能现在已损坏。
- 开发人员使用 git log 检查主分支的历史记录,因为开发人员可以快速推断项目的历史记录。
- 开发人员无法使用 git log 识别错误是何时引入的,因此开发人员执行 git bisect。
- 因为 Git 历史记录是干净的,所以git bisect在寻找回归时有一组精致的提交进行比较。开发人员很快找到引入错误的提交,并能够相应地采取行动。
我们有两个选项可以将 feature 分支集成到 main 分支中:直接合并或rebase之后合并。 前者将导致 三路合并和合并提交,而后者为 Fast-Forword 合并和完美的线性历史。 下图演示了在 main 分支上的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 reflog、git fetch 和 git push)查看 git rebase 的使用情况。