不要惧怕 Rebase

Git 的 rebase 命令是 Git 用户感到惧怕和迷惑的一个常见缘由,特别是那些来自可能更集中的版本控制系统的用户。这很正常。Rebase 是一个难以想象又充满魔力的怪兽,一上来无论三七二十一就改变历史。前端

Rebase 有点像指针。它是这样一个使人困惑的结构:每一个人都在谈论它,可是你并不清楚为何会有人使用它,而后忽然一切都“啪嗒”一下,整个想法都变得显而易见和难以置信的简单。android

在这篇文章中我会迫使你“啪嗒”一下,这样你就能够回到工做中并传播 git rebase 的神奇。ios

究竟什么是 Rebase?

Git Rebase 是一个很简单的工具,用来取出一些在某个地方建立的提交,并伪装它们一直是在另外一个地方建立的。git

好的,我知道了。但是这意味着什么呢?github

让咱们来看一个例子。咱们在这个仓库中有两个分支:masterfeature/foofeature/foo 是基于 master 分离出去的分支,而且在 feature/foo 分支上产生了一些提交。master 也发生了移动,就像世界不会由于少了你的关注而停滞不前。后端

这是目前的状态工具

咱们想将一些更改从 master 整合进 feature/foo 中,可是咱们不想每次执行这个整合时都处理一次使人讨厌的合并提交。区块链

Rebase 就是一个让你有能力整合发生在源分支上的更改而不须要执行合并(merge)从而不会产生合并提交的工具。人工智能

这是 rebase 以后的状况。(fast-forward 版本)翻译

DF 两个提交已经被从新放在master 的顶部,即当前指向的 G 提交。你可能会注意到这两个提交实际上已经被重命名为了 D* 和 *F,而且提交的 SHA-1 值也不同。这是为何呢?

Git 中的提交不可变动

一个提交具备一些与之相关的属性:一个父提交、一个时间戳、提交时仓库的快照(提交不只仅是变动集)。这些值是 Git 在计算标识一个提交的 SHA-1 时所用到的。

因为提交是不可变的,而且一个 SHA-1 应该惟一标识一个提交,所以 Git 须要建立一个新的提交来包含原始提交中相同的仓库快照,可是每一个提交都有一个不一样的父提交和时间戳

这致使新的提交看起来与原始提交相同,可是具备不一样的 SHA-1。


找出提交

当咱们从 feature/foo 分支上运行 git rebase master 时,Git 怎么知道哪些提交须要移动呢?

让咱们先看看每一个分支上的提交的文氏图(Venn diagram)。

从上图中咱们能够看到每个分支都有 ABC 这几个提交。master 分支还拥有 EG 提交可是 feature/foo 分支没有。feature/foo 拥有 FD 提交可是 master 分支没有。

Git 会作一个减法:{commits on feature/foo} — {commits on master},来找出正确的提交。这个结果就是 DF

咱们能证实这一点吗?

固然,一个简单方式是使用 git log 来看咱们从这组减法中获得的确切提交。

git log master..feature/foo 向咱们展现 bc1f36b640e713 提交。

若是你在 .. 后省略了一个分支,那么会默认为是当前分支。

看起来不错。让咱们来看看更普遍的视角以确保我不是在糊弄。

这些 sha-1 看起来很熟悉。

这里并无 76f5fd1 和 22033eb,由于咱们是从 master 分支的 7559a0b 提交开始分离的。


若是咱们如今执行一个 rebasemaster,咱们会当即看到 76f5fd122033eb 出如今咱们在 feature/foo 分支上建立出的提交的前面。

Git 正在像咱们指望中的那样从新应用提交。

看起来熟悉吗?

咱们以前见过这个了。

咱们如今有一个很好的线性历史。你应该可以想到在此刻 fast-forward 的合并会如何发生。

rebase 策略还有一个已知的额外好处,就是若是你的 CI 管道(CI pipeline)在功能分支上经过了,那么在合并后的主分支上它也会经过。若是是一个非线性的合并策略,你就不能保证这一点。


使用强制手段

若是 feature/foo 分支已经被推送过(push),而且在 rebase 以后尝试进行另外一个推送,Git 会很委婉地拒绝推送。这是为何呢?

Git 会尽其所能来防止意外覆盖历史,这是一件好事。

咱们来看一下 Git 所认为的 feature/foo 分支在远程仓库中是什么样的?

如今咱们来看一下咱们告诉 Git 要作的事情。

从 Git 的角度来看,提交 DF 即将丢弃。Git 会给你这样一行友好的信息:Updates were rejected because the tip of your current branch is behind

你或许会说,“可是我能够在你这个很棒的图片中清晰地看到,feature/foo 分支比以前更进一步了啊。” 这是一个很好的观察结果,可是 Git 只会看到远程仓库中的 feature/foo 包含 bc1f36b640e713,可是你本地的 feature/foo 不包含这些提交。所以为了避免丢失这些提交,Git 会委婉地拒绝一个正常的 git push,并要求你执行 git push --force


若是你从这篇文章中带走一件东西,那么请记住,rebase 只是简单的查找出在某个分支上建立的提交,而后使用相同的内容可是新的父提交或基础提交(base commit)来建立新的提交。


若是你喜欢个人文章,请为我点赞。

关注 HackernoonJared Ready 来获取更多高质量的软件工程相关的内容吧。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索