git rebase VS git merge? 更优雅的 git 合并方式值得拥有

写在前面

若是你不能很好的应用 Git,那么这里为你提供一个很是棒的 Git 在线练习工具 Git Online ,你能够更直观的看到你所使用的命令会产生什么效果

另外,你在使用 Git 合并分支时只会使用 git merge 吗?有时使用 git rebase 能够比 git merge 作出更优雅的操做git

合并与变基

Merge 与 Rebase

不知怎么,git rebase 命令被赋予了一个神奇的污毒声誉,初学者应该远离它,但它实际上可让开发团队在使用时更加轻松。面试

你能够将它理解成「七伤拳」,「七伤拳」并非不能练,只是练「七伤拳」有一个先诀条件,那就是内功境界必定要很是高,即你要充分理解 git rebase 命令

在本文中,咱们将 git rebasegit merge 命令进行比较。在 Git 工做流中,说明全部可使用 rebase 的场景安全

概念概述

关于 git rebase ,首先要理解的是它解决了和 git merge 一样的问题。这两个命令都旨在将更改从一个分支合并到另外一个分支,但两者的合并方式却有很大的不一样。服务器

当你在专用分支上开发新 feature 时,而后另外一个团队成员在 master 分支提交了新的 commits,这会发生什么?这会致使分叉的历史记录,对于这个问题,使用 Git 做为协做工具的任何人来讲都应该很熟悉。编辑器

分叉提交历史记录

如今,假设在 master 分支上的新提交与你正在开发的 feature 相关。须要将新提交合并到你的 feature 分支中,你能够有两个选择:merge 或者 rebase。工具

Merge 方式

最简单的方式是经过如下命令将 master 分支合并到 feature 分支中:学习

git checkout feature
git merge master

或者,你能够将其浓缩为一行命令:fetch

git merge feature master

这会在 feature 分支中建立一个新的 merge commit,它将两个分支的历史联系在一块儿,请看以下所示的分支结构:this

将master合并到功能分支中

使用 merge 是很好的方式,由于它是一种 非破坏性的 操做。现有分支不会以任何方式被更改。这避免了 rebase 操做所产生的潜在缺陷(下面讨论)。spa

另外一方面,这也意味着 feature 分支每次须要合并上游更改时,它都将产生一个额外的合并提交。若是master 提交很是活跃,这可能会严重污染你的 feature 分支历史记录。尽管可使用高级选项 git log 缓解此问题,但它可能使其余开发人员难以理解项目的历史记录

Rebase 方式

做为 merge 的替代方法,你可使用如下命令将 master 分支合并到 feature分支上:

git checkout feature
git rebase master

这会将整个 feature 分支移动到 master 分支的顶端,从而有效地整合了全部 master 分支上的提交。可是,与 merge 提交方式不一样,rebase 经过为原始分支中的每一个提交建立全新的 commits 来 重写 项目历史记录。

将功能分支从新映射到主服务器上

rebase 的主要好处是能够得到更清晰的项目历史。首先,它消除了 git merge 所需的没必要要的合并提交;其次,正如你在上图中所看到的,rebase 会产生完美线性的项目历史记录,你能够在 feature分支上没有任何分叉的状况下一直追寻到项目的初始提交。这样能够经过命令 git loggit bisectgitk 更容易导航查看项目。

可是,针对这样的提交历史咱们须要权衡其「安全性」和「可追溯性」。若是你不遵循 Rebase 的黄金法则,重写项目历史记录可能会对你的协做工做流程形成灾难性后果。并且,rebase 会丢失合并提交的上下文, 你也就没法看到上游更改是什么时候合并到 feature 中的。

交互式 Rebase

交互式 rebase 使你有机会在将 commits 移动到新分支时更改这些 commits。这比自动 rebase 更强大,由于它提供了对分支提交历史的彻底控制。一般,这用于在合并 feature 分支到 master 以前清理其杂乱的历史记录。

要使用交互式 rebase,须要使用 git rebase-i 选项:

git checkout feature
git rebase -i master

这将打开一个文本编辑器,列出即将移动的全部提交:

pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

