Git 分支

《Pro Git》读书笔记

接触Git这个工具已经很长一段时间了,用起来也是一直是半生不熟的状态,索性寒假有时间就系统的学习一下。选择的是《Pro Git》,讲解得非常详细的。


几乎所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。

Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。

分支简介

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。

首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在Git 仓库中这些校验和保存为树对象。

Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。

Git 又是怎么知道当前在哪一个分支上呢?也很简单,它有一个名为 HEAD 的特殊指针。请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的 HEAD 概念完全不同。在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)。

enter description here

分支切换会改变你工作目录中的文件

在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。

由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。

分支的新建与合并

在切换分支的时候,要留意你的工作目录和暂存区里那些还没有被提交的修改,它可能会和你即将检出的分支产生冲突从而阻止 Git 切换到该分支。最好的方法是,在你切换分支之前,保持好一个干净的状态。

请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。

在合并的时候,你应该注意到了”快进(fast-forward)”这个词。由于当前 master 分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。

另外一种更加普遍的合并情况是:Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。

遇到冲突时的分支合并

如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。此时 Git 做了合并,但是没有自动地创建一个新的合并提交。Git 会暂停下来,等待你去解决合并产生的冲突。你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并

分支开发工作流

长期分支

在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支;你可以定期地把某些特性分支合并入其他分支中。

enter description here

特性分支

enter description here

变基

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

enter description here

如上图所示,提取在 C4 中引入的补丁和修改,然后在 C3 的基础上再应用一次。在 Git 中,这种操作就叫做 变基。

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

更有趣的变基例子

在对两个分支进行变基时,所生成的“重演”并不一定要在目标分支上应用,你也可以指定另外的一个分支进行应用。就像 从一个特性分支里再分出一个特性分支的提交历史 中的例子这样。

1
$ git rebase --onto master server client

以上命令的意思是:“取出 client 分支,找出处于 client 分支和 server 分支的共同祖先之后的修改,然后把它们在 master 分支上重演一遍”。这理解起来有一点复杂,不过效果非常酷。

使用 git rebase [basebranch] [topicbranch] 命令可以直接将特性分支(即本例中的 server)变基到目标分支(即 master)上。这样做能省去你先切换到server 分支,再对其执行变基命令的多个步骤。

变基的风险

奇妙的变基也并非完美无缺,要用它得遵守一条准则:不要对在你的仓库外有副本的分支执行变基

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。

只要你把变基命令当作是在推送前清理提交使之整洁的工具,并且只在从未推送至共用仓库的提交上执行变基命令,你就不会有事。

变基vs合并

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

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

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

  • 本文作者: Sixzeroo
  • 本文链接: https://www.liuin.cn/2018/02/04/Git-分支/
  • 发布时间: 2018年2月4日 - 21时02分
  • 最后更新: 2019年1月6日 - 14时01分
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!