Git与Mercurial – 比较与对比
一段时间以来,我一直在使用颠覆我的个人项目。
越来越多的我一直听到关于Git和Mercurial,以及DVCS的很好的事情。
我想给整个DVCS一个旋转,但我不太熟悉任何选项。
Mercurial和Git有什么区别?
请注意,我并没有试图找出哪一个是“最好的”,甚至是我应该从哪一个开始。 我主要是在寻找关键领域,他们是相似的,他们是不同的,因为我有兴趣知道他们在实施和哲学方面有什么不同。
免责声明: 我使用Git,在git邮件列表上关注Git开发,甚至贡献一点Git(主要是gitweb)。 我从文档中知道Mercurial,并从FreeNode上的#revctrl IRC频道讨论了一些。
感谢所有在#mercurial IRC频道的人提供了有关Mercurial的帮助
概要
这里有一些表格的语法,比如Markdown的PHPMarkdown / MultiMarkdown / Maruku扩展
- 存储库结构: Mercurial不允许章鱼合并(与多于两个父母),也不标记非提交对象。
- 标签: Mercurial使用版本化的
.hgtags
文件,每个存储库标签都有特殊的规则,并且还支持.hg/localtags
本地标签; 在Git标签中refs驻留在refs/tags/
namespace中,默认情况下会自动提取并需要显式推送。 - 分行:在Mercurial的基本工作stream程是基于匿名头像 ; Git使用轻量级的命名分支,并且具有特殊types的分支( 远程跟踪分支 ),它们遵循远程仓库中的分支。
- 修订命名和范围: Mercurial提供版本号 ,本地存储库,并基于本地编号的相对修订(从tip,即当前分支计数)和修订范围; Git提供了一种相对于分支提示的修订方式,修订范围是拓扑(基于修订图)
- Mercurial使用重命名跟踪 ,而Git使用重命名检测来处理文件重命名
- networking: Mercurial支持SSH和HTTP“智能”协议和静态HTTP协议; 现代Git支持SSH,HTTP和GIT“智能”协议和HTTP(S)“哑”协议。 两者都支持离线传输的捆绑文件。
- Mercurial使用扩展 (插件)和build立的API; Git具有脚本function和build立的格式。
Git有一些与Mercurial不同的东西,但还有其他一些东西使它们相似。 两个项目都是互相借鉴的。 例如Mercurial中的hg bisect
命令(以前的分叉扩展 )受Git中的git bisect
命令的启发,而git bundle
想法则受hg bundle
启发。
存储库结构,存储修订
在Git中,对象数据库中有四种types的对象:包含文件内容的blob对象,存储目录结构的分层树对象,包括文件名和文件权限的相关部分(文件的可执行权限,是符号链接) , 提交包含作者信息的对象,指向由提交(通过项目的顶层目录的树对象)表示的存储库状态的快照的指针,以及对零个或多个父提交的引用,以及引用其他对象的标记对象,并且可以使用PGP / GPG进行签名。
Git使用两种存储对象的方式: 松散格式,每个对象都存储在一个单独的文件中(这些文件只写一次,从不修改), 打包格式将多个对象存储在一个文件中。 操作的primefaces性由以下事实提供:在写入对象之后写入新对象的引用(primefaces地使用create +重新命名技巧)。
Git仓库需要使用git gc
进行定期维护(以减less磁盘空间并提高性能),尽pipe现在Git可以自动执行此操作。 (这种方法提供了更好的压缩仓库。)
Mercurial(就我所知)将一个文件的历史logging保存在一个文件日志中 (我认为,还有额外的元数据,比如重命名跟踪和一些帮助信息)。 它使用称为manifest的扁平结构来存储目录结构,以及称为changelog的结构,其存储关于变更集(修订版)的信息,包括提交消息和零,一个或两个父母。
Mercurial使用事务日志来提供操作的primefaces性,并且在失败或中断操作之后依靠截断文件进行清理。 Revlogs是仅附加的。
查看Git和Mercurial中的存储库结构,可以看到Git更像是对象数据库(或者内容寻址的文件系统),而Mercurial更像传统的固定领域关系数据库。
区别:
在Git中, 树形对象构成了一个层次结构; 在水银清单文件是扁平结构。 在Git blob对象中存储一个文件内容的一个版本 ; 在Mercurial文件日志中存储整个单个文件的历史logging (如果我们在这里没有考虑任何与重命名有关的复杂情况)。 这意味着有不同的操作区域,Git比Mercurial快,其他所有事情都被认为是平等的(比如合并,或者显示项目历史),Mercurial比Git快的区域(比如应用补丁或者显示单个文件的历史)。 这个问题对最终用户可能不重要。
由于Mercurial的更新日志结构的固定logging结构,Mercurial中的提交最多只能有两个父母 ; 在Git中提交可以有两个以上的父母(所谓的“章鱼合并”)。 虽然您可以(理论上)通过一系列的双亲合并来replace章鱼合并,但是在Mercurial和Git存储库之间转换时可能会导致复杂的问题。
据我所知Mercurial没有相当于Git的注释标签 (标签对象)。 注释标签的特殊情况是签名标签 (带有PGP / GPG签名); 在Mercurial中可以使用GpgExtension来完成,该扩展与Mercurial一起分发。 你不能在Git中标记 Mercurial中的非提交对象 ,但这不是非常重要,我认为(一些git仓库使用标记的blob来分发公共PGP密钥来validation签名标签)。
参考文献:分支和标签
在Git引用(分支,远程跟踪分支和标签)驻留在提交的DAG之外(他们应该)。 refs/heads/
namespace( 本地分支 )中的引用指向提交,通常通过“git commit”更新; 他们指向分行的小费(头),这就是为什么这样的名字。 在refs/remotes/<remotename>/
namespace( 远程跟踪分支 )中的引用指向提交,跟随远程仓库<remotename>
分支,并通过“git fetch”或等效项进行更新。 refs/tags/
namespace( tags )中的引用通常指向提交(轻量级标签)或标签对象(带注释和签名的标签),并不意味着更改。
标签
在Mercurial中,你可以使用标签给持久化名称进行修改; 标签类似于忽略模式存储。 这意味着全局可见标记存储在版本库中的版本控制的.hgtags
文件中。 这有两个结果:首先,Mercurial必须对这个文件使用特殊的规则来获取所有标签的当前列表并且更新这样的文件(例如它读取最近提交的文件修订版本,而不是当前检出的版本)。 其次,你必须对这个文件进行修改,让其他用户/其他库(据我了解)可以看到新的标签。
Mercurial也支持存储在hg/localtags
本地标签 ,这些标签对其他人来说是不可见的(当然也是不可转让的)
在Git中,标签是固定的(常量)引用,存储在refs/tags/
namespace中的其他对象(通常是指向提交的标签对象)。 在默认情况下,当获取或推送一组版本时,git会自动提取或推送指向正在提取或推送的版本的标签。 不过,您可以在某种程度上控制 哪些标签被抓取或推送。
Git将轻量级标签(直接指向提交)和带注释的标签(指向标签对象,其中包含标签消息,其中可能包含PGP签名,然后指向提交)略有不同,例如,默认情况下,它在描述时只考虑带注释的标签提交使用“git描述”。
Git在Mercurial中没有严格等价的本地标签。 尽pipe如此,git的最佳做法build议设置单独的公共裸仓库,在其中推送已准备好的更改,并从其中进行克隆和提取。 这意味着您不会推送的标签(和分支)对您的存储库是私有的。 另一方面,您也可以使用heads
, remotes
或tags
以外的命名空间,例如local-tags
的本地标签。
个人观点:在我看来,标签应该位于修订图之外,因为它们是外部的(它们是指向修订图的指针)。 标签应该是非版本的,但可以转让。 Mercurialselect使用类似于忽略文件的机制,意味着它要么专门处理.hgtags
(文件in-tree是可转移的,但普通的是版本化的),或者只有本地标签( .hg/localtags
是非版本化的,但是.hg/localtags
)。
分行
在Git 本地分支 (分支提示或分支头)是提交的命名引用,其中可以增加新的提交。 分支也可以意味着活跃的开发线,即从分支尖端可达的所有提交。 本地分支位于refs/heads/
namespace,所以例如'master'分支的完全限定名是'refs / heads / master'。
Git中的当前分支(表示签出分支,新分配将分支的分支)是由HEAD引用引用的分支。 可以让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'远程跟踪中find'master'分支的状态('refs / heads / master'分支('refs / remotes / origin / master')。
Mercurial也叫做分支 ,分支名称embedded在一个提交中(在一个变更集中)。 这个名字是全球性的(在取回时转移)。 这些分支名称将永久logging为变更集元数据的一部分。 随着现代Mercurial你可以closures“命名分支”,并停止logging分行名称。 在这个机制中,分支的提示在飞行中计算。
Mercurial的“命名分支”应该在我看来被称为提交标签 ,因为它是什么。 有些情况下,“命名分支”可以有多个提示(多个无子提交),也可以由修改图的几个不相交部分组成。
在Git中没有这些Mercurial“embedded式分支”的等价物; 而且Git的理念是,虽然可以说分支包含了一些提交,但并不意味着提交属于某个分支。
请注意,Mercurial文档仍然build议至less为长期分支(每个存储库工作stream单个分支)使用单独的克隆(单独的存储库),即通过克隆分支 。
分支在推
Mercurial默认推动所有的头 。 如果要推送一个分支( 单头 ),则必须指定要推送的分支的最新修订版本。 您可以通过版本号(本地存储库),版本标识符,书签名称(本地存储库,不会被传输)或embedded分支名称(命名分支)来指定分支提示。
据我所知,如果你按照Mercurial的说法推送一系列包含被标记为某个“命名分支”的提交的修订版本,你将在你推送的版本库中拥有这个“命名分支”。 这意味着这种embedded式分支(“命名分支”)的名称是全局的 (关于给定的存储库/项目的克隆)。
默认情况下(受push.default
configurationvariables的限制)“git push”或者“git push < remote >”Git会推送匹配的分支 ,也就是只有那些已经存在的远程仓库中的等价分支。 你可以使用--all
选项将git push(“git push -all”)推送到所有分支 ,你可以使用“git push < remote > < branch >”来推送给定的单个分支 ,你可以使用“ git push < remote > HEAD“推送当前分支 。
以上所有假设Git没有configuration通过remote.<remotename>.push
configurationvariables推送哪个分支。
分支在取
注意:这里我使用Git术语,其中“获取”是指从远程存储库下载更改, 而不将这些更改与本地工作集成。 这就是“ git fetch
”和“ hg pull
”所做的。
如果我理解正确,默认情况下Mercurial从远程仓库获取所有头 ,但是可以通过“ hg pull --rev <rev> <url>
”或“ hg pull <url>#<rev>
”指定要获取的分支得到单个分支 。 您可以使用修订标识符,“命名分支”名称(embedded在更改日志中的分支)或书签名称来指定<rev>。 然而,书签名称(至less当前)不会被传送。 您获得的所有“命名分支”版本都将被转移。 “hg拉”存储分支的提示,它作为匿名,无名头像。
在Git默认情况下(对于由“git clone”创build的“origin”remote,以及使用“git remote add”创build的远程)“ git fetch
”(或者“ git fetch <remote>
”)从远程仓库refs/heads/
namespace),并将它们存储在refs/remotes/
namespace中。 这意味着例如远程“起源”中名为“master”(全名:'refs / heads / master')的分支将被存储(保存)为'origin / master' 远程跟踪分支 (全名:'refs /遥控器/来源/主“)。
您可以通过使用git fetch <remote> <branch>
来git fetch <remote> <branch>
Git中的单个分支 .Git将请求的分支存储在FETCH_HEAD中,这与Mercurial未命名的头类似。
这些只是强大的refspec Git语法默认情况下的例子:通过refspecs,您可以指定和/或configuration想要获取哪个分支以及在哪里存储它们。 例如,默认的“fetch all branches”的情况是由'+ refs / heads / *:refs / remotes / origin / *'通配符refspec来表示的,而'fetch single branch'则是refs / heads / <branch>的缩写。 。 Refspecs用于将远程仓库中分支(ref)的名称映射到本地参考名称。 但是你不需要知道(很多)关于refspecs能够有效地使用Git(主要感谢“git remote”命令)。
个人观点:我个人认为Mercurial中的“命名分支”(包含变更元数据中的分支名称)是全局命名空间的误导性devise,特别是对于分布式版本控制系统。 例如,让我们看看Alice和Bob在他们的存储库中有“命名分支”,名为“for-joe”的情况,这些分支没有任何共同之处。 但是,在Joe的仓库中,这两个分支将作为一个分支被虐待。 所以你不知怎么想出了防止分支名称冲突的公约。 这对于Git来说并不是问题,在Joe的仓库中,来自Alice的for-joe分支是'alice / for-joe',而Bob则是'bob / for-joe'。 另请参阅从 Mercurial wiki上引发的分支标识问题中分离分支名称 。
Mercurial的“书签分支”目前缺乏内核分发机制。
区别:
这个领域是Mercurial和Git之间的主要区别之一,正如詹姆斯·伍迪亚特和史蒂夫·洛什在答案中所说的那样。 默认情况下,Mercurial使用匿名轻量级代码行,其术语称为“头”。 Git使用轻量级命名分支,使用内射映射将远程仓库中分支的名称映射到远程跟踪分支的名称。 Git“强迫”你命名分支(除了单一的未命名的分支,情况称为分离的HEAD),但我认为这适用于分支繁重的工作stream程,如主题分支工作stream,意味着单个存储库范例中的多个分支。
命名修订
在Git中有很多命名修订的方法(例如在git rev-parse manpage中描述):
- 完整的SHA1对象名称(40字节的hexstring),或者是在存储库中唯一的子string
- 符号引用名称,例如“主”(指“主”分支)或“v1.5.0”(指标签)或“起源/下一个”(指远程跟踪分支)
- 后缀
^
到修订参数意味着提交对象的第一个父代,^n
合并提交的第n个父代。 修改参数的后缀〜n表示直接的第一条父行中的提交的第n个祖先。 可以将这些后缀组合起来,形成符号引用的path后面的修订说明符,例如'pu〜3 ^ 2〜3' - “git describe”的输出,也就是最接近的标签,后面跟着一个破折号和一些提交,后面跟一个短划线,一个'g'和一个缩写的对象名,例如'v1.6.5.1-75- g5bf8097' 。
还有涉及reflog的修订说明符,这里没有提到。 在Git中的每个对象,无论是承诺,标签,树或斑点有其SHA-1标识符; 有特殊的语法,例如'next:Documentation'或'next:README'来引用指定版本的树(目录)或blob(文件内容)。
Mercurial也有很多命名变更集的方法(例如在hg手册中描述):
- 一个纯整数被视为修订号。 需要记住的是,修订号对于给定的存储库是本地的 ; 在其他仓库中,它们可以是不同的。
- 负整数被视为从笔尖的连续偏移量,其中-1表示笔尖,-2表示笔尖之前的修订,等等。 它们也是存储库本地的 。
- 唯一的修订标识符(40位hexstring)或其唯一的前缀。
- 标签名称(与给定修订相关联的符号名称)或书签名称(扩展名:与给定头相关联的符号名称,本地存储库)或“命名分支”(提交标签;由“命名分支”给出的修订为所有提交提交标签的提交(无子提交),如果有多个提示标签,则提供最大的修订号)
- 保留名称“tip”是一个特殊的标签,它总是标识最新的修订版本。
- 保留名称“null”表示空版本。
- 保留名称“。” 表示父工作目录。
差异
正如你可以看到比较上面的列表Mercurial提供版本号,本地存储库,而Git不提供。 另一方面,Mercurial仅从'tip'(当前分支)提供相对偏移量,这是本地存储库(至less没有ParentrevspecExtension ),而Git允许指定任何提示后面的提交。
最新版本在Git中被命名为HEAD,在Mercurial中被命名为“tip” Git中没有null修订。 Mercurial和Git都可以拥有多个根(可以有多个父代提交,通常是以前单独的项目join的结果)。
另见:以利亚博客(newren's)上的许多不同types的修订说明文章。
个人观点:我认为修订号被高估了(至less对于分布式开发和/或非线性/分支历史)。 首先,对于分布式版本控制系统,它们必须是存储库本地的,或者需要以一种特殊的方式作为中央编号权威对待一些存储库。 其次,更大的项目,历史悠久,可以在5位数范围内进行修订,因此与仅缩短到6-7个字符的修订标识符相比,只提供less量的优势,并且意味着严格的sorting,而修订只是部分sorting的(我的意思是,修订n和n + 1不需要是父母和子女)。
修订范围
在Git中,修订范围是拓扑 。 常见的A..B
语法是线性历史意味着从A(但不包括A)到B(即范围从下面开放 )开始的修订范围是^AB
简写(“语法糖”),历史遍历命令意味着从B可到达的所有提交,不包括从A可到达的提交。这意味着即使A不是B的祖先, A..B
范围的行为也是完全可预测的(并且非常有用): A..B
平均值那么A和B共同祖先(合并基础)的修订范围到修订版B.
在Mercurial中,修订版本范围基于版本号的范围。 范围使用A:B
语法来指定,与Git范围相反作为一个closures的间隔 。 另外范围B:A是以相反顺序的范围A:B,这在Git中不是这种情况(但是请参阅下面有关A...B
语法的注释)。 但是这样的简单性带来了一个价格:修正范围A:B只有在A是B的祖先或反之亦然时才有意义,即具有线性历史; 否则(我猜)范围是不可预知的,结果是本地存储库(因为修订版本号本地存储库)。
Mercurial 1.6修正了这个问题,它具有新的拓扑修订范围 ,其中'A..B'(或'A :: B')被理解为既是X的后代又是Y的祖先的变更集合。 ,我猜,相当于Git中的“–ancestry-path A..B”。
Git对于修改的对称差异也有符号A...B
; 它意味着AB --not $(git merge-base AB)
,这意味着所有提交可以从A或B可达,但不包括从两个可达(从共同祖先可到达)的所有提交。
重命名
Mercurial使用重命名跟踪来处理文件重命名。 这意味着文件被重命名的信息在提交时被保存; 在Mercurial中,这些信息以文件日志(文件revlog)元数据中的“增强型差异”forms保存。 这样做的结果是你必须使用hg rename
/ hg mv
…或者你需要记得运行hg addremove
来做相似的重命名检测。
Git在版本控制系统中是独一无二的,它使用重命名检测来处理文件重命名。 这意味着文件被重命名的事实在需要的时候被检测到:当进行合并时,或者当显示差异时(如果请求/configuration)。 这具有改进重命名检测algorithm的优点,并且在提交时不被冻结。
在显示单个文件的历史logging时,Git和Mercurial都需要使用--follow
选项来跟随重命名。 当在git blame
/ hg annotate
显示一个文件的git blame
历史时,两者都可以按照重命名。
在Git中, git blame
命令可以跟踪代码移动,即使代码移动不是有益健康的文件重命名的一部分,也可以将代码从一个文件移动(或复制)到另一个文件。 据我所知,这个function是Git独有的(写作时,2009年10月)。
networking协议
Mercurial和Git都支持从相同的文件系统中获取和推送到存储库,其中存储库URL只是存储库的文件系统path。 两者都支持从包文件中获取。
Mercurial支持通过SSH和HTTP协议获取和推送。 对于SSH,需要目标计算机上的可访问shell帐户和已安装/可用的hg副本。 对于HTTP访问,需要运行hg-serve
或Mercurial CGI脚本,并且需要在服务器上安装Mercurial。
Git支持两种用于访问远程仓库的协议:
- “智能”协议 ,包括通过SSH访问和通过定制的git://协议(通过
git-daemon
),需要在服务器上安装git。 这些协议中的交换包括客户端和服务器协商他们共有的对象,然后生成和发送一个包文件。 现代Git包含对“智能”HTTP协议的支持。 - 包括HTTP和FTP(仅用于读取)和“HTTPS”(用于通过WebDAV进行推送)的“哑”协议不需要在服务器上安装git,但是它们确实需要存储库包含由
git update-server-info
生成的额外信息(通常从一个钩子运行)。 交换包括客户端执行提交链,并根据需要下载松散对象和包文件。 不足之处在于它下载的内容比严格要求的要严格得多(例如,在只有单个包文件的情况下,即使仅获取less量修订版也会得到整体下载),并且可能需要很多连接才能完成。
扩展:脚本与扩展(插件)
Mercurial是用Python实现的,为了提高性能,一些核心代码用C编写。 它提供了编写扩展 (插件)的API作为添加额外function的一种方式。 某些function(如“书签分支”或签名修订版)是在与Mercurial一起分发的扩展中提供的,并且需要将其打开。
Git是用C , Perl和shell脚本实现的 。 Git提供了许多适合在脚本中使用的低级命令( pipe道 )。 引入新特性的通常方式是将其编写为Perl或shell脚本,并且当用户界面稳定时,为了性能,可移植性以及shell脚本避开临界情况(此过程称为内build化 ),将其重写为C。
Git依赖并围绕[repository]格式和[network]协议构build。 JGit(Java,由EGit,Eclipse Git Plugin使用),Grit(Ruby),以及其他语言的Git(部分或全部) 重新实现 (部分重新实现和部分重复实现) ,Dulwich(Python),git#(C#)。
TL; DR
我想你可以通过对这两个video的看法来感觉这些系统是相似的还是不同的:
Linus Torvalds on Git( http://www.youtube.com/watch?v=4XpnKHJAok8 )
Bryan O'Sullivan在Mercurial上( http://www.youtube.com/watch?v=JExtkqzEoHY )
它们两者在devise上非常相似,但在实现方式上非常不同。
我使用Mercurial。 据我了解Git,一个主要的事情不同的是,它跟踪文件的内容,而不是文件本身。 Linus说,如果你将一个函数从一个文件移动到另一个文件,Git会在整个移动过程中告诉你这个函数的历史。
他们也说git比HTTP慢,但它有自己的networking协议和服务器。
作为一个SVN厚客户端,Git比Mercurial更好。 你可以拉和推SVN服务器。 Mercurial中这个function还在开发中
Mercurial和Git都有非常好的networking托pipe解决scheme(BitBucket和GitHub),但Google Code只支持Mercurial。 顺便说一下,他们对Mercurial和Git进行了非常详细的比较,以确定哪一个可以支持( http://code.google.com/p/support/wiki/DVCSAnalysis )。 它有很多很好的信息。
我前一段时间写了一篇关于Mercurial分支模型的博客文章,并将其与git的分支模型进行了比较。 也许你会发现它很有趣: http : //steveosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/
我经常使用两个。 The major functional difference is in the way Git and Mercurial name branches within repositories. With Mercurial, branch names are cloned and pulled along with their changesets. When you add changes to a new branch in Mercurial and push to another repository, the branch name is pushed at the same time. So, branch names are more-or-less global in Mercurial, and you have to use the Bookmark extension to have local-only lightweight names (if you want them; Mercurial, by default, uses anonymous lightweight codelines, which in its terminology are called "heads"). In Git, branch names and their injective mapping to remote branches are stored locally and you must manage them explicitly, which means knowing how to do that. This is pretty much where Git gets its reputation for being harder to learn and use than Mercurial.
As others will note here, there are lots and lots of minor differences. The thing with the branches is the big differentiator.
Take a look at Git vs. Mercurial: Please Relax blog post by Patrick Thomson, where he writes:
Git is MacGyver , Mercurial is James Bond
Note that this blog post is from August 7, 2008, and both SCM improved much since.
Mercurial is almost fully written in python. Git's core is written in C (and should be faster, than Mercurial's) and tools written in sh, perl, tcl and uses standard GNU utils. Thus it needs to bring all these utils and interpreters with it to system that doesn't contain them (eg Windows).
Both support work with SVN, although AFAIK svn support is broken for git on Windows (may be I am just unlucky/lame, who knows). There're also extensions which allow to interoperate between git and Mercurial.
Mercurial has nice Visual Studio integration . Last time I checked, plugin for Git was working but extremely slow.
They basic command sets are very similar(init, clone, add, status, commit, push, pull etc.). So, basic workflow will be the same. Also, there's TortoiseSVN-like client for both.
Extensions for Mercurial can be written in python (no surprise!) and for git they can be written in any executable form (executable binary, shell script etc). Some extensions are crazy powerful, like git bisect
.
If you need good Windows support, you might prefer Mercurial. TortoiseHg (Windows explorer plugin) manages to offer a simple to use graphical interface to a rather complex tool. As state here, you will also have a Visual Studio plugin . However, last time I tried, the SVN interface didn't work that well on Windows.
If you don't mind the command line interface, I would recommend Git. Not for technical reason but for a strategical one. The adoption rate of git is much higher. Just see how many famous open source projects are switching from cvs/svn to Mercurial and how many are switching to Git. See how many code/project hosting providers you can find with git support compared to Mercurial hosting.
After reading all over that Mercurial is easier (which I still believe it is, after all the internet community is of the opinion), when I started working with Git and Mercurial I felt Git is relatively simpler for me to adapt to (I started off with Mercurial with TortoiseHg) when working from the command line, mainly because the git commands were named appropriately according to me and are fewer in number. Mercurial has different naming for each command that does a distinct job, while Git commands can be multipurpose according to situation (for eg, checkout
). While Git was harder back then, now the difference is hardly substantial. YMMV.. With a good GUI client like TortoiseHg, true it was much easier to work with Mercurial and I did not have to remember the slightly confusing commands. I'm not going into detail how every command for the same action varied, but here are two comprehensive lists: 1 from Mercurial's own site and 2nd from 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 saves a record of every version of committed files internally, while Hg saves just the changesets which can have a smaller footprint. Git makes it easier to change the history compared to Hg, but then again its a hate-it-or-love-it feature. I like Hg for former and Git for latter.
What I miss in Hg is the submodule feature of Git. Hg has subrepos but that's not exactly Git submodule.
Ecosystem around the two can also influence one's choice: Git has to be more popular (but that's trivial), Git has GitHub while Mercurial has BitBucket , Mercurial has TortoiseHg for which I haven't seen an equivalent as good for Git.
Each has its advantages and disadvantages, with either of them you're not going to lose.
Check out Scott Chacon's post from a while back.
I think git has a reputation for being "more complicated", though in my experience it's not more complicated than it needs to be. IMO, the git model is way easier to understand (tags contain commits (and pointers to zero or more parent commits) contain trees contain blobs and other trees… done).
It's not just my experience that git is not more confusing than mercurial. I'd recommend again reading this blog post from Scott Chacon on the matter.
One difference totally unrelated to the DVCSs themselves:
Git seems to be very popular with C developers. Git is the de-facto repository for the Linux Kernel and this may be the reason why it is so popular with C developers. This is especially true for those that have the luxury of only working in the Linux/Unix world.
Java developers seem to favor Mercurial over Git. There are possibly two reasons for that: One is that a number of very large Java projects are hosted on Mercurial, including the JDK itself. Another is that the structure and clean documentation of Mercurial appeals to people coming from the Java camp whereas such people find Git inconsistent wrt command naming and lacking in documentation. I'm not saying that is actually true, I'm saying people have got used to something from their usual habitat and then they tend to choose DVCS from that.
Python developers almost exclusively favor Mercurial, I would assume. There's actually no rational reason for that other than the fact that Mercurial is based on Python. (I use Mercurial too and I really don't understand why people make a fuss about the implementation language of the DVCS. I don't understand a word of Python and if it wasn't for the fact that it is listed somewhere that it is based on Python then I wouldn't have known).
I don't think you can say that one DVCS fits a language better than another, so you shouldn't choose from that. But in reality people choose (partly) based on which DVCS they get most exposed to as part of their community.
(nope, I don't have usage statistics to back up my claims above .. it is all based on my own subjectivity)
I've used Git for a little over a year at my present job, and prior to that, used Mercurial for a little over a year at my previous job. I'm going to provide an evaluation from a user's perspective.
First, both are distributed version control systems. Distributed version control systems require a change in mindset from traditional version control systems, but actually work much better in many ways once one understands them. For this reason, I consider both Git and Mercurial much superior to Subversion, Perforce, etc. The difference between distributed version control systems and traditional version control systems is much larger than the difference between Git and Mercurial.
However, there are also significant differences between Git and Mercurial that make each better suited to its own subset of use cases.
Mercurial is simpler to learn. I got to the point where I rarely had to refer to documentation or notes after a few weeks of using Mercurial; I still have to refer to my notes regularly with Git, even after using it for a year. Git is considerably more complicated.
This is partly because Mercurial is just plain cleaner. You rarely have to branch manually in Mercurial; Mercurial will create an anonymous branch automatically for you if and when you need it. Mercurial nomenclature is more intuitive; you don't have to worry about the difference between "fetch" and "pull" as you do with Git. Mercurial is a bit less buggy. There are file name case sensitivity issues that used to cause problems when pushing projects across platforms with both Git and Mercurial; this were fixed in Mercurial some time ago while they hadn't been fixed in Git last I checked. You can tell Mercurial about file renames; with Git, if it doesn't detect the rename automatically – a very hit or miss proposition in my experience – the rename can't be tracked at all.
The other reason for Git's additional complication, however, is that much of it is needed to support additional features and power. Yes, it's more complicated to handle branching in Git – but on the other hand, once you have the branches, it's not too difficult to do things with those branches that are virtually impossible in Mercurial. Rebasing branches is one of these things: you can move your branch so that its base, instead of being the state of the trunk when you branched, is the state of the trunk now; this greatly simplifies version history when there are many people working on the same code base, since each of the pushes to trunk can be made to appear sequential, rather than intertwined. Similarly, it's much easier to collapse multiple commits on your branch into a single commit, which can again help in keeping the version control history clean: ideally, all the work on a feature can appear as a single commit in trunk, replacing all the minor commits and subbranches that the developer may have made while developing the feature.
Ultimately I think the choice between Mercurial and Git should depend on how large your version control projects are, measured in terms of the number of people working on them simultaneously. If you have a group of a dozen or more working on a single monolithic web application, for example, Git's more powerful branch management tools will make it a much better fit for your project. On the other hand, if your team is developing a heterogeneous distributed system, with only one or two developers working on any one component at any one time, using a Mercurial repository for each of the component projects will allow development to proceed more smoothly with less repository management overhead.
Bottom line: if you have a big team developing a single huge application, use Git; if your individual applications are small, with any scale coming from the number rather than the size of such applications, use Mercurial.