此列表准肯定义了执行 rebase 后分支的外观。经过更改 pick 命令或从新排序条目,你可使分支的历史记录看起来像你想要的任何内容。例如,若是第二次提交 fix 了第一次提交中的一个小问题,您可使用如下 fixup 命令将它们浓缩为一个提交:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

保存并关闭文件时,Git将根据您的指示执行 rebase,从而产生以下所示的项目历史记录:

使用交互式rebase来压缩提交

消除这种无心义的提交使你的功能历史更容易理解。这是 git merge 根本没法作到的事情。至于 commits 条目前的 pickfixupsquash 等命令,在 git 目录执行 git rebase -i 便可查看到,你们按需重排或合并提交便可,注释说明很是清晰,在此不作过多说明:

Rebase 的黄金法则

一旦你理解了什么是 rebase,最重要的是要学习何时不能使用它。git rebase 的黄金法则是永远不要在公共分支上使用它。

例如,想一想若是你 rebase master 分支到 feature 分支之上会发生什么:

从新分配主分支

rebase 将全部 master 分支上的提交移动 feature 分支的顶端。问题是这只发生在 你本身 的存储库中。全部其余开发人员仍在使用原始版本的 master。因为 rebase 致使全新 commit,Git 会认为你的 master 分支历史与其余人的历史不一样。

此时,同步两个 master 分支的惟一方法是将它们合并在一块儿,可是这样会产生额外的合并提交和两组包含相同更改的提交(原始提交和经过 rebase 更改的分支提交)。不用说,这是一个使人很是困惑的状况。

所以,在你运行 git rebase 命令以前,老是问本身,还有其余人在用这个分支吗? 若是答案是确定的,那就把你的手从键盘上移开,开始考虑采用非破坏性的方式进行改变(例如,git revert 命令)。不然,你能够为所欲为地重写历史记录。

Force Push

若是你尝试将 rebase 了的 master 分支推送回 remote repository,Git 将阻止你这样作,由于它会与远程master 分支冲突。可是,你能够经过传递 --force 标志来强制推送,以下所示:

# Be very careful with this command!
git push --force

这样你本身 repository 的内容将覆盖远程 master分支的内容,但这会使团队的其余成员感到困惑。所以,只有在确切知道本身在作什么时才要很是当心地使用此命令。

若是没有人在 feature branch 上做出更改,你可使用 force push 将本地内容推送到 remote repository 作清理工做

工做流程演练

rebase 能够根据你所在团队的须要方便的整合到现有的 Git 工做流程中。在本节中,咱们将了解 rebase 在功能开发的各个阶段能够提供的好处。

在任何工做流程中,利用 git rebase 是为每一个功能建立专用分支。这为你提供了必要的分支,以安全地利用 rebase:

在专用分支中开发功能

本地清理

将 rebase 归入工做流程的最佳方法之一是清理本地正在进行的功能。经过按期执行交互式 rebase,你能够确保功能中的每一个提交都具备针对性和意义。这可使你在编写代码时无需担忧将其分解为隔离的提交(多个提交),你能够在过后修复整合它。

使用 git rebase 时,有两种状况:feature 父分支(例如 master )的提交,或在 feature 中的早期提交。咱们在 交互式 Rebase 部分已经介绍了第一种状况的示例。咱们来看后一种状况,当你只须要修复最后几回提交时,如下命令仅作最后 3 次提交的交互式 rebase。

git checkout feature
git rebase -i HEAD~3

经过指定 HEAD~3 ,你实际上并无移动分支,你只是交互式地重写其后的3个提交。请注意,这不会将上游更改合并到 feature 分支中。

从新上头~3

若是要使用此方法重写整个功能,git merge-base 命令可用于查找 feature 分支的原始 base。如下内容返回原始 base 的提交ID,而后你能够将其传递给 git rebase

git merge-base feature master

交互式 rebase 的使用是引入git rebase 工做流的好方法,由于它只影响本地分支。其余开发人员惟一能看到的就是你提交的最终版,这应该是一个简洁易懂易跟踪的分支历史记录。

但一样,这仅适用于 私有 feature分支。若是你经过相同的功能分支(公共分支)与其余开发人员协做,那么你是 不被容许 重写其历史记录的。

