卷1:第12章 Mercurial

2018-02-24 15:55 更新

作者:Dirkjan Ochtman

译者:谢路云

状态:完成

原文链接:http://www.aosabook.org/en/mercurial.html

Mercurial是一个现代分布式版本控制系统(VCS),主要由Python语言编写,以及一小部分C代码,以提高性能。在本章中,我会讨论Mercurial设计上的一些关于算法和数据结构的决策。首先,请允许我简短的回顾一下版本控制系统的历史,介绍一些必要的背景知识。

12.1.版本控制简史

虽然这一章的主要内容关于Mercurial的体系结构,但其中的许多思想和其他版本控制系统是共通的。为了更好的讨论,我想先说明一些存在于不同版本控制系统中的概念和行为。为了恰当的说明这一切,我还将简单的介绍一下这个领域的历史。

版本控制系统的发明是为了帮助软件系统的开发人员并行的工作,而不必相互传递文件的拷贝并人工的记录文件的修改历史。我们可以将软件的源代码文件扩展到任意文件树。版本控制的主要功能之一就是传递树的变化。工作流程的基本循环是这样的:

  1. 从其他人处获取最新的文件树
  2. 对这个版本的树进行一系列修改
  3. 发布并使其他人可以获得这些修改

第一个动作,也就是获取一份本地的文件树,被称为“检出”(checkout)。我们获取和发布所有修改的地方叫做“版本库”(repository),而检出得到的目录则被称为“工作目录”、“工作树”或是“工作拷贝”。用版本库中的最新文件更新工作拷贝的动作就叫做“更新”(update)。有时候这还会涉及到“合并”(merge),也就是组合不同用户对同一个文件作出的修改。diff命令使我们能够查看树或文件在两个版本之间的变化,它最常见的用途是检查你的工作目录中的本地(未发布的)修改。修改的发布是通过一个“提交”(commit)命令完成的,它会将工作目录的改变保存到版本库中去。

12.1.1.集中式版本控制

史上第一个版本控制系统叫做“源代码控制系统”(Source Code Control System, SCCS),出现于1975年。它的主要功能是将差异保存在文件中,这比保存一个文件的多个版本的完整拷贝更经济,但它无法将这些差异传播给其他人。它的继任是1982年出现的“修订控制系统”(Revsion Control System, RCS)。RCS是SCCS更加先进并且免费的替代品(它至今仍在GNU项目的维护之下)。

RCS之后是“并行版本系统”(Concurrent Versioning System, CVS),发布于1986年。它最初是一组批量处理RCS修订文件的脚本。CVS最大的创新是实现了多个用户同时编辑相同的文件并在最后合并所有的修改(并行编辑)的模式。这也产生了“编辑冲突”的概念。开发者提交的新版本文件必须基于版本库的最新版本之上。如果版本库和我的工作目录都对文件作出了修改,那么我必须解决这些修改所产生的所有冲突(修改了同一行的情况)。

CVS也开创了“分支”(branch)以及“标签”(tag)的概念。分支使得开发者能够并行的工作在不同的任务之上,标签则可以用来标记版本库的一个快照以便引用。虽然CVS一开始是通过将版本库放在共享文件系统上来传递差异的,但随后CVS实现了C/S架构以适应大型网络中的应用(例如因特网)。

在2000年,三位开发者为了纠正CVS中的设计缺陷一同完成了一个新的版本控制系统,叫做Subversion。Subversion最主要的特点将工作树作为一个整体对待。也就是说,每个修订所作出的变更都应该具有原子性、一致性、隔离性和持久性。Subversion能够在工作目录中记录工作拷贝的检出版本,这样常用的diff操作(比较本地的工作树和检出的版本)就能更快的在本地进行。

Subversion的一个有趣的概念是标签和分支都是项目树的一部分。一个Subversion项目通常分为三个部分:tagsbranchestrunk。事实证明这种设计对于不熟悉版本控制系统的用户非常直观,尽管这种设计的天然灵活性也为各种版本库转换工具带来了大量问题,大部分是因为在其他版本控制系统中,tagsbranches有更加结构化的表示方法。

以上提到的都是“集中式”的版本控制系统。也就是说,尽管它们知道如何交换变更(从CVS开始),但是它们仍然依赖于另外一台计算机来记录版本库的历史。而“分布式”的版本控制系统则会在存在工作拷贝的每台计算机上都保存版本库的完整或是部分历史。

12.1.2.分布式版本控制

尽管Subversion相比CVS有了明显的进步,但它仍有许多缺点。首先,在所有的集中式版本控制系统中,由于版本库的历史集中在同一个地方,变更集的提交和发布实际上是一回事,这也意味着在没有网络的情况下是无法提交变更的。其次,在集中式版本控制系统中,访问版本库总是需要一次或者多次的网络请求,比分布式版本控制系统中的本地访问要慢的多。再次,以上提到的所有版本控制系统都不擅长于记录合并(随着系统的改进,有些能够支持)。在有许多人并行工作的大型团队之中,版本控制系统必须能够记录每个新的修订版本都包含了哪些变更,这样才能保证不丢失任何工作,并且后续的合并也能使用这些信息。第四,传统版本控制系统的集中特性有时非常别扭,它强迫你只能在一个地方进行集成。分布式版本控制的提倡者认为,使用分布式系统的团队的组织更加自然,开发者们能够根据项目的需要在任何推送和集成变更。

为了满足这些需求,已经出现了许多新的工具。我(从开源世界的角度)认为,2011年中最重要的三个当属Git、Mercurial和Bazaar。Git和Mercurial都始于2005年,当时Linux内核的开发者们决定不再继续使用专有系统BitKeeper。两者都是由Linux内核开发者发起的(分别是Linus Torvalds和Matt Mackall),以满足对能够处理上万文件的成百上千个变更集进行管理的版本控制系统的需求。Matt和Linus都深受另一个版本控制系统Monotone的影响。同一时期的Bazaar的开发则相对独立,但在被Canonical采纳为所有项目的版本控制系统之后也得到了很广泛的使用。

构建一个分布式的版本控制系统显然会遇到许多挑战,其中一部分是所有分布式系统所固有的。例如,在集中式系统中源代码控制服务器总是保存着一份统一的版本历史,但在分布式系统中是不存在统一的版本历史的。分布式系统允许并行的提交变更,这使得在任何版本库中按照时间将修订历史排序都是不可能的。

几乎所有的系统都采用了有向无环图(DAG)而非线性的变更集方式组织来解决这个问题( 图12.1 )。也就是说,新提交的变更是其基础版本的子版本,且不可能有任何版本的基础是自己或是自己的子嗣。在这个方案中,我们有三种特殊类型的修订版本: 没有父版本的“根版本”(root revision),一个版本库可以有多个根版本),有一个以上父版本的“合并版本”(merge revision),和没有子版本“头版本”(head revision)。每个版本库都是从一个空的根版本开始不断产生一系列变更集,最后得到一个或者多个头版本。当两个用户分别独立的提交了变更且其中一个人希望从另一个人那里拉取(pull)变更集时,他必须明确的将另一个人的变更合并从而得到一个新的版本,这个版本的提交将得到一个合并版本。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号