版本控制工具
2010年2月17日
如果你花时间跟软件开发人员谈论工具,我听到的最热门话题之一就是版本控制工具。一旦你开始使用版本控制工具,任何称职的开发人员都会这样做,那么它们就会成为你生活中不可或缺的一部分。版本控制工具不仅仅用于维护项目的历史记录,它们也是团队协作的基础。因此,听到人们经常抱怨版本控制工具的不足也就不足为奇了。在我们最近的Thoughtworks 技术雷达中,我们指出了两个企业应该评估使用的版本控制工具:Subversion 和分布式版本控制系统 (DVCS)。在这里,我想对此进行扩展,总结我们内部关于版本控制工具的许多讨论。
但首先要加一些盐。我根据与 Thoughtworks 内部同事以及外部各种朋友和同事的非科学的聚合对话写了这篇文章。我没有进行任何严格的测试或结构化比较,因此像我大多数写作一样,这是基于轶事证据。我近年来主要使用 Subversion 和 Mercurial,但我的使用模式并不代表软件开发团队的典型模式。我接触的大多数人喜欢以敏捷-XP 的方式工作(即使许多人对这个标签嗤之以鼻),并且需要支持这种工作方式的工具。我预计许多人会对这篇文章感到厌烦。我希望这种厌烦能导致一些好的文章,我可以链接到它们。
(在写完这篇文章后,我确实做了一个小的VcsSurvey,这并没有动摇我的结论。)
从根本上说,有三种版本控制系统获得了广泛的认可:Subversion (svn)、Git 和 Mercurial (hg)。
推荐门槛之下
许多工具未能通过推荐门槛。原因有两个:能力不足或知名度不足。
许多工具从 ThoughtWorkers 那里获得了关于其能力不足的一致抱怨。(ThoughtWorkers 本身就是如此,所有工具,包括首选工具,都会收到一些抱怨。那些低于门槛的工具会收到大部分抱怨。)特别是两个工具引起了很多批评:ClearCase(来自 IBM)和 TFS(来自 Microsoft)。它们受到如此多批评的一个原因是,它们在客户站点非常流行,通常公司政策强制要求使用它们(我将在最后描述一种应对策略)。
可以公平地说,这些问题通常会因公司围绕 VCS 使用的政策而加剧。我听说过一些对团队施加的真正奇怪的工作流程,这使得完成任何事情都成为一个持续的障碍。由于 VCS 是执行这些工作流程的工具,因此它确实会受到这种批评。
我不会详细介绍那些能力不足的工具存在的问题,那将是另一篇文章。(这可能已经让我在 IBM 和 Microsoft 更加不受欢迎了。)至少目前,我将把它留给这样一个事实:我尊敬的开发人员已经广泛使用过这些产品,并且不推荐它们。
将工具置于推荐门槛之下的第二个原因是,我没有听到关于某些工具的很多评论。这是一个问题,因为不太流行的工具很难找到知道如何使用它们的开发人员,或者想要了解它们的开发人员。否则好的工具会落后于此,原因有很多。我曾经听到人们说 Perforce 的好话,但现在感觉是它并没有比 Subversion 更具优势,更不用说 DVCS 了。说到 DVCS,除了我在这里重点介绍的两个之外,还有更多。特别是 Bazaar,我偶尔会听到一些关于它的好话,但同样,我听到的次数远不如 Git 或 Mercurial。
在我结束对那些低于门槛的工具的讨论之前,我只想说几句关于一个特别糟糕的工具:Visual Source Safe,或者我称之为:Visual Source Shredder。现在我们看到它的次数越来越少,谢天谢地,但如果你正在使用它,我们强烈建议你立即停止使用它。它不仅使用起来很痛苦,我还听说过太多关于存储库损坏的故事,以至于不敢用它来保存比 foo.txt 更重要的东西。
因此,剩下三种工具,我的联系人通常对它们感到满意。我发现有趣的是,这三种工具都是开源的。在这些工具之间进行选择,首先需要在集中式或分布式 VCS 模型之间做出决定,然后,如果你选择了 DVCS,则需要在 Git 和 Mercurial 之间做出选择。
分布式还是集中式
大多数情况下,在集中式和分布式之间进行选择取决于开发团队的技能和纪律。分布式系统在工作流程方面提供了很大的灵活性,但这种灵活性如果你的团队没有成熟到能够很好地利用它,就会很危险。Subversion 鼓励使用简单的中央存储库模型,不鼓励大规模分支。在使用持续集成的环境中,这是我大多数朋友喜欢的工作方式,这种模型相当适合。因此,Subversion 对于大多数环境来说都是一个不错的选择。
虽然 DVCS 在你如何安排工作流程方面提供了很大的灵活性,但我认识的大多数人仍然将他们的工作模式建立在共享主干存储库的概念之上,该存储库与持续集成一起使用。虽然现代 VCS 拥有几乎神奇的工具来合并不同人的更改,但这些合并仍然只是合并文本。持续集成仍然是实现语义一致性的必要条件。因此,即使是使用 DVCS 的团队通常仍然具有中央主存储库的概念。
与更酷的分布式兄弟相比,Subversion 有三个主要缺点。
由于分布式系统始终为你提供整个存储库的本地磁盘副本,这意味着存储库操作始终很快,因为它们不涉及对中央服务器的网络调用。如果你正在查看日志、与旧版本进行差异比较,以及任何其他涉及整个存储库的操作,这将是一个明显的区别。如果在我的家庭网络上都能注意到这一点,那么如果你的存储库位于另一个大陆,这将是一个巨大的问题——正如我们在分布式项目中发现的那样。
如果你离开连接到存储库的网络,分布式系统仍然允许你使用存储库。你可以在没有网络连接的飞机上提交工作的检查点、浏览历史记录以及比较版本。
最后一个缺点更多的是一个社会问题,而不是一个真正的工具问题。DVCS 鼓励快速分支以进行实验。你可以在 Subversion 中进行分支,但它们对所有人的可见性会让人们不愿意为实验性工作打开一个分支。同样,DVCS 鼓励工作检查点:提交不完整的更改,这些更改甚至可能无法编译或通过测试,到你的本地存储库。你也可以在 Subversion 的开发人员分支上这样做,但由于这些分支位于共享空间中,因此人们不太可能这样做。
最后一点也导致了反对 DVCS 的论点,即它鼓励无节制的分支,这在早期感觉很好,但很容易导致合并灾难。特别是功能分支方法是一种流行的方法,我不鼓励使用它。与之前类似的评论一样,我必须指出,鲁莽的分支不是特定于一种工具的。我经常听到 ClearCase 环境中的人抱怨同样的问题。但 DVCS 鼓励分支,这就是我指出团队需要更多技能才能很好地使用 DVCS 的主要原因。
有一种特殊情况,即使对于一个擅长使用 DVCS 的团队来说,Subversion 也是更好的选择。这种情况是,你正在协作的工件是二进制的,无法由 VCS 合并——例如 Word 文档或演示文稿。在这种情况下,你需要恢复到悲观锁定,使用单一作者签出——这需要一个集中式系统。
Git 还是 Mercurial
因此,如果你要走 DVCS 路线——你应该选择哪一个?Mercurial 和 Git 吸引了最多的关注,因此我认为选择就在它们之间。然后,选择归结为功能与易用性,再加上一点市场份额和 Github 的阴影。
Git 似乎确实以其功能而闻名。人们对它几乎神奇的能力赞不绝口,即使面对文件重命名,它也能自动正确地进行文本合并。我没有看到任何比较合并能力的客观测试,但主观意见倾向于 Git。
(合并重命名,正如我的同事 Lucas Ward 所定义的,描述了以下场景。我将 Foo.cs 重命名为 Bar.cs,Lucas 对 Foo.cs 做了一些更改。当我们合并时,他的更改会正确地应用于 Bar.cs。Git 和 Mercurial 都可以处理这种情况。)
对于许多人来说,Git 最大的缺点是它经常难以理解的命令和思维模型。Ben Butler-Cole 完美地表达了这一点:“那里有一个非常强大的东西在蠕动,它基本上可以做我可能要求它做的任何事情,如果我知道怎么做的话。”对于它的批评者来说,Git 缺乏可发现性——从其明显的设计中逐步推断出它做什么的能力。Git 的支持者说,这在很大程度上是因为它使用了一种与其他 VCS 不同的思维模型,因此你必须更多地忘记你对 VCS 的了解才能欣赏 Git。无论原因是什么,Git 似乎对那些喜欢学习内部机制的人更有吸引力,而 Mercurial 似乎对那些只想进行版本控制的人更有吸引力。
Github 的阴影在这里很重要。即使是 Git 怀疑论者也认为它是一个用于项目协作的绝佳场所。Mercurial 的等效工具,Bitbucket,并没有激发同样的喜爱。但是,其他网站可能会开始缩小差距,特别是Google Code 和 Microsoft 的 Codeplex。(我发现 Codeplex 使用 Mercurial 非常令人鼓舞。Microsoft 通常,正确地,因没有与互补的开源产品良好协作而受到批评。他们在其开源托管网站上使用 Mercurial 是一个非常令人鼓舞的迹象。)
从历史上看,Git 在 Windows 上运行得很糟糕,糟糕到我们不建议使用它。现在这种情况已经改变,只要你使用msysgit 而不是 Cygwin 运行它。我们现在的观点是,msysgit 足够好,可以使与 Mercurial 的比较对 Windows 来说不再是一个问题。
人们通常发现 Git 比 Mercurial 更擅长处理分支,特别是对于用于实验和检查点的短期分支。Mercurial 鼓励使用其他机制,例如单独存储库目录的快速克隆和队列修补,但 Git 的分支是一个更简单、更好的模型。
Mercurial 似乎在处理大型二进制文件时存在问题。我的一般建议是,这些东西通常最好用 Subversion 管理,但如果你拥有的大型二进制文件太少,不值得单独管理,那么 Mercurial 可能会因为你拥有的少量大型二进制文件而卡住。
多个 VCS
同时使用多个 VCS 通常很有价值。这通常是当需要使用比团队想要使用的 VCS 能力更低的 VCS 时。
我们经常遇到的情况是,公司有一个对 VCS 的不足标准(通常是 ClearCase),但我们希望高效地工作。在这种情况下,我们成功地使用了一个不同的 VCS 来进行日常团队工作,并在必要时提交到公司 VCS。因此,虽然团队 VCS 会看到每个人每天提交几次,但公司 VCS 每周或每两周才会看到一次提交。通常情况下,公司管理员也更喜欢这样。历史上,我们使用 svn 作为本地 VCS,但在未来,我们更有可能使用 DVCS 作为本地前端。
这种双重使用场景在 git-svn 中也很常见,人们在本地使用 git,但提交到共享的 svn 系统。Git-svn 是另一个更喜欢 git 而不是 mercurial 的原因。使用本地 DVCS 对远程站点工作特别有价值,因为网络中断和带宽问题会瘫痪远离集中式 VCS 的站点。
许多团队都可以从这种双 VCS 工作方式中受益,特别是如果他们的企业 VCS 强制执行了大量的企业仪式。使用双 VCS 通常可以让本地开发团队和企业控制者都更开心,因为他们对 VCS 的动机通常不同。
最后说明
请记住,虽然我在这里谈论了很多工具,但通常是实践和工作流程会产生更大的影响。工具当然可以使使用一套好的实践变得更容易,但最终还是由人们来为他们的环境使用有效的工作方式。我喜欢看到允许许多小的更改并使用持续集成快速集成的方案。我宁愿使用一个糟糕的工具,但有 CI,也不愿使用一个好的工具,但没有 CI。