作为开发人员, 我们中的许多人不得不在Merge和Rebase之间做出选择. 从互联网上获得的所有参考资料中, 每个人都认为"不要使用 Rebase, 它会导致严重问题". 在此, 我将解释什么是Merge和Rebase, 为什么应该(和不应该)使用它们, 以及如何这样做.
Git Merge和 Git Rebase的目的是一样的. 它们都是为了将多个分支的变更整合到一个分支中. 虽然最终目标是一样的, 但这两种方法实现目标的方式却不尽相同. 而且随着你成为了更好的软件开发者, 了解这些差异也是很有帮助的.
这个问题让 Git 社区产生了分歧. 有些人认为应该总是Rebase, 有些人则认为应该总是Merge. 每一方都有一些令人信服的论据.
Git Merge
Merge是开发人员使用版本控制系统的常见做法. 无论分支创建是为了测试, 修复错误还是其他原因, Merge都是将提交合并到另一个位置. 更具体地说, Merge就是将源分支的内容与目标分支整合在一起. 在此过程中, 只有目标分支发生了变化. 源分支的历史保持不变.
Merge: 主分支 -> 特性分支
优点
- 简单且熟悉
- 保留完整的历史记录和时间顺序
- 保持分支的上下文
缺点
- 提交历史可能会被大量合并提交污染
- 使用
git bisect
调试会变得更难
操作方法
使用checkout
和merge
命令将主分支合并到特性分支.
bash
$ git checkout feature
$ git merge master(or)
$ git merge master feature
这将在特性分支中创建一个新的"合并提交", 保存两个分支的历史记录.
Git Rebase
Rebase是把一个分支的改动整合到另一个分支的另一种方法. Rebase会把所有改动压缩成一个"补丁". 然后将补丁整合到目标分支上.
与合并不同的是, Rebase会将已完成的工作从一个分支转移到另一个分支, 从而使历史记录扁平化. 在此过程中, 不需要的历史记录会被消除.
Rebase是变更从层次结构顶层向下传递的方式, 而Merge则是变更向上回流的方式.
Rebase: 特性分支 -> 主分支
优点
- 流水化潜在的复杂历史
- 可轻松操作单个提交(例如还原提交)
- 在分支繁忙的版本库中避免Merge带来的提交"噪音"
- 通过将中间提交合并为单个提交来清理中间提交, 这对DevOps团队很有帮助
缺点
- 将功能压缩到少量提交可能会隐藏上下文
- 在团队合作时, 重新发布公共版本库可能很危险
- 工作量更大: 使用rebase随时更新特性分支
- 使用远程分支Rebase需要强制推送. 人们面临的最大问题是, 他们强制推送了, 却没有设置git push默认值. 这将导致所有分支的更新都使用相同的名称, 无论是本地分支还是远程分支.
如果重置错误, 无意中改写了历史, 可能会导致严重的问题, 所以请确保你知道自己在做什么! 如何重置?
操作方法
使用以下命令将特性分支rebase到主分支.
bash
$ git checkout feature
$ git rebase master
这将把整个特性分支移到主分支之上. 具体做法是重写项目历史, 为原始(特性)分支中的每个提交创建全新的提交.
交互式Rebase
当提交被移至新分支时, 可对其进行修改. 这比自动Rebase功能更强大, 因为它能完全控制分支的提交历史. 通常情况下, 在将特性分支合并到主分支之前, 它会用来清理杂乱的提交历史.
bash
$ git checkout feature
$ git rebase -i master
这将打开编辑器, 列出所有即将被移动的提交.
bash
pick 22d6d7c Commit message#1
pick 44e8a9b Commit message#2
pick 79f1d2h Commit message#3
这将准确定义分支在执行Rebase后的样子. 通过对实体重新排序, 你可以让历史看起来像你想要的任何样子. 例如, 你可以使用fixup
,squash
,edit
等命令来代替pick
.
选择哪个使用呢?
那么什么是最好的? 专家们有什么建议吗?
很难一概而论, 因为每个团队的情况都不一样. 但我们总得有个起点.
团队在制定 Git Rebase与Merge策略时, 需要考虑几个问题. 因为事实证明, 一种工作流程策略并不总是比另一种好. 这取决于你的团队的最终选择.
考虑整个组织的Rebase和Git能力水平. 确定与Merge的可追溯性和历史性相比, 你在多大程度上更看重Rebase的简便性.
最后, Merge和Rebase的决策应在明确的分支策略背景下考虑(参考 本文, 了解更多关于分支策略的信息). 成功的分支策略是围绕团队组织设计的.
我的推荐是什么?
随着团队的壮大, 使用始终Merge策略 将很难管理或跟踪开发变更. 如果要想取得干净且易于理解的提交历史, 使用Rebase是合乎情理且高效的.
通过考虑以下情况和指导原则, 你可以从Rebase中获得最佳效果:
- 你在本地开发: 如果你没有与其他人共享你的工作. 在这种情况下, 为了保持历史记录的整洁, 你应该选择Rebase而不是Merge. 如果你有自己的仓库副本, 而且没有与其他开发者共享, 那么即使你已经推送到自己的分支, 也可以安全地Rebase.
- 你的代码已准备好接受Review: 你创建了一个拉取请求. 其他人正在审查你的工作, 并有可能将其提取到他们的分支中进行本地审查. 此时, 你不应该将你的工作Rebase. 你应该创建"返工"提交, 并更新你的特性分支. 这有助于在拉取请求中实现可追溯性, 并防止意外的历史断点.
- Review已完成, 可以集成到目标分支中了. 恭喜你! 你即将删除你的特性分支. 鉴于其他开发人员从现在起不会再获取Merge这些变更, 这是你净化历史记录的好机会. 此时, 你可以重写历史, 将原始提交和那些讨厌的"pr rework"和"merge"提交合并为一小部分集中提交. 为这些提交创建明确的合并是可选的, 但却很有价值. 它会记录功能何时升级到主版本.
总结一下
我希望这篇文章能让你对Git merge 和Git rebase有一些了解. 但也许这篇文章能帮你消除疑虑, 让你采用适合自己团队的方法. 加油!
代码 = 咖啡 + 开发者