我们通常认为git merge具有两个父提交,例如,在我撰写本文时,最新的Linux内核合并是commit 2c5d955,它是4.10-rc6版本启动的一部分,它有两个父提交:
2c5d955合并分支' parisc-4.10-3' ... | *-2ad5d52 parisc:不要在使用中使用BITS_PER_LONG ... *-53cd1ad合并分支' i2c / for-current'的...
Git还支持章鱼合并,它有两个以上的父母,这对于从事较小项目的我们来说似乎很奇怪:难道要与三个或四个父母合并会令人困惑吗?嗯,这取决于内核维护者需要一次合并数十个不同的历史记录。一次执行一次30次合并提交比一次30次合并更令人困惑,特别是如果30次合并没有冲突。
章鱼比您预期的要普遍得多。内核历史记录中有649,306次提交.46,930(7.2%)是合并。在其中1,549(3.3%)是章鱼合并。(截至566cf87版本) ,这是我当前的HEAD。)
$ git log --oneline | wc -l 649306 $ git log --oneline --merges | wc -l 46930 $ git log --oneline --min-parents = 3 | wc -l 1549
作为比较点,Rails提交的所有内容中有20%是合并(63,111个中的12,401个),但章鱼的合并为零。我希望大多数git用户都不知道章鱼合并是可能的。
现在,一个显而易见的问题是:这些章鱼的合并量有多大?这里的行是续篇;该命令总共写了五行。本文中的所有命令都是我在实验时在终端中键入的,因此它们不一定易于阅读。我对结论和以下内容更感兴趣仅包括好奇的代码。
$(git log --min-parents = 2 --pretty =' format:%h%P' |> ruby -ne' / ^(\ w +)(。*)$ / =〜$ _;放入"#{$ 2.split.count}#{$ 1}"' |> sort -n |>尾部-1)66 2cde51f
$ git的日志-1 2cde51fcommit 2cde51fbd0f310c8a2c5f977e665c0ac3945b46dMerge:7471c5c c097d5f 74c375c 04c3a85 5095f55 4f534772f54d2a 56d37d8 192043c f467a0f bbe5803 3990c51 d754fa9516ea4b 69ae848 25c1a63 f52c919 111bd7b aafa85e dd407a371467e4 0f7f3d1 8778ac6 0406a40 308a0f3 2650bc4 8cb7a36323702b ef74940 3cec159 72aa62b 328089a 11db0da e1771bcf60e547 a010ff6 5e81543 58381da 626bcac 38136bd 06b2bd28c5178f 8e6ad35 008ef94 f58c4fc4 2309d67 5c15371 b65ab7326090a8 9ea6fbc 2c48643 1769267 f3f9a60 f25cf34 3f30026fbbf7fe c3e8494 e40e0b5 50c9697 6358711 0112b62 a0a0591b888edb d44008b 9a199b8 784cbf8Author:马克·布朗< [Egent电子邮件编为2014:65名称]
我刚刚从Takashi中提取了声音更新,结果得到了合并提交2cde51fbd0f3。那个有66个父母。
它被拉了,还可以,但是很明显,章鱼合并之间的平衡是可以的。和基督,那不是章鱼,那是克苏鲁的合并。
据我所知,这种不寻常的66位家长提交是对ASoC代码的各种更改的平凡合并。ASoC代表ALSA System on Chip。 "芯片上系统" ASoC是封装在单个硅片中的计算机的术语。将它们组合在一起,ASoC对嵌入式设备提供了良好的支持。
现在,这样的合并多久发生一次?绝不会!第二名合并是fa623d1与" only" 30个父母。但是,在足够的背景下,30到66个父母之间的距离很长也就不足为奇了。
git commit的父母数量可能是根据一个胖的单面分布(通常非正式地称为幂定律分布,但是由于这里没有意思的原因,通常不能严格正确地分配)。软件系统的许多特性都属于单方面的胖分布。我将生成一个图以确保...(随之而来的是图表布局的许多挑剔)。是的,它很胖而且很单调:
为了简洁明了,脂肪单面"这意味着小事物要比大事物多得多,而且事物的最大大小是不受限制的。内核包含45381个双父级合并,但只有一个66父级合并。考虑到足够的开发历史,我们可以期待与超过66位家长合并。
每个函数或每个模块的代码行也很冗长(一侧)(大多数函数和模块很小,但有些会很大;请考虑一下Web应用程序中的" User"类)。模块的更改率(大多数模块很少更改,但有些模块会不断更改;请再次考虑" User")这些分布在软件中随处可见,在对数-对数图上显示为直线,例如这个。
是的,对于父项数量最大的合并来说太多了。在分歧方面最大的合并呢?通过分歧,我的意思是要合并的两个分支之间的差异。我们可以通过简单地合并来衡量的父母彼此抵触并计算差异中的行数。
例如,如果一个分支一年前从master分支出去,改变了一条线,然后又合并回master,那么这段时间内对master的所有更改都将被计算在内,我们分支上的更改也将被计算在内。具有更直观的差异概念,但由于git不会保留分支元数据,因此很难或无法计算它们。
无论如何,作为计算差异的起点,以下是最新内核合并的差异:
用英语来说,该命令显示为:                            #差异线的数量以类似的方式。然后,作为测试,我们将搜索所有合并的结果,且合并的准确度为2,000行。
$(git log --merges --pretty ="%h" |同时读取x;做回声" $(git diff $(git log --pretty =%P $ x -1) | wc -l)" $ x完成> merges.txt)$ sort -n merges.txt | grep' \ b2000 \ b' 2000 3d6ce33 2000 7fedd7e 2000 f33f6f0
(此命令需要很长时间才能运行:我想大概需要十二个小时,尽管我大部分时间不在。)
我希望合并大小像父母计数一样遵循单边分布,应该在对数对数图中显示为一条直线,让我检查一下...是的:
(我已将diff大小取整为1000行,对它们进行装仓;否则,样本数量不足以形成有用的曲线。)
右下角的丑陋部分是由于量化,部分是由于缺少巨大提交而导致的小样本量,与之前的绘图一样。
差异的数量为22,445,760行!这似乎太大了–差异要比内核的整个源代码长。
Greg Kroah-Hartman于2016年9月19日在4.8-rc6的开发过程中做出了这一承诺.Greg是Linus Torvalds的一员。 " lieutenants" –他亲密,值得信赖的开发人员。大致来说,副官构成了内核拉取请求树的第一层。Greg维护内核,驱动程序内核,USB子系统以及其他几个子系统的稳定分支。
在更仔细地检查此合并之前,我们需要一些背景知识。通常,我们将合并视为菱形分支然后合并模式的一部分:
早在2014年,格雷格(Greg)就在一个全新的仓库中开始开发Greybus(一种用于移动设备的总线),就好像他正在开始一个全新的项目一样。它是在一个新的存储库中启动的,它与内核源的其余部分没有任何历史记录。合并后又添加了另一个“初始提交”。到内核,除了我们通常认为是2005年的初始提交之外,回购现在具有两个独立的初始提交,而不是通常的菱形分支和合并模式:
通过查看每个合并提交的父级中存在多少个文件,我们可以看到一些证据:
$ git log -1 f44dd18 | grep'合并:'合并:9395452 7398a66 $ git ls-tree -r 9395452 | wc -l 55499 $ git ls-tree -r 7398a66 | wc -l 148
一侧包含很多文件,因为它包含整个内核源代码;另一侧包含很少的文件,因为它是仅包含Greybus的独立历史记录。
像章鱼合并一样,这会让一些git用户感到奇怪。但是内核开发人员是git的专业用户,并且倾向于放弃使用它的功能,尽管肯定不会鲁ck放弃。
最后一个问题:这发生了多少次?有多少个单独的&initial"结果是内核有四个提交:
$ git log --max-parents = 0 --pretty =" format:%h%cd%s" --date = shorta101ad9 2016-02-23共享上游补丁程序cd26f1b 2014-08-11 greybus:初始commitbe0e5c0 2007-01-26 Btrfs:初始签入,基本工作树代码1da177e 2005-04-16 Linux-2.6.12-rc2
需要明确的是,如果我们忽略所有其他历史记录而绘制了这些提交,则看起来就像下面的图表。
566cf87(当前HEAD)| | | || | | *-a101ad9分享上游补丁| | || | *-cd26f1b greybus:初始提交| || *-be0e5c0 Btrfs:初始签入,基本工作树代码| *-1da177e Linux-2.6.12-rc2
这四个都是当前内核HEAD的遥远祖先,并且它们都没有父提交。从git的角度来看,内核历史始于。四个不同的时间,所有这些最终都合并在一起。
这四个中的第一个(在输出的底部)是我们通常认为的2005年对git的最初提交。第二个是文件系统btrfs的开发,它是独立完成的;第三个是Greybus ,也已经孤立地完成了,我们已经看到了。
$ git show --oneline --stat a101ad9a101ad9共享上游补丁README.md | 2 ++ 1个文件已更改,2个插入(+)
它只是创建了一个文件README.md。但是,它立即在提交e5451c8中合并到了正常的内核历史记录中!
$ git show e5451c8commit e5451c8f8330e03ad3cfa16048b4daf961af434f合并:a101ad9 3cf42ef作者:Laxman Dewangan< [email protected]>日期:2016年2月23日星期二19:37:08 2016 +0530
为什么有人会创建一个新的包含两行README文件的初始提交,然后立即将其合并到主线历史记录中?我无法提出任何原因,因此我怀疑这是偶然的!但是,不要造成任何伤害;这很奇怪。(更新:这是一次意外,莱纳斯以通常的方式对此做出了回应。)
顺便提一句,这也是历史上第二不同的提交,仅仅是因为它是无关提交的合并,就像我们更仔细地研究了Greybus合并一样。
有了它:Linux内核git历史上最奇怪的事情。有1,549张章鱼合并,其中有66个父级。差异最大的合并有22,445,760行diff,尽管它有点技术上的问题,因为它与回购的其余部分没有历史记录。内核有四个单独的" initial&#34 ;;提交,这是一个错误,绝不会出现在绝大多数git repos中,但所有这些都完全在git的设计参数之内。
(如果您喜欢这篇文章,您可能会喜欢“销毁所有软件”的截屏视频,其中一些逐步构建了本文中看到的各种复杂的shell命令,有条不紊地进行着。" Unix的历史研究特别相关。)