将上游更改合并到功能分支中

概念概述 部分中,咱们了解了 feature 分支可使用 git mergegit rebase 合并 master 分支的上游更改 。merge 是一个安全的方式,能够保留存 git repository 的整个历史记录,而 rebase 则是经过将 feature 分支移动到 master 顶端来建立线性历史记录。

这种使用 git rebase 相似于本地清理,但在此过程当中它包含了那些来自 master 上游提交。

请记住,将当前提交 rebase 到远程 branch(非 master 分支)同样是合法的。当与另外一个开发人员协做使用相同的功能而且你须要将他们的更改合并到你的 repository 时,就会发生这种状况。

例如,若是你和另外一个名为 John 的开发人员添加了对 feature 分支的提交,在你 fetch (注意 fetch 并不会自动 merge )来自 John 的远程 feature分支后,你的 repository 可能以下所示:

在同一个功能分支上进行协做

你能够整合上来自上游的分叉:要么用 john/feature merge 本地 feature ,要么 rebase 本地featurejohn/feature 的顶部。

合并与从新定位到远程分支

请注意,此 rebase 不违反 Rebase 黄金规则,由于只有你的本地 feature 提交被移动, 以前的全部内容都不会受到影响。这就像是说 "将个人更改添加到 John 已经完成的工做中"。在大多数状况下,这比经过合并提交与远程分支同步更直观。

默认状况下,使用 git pull 命令执行合并,但你能够经过向其传递 --rebase 选项来强制它将远程分支 以 rebase 方式集成。

git pull --rebase

使用 Pull 请求 Review Feature

若是你在代码审查过程当中使用 pull 请求,在使用了 pull 请求以后你应该避免使用 git rebase 。一旦你发出 pull 请求,其余开发人员就会查看你的提交,这意味着它是一个 公共 分支。重写其历史记录将使 Git 和你的队友没法跟踪添加到该功能的任何后续提交。

其余开发人员的任何更改都须要合并 git merge 而不是 git rebase

所以,在提交拉取请求以前,一般使用交互式 rebase 清理代码一般是个好的办法。注意使用顺序

集成已批准的功能

在你的团队批准某项 feature 后,你能够选择将该功能 rebase 到 master 分支的顶端,而后git merge再将该功能集成到主代码库中。

这与将上游更改合并到 feature 分支中的状况相似,但因为你不容许在 master 分支中重写提交,所以你必须最终使用 git merge 该功能进行集成。可是,经过在 merge 以前执行 rebase,你能够确保会以 fast-forward 方式 merge,从而产生完美的线性历史记录。

使用和不使用rebase将功能集成到master中

若是您不熟悉 git rebase,能够随时在临时分支中执行 rebase。这样,若是你不当心弄乱了功能的历史记录,能够查看原始分支,而后重试。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch

总结

若是你更喜欢没有没必要要的干净的合并提交,线性历史记录,你就须要开始了解使用 rebase 功能。同时你应该会使用 git rebase 而不是 git merge 集成来自另外一个分支的更改。

另外一方面,若是你想保留项目的完整历史记录并避免重写公共提交的风险,你能够坚持下去git merge。这两种选择都是彻底有效的,但至少如今你能够选择利用 git rebase 的好处 。

灵魂追问

  1. 你有使用过 git rebase 吗?这样清晰的线形历史是否是能够尝试一下呢?
  2. 交互式 rebase 提交条目前的命令 fixup 等你能灵活使用吗
  3. 在 feature 分支上开发时,试试 git pull -rebase?
带着疑问去思考,而后串联,进而概括总结,不断追问本身,进行自我辩证,像侦查嫌疑案件同样看待技术问题,漆黑的街道,你我一块儿寻找线索,你就是技术界大侦探福尔摩斯

提升效率工具


推荐阅读

欢迎持续关注公众号:「日拱一兵」

  • 前沿 Java 技术干货分享
  • 高效工具汇总
  • 面试问题分析与解答
  • 技术资料领取

持续关注,带你像读侦探小说同样轻松趣味学习 Java 技术栈相关知识

相关文章
相关标签/搜索