文章目录
  1. 1. Git中分支原理
  2. 2. 相关命令
  3. 3. 分支合并
  4. 4. 分支衍合
    1. 4.1. 优点
    2. 4.2. 风险

几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。在很多版本控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副本,对大型项目来说会花费很长时间。Git 的分支可谓是难以置信的轻量级,它的新建操作几乎可以在瞬间完成,并且在不同分支间切换起来也差不多一样快。

Git中分支原理

在 Git 中提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容快照的指针,包含本次提交的作者等相关附属信息,包含零个或多个指向该提交对象的父对象指针:首次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。

直观起见,我们假设工作目录有三个文件,准备将它们暂存后提交。暂存操作会对每一个文件计算校验和(即第一章中提到的 SHA-1 哈希字串),然后把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 类型的对象存储这些快照),并将校验和加入暂存区域。

然后当我们commit之后,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。

作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针(译注:即下图中的 parent 对象)

Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。Git 会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。

创建新分支testing,会在当前 commit 对象上新建一个分支指针

Git 是如何知道你当前在哪个分支上工作的呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针。运行 git branch 命令,仅仅是建立了一个新的分支,但不会自动切换(使用git checkout命令)到这个分支中去,所以在这个例子中,我们依然还在 master 分支里工作。

变更testing分支并提交:

由于 Git 中的分支实际上仅是一个包含所指对象校验和(40 个字符长度 SHA-1 字串)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入 41 个字节(外加一个换行符)那么简单,当然也就很快了。

相关命令

  1. 新建分支

    git branch testing
    
  2. 切换到指定分支

    git checkout testing
    
  3. 新建并切换到该分支

    git checkout -b testing //等同于1和2
    
  4. 合并

    git merge testing
    
  5. 删除分支

    git branch -d testing
    
  6. 查看各个分支最后一个提交对象的信息

    git branch -v
    
  7. 查看哪些分支已被并入当前分支

    git branch --merged
    
  8. 查看尚未合并的工作

    git branch --no-merged
    

分支合并

  1. 简单并入

    hotfix 分支是从 master 分支所在点分化出来的。由于当前 master 分支所在的提交对象是要并入的 hotfix 分支的直接上游,Git 只需把 master 分支指针直接右移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)。

  2. 三方合并

    这次合并操作的底层实现,并不同于之前 hotfix 的并入方式。因为这次你的开发历史是从更早的地方开始分叉的。由于当前 master 分支所指向的提交对象(C4)并不是 iss53 分支的直接祖先,Git 不得不进行一些额外处理。就此例而言,Git 会用两个分支的末端(C4 和 C5)以及它们的共同祖先(C2)进行一次简单的三方合并计算。

    对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象(C6):

    如果出现冲突,需要人手动解决。

分支衍合

把一个分支中的修改整合到另一个分支的办法有两种:merge 和 rebase(衍合)。rebase命令可以把在一个分支里提交的改变移到另一个分支里重放一遍。

git checkout experiment
git rebase master

它的原理是回到两个分支最近的共同祖先,根据当前分支(也就是要进行衍合的分支 experiment)后续的历次提交对象(这里只有一个 C3),生成一系列文件补丁,然后以基底分支(也就是主干分支 master)最后一个提交对象(C4)为新的出发点,逐个应用之前准备好的补丁文件,最后会生成一个新的合并提交对象(C3’),从而改写 experiment 的提交历史,使它成为 master 分支的直接下游

然后再进行一次快进合并就可以了。

优点

现在的 C3’ 对应的快照,其实和普通的三方合并,即上个例子中的 C5 对应的快照内容一模一样了。虽然最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。如果视察一个衍合过的分支的历史记录,看起来会更清楚:仿佛所有修改都是在一根线上先后进行的,尽管实际上它们原本是同时并行发生的。

风险

一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作

文章目录
  1. 1. Git中分支原理
  2. 2. 相关命令
  3. 3. 分支合并
  4. 4. 分支衍合
    1. 4.1. 优点
    2. 4.2. 风险