Subversion 用户眼中的 Git (9): 单亲 VS 多亲
SVN 和 GIT 对比的系列博文尚有几篇一直放在草稿中,处于构思阶段,今天从故纸堆里检出来(checkout?)
我们在《Subversion 用户眼中的 Git (7): 完全不同的分支和里程碑的实现》中介绍过,Git 和 Svn 的分支实现机制完全的不同,这也直接导致了 SVN 在分支合并中困难重重。尽管在 SVN 1.5 之后,通过 svn:mergeinfo 属性引入了合并追踪机制,但是在特定情况下,合并仍会出现很多困难。
在《SVN 树冲突和目录丢失问题(1)》系列博文中,介绍了帮助我的一个朋友解决SVN树冲突的过程。这实际上在 GIT 中是 “a piece of cake”。你可以用 Git 模拟一下不同分支中文件目录改名引发合并冲突,在Git 中解决的是那么自然和漂亮!
这是为什么呢?因为 SVN 是单亲家庭,而 GIT 是双亲/多亲家庭啊。
SVN 的单亲家庭
在博文《Subversion 用户眼中的 Git (4): 全局版本号和全球版本号》中我们提到过,SVN 的版本号是连续的版本号。每一次新的提交都会版本号+1 ,而无论这个提交是在哪个分支中进行的。我们在《Subversion 用户眼中的 Git (7): 完全不同的分支和里程碑的实现》也提到过,SVN一个提交可以同时修改不同分支的不同文件,因为提交命令可以在 /trunk, /branches, /tags 的上一级目录执行。- SVN 的提交是单线索的,每一个提交(最原始的提交0除外)都只有一个单亲节点(版本号小一个的提交节点)
- SVN 的提交链只有一条,仅从版本号和提交说明,我们无法获得分支图
- SVN 的分支图在某些工具(如乌龟SVN)可以提供,那是需要对提交内容进行检查,对目录拷贝动作视为分支,对 svn:mergeinfo 的改动视为合并,但这会由于目录管理的灵活性,导致千奇百怪的分支图表
- SVN 的单亲节点的设计,是SVN分支合并输在起跑线上的唯一原因。也是 SVN 永远无法在 GIT 面前抬头的最重要原因
Git 的双亲/多亲家庭
Git 的提交实际上是按照多亲进行设计的,即一个提交可以包含两个以上的多亲节点,不过一般合并是双分支合并,因此合并节点以双亲居多。- 大部分提交实际上只用到了多亲节点中的一个,即大部分提交是对前一个提交的修改
- 合并操作一般用到两个双亲节点(或者更多),这很自然 合并操作是将两个提交合二为一,因此在新的合并结果的提交后产生的节点会有两个双亲节点。
- 不必非要基于最新节点提交,因为作为分布式版本控制系统,你根本不知道是不是有人在你之前进行和提交
- 你甚至可以先切换(检出)到之前老的提交节点,修改代码再进行提交,提交就形成分支
- 和其他人的版本库合并,也会形成分支
- 可以用下面的命令查看提交关系图
- 最简单的命令是:
$ git log --graph
- 还可以通过工具 gitk,qgit 等提供图形化的分支显示界面
- 最简单的命令是:
- 下面是一个分支关系图示例。其中 A 为顶级节点(最新提交);B 节点是三个多亲节点的合并;后面类似公式一样的东西是 Git 节点的快捷操作符示例。
G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2