Git和Mercurial -比较和对比

一段时间以来,我一直在我的个人项目中使用subversion。

我听到越来越多关于Git、Mercurial和DVCS的好消息。

我想尝试一下DVCS,但我对这两种选择都不太熟悉。

Mercurial和Git之间有什么区别?

注意:我试图找出哪一个是“最好的”,甚至我应该从哪一个开始。我主要寻找它们相似和不同的关键领域,因为我想知道它们在实现和理念方面有何不同。

41313 次浏览

我想你们可以通过这两个视频了解这些系统的相似或不同之处:

Linus Torvalds on Git (http://www.youtube.com/watch?v=4XpnKHJAok8)
布莱恩·奥沙利文谈Mercurial (http://www.youtube.com/watch?v=JExtkqzEoHY)

它们在设计上非常相似,但在实现上却非常不同。

我使用Mercurial。就我对Git的理解而言,Git的一个主要不同之处在于它跟踪文件的内容而不是文件本身。Linus说,如果你将一个函数从一个文件移动到另一个文件,Git会告诉你整个移动过程中该函数的历史。

他们还说git在HTTP上更慢,但它有自己的网络协议和服务器。

Git作为SVN的厚客户端比Mercurial更好。可以对SVN服务器进行拉推操作。这个功能在Mercurial中仍处于开发阶段

Mercurial和Git都有非常好的网络托管解决方案(BitBucket和GitHub),但谷歌代码只支持Mercurial。顺便说一下,他们对Mercurial和Git进行了非常详细的比较,以决定支持哪一个(http://code.google.com/p/support/wiki/DVCSAnalysis)。它有很多好的信息。

Mercurial几乎完全是用python编写的。Git的核心是用C语言编写的(应该比Mercurial的更快),工具是用sh、perl、tcl编写的,并使用标准的GNU utils。因此,它需要将所有这些util和解释器带到不包含它们的系统中(例如Windows)。

两者都支持SVN,尽管AFAIK SVN在Windows上对git的支持是坏的(可能是我不走运/蹩脚,谁知道呢)。还有一些扩展允许git和Mercurial之间的互操作。

Mercurial有很好的Visual Studio集成。上次我检查时,Git插件正在工作,但非常慢。

它们的基本命令集非常相似(init, clone, add, status, commit, push, pull等)。所以,基本的工作流程是一样的。另外,两者都有类似tortoisesvn的客户端。

Mercurial的扩展可以用python编写(这并不奇怪!),而git的扩展可以用任何可执行形式编写(可执行二进制文件、shell脚本等)。有些扩展功能非常强大,比如git bisect

两者我都经常使用。主要的功能区别在于Git和Mercurial在存储库中命名分支的方式。使用Mercurial,分支名称将被克隆并与其变更集一起提取。当您向Mercurial中的新分支添加更改并将其推送到另一个存储库时,该分支名称将同时被推送。因此,分支名称在Mercurial中或多或少是全局的,您必须使用Bookmark扩展来拥有仅在本地使用的轻量级名称(如果您需要它们;默认情况下,Mercurial使用匿名轻量级代码线,在术语中称为“头部”)。在Git中,分支名称及其到远程分支的内射映射存储在本地,您必须显式地管理它们,这意味着知道如何做到这一点。这就是Git比Mercurial更难学习和使用的原因。

正如其他人在这里注意到的,有很多很多微小的差异。分支是一个很大的微分器。

看看前阵子的Scott Chacon的帖子

我认为git以“更复杂”而闻名,尽管在我的经验中,它并没有比它需要的更复杂。在我看来,git模型道路更容易理解(标签包含提交(和指向零个或多个父提交的指针)包含树包含blob和其他树…完成)。

git并不比mercurial更令人困惑,这不仅仅是我的经验。我建议在这个问题上再次阅读Scott Chacon的这篇博客文章

免责声明: 我使用Git,在Git邮件列表上关注Git开发,甚至还为Git做了一些贡献(主要是gitweb)。我从文档和FreeNode上#revctrl IRC频道的讨论中了解了Mercurial。

感谢所有在#mercurial IRC频道为这篇文章提供帮助的人



总结

在这里,它将很好有一些语法表,一些像在PHPMarkdown / MultiMarkdown / Maruku扩展Markdown

  • 库结构: Mercurial不允许章鱼合并(有两个以上的父类),也不允许标记非提交对象。
  • Mercurial使用版本化的.hgtags文件,为每个存储库标记提供特殊规则,并且还支持.hg/localtags中的本地标记;在Git中,标签是驻留在refs/tags/命名空间中的引用,默认情况下,在获取时自动跟随并需要显式推送。
  • 在Mercurial的基本工作流程是基于匿名的头;Git使用轻量级的命名分支,并具有特殊类型的分支(remote-tracking分支),跟随远程存储库中的分支。
  • 修订命名和范围: Mercurial提供了修改数据,本地到存储库,并基于这个当地的编号的相对修订(从尖端计数,即当前分支)和修订范围;Git提供了一种方法来引用相对于分支尖端的修订,修订范围是拓扑的(基于修订图)
  • Mercurial使用重命名跟踪,而Git使用重命名检测来处理文件重命名
  • 网络: Mercurial支持SSH和HTTP "smart"协议,以及静态HTTP协议;现代Git支持SSH, HTTP和Git "smart"协议,和HTTP(S) "dumb"协议。两者都支持离线传输的bundle文件。
  • Mercurial使用扩展(插件)和已建立的API;Git有scriptability和已建立的格式。

Mercurial与Git有一些不同之处,但也有一些相似之处。这两个项目都借鉴了彼此的想法。例如,Mercurial中的hg bisect命令(以前的平分扩展)受到Git中的git bisect命令的启发,而git bundle的想法则受到hg bundle的启发。

存储库结构,存储修订

在Git的对象数据库中有四种类型的对象:包含文件内容的对象,存储目录结构的分层对象,包括文件名和文件权限的相关部分(文件的可执行权限,是一个符号链接),包含作者信息的提交对象,由提交表示的修订时存储库状态快照的指针(通过项目顶部目录的树对象),以及对零个或多个父提交的引用,和标签对象,它们引用其他对象,可以使用PGP / GPG进行签名。

Git使用两种方式存储对象:宽松的格式,其中每个对象存储在一个单独的文件中(这些文件只写入一次,从不修改),以及包装格式,其中许多对象存储在一个文件中。操作的原子性是由这样一个事实提供的,即对新对象的引用是在写入对象后写入的(原子地,使用create + rename技巧)。

Git存储库需要使用git gc进行定期维护(以减少磁盘空间并提高性能),尽管现在Git会自动进行维护。(此方法提供了更好的存储库压缩。)

Mercurial(据我所知)将文件的历史存储在filelog中(一起,我认为,有额外的元数据,如重命名跟踪,和一些帮助信息);它使用名为清单的平面结构来存储目录结构,使用名为更新日志的结构来存储关于变更集(修订)的信息,包括提交消息和0、1或2个父级。

Mercurial使用事务日志提供操作的原子性,并在操作失败或中断后依赖删除文件进行清理。revlog只能追加。

对比Git和Mercurial中的存储库结构,可以发现Git更像对象数据库(或内容寻址的文件系统),而Mercurial更像传统的固定字段关系数据库。

< p > 差异:
在Git中,对象形成了一个分层结构;在Mercurial的清单文件是结构。在Git中对象存储文件内容的一个版本;在Mercurial filelog中存储一个文件的全部历史(如果我们在这里不考虑重命名的任何并发症)。这意味着在不同的操作领域,Git会比Mercurial快,其他方面都是一样的(如合并,或显示项目的历史),而在某些领域,Mercurial会比Git快(如应用补丁,或显示单个文件的历史)。这个问题对最终用户来说可能不重要。 > < /晚餐

由于Mercurial的更新日志结构的固定记录结构,在Mercurial中提交只能有最多两名家长;Git中的提交可以有两个以上的父文件(所谓的“章鱼合并”)。虽然(理论上)可以用一系列双亲合并来取代octopus合并,但在Mercurial和Git存储库之间进行转换时,这可能会导致复杂情况。

据我所知,Mercurial在Git中没有等价的注释标签(标记对象)。带注释标签的特殊情况是签署了标签(带有PGP / GPG签名);在Mercurial中等价的扩展可以使用GpgExtension来完成,该扩展与Mercurial一起分发。你不能像在Git中那样在Mercurial中标记非提交对象,但我认为这不是很重要(一些Git存储库使用带标记的blob来分发公共PGP密钥来验证签名标记)。

引用:分支和标记

在Git中,引用(分支、远程跟踪分支和标记)驻留在提交的DAG之外。refs/heads/命名空间(当地的分支机构)中的引用指向提交,通常由"git commit";;他们指的是树枝的顶端,这就是为什么有这样的名字。refs/remotes/<remotename>/命名空间(remote-tracking分支)中的引用指向提交,遵循远程存储库<remotename>中的分支,并通过“;git fetch"或同等学历。refs/tags/命名空间(标签)中的引用通常指向提交(轻量级标记)或标记对象(带注释的和带签名的标记),并不意味着要更改。

标签

在Mercurial中,您可以使用标签为修订提供持久名称;标签的存储方式与忽略模式类似。这意味着全局可见的标记存储在存储库中修订控制的.hgtags文件中。这有两个后果:首先,Mercurial必须对这个文件使用特殊的规则来获取所有标签的当前列表并更新该文件(例如,它读取文件最近提交的修订,而不是当前签出的版本);其次,你必须对这个文件进行修改,以使新标签对其他用户/其他存储库可见(据我所知)。

Mercurial还支持当地的标签,它存储在hg/localtags中,对其他人是不可见的(当然是不可转移的)

在Git中,标记是对存储在refs/tags/命名空间中的其他对象(通常是指向提交的标记对象)的固定(常量)命名引用。默认情况下,当获取或推送一组修订时,git会自动获取或推送指向正在获取或推送的修订的标记。尽管如此,你可以在某种程度上控制或pushing。

Git对待轻量级标记(直接指向提交)和带注释的标记(指向标记对象,包含标记消息,可选包含PGP签名,然后指向提交)略有不同,例如默认情况下,当使用“;Git describe"”描述提交时,它只考虑带注释的标记。

Git在Mercurial中没有严格的本地标记对等物。然而,git最佳实践建议设置单独的公共裸库,您可以将准备好的更改推入其中,其他人可以从中克隆和获取。这意味着您不推送的标记(和分支)对于存储库来说是私有的。另一方面,你也可以使用除headsremotestags之外的命名空间,例如局部标记使用local-tags

个人意见:在我看来,标签应该位于修订图之外,因为它们在修订图之外(它们是指向修订图的指针)。标签应该是无版本控制的,但是可以转移。Mercurial选择使用一种类似于忽略文件的机制,这意味着它要么必须特殊地处理.hgtags(文件树是可转移的,但普通的它是有版本的),要么只有本地标记(.hg/localtags是非版本的,但不可转移)。

分支机构

在Git中本地分支(分支尖端或分支头)是一个对提交的命名引用,在这里可以生成新的提交。分支也可以表示活跃的开发线,即从分支尖端可到达的所有提交。本地分支驻留在refs/heads/命名空间中,例如,'master'分支的完全限定名是'refs/heads/master'。

Git中的当前分支(即检出的分支,以及新提交的分支)是由HEAD引用引用的分支。可以让HEAD直接指向提交,而不是符号引用;这种处于匿名分支上的情况被称为分离的头 ("git分支"显示您在'(无分支)')。

在Mercurial中有匿名分支(分支头),并且可以使用书签(通过书签扩展)。这样的书签分支纯粹是本地的,并且这些名称(直到1.6版)不能使用Mercurial转移。可以使用rsync或scp将.hg/bookmarks文件复制到远程存储库。你也可以使用hg id -r <bookmark> <url>来获取书签当前尖端的修订id。

因为1.6的书签可以推/拉。BookmarksExtension页有一个关于使用远程存储库的部分。区别在于,在Mercurial中书签的名称是全球,而在Git中'remote'的定义也描述了分支名称的映射,从远程存储库中的名称到本地远程跟踪分支的名称;例如,refs/heads/*:refs/remotes/origin/*映射意味着可以在远程存储库的'origin/master'远程跟踪分支('refs/remotes/origin/master')中找到'master'分支('refs/remotes/origin/master')的状态。

Mercurial也有所谓的命名的分支,其中在提交(在变更集中)中的分支名称是嵌入式。这样的名称是全局的(在获取时传输)。这些分支名称被永久地记录为变更集\u2019s元数据的一部分。使用现代Mercurial,您可以关闭“命名分支”;并停止记录分支名称。在该机构中,分支的尖端是动态计算的。

Mercurial的“命名分支”;在我看来应该叫<强>提交标签< / >强,因为这就是它们。在某些情况下,“命名分支”;可以有多个提示(多个无子提交),也可以由修订图的几个不相连的部分组成。

没有与Mercurial的“嵌入式分支”等价物。在Git中;此外,Git的理念是,虽然我们可以说分支包含一些提交,但这并不意味着提交属于某个分支。

注意,Mercurial文档仍然建议至少对长期存在的分支(每个存储库工作流有一个分支)使用单独的克隆(单独的存储库),即通过克隆进行分支

推树枝

Mercurial默认push 所有的头。如果你想要推送单个分支(单头),你必须指定你想要推送的分支的尖端修订。您可以通过修订号(对存储库本地)、修订标识符、书签名(对存储库本地,不会被传输)或嵌入的分支名(命名分支)来指定分支提示。

据我所知,如果你推送一系列修订,其中包含标记为“指定分支”的提交;用Mercurial的话说,你将拥有这个“命名的分支”;在您推送到的存储库中。这意味着这些嵌入分支(命名分支)的名称是全球(对于给定存储库/项目的克隆)。

默认情况下(受制于push.default配置变量)"git push"或“git push <远程>&”;Git会推送匹配的分支,也就是说,只有那些在你推送的远程存储库中已经有等效的本地分支。你可以使用--all选项来git-push ("git push——all")来推送所有分支,你可以使用"git push <远程>& lt; 分支>“来推给定单个分支,你可以使用"git push <远程>HEAD"来推当前分支

以上都假设Git没有配置通过remote.<remotename>.push配置变量来推送哪个分支。

抓取中的分支

注意:这里我使用Git术语,其中"fetch"意味着从远程存储库下载更改,而不需要将这些更改与本地工作集成。这就是“git fetch"和“;hg pull"所做的事。

如果我理解正确的话,默认情况下Mercurial从远程存储库获取所有的头,但你可以指定分支通过“;hg pull --rev <rev> <url>"或“;hg pull <url>#<rev>"来得到单分支。你可以指定<rev>使用修订标识符,“命名分支”;名称(在变更日志中嵌入的分支),或书签名称。但书签名称(至少目前)不会被转移。所有“命名分支”;你得到的修改属于转移。“hg pull"把它取来的树枝顶端储存成匿名的、不知名的头。

在Git默认情况下(对于由“;Git clone”创建的“origin”远程,以及使用“;Git remote add”创建的远程)&;git fetch"(或"git fetch <remote>")从远程存储库(从refs/heads/命名空间)获取所有分支,并将它们存储在refs/remotes/命名空间中。这意味着,例如,在远程'origin'中名为'master'(全名:'refs/heads/master')的分支将被存储(保存)为'origin/master' remote-tracking分支(全名:'refs/remotes/origin/master')。

你可以使用git fetch <remote> <branch>在Git中获取单分支——Git会将请求的分支存储在FETCH_HEAD中,这与Mercurial的未命名头类似。

这些只是强大的refspec Git语法的默认情况的例子:通过refspecs,你可以指定和/或配置想要获取的分支,以及在哪里存储它们。例如默认“fetch all branches”;Case由'+refs/heads/*:refs/remotes/origin/*'通配符refspec表示,以及';fetch single branch"是'refs/heads/<branch>:'的缩写。Refspecs用于将远程存储库中的分支(refs)名称映射到本地refs名称。但是你不需要知道(太多)refspecs就能有效地与Git一起工作(主要感谢“Git远程”;命令)。

我个人认为“命名分支”;(分支名称嵌入在变更集元数据中)在Mercurial中的全局命名空间设计是错误的,特别是对于分布式版本控制系统。举个例子,Alice和Bob都有“named branch”;在它们的存储库中命名为“for-joe”,这些分支没有任何共同之处。然而,在Joe的存储库中,这两个分支会被当作一个分支来处理。因此,您以某种方式提出了防止分支名称冲突的约定。这不是Git的问题,在Joe的存储库中,Alice的“for-joe”分支将是“Alice /for-joe”,而Bob的分支将是“Bob /for-joe”。另见Mercurial wiki上提出的将分支名称与分支标识分离问题。

Mercurial的“书签分支”;目前缺乏核心内分发机制

< p > 差异:
这是Mercurial和Git之间的主要区别之一,正如詹姆斯woodyatt史蒂夫Losh在他们的回答中所说的那样。默认情况下,Mercurial使用匿名轻量级代码线,在其术语中称为“头部”。Git使用轻量级的命名分支,使用内射映射将远程存储库中的分支名称映射到远程跟踪分支的名称。Git“forces"你需要为分支命名(好吧,除了单个未命名的分支,称为分离HEAD的情况),但我认为这对于分支较多的工作流(例如主题分支工作流)更有效,这意味着在单个存储库范例中有多个分支

命名的修正

在Git中,有许多命名修订的方法(例如在git rev-parse manpage中描述):

  • 完整的SHA1对象名称(40字节的十六进制字符串),或存储库中唯一的该对象的子字符串
  • 一个符号引用名称,例如'master'(指'master'分支),或'v1.5.0'(指标签),或'origin/next'(指远程跟踪分支)
  • 后缀^到revision参数表示提交对象的第一个父对象,^n表示合并提交的第n个父对象。修订参数的后缀~n表示在第一行中提交的第n个祖先。这些后缀可以组合在一起,形成遵循符号引用路径的修订说明符,例如。《聚氨酯~ 3 ^ 2 ~ 3》
  • “git描述”的输出,即一个最接近的标签,后面可选地跟着一个破折号和一些提交,后面跟着一个破折号,一个'g'和一个缩写的对象名称,例如'v1.6.5.1-75-g5bf8097'。

还有涉及reflog的修订说明符,这里没有提到。在Git中,每个对象,无论是提交、标记、树还是blob都有自己的SHA-1标识符;有一些特殊的语法,比如e.g.。'next:文档'或'next:README'指树(目录)或blob(文件内容)在指定的修订。

Mercurial也有许多命名变更集的方法(例如在hg manpage中描述):

  • 普通整数被视为修订号。我们需要记住修订号是本地到给定存储库;在其他存储库中,它们可能不同。
  • 负整数被视为从尖端开始的连续偏移,-1表示尖端,-2表示尖端之前的修订,以此类推。它们也是当地的 to repository。
  • 唯一修订标识符(40位十六进制字符串)或其唯一前缀。
  • 标签名称(与给定修订相关的符号名称),或者书签名称(具有扩展名:与给定标头相关的符号名称,本地存储库),或者“命名分支”。(提交标签;“named branch”给出的修正;是具有给定提交标签的所有提交中的tip(无子提交),如果有多个这样的提示,则使用最大的修订号)
  • 保留名称"tip"是一个特殊标记,它总是标识最新的修订。
  • 保留名称“null"null修订。
  • 保留名称";工作目录父目录。
< p > 差异
从上面的列表可以看出,Mercurial提供了本地版本号,而Git没有。另一方面,Mercurial只提供从'tip'(当前分支)的相对偏移量,它是存储库的本地(至少没有ParentrevspecExtension),而Git允许从任何tip指定任何提交

最新的修订在Git中被命名为HEAD,而“;tip"反复无常的;Git中没有空修订。Mercurial和Git都可以有多个根(可以有多个无父提交;这通常是以前独立的项目合并的结果)。

参见: 许多不同种类的修订说明符文章以利亚的博客(newren的)。

个人意见:我认为修订号被高估了(至少对于分布式开发和/或非线性/分支历史而言)。首先,对于分布式版本控制系统,它们必须是本地存储库,或者需要以一种特殊的方式将某些存储库视为中央编号机构。其次,较大的项目,具有较长的历史,可以在5位数范围内进行修订,因此它们比缩短至6-7个字符的修订标识符略有优势,并意味着严格的排序,而修订只是部分排序(我的意思是,修订n和n+1不需要是父版本和子版本)。

修改范围

在Git的修订范围是拓扑。常见的A..B语法,对于线性历史意味着修订范围从A开始(但不包括A),结束于B(即范围是从下面打开),是^A B的简写(&;语法sugar"),对于历史遍历命令意味着所有可从B到达的提交,不包括那些可从A到达的。这意味着A..B范围的行为是完全可预测的(而且非常有用),即使A不是B的祖先:A..B表示从A和B的共同祖先(合并基数)到修订B的修订范围。

在Mercurial中,修订范围基于修改数据的范围。Range使用A:B语法指定,与Git相反,Range充当闭区间。同样范围B:A是范围A:B的倒序,这在Git中不是这样的(但参见下面关于A...B语法的注释)。但这种简单是有代价的:修订范围a:B只有当a是B的祖先时才有意义,反之亦然,即具有线性历史;否则(我猜)范围是不可预测的,结果是本地存储库(因为修订号是本地存储库)。

Mercurial 1.6修复了这个问题,它有新的拓扑修正范围,其中'A..B'(或'A::B')被理解为既是X的后代,又是y的祖先的变更集的集合。我想,这相当于'——祖宗路径A..B'在Git里。

Git也有标记A...B用于对称的修订差异;它的意思是A B --not $(git merge-base A B),这意味着从A或B中可到达的所有提交,但不包括从它们两个中可到达的所有提交(从共同祖先中可到达)。

重命名

Mercurial使用重命名跟踪来处理文件重命名。这意味着关于文件重命名的信息在提交时保存;在Mercurial中,此信息保存在“enhanced diff"filelog (file revlog)元数据中的form。这样做的结果是你必须使用hg rename / hg mv…或者你需要记得运行hg addremove来做基于相似性的重命名检测。

Git在版本控制系统中是独一无二的,因为它使用重命名检测来处理文件重命名。这意味着文件被重命名的事实是在需要的时候被检测到的:当执行合并时,或者当显示diff时(如果请求/配置)。这样做的好处是可以改进重命名检测算法,并且不会在提交时被冻结。

Git和Mercurial在显示单个文件的历史记录时都需要使用--follow选项来跟随重命名。在显示git blame / hg annotate中文件的逐行历史时,两者都可以跟随重命名。

在Git中,git blame命令能够跟踪代码移动,也可以将代码从一个文件移动(或复制)到另一个文件,即使代码移动不是完整文件重命名的一部分。据我所知,这个特性是Git独有的(在撰写本文时,2009年10月)。

网络协议

Mercurial和Git都支持从同一个文件系统上的存储库获取和推送,其中存储库URL只是到存储库的文件系统路径。两者都支持从包文件中获取。

Mercurial支持通过SSH和HTTP协议获取和推送。对于SSH,需要在目标机器上有一个可访问的shell帐户和一个已安装/可用的hg副本。对于HTTP访问,需要运行hg-serve或Mercurial CGI脚本,并且需要在服务器机器上安装Mercurial。

Git支持两种用于访问远程存储库的协议:

  • “smart"协议,包括通过SSH和通过自定义git://协议(通过git-daemon)访问,需要在服务器上安装git。这些协议中的交换包括客户端和服务器协商他们有哪些共同的对象,然后生成并发送包文件。现代Git包括对“smart”的支持;HTTP协议。
  • “dumb"协议,包括HTTP和FTP(仅用于获取),以及HTTPS(通过WebDAV推送),不需要在服务器上安装git,但它们确实要求存储库包含由git update-server-info生成的额外信息(通常从钩子运行)。交换包括客户端遍历提交链,并根据需要下载松散对象和包文件。缺点是它会下载超出严格要求的内容(例如,在极端情况下,当只有一个包文件时,即使只获取了几个修订,它也会下载整个包文件),并且它可能需要许多连接才能完成。

扩展:可脚本性vs扩展(插件)

Mercurial是在Python中实现的,一些核心代码是用C语言编写的,以提高性能。它提供了用于编写扩展(插件)的API,作为添加额外功能的一种方式。一些功能,如“书签分支”;或签署修订,是在Mercurial分发的扩展中提供的,需要打开它。

Git在CPerlshell脚本中实现。Git提供了许多适合在脚本中使用的低级命令(管道)。引入新特性的通常方式是将其编写为Perl或shell脚本,当用户界面稳定时,为了性能、可移植性和避免shell脚本的极端情况(此过程称为builtinification),用C重写它。

Git依赖并构建在[存储库]格式和[网络]协议之上。除了语言绑定,还有其他语言(部分或完整)的Git 重新(其中一些是部分重新实现,部分是对Git命令的包装):JGit (Java,由EGit使用,Eclipse Git插件),Grit (Ruby), Dulwich (Python), git# (c#)。


博士TL;

如果您需要良好的Windows支持,您可能更喜欢Mercurial。TortoiseHg (Windows资源管理器插件)设法为一个相当复杂的工具提供了一个简单易用的图形界面。作为这里的state,你也会有一个Visual Studio插件。但是,上次我尝试时,SVN界面在Windows上工作得不太好。

如果您不介意使用命令行界面,我推荐使用Git。不是出于技术原因,而是出于战略原因。git的采用率比高。看看有多少著名的开源项目正在从cvs/svn切换到Mercurial,又有多少项目正在切换到Git。看看与Mercurial托管相比,你能找到多少支持git的代码/项目托管提供商。

在阅读了Mercurial更容易(我仍然相信它是,毕竟互联网社区的意见),当我开始使用Git和Mercurial时,我觉得Git对我来说相对更容易适应(我开始使用Mercurial与TortoiseHg),当从命令行工作时,主要是因为git命令的命名符合我的要求,而且数量较少。 Mercurial对每个命令有不同的命名,做不同的工作,而Git命令可以根据情况多用途(例如,checkout)。虽然Git在当时比较难,但现在差别不大了。YMMV . .使用像TortoiseHg这样的优秀GUI客户端,使用Mercurial确实容易得多,而且我不需要记住那些有点令人困惑的命令。我不会详细说明相同操作的每个命令是如何变化的,但这里有两个综合列表:1来自Mercurial自己的网站第二段摘自wikivs

╔═════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════╗
║           Git               ║                Mercurial                                                                       ║
╠═════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════╣
║ git pull                    ║ hg pull -u                                                                                     ║
║ git fetch                   ║ hg pull                                                                                        ║
║ git reset --hard            ║ hg up -C                                                                                       ║
║ git revert <commit>         ║ hg backout <cset>                                                                              ║
║ git add <new_file>          ║ hg add <new_file> (Only equivalent when <new_file> is not tracked.)                            ║
║ git add <file>              ║ Not necessary in Mercurial.                                                                    ║
║ git add -i                  ║ hg record                                                                                      ║
║ git commit -a               ║ hg commit                                                                                      ║
║ git commit --amend          ║ hg commit --amend                                                                              ║
║ git blame                   ║ hg blame or hg annotate                                                                        ║
║ git blame -C                ║ (closest equivalent): hg grep --all                                                            ║
║ git bisect                  ║ hg bisect                                                                                      ║
║ git rebase --interactive    ║ hg histedit <base cset> (Requires the HisteditExtension.)                                      ║
║ git stash                   ║ hg shelve (Requires the ShelveExtension or the AtticExtension.)                                ║
║ git merge                   ║ hg merge                                                                                       ║
║ git cherry-pick <commit>    ║ hg graft <cset>                                                                                ║
║ git rebase <upstream>       ║ hg rebase -d <cset> (Requires the RebaseExtension.)                                            ║
║ git format-patch <commits>  ║ hg email -r <csets> (Requires the PatchbombExtension.)                                         ║
║   and git send-mail         ║                                                                                                ║
║ git am <mbox>               ║ hg mimport -m <mbox> (Requires the MboxExtension and the MqExtension. Imports patches to mq.)  ║
║ git checkout HEAD           ║ hg update                                                                                      ║
║ git log -n                  ║ hg log --limit n                                                                               ║
║ git push                    ║ hg push                                                                                        ║
╚═════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════╝

Git在内部保存提交文件的每个版本的记录,而Hg只保存更改集,占用空间更小。与Hg相比,Git更容易改变历史,但它又是一个令人讨厌或喜欢的特性。前者我喜欢Hg,后者我喜欢Git。

我在Hg中错过的是Git的子模块特性。Hg有subrepos,但那不是Git的子模块。

围绕这两者的生态系统也会影响一个人的选择:Git必须更受欢迎(但这是微不足道的),Git有GitHub,而Mercurial有BitBucket都, Mercurial有TortoiseHg,我还没有看到与Git一样好的同类产品。

每一种都有它的优点和缺点,用它们中的任何一种你都不会输。

与DVCSs本身完全无关的一个区别是:

Git似乎很受C开发人员的欢迎。Git是Linux内核的事实上的存储库,这可能是它在C开发人员中如此受欢迎的原因。对于那些只在Linux/Unix世界中工作的人来说尤其如此。

Java开发人员似乎更喜欢Mercurial而不是Git。这可能有两个原因:一个是许多非常大的Java项目托管在Mercurial上,包括JDK本身。另一个原因是Mercurial的结构和清晰的文档吸引了来自Java阵营的人,而这些人发现Git不一致的wrt命令命名和缺乏文档。我并不是说这是真的,我是说人们已经习惯了他们通常栖息地的一些东西,然后他们倾向于从中选择DVCS。

我认为Python开发人员几乎都喜欢Mercurial。除了Mercurial是基于Python的这一事实之外,实际上没有任何合理的原因。(我也使用Mercurial,我真的不明白为什么人们对DVCS的实现语言大惊小怪。我不懂Python的一个字,如果不是因为它在某个地方列出了它是基于Python的,那么我不会知道)。

我认为你不能说一个DVCS比另一个更适合一种语言,所以你不应该从中做出选择。但在现实中,人们选择(部分)是基于他们在社区中接触最多的DVCS。

(不,我没有使用统计数据来支持我上面的说法..这都是基于我自己的主观)

在我目前的工作中,我已经使用Git一年多一点,在此之前,我在上一份工作中使用Mercurial一年多一点。我将从用户的角度提供一个评估。

首先,两者都是分布式版本控制系统。分布式版本控制系统需要改变传统版本控制系统的思维方式,但一旦理解了它们,实际上在许多方面工作得更好。出于这个原因,我认为Git和Mercurial都比Subversion、Perforce等要优越得多。分布式版本控制系统与传统版本控制系统之间的差异远远大于Git与Mercurial之间的差异。

然而,Git和Mercurial之间也存在显著的差异,这使得它们都更适合于自己的用例子集。

Mercurial比较容易学习。在使用Mercurial几周后,我几乎不需要参考文档或笔记;即使在使用Git一年之后,我仍然需要定期查阅我的笔记。Git要复杂得多。

这在一定程度上是因为Mercurial只是简单的清洁。在Mercurial中,您很少需要手动进行分支;当您需要时,Mercurial会自动为您创建一个匿名分支。水银命名法更直观;你不需要像使用Git那样担心“fetch”和“pull”之间的区别。Mercurial没有那么多bug。在使用Git和Mercurial跨平台推动项目时,文件名大小写敏感问题曾导致问题;这在Mercurial中已经修复了一段时间,而在Git中还没有修复。您可以告诉Mercurial有关文件重命名的信息;对于Git,如果它没有自动检测到重命名——在我的经验中,这是一个非常偶然的命题——重命名根本无法被跟踪。

然而,Git增加复杂性的另一个原因是,它需要大量的功能来支持额外的特性和功能。是的,在Git中处理分支更加复杂——但另一方面,一旦您拥有了分支,使用这些分支完成在Mercurial中几乎不可能完成的事情就不那么困难了。重基分支就是其中之一:你可以移动你的分支,这样它的基就不再是你分支时的主干状态,而是现在的主干状态;当有许多人在同一个代码库上工作时,这极大地简化了版本历史,因为可以使每个推送到主干的操作看起来是顺序的,而不是交织在一起的。类似地,将分支上的多次提交合并为一次提交要容易得多,这可以再次帮助保持版本控制历史记录的干净:理想情况下,一个特性上的所有工作都可以在主干中作为一次提交出现,取代开发人员在开发该特性时可能做的所有次要提交和子分支。

最终,我认为Mercurial和Git之间的选择应该取决于您的版本控制项目有多大,以同时从事这些项目的人数来衡量。例如,如果你有一个十几个或更多的团队在开发一个单一的web应用程序,Git更强大的分支管理工具将使它更适合你的项目。另一方面,如果您的团队正在开发一个异构的分布式系统,并且在任何时候只有一到两个开发人员在任何一个组件上工作,那么对每个组件项目使用Mercurial存储库将允许开发更顺利地进行,并且存储库管理开销更少。

底线:如果你有一个庞大的团队在开发一个庞大的应用程序,那就使用Git;如果您的单个应用程序很小,任何规模都来自于这些应用程序的数量而不是大小,请使用Mercurial。