说mercurial(hg)的优点和适用的团队需要和其他的版本控制工具(VCS)比较。因为参加不同的项目,我用过hg,git,fossil,plastic scm,svn,tfs,而且自己也在写一个hg的插件,所以试着回答一下。一句话简答:mercurial的优点(也就是其他家不太具备的)有revset(这个好多人都没说),扩展性,append only的存储结构,等等。

版本控制工具的选择和实际的需求联系很紧,比如是否是整个公司一个repository,是否需要复杂的权限管理,是否需要管理大量的二进制文件,对线性历史是否有要求,是否允许修改历史,是否具备扩展性,分布式/分支,是否集成code review还有issue tracker等等。FreeBSD项目之前纠结过从CVS出来换什么,有个很长的考察列表,不过时间很久远了,参考用: https://wiki.freebsd.org/VersionControl

有的团队将所有代码都放到一个仓库,比如Google和Facebook。这种模式很好,在碰到产品出问题的时候,是很容易对仓库bisect定位错误。你的VCS不支持或者支持不好,是你的问题,你不能说这种方法错,或者强令要求改为小型仓库。这个也是2012年Facebook去求助的焦点问题,Facebook现在用自己开发的插件(hgwatchman, remotefilelog),结合memcached去做缓存,本机只需要存hg的部分历史,需要没有存储部分的时候会自动获取,这个很赞了。

有的团队需要管理大量的二进制文件,比如音乐图片素材,这种需要在游戏团队很多,这个需要VCS能只在本机保存需要的二进制文件,不需要该二进制文件的所有历史版本。Mount&Blade的一些三方mod开发是用的SVN(虽然慢),Unity3D倾向同是Mono社区的Plastic SCM。hg通过largefiles支持二进制大文件本机只存一个。

有的团队需要对代码加上权限控制,谁能写,谁能读,是否有隐藏的分支等等。目前流行的git和hg由于都将版本信息和代码根目录的状态绑定(git的commit节点和hg的manifest)都是按照整个repository来控制权限,粒度太大。商用的大部分能精细版本控制(大多采用数据库后台,所有的对象都是UUID,然后逐对象设置权限),SVN也可以。fossil有限的权限设置。

有线性历史要求的,要么是在commit之前有强制update,要么要求强力的rebase,patch queue的能力。比如postgresql社区转为git,但是还是坚持以前的diff patch,linear history。有的采用pull request的模式开发,比如netflix(Non-Continuous Reviews),也就是在分支上开发功能再合并到稳定版中;也有在主线上开发,然后分一个稳定版分支出去(采用较长周期产品冻结-发布模式的)。代码的merge分为一般的merge(比如现在的hg),而git、plastic scm采用recursive merge,合并的错误率可以下降,因此git敢开octopus merge。当然最新的hg在搞consensus merge,应该可以更好的应付超级庞大复杂的branch/merge模式。

是否允许修改历史,这个是一个重要的争论点。比如fossil认为代码历史是绝对不允许改的,因为有些领域的软件因为安全审计等问题,历史是审计的一部分。mercurial认为已经publish的代码不能动,所以引进了phase插件(发布、草稿、保密三个不同状态)。最近几个版本中mercurial正不断完善的Evolve插件,将历史的修改也变成历史的一部分(大事件)。

扩展性,git的子命令,比如git svn,是独立的git-svn程序,git的扩展是基于git存储模型的一致性(一个基于内容跟踪的文件系统)。而hg则是基于api的扩展性。喜欢git的大多用bash等脚本去扩展,而用hg的则用python插件。

分布式/分支是口水战最多的,大家做法不同,没有什么高下之分。git引以为豪的是指针式的设计,分支就是指向新commit的指针,和其他人数据交换是交换不同的object并更新指针指向的commit。hg采用的比较manifest,找出亲缘关系,然后互通有无。如果说到几个分支同是开发,bzr可以给每个分支开一个目录,hg也有share插件,可以几个本地working directories共享一个repository。plastic scm是一个服务器内采用C/S结构,服务器间可以replicate。perforce则是引入本地缓存,可以保存非当前版本的历史。

这里再多说两句,之前有人说的好,hg的branch代表一种lineage(世系),这个和git的branch不一样。比如同样是把development的代码合并到stable上,git里面看到的stable是包含所有的development和之前的stable,而hg里面,这是两个不同的世系,hg log -b stable就只显示stable,merge过来的development的世系不会显示。这个是一种选择,没有好坏。

至于集成的code review还有issue tracker,plastic scm有code review,fossil集成issue tracker和wiki,hg可以通过插件支持一个分布式的code review。

最后再回到hg的那几个优点。

revset是hg内部查询revision的一种DSL,比如‘hg help revset’里面的‘hg log -r “head() and not closed()”’,具体意思可以翻帮助。

append-only storage,hg的存储格式是revlog,只能追加,除了容易实现原子性的操作之外,对磁盘读写也会变少,这个很适合大项目,也是Facebook看重hg的一个原因(另一个是因为hg的扩展体系)。这种结构也使得hg annotate(hg blame)变得很快,git blame很慢的,也是去年2012年Facebook去抱怨的原因了。之前Mozilla的人也测过generaldelta和parentdelta,一个省,一个比较快(parentdelta就是顺序的读磁盘)。hg的annotate就是顺着这个append-only的存储文件顺序构造哪个版本改了啥,所以即便很大的仓库很长的历史,这一点上不会慢。

插件体系前文已经不断提到,各种插件帮助hg变得很强,但是默认各种插件是不开启的,因为hg的哲学是提供一个简单可用的内核,对初学者保持简单,一旦你开始向探索更多功能,成为一个进阶用户,就自己去开启好了。补充一下,极其好用的hg-git和hg-svn插件在tortoisehg 3.0正式集成了,享用就直接开启。(合作了几个项目都没问题,hg-svn感觉比git-svn直白,好像就是在用svn一样,但是又可以享用hg的好处,即便有了复杂的操作,也只需要push回svn之前rebase/histedit一下;hg-svn比git-svn快,就我测试而言)

另tortoisehg本身就是极为好用的,不像tortoisegit是tortoisesvn的直接移植,用起来总是不舒服,还没gitextension好用。

有个缺点是,hg对向后兼容的执着,要求支持python 2.4啦,要求不许修改wire protocol啦。于是现在你在hg重命名一个文件,这个文件在后台其实存了两份。但是随着新的bundle2,这些问题应该可以解决,因为这个无非引入一个新的delta policy罢了。

— 完 —

本文作者:知乎用户(登录查看详情)

【知乎日报】
你都看到这啦,快来点我嘛 Σ(▼□▼メ)

此问题还有 3 个回答,查看全部。
延伸阅读:
有哪些互联网产品的文案(或 Slogan)让你为之动容?
GitHub、Bitbucket、Google Code 各有哪些优缺点?

分享到