作为一个强大的分布式版本控制系统,Git 的诞生使得多个开发人员能够在同一个项目上同时工作,也允许其跟踪代码的所有更改。它不仅改变了开发和协作的方式,还提升了软件开发的效率、质量和灵活性。掌握和熟练使用 Git 已成为现代软件开发中不可或缺的一部分。
那么,Git 究竟是怎么开发出来的?又有哪些趣事?本文作者 Nicholas Yan 进行了详细的回顾与分享。
原文链接:https://graphite.dev/blog/bitkeeper-linux-story-of-git-creation
作者 | Nicholas Yan 责编 | 夏萌
译者 | 弯月
出品 | CSDN(ID:CSDNnews)
2005年4月3日,Linus发布了Linux内核的一个候选版本2.6.12-rc2。Linus表示,这个发布候选版本并不是太有趣,「diffstat (用于显示文件差异统计信息的工具) 输出说明了一切:这是许多非常小的变更,融合了大量小的改进和错误修复」,但这个版本成为了一个重要的标志,因为这是最后一个Linux内核的非Git版本。
BitKeeper
在使用BitKeeper(分布式版本控制系统)之前的十年里,Linux内核的版本控制一直是Linus自己。
具体流程如下:开发人员将tarballs和补丁提交给几个Linus信任的助手。助手们通过审核后,将补丁发送给Linus。最后,Linus亲手将它们合并到自己的源代码树中,然后发布。
当然,Linus本人并不是一个「完美的版本控制服务」。1998年,知名程序员Larry McVoy通过Linux内核邮件列表首次提出了BitKeeper的想法,他写道:「很明显,我们的领袖[Linus]目前有点超负荷,补丁可能会丢失。」
尽管如今看来,这种手动的工作流程也未免太原始了,但在当时,Linus认为这种工作流程比其他选择(主要是CVS)更好。
后来,2007年Linus在Google发表了关于Git的演讲,他提到了他的一个核心设计原则:「 有什么是CVS不会做的? 」当然,这种厌恶自然也延伸到了SVN,同样是在那次演讲中,他笑着说:「观众席中有Subversion用户吗?你们可以离席了。我非常讨厌CVS,所以我认为Subversion是有史以来最没有意义的项目。有一段时间里Subversion的整体口号是正确实现CVS。以这个口号为出发点,你将一事无成。因为CVS根本无法正确实现。」
Linus认为CVS的核心问题在于其集中化的性质。由于Linux开发人员有数百名之多,所以Linus认为每个人都拥有自己独立的代码库副本至关重要,因为只有这样他们才能在自己的分支上开发。这不仅对线下的工作有帮助,而且对内部管理也很有帮助。每位开发人员都可以自由地向自己的代码库提交任何代码,而且他们有机会说服社区他们的变更是有价值的。这样可以防止拥有提交权限的贡献者成为唯一的代码库的守门人。
BitKeeper与CVS截然不同。在前面提到的1998年关于BitKeeper的介绍中,Larry McVoy勾勒了一个系统,这个系统与如今我们所了解的源代码控制非常相似,但在当时却完全不同。Larry McVoy写道:
支持所有这些操作的机制是一个分布式源代码管理系统。该系统的主要特点是:
每个人都有一个代码库(相比之下,CVS只有一个代码库)。
代码变更可以作为「超级补丁」(又称为「变更集」)发送。变更集只是一个补丁文件,包含以下内容:
所有变更,按照修订版本组织。
一个标识符,表示补丁应该应用在树的哪个位置(如果你的更新落后于补丁发送者,则补丁将失败)。
所有变更的修订历史。
元数据,如路径名变更,符号标签(如 alpha2 或 linux-2.1.133)等。
一个名为「开发线」(LOD)的新概念。从逻辑上讲,它是一个分支,但不需要在分支上。补丁可以(也将会)成为自己的 LOD。你可以在LOD上执行「将此应用于主分支」之类的操作。
后来,Linus对BitKeeper表示了极大的赞赏,认为BitKeeper改变了他的看法,而Git的灵感也来源于此:「 BitKeeper不仅是第一个我觉得值得尝试的版本控制系统,也让我明白了使用这类系统的意义以及如何正确使用这类系统。所以,从技术的角度来看,Git的许多方面虽然与BitKeeper有很大的区别(这是另一个设计目标,因为我不希望Git成为BitKeeper的克隆),但Git的许多工作流程都借鉴了BitKeeper。 」
许可
虽然Linus对BitKeeper的评价很高,但2002年在Linux内部使用该工具的问题上,他的决定引发了Linux内核邮件列表上大规模的争论。
争论源自何处?Larry McVoy在构建BitKeeper时,将其作为了一个商业的闭源项目(BitMover)。尽管人们能够使用BitKeeper的免费社区版本,但有一个限制性许可。
根据维基百科记载:「社区版BitKeeper的许可允许开发人员在开源或自由软件项目中免费使用该工具,前提是这些开发人员在使用BitKeeper期间以及之后的一年内,不得参与竞争工具(如Concurrent Versions System、GNU arch、Subversion以及ClearCase)的开发。不论竞争工具是免费还是专有的,这条限制都适用。」
自由软件的宣传者Richard Stallman也发表了意见:「BitKeeper许可掌握了控制权。实际上是在说,‘你没有权利使用BitKeeper,只有临时的使用许可,而我们可以随时撤销这个许可。我们允许你使用BitKeeper,你应该对此心存感激。不要做任何我们不喜欢的事情,否则我们可能会撤销这些特权。自由软件运动的产生正是源自大家对这种许可的愤怒。’」
但Linus采取了更务实的观点,从他的角度来看,他不过是需要最适合的工具,而不会在意这些工具源自何方。2007年,他曾表示:「尽管BitKeeper的许可有问题,但我仍然很高兴,因为坦白说,对于我而言,我做开源是因为我认为开源才是构建软件的唯一正确方式。但同时,我会使用最适合的工具,坦白说,BitKeeper就是我想要的工具。」
然而,这段不安的联姻无法持久。
2005年,Linux内核开发人员Andrew Tridgell违反了许可,并实施了逆向工程。他能够提取BitKeeper代码而不需要遵循BitKeeper许可。在Andrew Tridgell看来,这完全符合道德准则:「我在编写这个工具时根本没有使用BitKeeper,因此从未受过BitKeeper许可的约束。」
然而Larry McVoy对此有不同的看法。最初,Linus是站在他这边的:
「如果有人编写一个免费的替代品,Larry完全能接收……Larry不能接受的是,有人通过逆向工程他的代码编写了一个免费的替代品。Larry的道德立场非常明确:‘你可以与我竞争,但你不能搭我的便车。自己解决问题,公平竞争。不要通过我的解决方案来竞争。’BitKeeper许可也正是为了表达这一点:‘少来搭我的便车,你这个爱占便宜的家伙。’而我[Linus]无法反驳这一点。」
Linus充当了三个月的调停,但最终未能达成和解。
2005年4月6日,Linus通过Linux内核邮件列表发了一封主题为「内核SCM长篇故事……」的邮件,开启了一系列改变整个行业的事件:
「可能部分人已知情,在过去的一两个月里,我们一直在努力解决BitKeeper的使用冲突。但并未取得成功,因此内核团队正在寻找替代方案。」
他开玩笑地谈到了历史:「并不是说我选择BitKeeper完全没有冲突」,并强调了他对BitKeeper团队的感激之情。
尽管如此,Linus回顾这段时光时仍然充满了喜悦:
事实上,BitKeeper从根本上改变了我们的做事方式。从细粒度的变更集跟踪,到我最终信任次级维护者承担更加重要的工作,我们无需再逐个出补丁。因此,与BitKeeper的三年合作绝对没有浪费,我相信我们改进了工作方式,而我正在考虑的事情之一就是确保这些方式持续有效。
我想说,我个人对BitKeeper和Larry非常满意。虽然我们的合作未能成功,但对Linux内核的开发产生了重大影响。我们必须找到一套工具来代替BitKeeper的功能。
Linus去度假
虽然4月6日Linus通过邮件列表公布了双方合作失败的消息,但实际上他已经展开了紧张的工作。就在2.6.12-rc2发布的三天前,他停止了Linux内核的工作,并全力寻找BitKeeper的替代方案。
Linus的目标是「在两周内拿出可以投入使用的工具。」他在4月6日的邮件中宣布:「我将离线一周(你可以认为‘Linus去度假了’),我请求继续维护BitKeeper树的人可以把结果作为(单独的)补丁发送给我,因为我也需要合并一部分代码。」
很明显,Linus的邮件透漏出了紧迫感,Linux内核的下一个版本发布受阻,他必须尽快解决这个问题。
他甚至在4月7日的一封邮件中提到了最坏打算,Linux内核可能会转而使用集中式的版本控制系统:「请注意,我厌恶集中式的SCM模型,但如果事态严重,导致我们短时间内(一个月或两个月)无法并行合并代码,我会通过一个信赖的网站搭建SVN之类的系统,允许少数几个人提交代码,并将合并的工作分给几个人,避免我自己成为瓶颈。」
虽然事后来看,结果很明显,但在当时,根据这些邮件,编写一个自定义的版本控制系统尚遥不可及。从4月6日的第一封邮件到4月12日的最后一封邮件,期间来往的邮件多达205封,他们讨论了许多其他开源替代方案,比如Monotone、GNU arch、Bazaar-ng、Darcs,其中一些工具的创建者也参与进来宣传自己的项目。
主要考虑因素,尤其是从Linus的角度来看,是每个工具的总体性能。总的来说,205封邮件涉及很多关于各种工具的性能和效率的讨论。
每个人心中最大的忧虑在于:现有工具中是否有一款适用于Linux内核这种规模的项目?
4月8日,距离最初的邮件发出过去了两天,也就是Linus全身心投入工作5天后,他分享了一个最新消息:「由于monotone太慢,请各位在手头的工作之余,试试看这个快速的挑战:kernel.org:/pub/linux/kernel/people/torvalds/。」
Git诞生了。
Git的第一行代码
有关Linus编写初版Git仅用了两周时间的传闻,我们需要加上一则补充说明:如今我们所说的git主要是面向用户的命令和整体工作流程,但当时的目标和任务有很大的不同,而且范围非常有限。
人们在邮件列表上争论各种工具和方法的优缺点时,有人粗略描述了他们的需求:「慢一点没关系,只要不是慢得离谱。临时解决方案的目的是建立线上的补丁工作流程。」
初版的Git更像是一个内容可寻址的文件系统,算不上是一个完整的源代码管理系统。Linus在另一封邮件中解释说:
(*) 我称之为「提交」,但实际上更简单。实际上表明「这是我的<目录的当前状态>,我从<之前的目录状态集合>到达了这个状态,其原因是<**>」。
顺便说一下,我的设计意图就在于此。「git」并不在意合并之类的操作。你可以使用任何SCM合并代码。「git」所做的只是跟踪目录状态(以及发展到该状态的经过),仅此而已。git并不会执行合并操作,实际功能并不多。
也就是说,你在Git存档上「拉取」或「推送」时,就会得到所有目录状态的「联合」。其中的HEAD就是指向「目录状态之海」的一个指针,但要想合并两个目录状态,就必须使用其他工具。
虽然来往的邮件讨论了各种工作流程和移动提交,但Linus构建的客户端并没有考虑这些操作。
Linus表示:「实际上‘Git’非常简单,仅用了4天就编写完成了。大部分时间实际上并不是在编写代码,而是思考数据结构。」
这是他后来反复提及的感受,有时会被断章取义,但确实Linus所编写代码的新颖之处就在于数据结构的选择。Linus分享了前几次提交,但没有关于工作流程的讨论。
少数人主张使用SQL来存储变更。Linus在后来的对话(很符合他个人的发言风格)中表示:
> 为什么后端使用了目录树,而没有使用sql?
因为sql很糟糕?
我可以想出几百万种方法来降低速度。请你提出提高速度的方法。
—— Linus
但也有一些人看好Git,并渴望使用Git。就在Linus请求大家查看他的代码的同一天,有几个人发回了一些脚本,其中包含了在他的基础之上构建的其他功能。
在Linus投入真正的工作两周后,即2005年4月17日,他发了一封邮件:「第一次真正的内核Git合并!」
未来的发展
二十年后,再读当初的那些邮件,最令我开心的部分是看到当时的作者们未曾预料到的未来。
一位用户在评估源代码控制替代产品monotone时表示:
有点令人恼火的是,monotone似乎没有网页界面。我以前在追踪bug时经常使用BitKeeper的网页界面,打开网页浏览窗口查看文件的修订版本和签入评论等操作真的很快。你们知道是否有人正在开发这样的界面吗?
当时,源代码控制和代码审查的网页界面刚刚开始流行,Google,Guido van Rossum 正在开发他们的第一个基于网页的专用代码审查工具Mondrian。而在两年后,GitHub成立了。
有关性能的讨论也充满了讽刺意味。
关于如果Git后端使用基于网络的文件系统性能会如何的讨论,如今已被颠覆。Google(FUSE)和Meta(EdenFS)这类的大公司拥有庞大的单一代码库,对于他们来说,这些基于网络的源代码控制文件系统是扩展源代码控制和构建的关键。
而最初激励Linus编写Git的核心问题(现有的版本控制替代产品无法支持Linux内核代码库的大量历史记录和大规模的提交吞吐量)将在几年后再次上演,Meta将主代码库从Git迁移出去,也出自同一个原因。
当然,当时参与讨论的各位并不知道这一切。
Git的创建是为了打破Linux内核发布的阻碍,并非在全球重新发明所有源代码管理。Linus的评论强调他认为源代码管理是其他工具的领域,这些工具将成为Git的接口。
在回顾历史时,我们常常将其浪漫化为灵感突然迸发。然而,Git的创建说明了更加严酷的现实:围绕许可的分歧逐渐升级;打破阻碍的紧急备选方案的需求;多年的打磨和迭代并非源自发明者,而是由社区推动。
推荐阅读:
「如今马斯克的 X,失去了唯一一位公关」