关于Git分支变基操作的一些笔记

Posted 山河已无恙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于Git分支变基操作的一些笔记相关的知识,希望对你有一定的参考价值。

写在前面


  • 今天和小伙伴们分享一些Git分支变基操作的笔记
  • 博文为《Pro Git》读书笔记整理
  • 内容涉及:
    • 变基的基本操作
    • 多分支变基
    • 变基的风险
    • 变基和合并的比较
  • 感谢开源这本书的作者和把这本书翻译为中文的大佬们
  • 理解不足小伙伴帮忙指正,书很不错,感兴趣小伙伴可以去拜读

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase。

在本节中我们将学习什么是“变基”,怎样使用“变基”

之前介绍过,整合分支最容易的方法是 merge 命令。 它会把两个分支的最新快照(C3 和 C4)以及二者最近的共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照(并提交)。

其实,还有一种方法:你可以提取在C4中引入的补丁和修改,然后在C3的基础上应用一次。在Git中,这种操作就叫做 变基(rebase)

变基的基本操作

你可以使用rebase命令将提交到某一分支上的所有修改都移至另一分支上,就好像 “重新播放” 一样。在这个例子中,你可以检出 experiment 分支,然后将它变基到 master 分支上:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理是:

  • 首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master) 的最近共同祖先 C2
  • 然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,
  • 然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。 (译注:写明了 commit id,以便理解,下同)

现在回到 master 分支,进行一次快进合并。

$ git checkout master
$ git merge experiment

此时,C4’指向的快照就和the merge example中C5指向的快照一模一样了。这两种整合方法的最终结果没有任何区别,但是 变基使得提交历史更加整洁

你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁

请注意,无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在。

变基到新分支

在对两个分支进行变基时,所生成的“重放”并不一定要在目标分支上应用,你也可以指定另外的一个分支进行应用。

你创建了一个主题分支 server,为服务端添加了一些功能,提交了 C3 和 C4。 然后从 C3 上创建了主题分支 client,为客户端添加了一些功能,提交了 C8 和 C9。 最后,你回到 server 分支,又提交了 C10。

假设你希望将 client 中的修改合并到主分支并发布,但暂时并不想合并 server 中的修改, 因为它们还需要经过更全面的测试。这时,你就可以使用 git rebase 命令的 --onto 选项, 选中在 client 分支里但不在 server 分支里的修改(即 C8 和 C9),将它们在 master 分支上重放:

$ git rebase --onto master server client

以上命令的意思是:“取出 client 分支,找出它从 server 分支分歧之后的补丁, 然后把这些补丁在 master 分支上重放一遍,让 client 看起来像直接基于 master 修改一样”。

现在可以快进合并 master 分支了。(如图 快进合并 master 分支,使之包含来自 client 分支的修改)

$ git checkout master
$ git merge client

接下来你决定将 server 分支中的修改也整合进来。 使用 git rebase <basebranch> <topicbranch> 命令可以直接将主题分支 (即本例中的 server)变基到目标分支(即 master)上。 这样做能省去你先切换到server 分支,再对其执行变基命令的多个步骤。

$ git rebase master server

$ git checkout master
$ git merge server

至此,client 和 server 分支中的修改都已经整合到主分支里了, 你可以删除这两个分支,最终提交历史会变
成图 最终的提交历史 中的样子:

$ git branch -d client
$ git branch -d server

变基的风险

如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

变基vs.合并

提交历史到底意味着什么?

  • 有一种观点认为,仓库的提交历史即是记录实际发生过什么。它是针对历史的文档,本身就有价值,不能乱改。从这个角度看来,改变提交历史是一种亵渎,你使用谎言掩盖了实际发生过的事情。如果由合并产生的提交历史是一团糟怎么办?既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。

  • 另一种观点则正好相反,他们认为提交历史是项目过程中发生的事。没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。持这一观点的人会使用rebase及filter-branch等工具来编写故事,怎么方便后来的读者就怎么写。

到底合并还是变基好?

总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

博文参考


《Pro Git》

以上是关于关于Git分支变基操作的一些笔记的主要内容,如果未能解决你的问题,请参考以下文章

Git从青铜到王者第四篇:Git的分支与合并

功能分支变基后 Git 推送被拒绝

功能分支变基后 Git 推送被拒绝

对于git中变基操作的粗略认识

git 四个基本对象分支三个存储区reset-revert-变基cherry-pick

Git中分支变基的原理