软件开发是混乱的。如此多的错误转向,要修复的错别字,稍后要纠正的快速黑客和临时凑合,在过程后期发现的差一错误。借助版本控制,您可以完整地记录在创建“完美”最终产品(准备提交到上游的补丁)过程中所犯的每一个错误转向和纠正。就像电影的 NG 片段一样,它们有点令人尴尬,有时也很有趣。
如果您可以使用版本控制定期在路点保存您的工作,然后在您准备好提交以供审查的内容时,您可以隐藏所有这些私有草稿工作,而只提交一个完美的补丁,那岂不是很棒吗?认识一下 git rebase -i,这是重写历史并让每个人都认为您第一次就生成完美代码的完美方法!
git rebase 有什么作用?
如果您不熟悉 Git 的复杂性,这里有一个简要概述。在底层,Git 将项目的不同版本与唯一标识符关联起来,该标识符由父节点的唯一标识符的哈希值以及新版本与其父节点之间的差异组成。这创建了一个修订树,每个签出项目的人都会获得自己的副本。不同的人可以朝不同的方向发展项目,每个人都可能从不同的分支点开始。

左侧“origin”仓库中的 master 分支和右侧个人副本上的私有分支。
有两种方法可以将您的工作集成回原始仓库中的 master 分支:一种是使用 git merge,另一种是使用 git rebase。它们的工作方式截然不同。
当您使用 git merge 时,将在 master 分支上创建一个新提交,其中包含来自 origin 的所有更改以及您的所有本地更改。如果存在任何冲突(例如,如果其他人更改了您也在处理的文件),这些冲突将被标记,您有机会在将此合并提交提交到本地仓库之前解决冲突。当您将更改推送回父仓库时,您的所有本地工作将显示为 Git 仓库的其他用户的分支。
但是 git rebase 的工作方式不同。它会回退您的提交,并从 master 分支的顶端再次重放这些提交。这会导致两个主要变化。首先,由于您的提交现在从不同的父节点分支出来,它们的哈希值将被重新计算,并且任何克隆您的仓库的人现在都可能拥有仓库的损坏副本。其次,您没有合并提交,因此在将您的更改重放到 master 分支上时,会识别出任何合并冲突,您需要在继续 rebase 之前修复它们。当您现在推送您的更改时,您的工作不会显示在分支上,并且看起来好像您从 master 分支的最新提交中编写了所有更改。

合并提交(左)保留历史记录,而 rebase(右)重写历史记录。
但是,这两种选择都有一个缺点:每个人都可以看到您在准备好共享代码之前在本地解决问题时所做的所有涂鸦和编辑。这就是 git rebase 的 --interactive(或简写为 -i)标志发挥作用的地方。
介绍 git rebase -i
git rebase 的最大优势在于它可以重写历史记录。但是,为什么要仅仅停留在假装您从以后的某个点分支出来呢?有一种方法可以更进一步,并重写您如何获得准备提出的代码:git rebase -i,一个交互式的 git rebase。
此功能是 Git 中的“魔法时间机器”功能。该标志允许您在执行 rebase 时对修订历史记录进行复杂的更改。您可以隐藏您的错误!将许多小的更改合并为一个干净的功能补丁!重新排序事物在修订历史记录中出现的方式!

当您运行 git rebase -i 时,您会获得一个编辑器会话,其中列出了正在 rebase 的所有提交,以及您可以对它们执行的许多选项。默认选择是 pick。
- Pick 在您的历史记录中维护提交。
- Reword 允许您更改提交消息,可能修复错别字或添加其他注释。
- Edit 允许您在重放分支的过程中对提交进行更改。
- Squash 将多个提交合并为一个。
- 您可以通过在文件中移动提交来重新排序提交。
完成后,只需保存最终结果,rebase 就会执行。在您选择修改提交的每个阶段(使用 reword、edit、squash 或存在冲突时),rebase 都会停止并允许您在继续之前进行适当的更改。
上面的示例导致“One-liner bug fix”和“Integrate new header everywhere”合并为一个提交,而“New header for docs website”和“D'oh - typo. Fixed”合并为另一个。就像魔术一样,其他提交中所做的工作仍然在您的分支上,但相关的提交已从您的历史记录中消失!
这使得使用 git send-email 或通过针对父仓库创建拉取请求并使用您新整理的补丁集,轻松地向上游项目提交干净的补丁。这有很多优点,包括使您的代码更易于审查、更易于接受和更易于合并。
5 条评论