Git 和 Mercurial - 比较和对比
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1598759/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Git and Mercurial - Compare and Contrast
提问by TM.
For a while now I've been using subversion for my personal projects.
一段时间以来,我一直在为我的个人项目使用 subversion。
More and more I keep hearing great things about Git and Mercurial, and DVCS in general.
我越来越多地听到关于 Git 和 Mercurial 以及 DVCS 的好消息。
I'd like to give the whole DVCS thing a whirl, but I'm not too familiar with either option.
我想尝试一下整个 DVCS,但我对这两种选择都不太熟悉。
What are some of the differences between Mercurial and Git?
Mercurial 和 Git 之间有哪些区别?
Note: I'm nottrying to find out which one is "best" or even which one I should start with. I'm mainly looking for key areas where they are similar, and where they are different, because I am interested to know how they differ in terms of implementation and philosophy.
注意:我不是要找出哪一个是“最好的”,甚至不是我应该从哪一个开始。我主要寻找它们相似和不同的关键领域,因为我很想知道它们在实施和理念方面有何不同。
采纳答案by Jakub Nar?bski
Disclaimer:I use Git, follow Git development on git mailing list, and even contribute a bit to Git (gitweb mainly). I know Mercurial from documentation and some from discussion on #revctrl IRC channel on FreeNode.
免责声明:我使用 Git,在 git 邮件列表上关注 Git 开发,甚至对 Git(主要是 gitweb)做出了一些贡献。我从文档和一些关于 FreeNode 上 #revctrl IRC 频道的讨论中了解 Mercurial。
Thanks to all people on on #mercurial IRC channel who provided help about Mercurial for this writeup
感谢#mercurial IRC 频道上的所有人为这篇文章提供了有关 Mercurial 的帮助
Summary
概括
Here it would be nice to have some syntax for table, something like in PHPMarkdown / MultiMarkdown / Maruku extension of Markdown
这里最好有一些表格语法,比如 PHPMarkdown / MultiMarkdown / Markdown 的 Maruku 扩展
- Repository structure:Mercurial doesn't allow octopus merges (with more than two parents), nor tagging non-commit objects.
- Tags:Mercurial uses versioned
.hgtags
file with special rules for per-repository tags, and has also support for local tags in.hg/localtags
; in Git tags are refs residing inrefs/tags/
namespace, and by default are autofollowed on fetching and require explicit pushing. - Branches:In Mercurial basic workflow is based on anonymous heads; Git uses lightweight named branches, and has special kind of branches (remote-tracking branches) that follow branches in remote repository.
- Revision naming and ranges:Mercurial provides revision numbers, local to repository, and bases relative revisions (counting from tip, i.e. current branch) and revision ranges on this localnumbering; Git provides a way to refer to revision relative to branch tip, and revision ranges are topological (based on graph of revisions)
- Mercurial uses rename tracking, while Git uses rename detectionto deal with file renames
- Network:Mercurial supports SSH and HTTP "smart" protocols, and static HTTP protocol; modern Git supports SSH, HTTP and GIT "smart" protocols, and HTTP(S) "dumb" protocol. Both have support for bundles files for off-line transport.
- Mercurial uses extensions(plugins) and established API; Git has scriptabilityand established formats.
- 存储库结构:Mercurial 不允许八达通合并(具有两个以上的父项),也不允许标记未提交的对象。
- 标签:Mercurial 使用版本化
.hgtags
文件对每个存储库标签具有特殊规则,并且还支持.hg/localtags
. 在 Git 标签中的引用是驻留在refs/tags/
命名空间中的,默认情况下在获取时自动跟随并需要显式推送。 - 分支:在 Mercurial 中,基本工作流基于匿名头;Git 使用轻量级命名分支,并具有跟随远程存储库中分支的特殊类型的分支(远程跟踪分支)。
- 修订命名和范围:Mercurial 提供修订号,本地到存储库,并基于此本地编号的相对修订(从提示计数,即当前分支)和修订范围;Git 提供了一种引用相对于分支提示的修订的方法,修订范围是拓扑的(基于修订图)
- Mercurial 使用重命名跟踪,而 Git 使用重命名检测来处理文件重命名
- 网络:Mercurial 支持 SSH 和 HTTP “智能”协议,以及静态 HTTP 协议;现代 Git 支持 SSH、HTTP 和 GIT“智能”协议,以及 HTTP(S)“哑”协议。两者都支持用于离线传输的捆绑文件。
- Mercurial 使用扩展(插件)和已建立的 API;Git 具有脚本性和既定格式。
There are a few things that differ Mercurial from Git, but there are other things that make them similar. Both projects borrow ideas from each other. For example hg bisect
command in Mercurial (formerly bisect extension) was inspired by git bisect
command in Git, while idea of git bundle
was inspired by hg bundle
.
Mercurial 与 Git 有一些不同之处,但还有一些其他方面使它们相似。这两个项目相互借鉴。例如hg bisect
,Mercurial 中的 command(以前称为bisect 扩展)的灵感来自git bisect
Git 中的command,而idea 的git bundle
灵感来自hg bundle
.
Repository structure, storing revisions
存储库结构,存储修订
In Git there are four types of objects in its object database: blobobjects which contain contents of a file, hierarchical treeobjects which store directory structure, including file names and relevant parts of file permissions (executable permission for files, being a symbolic link), commitobject which contain authorship info, pointer to snapshot of state of repository at revision represented by a commit (via a tree object of top directory of project) and references to zero or more parent commits, and tagobjects which reference other objects and can be signed using PGP / GPG.
在 Git 的对象数据库中有四种类型的对象:包含文件内容的blob对象,存储目录结构的分层树对象,包括文件名和文件权限的相关部分(文件的可执行权限,作为符号链接) ,提交包含著作权信息,指针信息库的状态的快照在由a表示修订提交(经由项目的顶级目录树对象)对象,并引用到零个或多个父提交,标签哪个参考其他对象,并且可以物体使用 PGP / GPG 进行签名。
Git uses two ways of storing objects: looseformat, where each object is stored in a separate file (those files are written once, and never modified), and packedformat where many objects are stored delta-compressed in a single file. Atomicity of operations is provided by the fact, that reference to a new object is written (atomically, using create + rename trick) after writing an object.
Git 使用两种存储对象的方式:松散格式,其中每个对象存储在单独的文件中(这些文件只写入一次,永远不会修改),以及打包格式,其中许多对象以增量压缩方式存储在单个文件中。操作的原子性由以下事实提供,即在写入对象后写入对新对象的引用(原子地,使用创建 + 重命名技巧)。
Git repositories require periodic maintenance using git gc
(to reduce disk space and improve performance), although nowadays Git does that automatically. (This method provides better compression of repositories.)
Git 存储库需要定期维护git gc
(以减少磁盘空间并提高性能),尽管现在 Git 会自动执行此操作。(此方法提供了更好的存储库压缩。)
Mercurial (as far as I understand it) stores history of a file in a filelog(together, I think, with extra metadata like rename tracking, and some helper information); it uses flat structure called manifestto store directory structure, and structure called changelogwhich store information about changesets (revisions), including commit message and zero, one or two parents.
Mercurial(据我所知)将文件的历史记录存储在文件日志中(我认为,还有额外的元数据,如重命名跟踪和一些帮助信息);它使用称为manifest 的扁平结构来存储目录结构,并使用称为changelog 的结构来存储有关变更集(修订)的信息,包括提交消息和零、一或两个父项。
Mercurial uses transaction journalto provide atomicity of operations, and relies on truncatingfiles to clean-up after failed or interrupted operation. Revlogs are append-only.
Mercurial 使用事务日志来提供操作的原子性,并在操作失败或中断后依靠截断文件进行清理。Revlogs 是仅附加的。
Looking at repository structure in Git versus in Mercurial, one can see that Git is more like object database (or a content-addressed filesystem), and Mercurial more like traditional fixed-field relational database.
比较 Git 和 Mercurial 中的存储库结构,可以看到 Git 更像是对象数据库(或内容寻址的文件系统),而 Mercurial 更像是传统的固定字段关系数据库。
Differences:
In Git the treeobjects form a hierarchicalstructure; in Mercurial manifestfile is flatstructure. In Git blobobject store one versionof a contents of a file; in Mercurial filelogstores whole history of a single file(if we do not take into account here any complications with renames). This means that there are different areas of operations where Git would be faster than Mercurial, all other things considered equal (like merges, or showing history of a project), and areas where Mercurial would be faster than Git (like applying patches, or showing history of a single file). This issue might be not important for end user.
不同点:
Git 中树形对象形成层次结构;Mercurial清单文件是扁平结构。在 Git blob对象中存储文件内容的一个版本;在 Mercurial 文件日志中存储单个文件的整个历史(如果我们在这里不考虑重命名的任何复杂性)。这意味着在不同的操作领域,Git 会比 Mercurial 快,所有其他的事情都被认为是相同的(比如合并,或显示项目的历史),以及 Mercurial 比 Git 快的领域(比如应用补丁,或显示单个文件的历史记录)。这个问题对于最终用户来说可能并不重要。
Because of the fixed-record structure of Mercurial's changelogstructure, commits in Mercurial can have only up to two parents; commits in Git can have more than two parents (so called "octopus merge"). While you can (in theory) replace octopus merge by a series of two-parent merges, this might cause complications when converting between Mercurial and Git repositories.
由于 Mercurial 的变更日志结构的固定记录结构,Mercurial 中的提交最多只能有两个父级;Git 中的提交可以有两个以上的父项(所谓的“章鱼合并”)。虽然您(理论上)可以通过一系列双父合并替换章鱼合并,但这可能会导致在 Mercurial 和 Git 存储库之间转换时出现复杂情况。
As far as I know Mercurial doesn't have equivalent of annotated tags(tag objects) from Git. A special case of annotated tags are signed tags(with PGP / GPG signature); equivalent in Mercurial can be done using GpgExtension, which extension is being distributed along with Mercurial. You can't tag non-commit objectin Mercurial like you can in Git, but that is not very important, I think (some git repositories use tagged blob to distribute public PGP key to use to verify signed tags).
据我所知,Mercurial 没有相当于来自 Git的带注释的标签(标签对象)。带注释标签的一种特殊情况是签名标签(带有 PGP / GPG 签名);Mercurial 中的等效项可以使用GpgExtension完成,该扩展名与 Mercurial 一起分发。您不能像在 Git 中那样在 Mercurial 中标记非提交对象,但这并不是很重要,我认为(一些 git 存储库使用标记 blob 来分发公共 PGP 密钥以用于验证签名标记)。
References: branches and tags
参考:分支和标签
In Git references (branches, remote-tracking branches and tags) reside outside DAG of commits (as they should). References in refs/heads/
namespace (local branches) point to commits, and are usually updated by "git commit"; they point to the tip (head) of branch, that's why such name. References in refs/remotes/<remotename>/
namespace (remote-tracking branches) point to commit, follow branches in remote repository <remotename>
, and are updated by "git fetch" or equivalent. References in refs/tags/
namespace (tags) point usually to commits (lightweight tags) or tag objects (annotated and signed tags), and are not meant to change.
在 Git 中,引用(分支、远程跟踪分支和标签)位于提交的 DAG 之外(它们应该如此)。refs/heads/
命名空间(本地分支)中的引用指向提交,通常由“git commit”更新;他们指向分支的尖端(头部),这就是为什么这样的名字。refs/remotes/<remotename>/
命名空间(远程跟踪分支)中的引用指向提交,跟踪远程存储库中的分支,<remotename>
并由“git fetch”或等效项更新。refs/tags/
命名空间(tags)中的引用通常指向提交(轻量级标签)或标签对象(带注释和签名的标签),并且不打算更改。
Tags
标签
In Mercurial you can give persistent name to revision using tag; tags are stored similarly to the ignore patterns. It means that globally visible tags are stored in revision-controlled .hgtags
file in your repository. That has two consequences: first, Mercurial has to use special rules for this file to get current list of all tags and to update such file (e.g. it reads the most recently committed revision of the file, not currently checked out version); second, you have to commit changes to this file to have new tag visible to other users / other repositories (as far as I understand it).
在 Mercurial 中,您可以使用tag为修订版指定持久名称;标签的存储方式类似于忽略模式。这意味着全局可见的标签存储.hgtags
在您的存储库中的修订控制文件中。这有两个后果:首先,Mercurial 必须对这个文件使用特殊的规则来获取所有标签的当前列表并更新这个文件(例如,它读取文件的最近提交的修订版,而不是当前检出的版本);其次,您必须提交对此文件的更改,以使其他用户/其他存储库可以看到新标签(据我所知)。
Mercurial also supports local tags, stored in hg/localtags
, which are not visible to others (and of course are not transferable)
Mercurial 还支持存储在 中的本地标签,hg/localtags
其他人不可见(当然不可转让)
In Git tags are fixed (constant) named references to other objects (usually tag objects, which in turn point to commits) stored in refs/tags/
namespace. By default when fetching or pushing a set of revision, git automatically fetches or pushes tags which point to revisions being fetched or pushed. Nevertheless you can controlto some extent which tags are fetchedor pushed.
在 Git 中,标签是对存储在refs/tags/
命名空间中的其他对象(通常是标签对象,它们又指向提交)的固定(常量)命名引用。默认情况下,在获取或推送一组修订时,git 会自动获取或推送指向正在获取或推送的修订的标签。尽管如此,您可以在一定程度上控制获取或推送哪些标签。
Git treats lightweight tags (pointing directly to commits) and annotated tags (pointing to tag objects, which contain tag message which optionally includes PGP signature, which in turn point to commit) slightly differently, for example by default it considers only annotated tags when describing commits using "git describe".
Git 处理轻量级标签(直接指向提交)和带注释的标签(指向标签对象,其中包含可选包含 PGP 签名的标记消息,然后指向提交)略有不同,例如默认情况下,它在描述时只考虑带注释的标签使用“git describe”提交。
Git doesn't have a strict equivalent of local tags in Mercurial. Nevertheless git best practices recommend to setup separate public bare repository, into which you push ready changes, and from which others clone and fetch. This means that tags (and branches) that you don't push, are private to your repository. On the other hand you can also use namespace other than heads
, remotes
or tags
, for example local-tags
for local tags.
Git 在 Mercurial 中没有严格等同的本地标签。尽管如此,git 最佳实践建议设置单独的公共裸存储库,您将准备好的更改推送到其中,其他人从中克隆和获取。这意味着您不推送的标签(和分支)对于您的存储库是私有的。另一方面,您还可以使用heads
,remotes
或以外的命名空间tags
,例如local-tags
用于本地标签。
Personal opinion:In my opinion tags should reside outside revision graph, as they are external to it (they are pointers into graph of revisions). Tags should be non-versioned, but transferable. Mercurial's choice of using a mechanism similar to the one for ignoring files, means that it either has to treat .hgtags
specially (file in-tree is transferable, but ordinary it is versioned), or have tags which are local only (.hg/localtags
is non-versioned, but untransferable).
个人意见:在我看来,标签应该位于修订图之外,因为它们是外部的(它们是指向修订图的指针)。标签应该是非版本化的,但可以转移。Mercurial 选择使用类似于忽略文件的机制,这意味着它要么必须.hgtags
特殊处理(文件 in-tree 是可传输的,但通常它是版本化的),或者具有仅本地化的标签(.hg/localtags
非版本化,但不可转让)。
Branches
分行
In Git local branch(branch tip, or branch head) is a named reference to a commit, where one can grow new commits. Branch can also mean active line of development, i.e. all commits reachable from branch tip. Local branches reside in refs/heads/
namespace, so e.g. fully qualified name of 'master' branch is 'refs/heads/master'.
在 Git本地分支(分支提示或分支头)中是对提交的命名引用,可以在其中增加新的提交。分支也可以表示活跃的开发线,即从分支提示可到达的所有提交。本地分支驻留在refs/heads/
命名空间中,因此例如“master”分支的完全限定名称是“refs/heads/master”。
Current branch in Git (meaning checked out branch, and branch where new commit will go) is the branch which is referenced by the HEAD ref. One can have HEAD pointing directly to a commit, rather than being symbolic reference; this situation of being on an anonymous unnamed branch is called detached HEAD("git branch" shows that you are on '(no branch)').
Git 中的当前分支(意味着签出的分支,以及新提交所在的分支)是 HEAD ref 引用的分支。可以让 HEAD 直接指向提交,而不是符号引用;这种处于匿名未命名分支上的情况称为分离的 HEAD(“git 分支”表明您在“(无分支)”上)。
In Mercurial there are anonymous branches (branch heads), and one can use bookmarks (via bookmark extension). Such bookmark branchesare purely local, and those names were (up to version 1.6) not transferable using Mercurial. You can use rsync or scp to copy the .hg/bookmarks
file to a remote repository. You can also use hg id -r <bookmark> <url>
to get the revision id of a current tip of a bookmark.
在 Mercurial 中有匿名分支(分支头),并且可以使用书签(通过书签扩展)。这样的书签分支纯粹是本地的,并且这些名称(直到版本 1.6)不能使用 Mercurial 进行转移。您可以使用 rsync 或 scp 将.hg/bookmarks
文件复制到远程存储库。您还可以使用hg id -r <bookmark> <url>
来获取书签当前提示的修订 ID。
Since 1.6 bookmarks can be pushed/pulled. The BookmarksExtensionpage has a section on Working With Remote Repositories. There is a difference in that in Mercurial bookmark names are global, while definition of 'remote' in Git describes also mapping of branch namesfrom the names in remote repository to the names of local remote-tracking branches; for example refs/heads/*:refs/remotes/origin/*
mapping means that one can find state of 'master' branch ('refs/heads/master') in the remote repository in the 'origin/master' remote-tracking branch ('refs/remotes/origin/master').
由于 1.6 书签可以推/拉。该BookmarksExtension页面有一个节正在与远程仓库。不同之处在于 Mercurial 中书签名称是global,而 Git 中 'remote' 的定义还描述了分支名称从远程存储库中的名称到本地远程跟踪分支名称的映射;例如,refs/heads/*:refs/remotes/origin/*
映射意味着可以在“origin/master”远程跟踪分支(“refs/remotes/origin/master”)的远程存储库中找到“master”分支(“refs/heads/master”)的状态。
Mercurial has also so called named branches, where the branch name is embeddedin a commit (in a changeset). Such name is global (transferred on fetch). Those branch names are permanently recorded as part of the changeset\u2019s metadata. With modern Mercurial you can close "named branch" and stop recording branch name. In this mechanism tips of branches are calculated on the fly.
Mercurial 也有所谓的命名分支,其中分支名称嵌入在提交中(在变更集中)。这样的名称是全局的(在获取时传输)。这些分支名称被永久记录为变更集元数据的一部分。使用现代 Mercurial,您可以关闭“命名分支”并停止记录分支名称。在这种机制中,分支的尖端是动态计算的。
Mercurial's "named branches" should in my opinion be called commit labelsinstead, because it is what they are. There are situations where "named branch" can have multiple tips (multiple childless commits), and can also consist of several disjoint parts of graph of revisions.
在我看来,Mercurial 的“命名分支”应该被称为提交标签,因为它们就是这样。在某些情况下,“命名分支”可以有多个提示(多个无子提交),也可以由修订图的几个不相交部分组成。
There is no equivalent of those Mercurial "embedded branches" in Git; moreover Git's philosophy is that while one can say that branch includes some commit, it doesn't mean that a commit belongs to some branch.
Git 中没有与 Mercurial 中的那些“嵌入式分支”等价的东西;此外,Git 的哲学是,虽然可以说分支包含一些提交,但这并不意味着提交属于某个分支。
Note that Mercurial documentation still proposes to use separate clones (separate repositories) at least for long-lived branches (single branch per repository workflow), aka branching by cloning.
请注意,Mercurial 文档仍然建议至少对长期存在的分支(每个存储库工作流程有一个分支)使用单独的克隆(单独的存储库),也就是通过 cloning 进行分支。
Branches in pushing
分支在推
Mercurial by default pushes all heads. If you want to push a single branch (single head), you have to specify tip revision of the branch you want to push. You can specify branch tip by its revision number (local to repository), by revision identifier, by bookmark name (local to repository, doesn't get transferred), or by embedded branch name (named branch).
Mercurial 默认推送所有 head。如果要推送单个分支(single head),则必须指定要推送的分支的最新修订版。您可以通过修订号(存储库本地)、修订标识符、书签名称(存储库本地,不会传输)或嵌入的分支名称(命名分支)指定分支提示。
As far as I understand it, if you push a range of revisions that contain commits marked as being on some "named branch" in Mercurial parlance, you will have this "named branch" in the repository you push to. This means that names of such embedded branches ("named branches") are global(with respect to clones of given repository / project).
据我了解,如果您推送一系列包含标记为在 Mercurial 术语中的某个“命名分支”上的提交的修订,您将在推送到的存储库中拥有这个“命名分支”。这意味着此类嵌入式分支(“命名分支”)的名称是全局的(相对于给定存储库/项目的克隆)。
By default (subject to push.default
configuration variable) "git push" or "git push <remote>" Git would push matching branches, i.e. only those local branches that have their equivalent already present in remote repository you push into. You can use --all
option to git-push ("git push --all") to push all branches, you can use "git push <remote> <branch>" to push a given single branch, and you can use "git push <remote> HEAD" to push current branch.
默认情况下(取决于push.default
配置变量)“git push”或“git push <远程>”Git 会推送匹配的分支,即只有那些在你推送到的远程存储库中已经存在它们的等效分支的本地分支。您可以使用--all
git-push ("git push --all") 选项来推送所有分支,您可以使用 "git push < remote> < branch>" 来推送给定的单个分支,您可以使用 "git push <远程> HEAD”推送当前分支。
All of the above assumes that Git isn't configured which branches to push via remote.<remotename>.push
configuration variables.
以上所有内容均假设 Git 未配置通过remote.<remotename>.push
配置变量推送哪些分支。
Branches in fetching
提取中的分支
Note:here I use Git terminology where "fetch" means downloading changes from remote repository withoutintegrating those changes with local work. This is what "git fetch
" and "hg pull
" does.
注意:这里我使用 Git 术语,其中“获取”意味着从远程存储库下载更改而不将这些更改与本地工作集成。这就是 " git fetch
" 和 " hg pull
" 所做的。
If I understand it correctly, by default Mercurial fetches all headsfrom remote repository, but you can specify branch to fetch via "hg pull --rev <rev> <url>
" or "hg pull <url>#<rev>
" to get single branch. You can specify <rev> using revision identifier, "named branch" name (branch embedded in changelog), or bookmark name. Bookmark name however (at least currently) doesn't get transferred. All "named branches" revisions you get belong to get transferred. "hg pull" stores tips of branches it fetched as anonymous, unnamed heads.
如果我理解正确的话,默认情况下 Mercurial从远程存储库中获取所有头,但您可以通过“ hg pull --rev <rev> <url>
”或“ hg pull <url>#<rev>
”指定要获取的分支以获取单个分支。您可以使用修订标识符、“命名分支”名称(嵌入在更改日志中的分支)或书签名称来指定 <rev>。然而,书签名称(至少目前)不会被转移。您获得的所有“命名分支”修订都属于转移。“hg pull”存储它作为匿名、未命名的头部获取的分支的提示。
In Git by default (for 'origin' remote created by "git clone", and for remotes created using "git remote add") "git fetch
" (or "git fetch <remote>
") gets all branchesfrom remote repository (from refs/heads/
namespace), and stores them in refs/remotes/
namespace. This means for example that branch named 'master' (full name: 'refs/heads/master') in remote 'origin' would get stored (saved) as 'origin/master' remote-tracking branch(full name: 'refs/remotes/origin/master').
在 Git 默认情况下(对于由“git clone”创建的“origin”远程,以及使用“git remote add”创建的远程)“ git fetch
”(或“ git fetch <remote>
”)从远程存储库(来自命名空间)获取所有分支refs/heads/
,并将它们存储在refs/remotes/
命名空间。这意味着,例如,远程“origin”中名为“master”(全名:“refs/heads/master”)的分支将被存储(保存)为“origin/master”远程跟踪分支(全名:“refs/遥控器/起源/主')。
You can fetch single branchin Git by using git fetch <remote> <branch>
- Git would store requested branch(es) in FETCH_HEAD, which is something similar to Mercurial unnamed heads.
您可以通过使用在 Git 中获取单个分支git fetch <remote> <branch>
- Git 会将请求的分支存储在 FETCH_HEAD 中,这类似于 Mercurial 未命名的头部。
Those are but examples of default cases of powerful refspecGit syntax: with refspecs you can specify and/or configure which branches one want to fetch, and where to store them. For example default "fetch all branches" case is represented by '+refs/heads/*:refs/remotes/origin/*' wildcard refspec, and "fetch single branch" is shorthand for 'refs/heads/<branch>:'. Refspecs are used to map names of branches (refs) in remote repository to local refs names. But you don't need to know (much) about refspecs to be able to work effectively with Git (thanks mainly to "git remote" command).
这些只是强大的refspecGit 语法的默认情况的示例:使用 refspecs,您可以指定和/或配置想要获取的分支,以及存储它们的位置。例如,默认“获取所有分支”的情况由 '+refs/heads/*:refs/remotes/origin/*' 通配符 refspec 表示,而“获取单个分支”是 'refs/heads/<branch>:' 的简写. Refspecs 用于将远程存储库中的分支 (refs) 名称映射到本地 refs 名称。但是您不需要(过多)了解 refspecs 就能有效地使用 Git(主要归功于“git remote”命令)。
Personal opinion:I personally think that "named branches" (with branch names embedded in changeset metadata) in Mercurial are misguided design with its global namespace, especially for a distributedversion control system. For example let's take case where both Alice and Bob have "named branch" named 'for-joe' in their repositories, branches which have nothing in common. In Joe's repository however those two branches would be mistreated as a single branch. So you have somehow come up with convention protecting against branch name clashes. This is not problem with Git, where in Joe's repository 'for-joe' branch from Alice would be 'alice/for-joe', and from Bob it would be 'bob/for-joe'. See also Separating branch name from branch identityissue raised on Mercurial wiki.
个人观点:我个人认为 Mercurial 中的“命名分支”(分支名称嵌入在变更集元数据中)是对其全局命名空间的误导设计,尤其是对于分布式版本控制系统。例如,让我们假设 Alice 和 Bob 在他们的存储库中都有名为“for-joe”的“命名分支”,这些分支没有任何共同点。然而,在 Joe 的存储库中,这两个分支将被视为一个分支。所以你以某种方式提出了防止分支名称冲突的约定。这对 Git 来说不是问题,在 Joe 的存储库中,来自 Alice 的“for-joe”分支是“alice/for-joe”,而来自 Bob 的分支是“bob/for-joe”。
Mercurial's "bookmark branches" currently lack in-core distribution mechanism.
Mercurial 的“书签分支”目前缺乏核心分发机制。
Differences:
This area is one of the main differences between Mercurial and Git, as james woodyattand Steve Loshsaid in their answers. Mercurial, by default, uses anonymous lightweight codelines, which in its terminology are called "heads". Git uses lightweight named branches, with injective mapping to map names of branches in remote repository to names of remote-tracking branches. Git "forces" you to name branches (well, with exception of single unnamed branch, situation called detached HEAD), but I think this works better with branch-heavy workflows such as topic branch workflow, meaning multiple branches in single repository paradigm.
差异:
这一领域是 Mercurial 和 Git 之间的主要差异之一,正如james woodyatt和Steve Losh在他们的回答中所说。默认情况下,Mercurial 使用匿名轻量级代码行,在其术语中称为“头部”。Git 使用轻量级命名分支,通过内射映射将远程存储库中的分支名称映射到远程跟踪分支的名称。Git“强制”您命名分支(嗯,除了单个未命名分支,称为分离 HEAD 的情况),但我认为这在分支密集的工作流中效果更好,例如主题分支工作流,这意味着单个存储库范式中的多个分支。
Naming revisions
命名修订
In Git there are many ways of naming revisions (described e.g. in git rev-parsemanpage):
在 Git 中有很多命名修订的方法(例如在git rev-parse 手册页中描述):
- The full SHA1 object name (40-byte hexadecimal string), or a substring of such that is unique within the repository
- A symbolic ref name, e.g. 'master' (referring to 'master' branch), or 'v1.5.0' (referring to tag), or 'origin/next' (referring to remote-tracking branch)
- A suffix
^
to revision parameter means the first parent of a commit object,^n
means n-th parent of a merge commit. A suffix~n
to revision parameter means n-th ancestor of a commit in straight first-parent line. Those suffixes can be combined, to form revision specifier following path from a symbolic reference, e.g. 'pu~3^2~3' - Output of "git describe", i.e. a closest tag, optionally followed by a dash and a number of commits, followed by a dash, a 'g', and an abbreviated object name, for example 'v1.6.5.1-75-g5bf8097'.
- 完整的 SHA1 对象名称(40 字节的十六进制字符串),或在存储库中唯一的子字符串
- 符号引用名称,例如“master”(指的是“master”分支)或“v1.5.0”(指标记)或“origin/next”(指的是远程跟踪分支)
^
修订参数的后缀表示提交对象的第一个父对象,^n
表示合并提交的第 n 个父对象。~n
修订参数的后缀表示直接第一父行中提交的第 n 个祖先。可以组合这些后缀,以形成遵循符号引用路径的修订说明符,例如“pu~3^2~3”- “git describe”的输出,即最接近的标签,可选地后跟一个破折号和一些提交,然后是一个破折号、一个'g'和一个缩写的对象名称,例如'v1.6.5.1-75- g5bf8097'。
There are also revision specifiers involving reflog, not mentioned here. In Git each object, be it commit, tag, tree or blob has its SHA-1 identifier; there is special syntax like e.g. 'next:Documentation' or 'next:README' to refer to tree (directory) or blob (file contents) at specified revision.
还有涉及 reflog 的修订说明符,这里没有提到。在 Git 中,每个对象,无论是提交、标记、树还是 blob,都有其 SHA-1 标识符;有特殊的语法,例如“next:Documentation”或“next:README”来引用指定修订版的树(目录)或 blob(文件内容)。
Mercurial also has many ways of naming changesets (described e.g. in hgmanpage):
Mercurial 也有许多命名变更集的方法(例如在hg 手册页中描述):
- A plain integer is treated as a revision number. One need to remember that revision numbers are local to given repository; in other repository they can be different.
- Negative integers are treated as sequential offsets from the tip, with -1 denoting the tip, -2 denoting the revision prior to the tip, and so forth. They are also localto repository.
- An unique revision identifier (40-digit hexadecimal string) or its unique prefix.
- A tag name (symbolic name associated with given revision), or a bookmark name (with extension: symbolic name associated with given head, local to repository), or a "named branch" (commit label; revision given by "named branch" is tip (childless commit) of all commits with given commit label, with largest revision number if there are more than one such tip)
- The reserved name "tip" is a special tag that always identifies the most recent revision.
- The reserved name "null" indicates the null revision.
- The reserved name "." indicates the working directory parent.
- 普通整数被视为修订号。需要记住,修订号是给定存储库的本地版本;在其他存储库中,它们可以不同。
- 负整数被视为从尖端开始的连续偏移量,-1 表示尖端,-2 表示尖端之前的修订,依此类推。它们也是存储库本地的。
- 唯一的修订标识符(40 位十六进制字符串)或其唯一前缀。
- 标签名称(与给定修订版相关联的符号名称),或书签名称(带扩展名:与给定头相关联的符号名,存储库本地),或“命名分支”(提交标签;“命名分支”给出的修订版是具有给定提交标签的所有提交的提示(无子提交),如果有多个这样的提示,则具有最大的修订号)
- 保留名称“tip”是一个特殊标签,它始终标识最新的修订版本。
- 保留名称“null”表示空版本。
- 保留名称“.” 表示工作目录父级。
Differences
As you can see comparing above lists Mercurial offers revision numbers, local to repository, while Git doesn't. On the other hand Mercurial offers relative offsets only from 'tip' (current branch), which are local to repository (at least without ParentrevspecExtension), while Git allows to specify any commit following from any tip.
差异
正如您在比较上面的列表中看到的那样,Mercurial 提供版本号,本地存储库,而 Git 不提供。另一方面,Mercurial 仅提供来自“tip”(当前分支)的相对偏移量,它们是存储库本地的(至少没有ParentrevspecExtension),而 Git 允许指定来自任何提示的任何提交。
The most recent revision is named HEAD in Git, and "tip" in Mercurial; there is no null revision in Git. Both Mercurial and Git can have many root (can have more than one parentless commits; this is usually result of formerly separate projects joining).
最新版本在 Git 中命名为 HEAD,在 Mercurial 中命名为“tip”;Git 中没有空修订版。Mercurial 和 Git 都可以有多个 root(可以有多个无父提交;这通常是以前独立项目加入的结果)。
See also:Many different kinds of revision specifiersarticle on Elijah's Blog (newren's).
另请参阅:以利亚博客(newren's)上的许多不同类型的修订说明符文章。
Personal opinion:I think that revision numbersare overrated (at least for distributed development and/or nonlinear / branchy history). First, for a distributed version control system they have to be either local to repository, or require treating some repository in a special way as a central numbering authority. Second, larger projects, with longer history, can have number of revisions in 5 digits range so they are offer only slight advantage over shortened to 6-7 character revision identifiers, and imply strict ordering while revisions are only partially ordered (I mean here that revisions n and n+1 doesn't need to be parent and child).
个人意见:我认为修订号被高估了(至少对于分布式开发和/或非线性/分支历史)。首先,对于分布式版本控制系统,它们必须是存储库本地的,或者需要以特殊方式将某个存储库视为中央编号机构。其次,历史较长的大型项目可以有 5 位数字范围内的修订数量,因此与缩短为 6-7 个字符的修订标识符相比,它们仅具有轻微的优势,并且暗示严格排序,而修订仅部分排序(我的意思是这里修订版 n 和 n+1 不需要是父级和子级)。
Revision ranges
修订范围
In Git revision ranges are topological. Commonly seen A..B
syntax, which for linear history means revision range starting at A (but excluding A), and ending at B (i.e. range is open from below), is shorthand ("syntactic sugar") for ^A B
, which for history traversing commands mean all commits reachable from B, excluding those reachable from A. This means that the behavior of A..B
range is entirely predictable (and quite useful) even if A is not ancestor of B: A..B
means then range of revisions from common ancestor of A and B (merge base) to revision B.
在 Git 中,修订范围是拓扑的。常见的A..B
语法,对于线性历史意味着修订范围从 A 开始(但不包括 A),并在 B 结束(即范围从下面打开),是简写(“语法糖”) for ^A B
,对于历史遍历命令意味着所有提交可从 B 到达,不包括那些可从 A 到达的提交。这意味着A..B
即使 A 不是 B 的祖先,范围的行为也是完全可预测的(并且非常有用):A..B
意味着来自 A 和 B 共同祖先的修订范围(合并基础) 到修订版 B。
In Mercurial revision ranges are based on range of revision numbers. Range is specified using A:B
syntax, and contrary to Git range acts as a closed interval. Also range B:A is the range A:B in reverse order, which is not the case in Git (but see below note on A...B
syntax). But such simplicity comes with a price: revision range A:B makes sense only if A is ancestor of B or vice versa, i.e. with linear history; otherwise (I guess that) the range is unpredictable, and the result is local to repository (because revision numbers are local to repository).
在 Mercurial 中,修订范围基于修订号范围。Range 是使用A:B
语法指定的,与 Git range 相反,它充当了闭区间。范围 B:A 也是范围 A:B 的相反顺序,这在 Git 中不是这种情况(但请参阅下面的A...B
语法注释)。但这种简单是有代价的:只有当 A 是 B 的祖先时,修订范围 A:B 才有意义,反之亦然,即具有线性历史;否则(我猜)范围是不可预测的,结果是存储库本地的(因为修订号是存储库本地的)。
This is fixed with Mercurial 1.6, which has new topological revision range, where 'A..B' (or 'A::B') is understood as the set of changesets that are both descendants of X and ancestors of Y. This is, I guess, equivalent to '--ancestry-path A..B' in Git.
Mercurial 1.6 修复了这个问题,它具有新的拓扑修订范围,其中“A..B”(或“A::B”)被理解为既是 X 的后代又是 Y 的祖先的变更集集。这是,我猜,相当于 Git 中的“--ancestry-path A..B”。
Git also has notation A...B
for symmetric difference of revisions; it means A B --not $(git merge-base A B)
, which means all commits reachable from either A or B, but excluding all commits reachable from both of them (reachable from common ancestors).
Git 也有表示A...B
修订的对称差异的符号;这意味着A B --not $(git merge-base A B)
,这意味着所有提交都可以从 A 或 B 到达,但不包括所有可以从它们两者到达的提交(可从共同祖先到达)。
Renames
重命名
Mercurial uses rename trackingto deal with file renames. This means that the information about the fact that a file was renamed is saved at the commit time; in Mercurial this information is saved in the "enhanced diff" form in filelog(file revlog) metadata. The consequence of this is that you have to use hg rename
/ hg mv
... or you need to remember to run hg addremove
to do similarity based rename detection.
Mercurial 使用重命名跟踪来处理文件重命名。这意味着关于文件被重命名的信息在提交时保存;在水银这个信息被保存在“增强的差异”形式filelog(文件revlog)元数据。这样做的结果是您必须使用hg rename
/ hg mv
... 或者您需要记住运行hg addremove
以进行基于相似性的重命名检测。
Git is unique among version control systems in that it uses rename detectionto deal with file renames. This means that the fact that file was renamed is detected at time it is needed: when doing a merge, or when showing a diff (if requested / configured). This has the advantage that rename detection algorithm can be improved, and is not frozen at time of commit.
Git 在版本控制系统中是独一无二的,因为它使用重命名检测来处理文件重命名。这意味着文件被重命名的事实在需要时被检测到:在进行合并时,或在显示差异时(如果请求/配置)。这样做的好处是可以改进重命名检测算法,并且不会在提交时冻结。
Both Git and Mercurial require using --follow
option to follow renames when showing history of a single file. Both can follow renames when showing line-wise history of a file in git blame
/ hg annotate
.
--follow
在显示单个文件的历史记录时,Git 和 Mercurial 都需要使用选项来跟随重命名。在git blame
/ 中显示文件的逐行历史记录时,两者都可以跟随重命名hg annotate
。
In Git the git blame
command is able to follow code movement, also moving (or copying) code from one file to the other, even if the code movement is not part of wholesome file rename. As far as I know this feature is unique to Git (at the time of writing, October 2009).
在 Git 中,该git blame
命令能够跟踪代码移动,也可以将代码从一个文件移动(或复制)到另一个文件,即使代码移动不是完整文件重命名的一部分。 据我所知,此功能是 Git 独有的(在撰写本文时,2009 年 10 月)。
Network protocols
网络协议
Both Mercurial and Git have support for fetching from and pushing to repositories on the same filesystem, where repository URL is just a filesystem path to repository. Both also have support for fetching from bundle files.
Mercurial 和 Git 都支持从同一文件系统上的存储库获取和推送到存储库,其中存储库 URL 只是存储库的文件系统路径。两者也都支持从包文件中获取。
Mercurial support fetching and pushing via SSH and via HTTP protocols. For SSH one needs an accessible shell account on the destination machine and a copy of hg installed / available. For HTTP access the hg-serve
or Mercurial CGI script running is required, and Mercurial needs to be installed on server machine.
Mercurial 支持通过 SSH 和 HTTP 协议获取和推送。对于 SSH,需要在目标机器上有一个可访问的 shell 帐户和已安装/可用的 hg 副本。对于 HTTP 访问,hg-serve
需要运行 Mercurial CGI 脚本,并且需要在服务器机器上安装 Mercurial。
Git supports two kinds of protocols used to access remote repository:
Git 支持两种用于访问远程仓库的协议:
- "smart" protocols, which include access via SSH and via custom git:// protocol (by
git-daemon
), require having git installed on server. The exchange in those protocols consist of client and server negotiating about what objects they have in common, and then generating and sending a packfile. Modern Git includes support for "smart" HTTP protocol. - "dumb" protocols, which include HTTP and FTP (only for fetching), and HTTPS (for pushing via WebDAV), do not require git installed on server, but they do require that repository contains extra information generated by
git update-server-info
(usually run from a hook). The exchange consist of client walking the commit chain and downloading loose objects and packfiles as needed. The downside is that it downloads more than strictly required (e.g. in corner case when there is only single packfile it would get downloaded whole even when fetching only a few revisions), and that it can require many connections to finish.
- “智能”协议,包括通过 SSH 和自定义 git:// 协议(by
git-daemon
)访问,需要在服务器上安装 git。这些协议中的交换包括客户端和服务器协商它们有哪些共同的对象,然后生成和发送包文件。现代 Git 包括对“智能”HTTP 协议的支持。 - “哑”协议,包括 HTTP 和 FTP(仅用于获取)和 HTTPS(用于通过 WebDAV 推送),不需要在服务器上安装 git,但它们确实要求存储库包含由
git update-server-info
(通常从钩子运行)生成的额外信息)。交换包括客户端遍历提交链并根据需要下载松散对象和包文件。缺点是它下载的内容比严格要求的要多(例如,在只有一个包文件的极端情况下,即使只获取几个修订版,它也会被完整下载),并且可能需要许多连接才能完成。
Extending: scriptability vs extensions (plugins)
扩展:脚本能力与扩展(插件)
Mercurial is implemented in Python, with some core code written in C for performance. It provides API for writing extensions(plugins) as a way of adding extra features. Some of functionality, like "bookmark branches" or signing revisions, is provided in extensions distributed with Mercurial and requires turning it on.
Mercurial 是用Python实现的,为了提高性能,一些核心代码是用 C 编写的。它提供了用于编写扩展(插件)的API,作为添加额外功能的一种方式。一些功能,如“书签分支”或签名修订,是在随 Mercurial 分发的扩展中提供的,需要打开它。
Git is implemented in C, Perland shell scripts. Git provides many low level commands (plumbing) suitable to use in scripts. The usual way of introducing new feature is to write it as Perl or shell script, and when user interface stabilizes rewrite it in C for performance, portability, and in the case of shell script avoiding corner cases (this procedure is called builtinification).
Git 是用C、Perl和shell 脚本实现的。Git 提供了许多适合在脚本中使用的低级命令(管道)。引入新特性的常用方法是将其编写为 Perl 或 shell 脚本,并在用户界面稳定后用 C 重写它以提高性能、可移植性,并在 shell 脚本的情况下避免极端情况(此过程称为builtinification)。
Git relies and is built around [repository] formats and [network] protocols. Instead of language bindings there are (partial or complete) reimplementationsof Git in other languages (some of those are partially reimplementations, and partially wrappers around git commands): JGit (Java, used by EGit, Eclipse Git Plugin), Grit (Ruby), Dulwich (Python), git# (C#).
Git 依赖并围绕 [repository] 格式和 [network] 协议构建。除了语言绑定之外,还有其他语言(部分或完整)重新实现Git(其中一些是部分重新实现,部分是围绕 git 命令的包装):JGit(Java,由 EGit 使用,Eclipse Git 插件),Grit(Ruby) , Dulwich (Python), git# (C#)。
TL;DR
TL; 博士
回答by artemb
I think you can get a feeling of what those systems are similar or different in by whatching those two videos:
我认为您可以通过观看这两个视频来了解这些系统的相似之处或不同之处:
Linus Torvalds on Git (http://www.youtube.com/watch?v=4XpnKHJAok8)
Bryan O'Sullivan on Mercurial (http://www.youtube.com/watch?v=JExtkqzEoHY)
Git 上的 Linus Torvalds ( http://www.youtube.com/watch?v=4XpnKHJAok8)
Mercurial 上的 Bryan O'Sullivan ( http://www.youtube.com/watch?v=JExtkqzEoHY)
Both of them are very similar in design but very different in implementations.
两者在设计上非常相似,但在实现上却大不相同。
I use Mercurial. As far as I understand Git, one major thing git is different is that it tracks contents of files instead of files themselves. Linus says that if you move a function from one file to another, Git will tell you the history of that single function across the move.
我使用水银。就我理解的 Git 而言,git 的一个主要区别是它跟踪文件的内容而不是文件本身。Linus 说,如果你将一个函数从一个文件移动到另一个文件,Git 会告诉你整个移动过程中该单个函数的历史。
They also say that git is slower over HTTP but it has it's own network protocol and server.
他们还说 git 比 HTTP 慢,但它有自己的网络协议和服务器。
Git works better as an SVN thick client than Mercurial. You can pull and push against an SVN server. This functionality is still under development in Mercurial
Git 作为 SVN 胖客户端比 Mercurial 更有效。您可以拉动和推入 SVN 服务器。此功能仍在 Mercurial 中开发
Both Mercurial and Git have very nice web hosting solutions available (BitBucket and GitHub), but Google Code supports Mercurial only. By the way, they have a very detailed comparison of Mercurial and Git they did for deciding which one to support (http://code.google.com/p/support/wiki/DVCSAnalysis). It has a lot of good info.
Mercurial 和 Git 都有非常好的网络托管解决方案(BitBucket 和 GitHub),但 Google Code 仅支持 Mercurial。顺便说一下,他们对 Mercurial 和 Git 进行了非常详细的比较,以确定支持哪一个(http://code.google.com/p/support/wiki/DVCSAnalysis)。它有很多很好的信息。
回答by Steve Losh
I wrote a blog entry about Mercurial's branching models a while ago, and included comparisons to git's branching model. Maybe you'll find it interesting: http://stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/
不久前我写了一篇关于 Mercurial 分支模型的博客文章,其中包括与 git 分支模型的比较。也许你会发现它很有趣:http: //stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/
回答by james woodyatt
I use both quite regularly. 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.
我经常使用两者。主要的功能区别在于存储库中 Git 和 Mercurial 名称分支的方式。使用 Mercurial,分支名称被克隆并与其变更集一起拉取。当您向 Mercurial 中的新分支添加更改并推送到另一个存储库时,会同时推送分支名称。因此,分支名称在 Mercurial 中或多或少是全局的,您必须使用 Bookmark 扩展来拥有仅本地的轻量级名称(如果您需要它们;默认情况下,Mercurial 使用匿名轻量级代码行,在其术语中是称为“头”)。在 Git 中,分支名称及其到远程分支的内射映射存储在本地,您必须明确管理它们,这意味着知道如何做到这一点。
As others will note here, there are lots and lots of minor differences. The thing with the branches is the big differentiator.
正如其他人会在这里指出的那样,有很多细微的差异。分支是最大的区别。
回答by Jakub Nar?bski
Take a look at Git vs. Mercurial: Please Relaxblog post by Patrick Thomson, where he writes:
Git is MacGyver, Mercurial is James Bond
看看Git 与 Mercurial:请放松Patrick Thomson 的博客文章,他写道:
Git 是 MacGyver,Mercurial 是 James Bond
Note that this blog post is from August 7, 2008, and both SCM improved much since.
请注意,这篇博文发表于 2008 年 8 月 7 日,此后两个 SCM 都得到了很大改进。
回答by nawfal
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 siteand 2nd from wikivs.
在阅读了所有关于 Mercurial 更容易(我仍然相信它,毕竟互联网社区的意见)之后,当我开始使用 Git 和 Mercurial 时,我觉得 Git 对我来说相对更容易适应(我开始使用 Mercurial 和 TortoiseHg)在命令行中工作时,主要是因为 git 命令根据我的需要适当命名并且数量较少。Mercurial 对执行不同工作的每个命令有不同的命名,而 Git 命令可以根据情况具有多种用途(例如,checkout
)。虽然当时 Git 更难,但现在差别不大。YMMV .. 有了像 TortoiseHg 这样的优秀 GUI 客户端,使用 Mercurial 确实要容易得多,而且我不必记住稍微令人困惑的命令。我不会详细说明同一动作的每个命令如何变化,但这里有两个综合列表:一个来自 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 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.
Git 在内部保存已提交文件的每个版本的记录,而 Hg 只保存可能占用较小空间的变更集。与 Hg 相比,Git 更容易更改历史记录,但它又是一个讨厌它或喜欢它的功能。我喜欢前者的 Hg 和后者的 Git。
What I miss in Hg is the submodule feature of Git. Hg has subrepos but that's not exactly Git submodule.
我在 Hg 中怀念的是 Git 的子模块功能。Hg 有 subrepos 但这不完全是 Git 子模块。
Ecosystem around the two can also influence one's choice: Git has to be more popular (but that's trivial), Git has GitHubwhile Mercurial has BitBucket, Mercurial has TortoiseHg for which I haven't seen an equivalent as good for Git.
围绕这两者的生态系统也会影响一个人的选择:Git 必须更受欢迎(但那是微不足道的),Git 有GitHub,而 Mercurial 有BitBucket,Mercurial 有 TortoiseHg,我还没有看到与 Git 相同的等价物。
Each has its advantages and disadvantages, with either of them you're not going to lose.
每个都有其优点和缺点,您不会失去任何一个。
回答by Eric Darchis
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.
如果您需要良好的 Windows 支持,您可能更喜欢 Mercurial。TortoiseHg(Windows 资源管理器插件)设法为相当复杂的工具提供了一个简单易用的图形界面。正如此处所述,您还将拥有一个Visual Studio 插件。但是,上次我尝试时,SVN 界面在 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 muchhigher. 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.
如果你不介意命令行界面,我会推荐 Git。不是出于技术原因,而是出于战略原因。git 的采用率要高得多。看看有多少著名的开源项目从 cvs/svn 切换到 Mercurial,有多少切换到 Git。查看与 Mercurial 托管相比,您可以通过 git 支持找到多少代码/项目托管提供商。
回答by elder_george
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 (e.g. Windows).
Mercurial 几乎完全是用 python 编写的。Git 的核心是用 C 编写的(应该比 Mercurial 的更快)和用 sh、perl、tcl 编写的工具,并使用标准的 GNU utils。因此,它需要将所有这些实用程序和解释器带到不包含它们的系统(例如 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.
两者都支持与 SVN 一起使用,尽管 AFAIK svn 支持在 Windows 上被 git 破坏了(也许我只是不走运/跛脚,谁知道)。还有一些扩展允许在 git 和 Mercurial 之间进行互操作。
Mercurial has nice Visual Studio integration. Last time I checked, plugin for Gitwas working but extremely slow.
Mercurial 有很好的Visual Studio 集成。上次我检查时,Git 插件正在运行,但速度非常慢。
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.
它们的基本命令集非常相似(init、clone、add、status、commit、push、pull 等)。因此,基本工作流程将是相同的。此外,两者都有类似 TortoiseSVN 的客户端。
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
.
Mercurial 的扩展可以用 python 编写(毫不奇怪!),而对于 git,它们可以用任何可执行形式(可执行二进制文件、shell 脚本等)编写。一些扩展非常强大,比如git bisect
.
回答by Dustin
Check out Scott Chacon's postfrom 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 wayeasier to understand (tags contain commits (and pointers to zero or more parent commits) contain trees contain blobs and other trees... done).
我认为 git 以“更复杂”着称,但根据我的经验,它并没有比需要的更复杂。IMO,git的模型的方式更容易理解(标签包含提交(和指针,以零个或多个父提交)含有树包含斑点和其他树木......完成)。
It's not just my experience that git is not more confusing than mercurial. I'd recommend again reading this blog post from Scott Chaconon the matter.
git 并不比 mercurial 更令人困惑,这不仅仅是我的经验。我建议再次阅读Scott Chacon关于此事的这篇博文。
回答by Warren Dew
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.
我在目前的工作中使用 Git 一年多一点,在此之前,在我之前的工作中使用 Mercurial 一年多一点。我将从用户的角度提供评估。
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.
首先,两者都是分布式版本控制系统。分布式版本控制系统需要改变传统版本控制系统的思维方式,但一旦了解它们,实际上在许多方面会更好地工作。基于这个原因,我认为 Git 和 Mercurial 都比 Subversion、Perforce 等优越很多。分布式版本控制系统和传统版本控制系统的区别远大于 Git 和 Mercurial 的区别。
However, there are also significant differences between Git and Mercurial that make each better suited to its own subset of use cases.
然而,Git 和 Mercurial 之间也存在显着差异,这使得它们各自更适合自己的用例子集。
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.
Mercurial 更容易学习。在使用 Mercurial 几周后,我很少需要参考文档或笔记;即使使用了一年,我仍然需要定期使用 Git 参考我的笔记。Git要复杂得多。
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.
这部分是因为 Mercurial 只是简单的清洁剂。您很少需要在 Mercurial 中手动分支;Mercurial 会在您需要时自动为您创建一个匿名分支。Mercurial 命名法更直观;您不必像使用 Git 那样担心“获取”和“拉取”之间的区别。Mercurial 的问题有点少。在使用 Git 和 Mercurial 跨平台推送项目时,存在文件名区分大小写问题;这在前一段时间在 Mercurial 中已修复,而我上次检查时尚未在 Git 中修复。你可以告诉 Mercurial 文件重命名;使用 Git,如果它没有自动检测到重命名 - 根据我的经验,这是一个非常受欢迎的命题 - 根本无法跟踪重命名。
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.
然而,Git 额外复杂性的另一个原因是它需要大部分来支持额外的特性和功能。是的,在 Git 中处理分支更复杂 - 但另一方面,一旦你有了分支,用那些在 Mercurial 中几乎不可能的分支来做事情并不太难。Rebase 分支是其中之一:你可以移动你的分支,这样它的基础,而不是你分支时的主干状态,现在是主干的状态;当有很多人在同一个代码库上工作时,这大大简化了版本历史,因为每次推送到主干都可以显示为顺序,而不是交织在一起。同样,将分支上的多个提交合并为一个提交要容易得多,
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.
最终,我认为 Mercurial 和 Git 之间的选择应该取决于你的版本控制项目有多大,以同时工作的人数来衡量。例如,如果您有十几个或更多的团队在开发一个单一的 Web 应用程序,那么 Git 更强大的分支管理工具将使其更适合您的项目。另一方面,如果您的团队正在开发异构分布式系统,并且在任何时候只有一两个开发人员在处理任何一个组件,那么为每个组件项目使用 Mercurial 存储库将使开发更顺利地进行存储库管理开销。
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.
底线:如果你有一个大团队开发单个巨大的应用程序,请使用 Git;如果您的单个应用程序很小,并且任何规模都来自此类应用程序的数量而不是大小,请使用 Mercurial。