version-control mercurial教學 - Git與Mercurial-比較與對比

mercurial中文 git比較 (10)





請注意,我並沒有試圖找出哪一個是“最好的”,或者甚至是我應該從哪一個開始。 我主要尋找他們相似的地方和他們不同的地方,因為我有興趣了解他們在實施和哲學方面的差異。


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.

As others will note here, there are lots and lots of minor differences. The thing with the branches is the big differentiator.

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 .

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)


Linus Torvalds on Git(
Bryan O'Sullivan在Mercurial上(


我使用Mercurial。 就我理解Git而言,git的一個主要不同之處在於它跟踪文件的內容而不是文件本身。 Linus說如果你將一個函數從一個文件移動到另一個文件,Git會告訴你整個移動過程中單個函數的歷史。


作為SVN厚客戶端,Git比Mercurial更好。 你可以拉和推SVN服務器。 該功能在Mercurial中仍在開發中

Mercurial和Git都有非常好的網絡託管解決方案(BitBucket和GitHub),但Google Code只支持Mercurial。 順便說一下,他們對Mercurial和Git進行了非常詳細的比較,他們在決定支持哪一種( )時做了相應的比較。 它有很多很好的信息。

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.

免責聲明: 我使用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基本工作流程基於匿名頭像 ; Git使用輕量級命名分支,並具有特殊類型的分支( 遠程跟踪分支 ),它們遵循遠程存儲庫中的分支。
  • 修訂命名和範圍: Mercurial提供版本號 ,本地存儲庫,並且基於本地編號的相對修訂(從tip,即當前分支計數)和修訂範圍; Git提供了一種方法來引用相對於分支提示的修訂,並且修訂範圍是拓撲(基於修訂圖)
  • Mercurial使用重命名跟踪 ,而Git使用重命名檢測來處理文件重命名
  • 網絡: Mercurial支持SSH和HTTP“智能”協議和靜態HTTP協議; 現代Git支持SSH,HTTP和GIT“智能”協議和HTTP(S)“啞”協議。 兩者都支持離線傳輸的捆綁文件。
  • Mercurial使用擴展 (插件)和建立的API; Git具有可編寫腳本和建立格式。

Meritial與Git有一些不同之處,但還有其他一些東西使它們相似。 兩個項目都藉鑑了彼此的想法。 例如Mercurial中的hg bisect命令(以前的分叉擴展 )受Git中的git bisect命令的啟發,而git bundle想法受hg bundle啟發。


在Git的對像數據庫中有四種類型的對象:包含文件內容的blob對象,存儲目錄結構的分層對象,包括文件名和文件權限的相關部分(文件的可執行權限,作為符號鏈接) , 提交包含作者信息的對象,指向由提交(通過項目頂部目錄的樹對象)表示的存儲庫狀態快照的指針,以及對零個或多個父提交的引用,以及引用其他對象的標記對象,並且可以使用PGP / GPG進行簽名。

Git使用兩種存儲對象的方式: 鬆散格式,其中每個對象存儲在一個單獨的文件中(這些文件只寫一次,從不修改),以及打包格式,其中許多對象存儲在單個文件中。 操作的原子性由以下事實提供:在寫入對象之後寫入對新對象的引用(使用創建+重命名技巧,原子性)。

Git存儲庫需要使用git gc進行定期維護(以減少磁盤空間並提高性能),儘管現在Git會自動執行此操作。 (這種方法可以更好地壓縮存儲庫。)

Mercurial(據我了解)將一個文件的歷史記錄存儲在一個文件日誌中 (我認為,與額外的元數據,如重命名跟踪和一些幫助信息一起); 它使用稱為manifest的平坦結構來存儲目錄結構,以及稱為changelog的結構,其存儲關於變更集(修訂版)的信息,包括提交消息和零,一個或兩個父母。

Mercurial使用事務日誌來提供操作的原子性,並依靠截斷文件在失敗或中斷操作後進行清理。 Revlogs只能追加。


在Git中, 樹形對象形成了一個層次結構; 在Mercurial 清單文件是扁平結構。 在Git blob對像中存儲文件內容的一個版本 ; 在Mercurial文件日誌中存儲單個文件的整個歷史記錄 (如果我們在這裡沒有考慮重命名的任何復雜情況)。 這意味著有不同的操作區域,Git比Mercurial快,所有其他事物都被認為是相同的(比如合併,或者顯示項目歷史),以及Mercurial比Git快的區域(比如應用補丁或者顯示單個文件的歷史)。 這個問題對最終用戶可能不重要。

由於Mercurial的更新日誌結構的固定記錄結構,Mercurial中的提交最多只能有兩個父母 ; 在Git中提交可以有兩個以上的父母(所謂的“章魚合併”)。 雖然您可以(理論上)通過一系列雙親合併來替換章魚合併,但在Mercurial和Git存儲庫之間轉換時可能會導致並發症。

據我所知Mercurial沒有相當於Git的註釋標籤 (標籤對象)。 註釋標籤的特殊情況是帶簽名的標籤 (帶有PGP / GPG簽名); 在Mercurial中可以使用GpgExtension完成,該擴展與Mercurial一起發布。 你不能在Git中標記 Mercurial中的非提交對象 ,但這並不是非常重要,我認為(某些git存儲庫使用標記的blob來分髮用於驗證簽名標記的公共PGP密鑰)。


在Git引用(分支,遠程跟踪分支和標籤)駐留在提交的DAG之外(因為它們應該)。 refs/heads/ namespace( 本地分支 )中的引用指向提交,通常通過“git commit”更新; 他們指向分行的小費(頭),這就是為什麼這樣的名字。 refs/remotes/<remotename>/ namespace( 遠程跟踪分支 )中的引用指向提交,跟隨遠程倉庫<remotename>分支,並通過“git fetch”或等效項更新。 refs/tags/ namespace( 標籤 )中的引用通常指向提交(輕量級標籤)或標籤對象(註釋和簽名標籤),並且不打算更改。


在Mercurial中,您可以使用標記給持久化名稱進行修訂; 標籤的存儲方式與忽略模式類似。 這意味著全局可見標籤存儲在版本庫中的版本控制.hgtags文件中。 這有兩個後果:首先,Mercurial必須對此文件使用特殊規則來獲取所有標記的當前列表並更新此文件(例如,它讀取最近提交的文件修訂版本,當前未檢出版本); 其次,您必須對此文件進行更改,以便讓其他用戶/其他存儲庫(據我了解)顯示新標籤。

Mercurial還支持存儲在hg/localtags 本地標籤 ,這些標籤對他人不可見(當然也不可轉讓)

在Git中,標籤是固定的(常量)引用,存儲在refs/tags/ namespace中的其他對象(通常是標籤對象,這些對象又指向提交)。 默認情況下,當獲取或推送一組版本時,git會自動提取或推送指向正在提取或推送的版本的標籤。 不過,您可以在某種程度上控制 哪些代碼被抓取或推送。


Git在Mercurial中沒有嚴格等效的本地標籤。 儘管如此,git的最佳做法建議設置單獨的公共裸倉庫,在其中推送已準備好的更改,並從中進行克隆和提取。 這意味著您不會推送的標籤(和分支)對您的存儲庫是私有的。 另一方面,您也可以使用除headsremotestags以外的名稱空間,例如local-tags的本地標籤。

個人觀點:在我看來,標籤應該位於修訂圖之外,因為它們是外部的(它們是指向修訂圖的指針)。 標籤應該是非版本的,但可以轉讓。 Mercurial選擇使用類似於忽略文件的機制,意味著它必須專門處理.hgtags (文件樹中的內容是可轉移的,但普通的是版本化的),或者只有本地標籤( .hg/localtags不是版本控制的,但是.hg/localtags )。


在Git 本地分支 (分支提示或分支頭)是一個提交的命名引用,其中可以增加新的提交。 分支也可以指活躍的開發線,即從分支尖端可達的所有提交。 本地分支位於refs/heads/ namespace中,因此例如'master'分支的完全限定名稱是'refs / heads / master'。

Git中的當前分支(表示檢出分支,以及新分配將分支的分支)是由HEAD引用引用的分支。 可以讓HEAD直接指向提交,而不是符號引用; 在匿名匿名分支上的這種情況叫做detached HEAD (“git branch”表明你在'(no branch)'上)。

在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'遠程跟踪的遠程存儲庫中找到'master'分支的狀態('refs / heads / master')分支('refs / remotes / origin / master')。

Mercurial也被稱為命名分支 ,其中分支名稱嵌入在提交中(在變更集中)。 這個名字是全球性的(在取回時轉移)。 這些分支名稱將永久記錄為變更集元數據的一部分。 使用現代Mercurial,您可以關閉“命名分支”並停止錄製分支名稱。 在這個機制中,分支的提示可以實時計算。

Mercurial的“命名分支”在我看來應該被稱為提交標籤 ,因為它就是這樣。 有些情況下,“命名分支”可以有多個提示(多個無子提交),也可以由修訂圖的幾個不相交部分組成。

在Git中沒有這些Mercurial“嵌入式分支”的等價物; 而且Git的理念是,雖然可以說分支包含一些提交,但並不意味著提交屬於某個分支。



Mercurial默認推動所有頭 。 如果您想推送一個分支( 單頭 ),則必須指定要推送的分支的最新修訂版本。 您可以通過其修訂版本號(本地存儲庫),修訂版標識符,書籤名稱(本地存儲庫,不會被轉移)或嵌入分支名稱(命名分支)來指定分支技巧。

據我了解,如果你按照Mercurial的說法推送一系列包含標記為在某個“命名分支”上的提交的修訂版本,那麼你將在存儲庫中推送這個“命名分支”。 這意味著此類嵌入式分支(“命名分支”)的名稱是全局的 (關於給定存儲庫/項目的克隆)。

默認情況下(取決於push.default配置變量)“git push”或“git push < remote >”Git會推送匹配的分支 ,也就是說,只有那些已經存在於遠程倉庫中的本地分支才會被推入。 你可以使用--all選項讓git push(“git push --all”)推送所有分支 ,你可以使用“git push < remote > < branch >”來推送給定的單個分支 ,你可以使用“ git push < remote > HEAD“推送當前分支



注意:這裡我使用Git術語,其中“獲取”意味著從遠程存儲庫下載更改, 而不將這些更改與本地工作集成。 這就是“ git fetch ”和“ hg pull ”的作用。

如果我理解正確,默認情況下Mercurial從遠程存儲庫獲取所有頭 ,但是您可以指定要通過“ hg pull --rev <rev> <url> ”或“ hg pull <url>#<rev> ”獲取的分支以得到單個分支 。 您可以使用修訂標識符,“命名分支”名稱(嵌入在changelog中的分支)或書籤名稱來指定<rev>。 但是,書籤名稱(至少當前)不會被傳送。 您獲得的所有“命名分支”修訂版都將被轉移。 “hg pull”存儲它作為匿名匿名頭部提取的分支的提示。

在Git默認情況下(對於由'git clone'創建的'origin'remote,以及使用“git remote add”創建的遠程)“ git fetch ”(或者“ git fetch <remote> ”)從遠程倉庫refs/heads/ namespace),並將它們存儲在refs/remotes/ namespace中。 這意味著例如遠程“起源”中名為'master'(全名:'refs / heads / master')的分支將被存儲(保存)為'origin / master' 遠程跟踪分支 (全名:'refs /遙控器/來源/主“)。

你可以通過使用git fetch <remote> <branch> Git中的單個分支 - Git會將請求的分支存儲在FETCH_HEAD中,這與Mercurial未命名的頭相似。

這些只是強大的refspec Git語法默認情況下的示例:使用refspecs,您可以指定和/或配置想要獲取的分支以及存儲它們的位置。 例如,默認的“獲取所有分支”情況由'+ refs / heads / *:refs / remotes / origin / *'通配符refspec表示,“fetch single branch”表示'refs / heads / <branch>的簡寫:'' 。 Refspecs用於將遠程存儲庫中分支(ref)的名稱映射到本地refs名稱。 但你不需要知道(很多)關於refspecs能夠有效地使用Git(主要感謝“git remote”命令)。

個人觀點:我個人認為Mercurial中的“命名分支”(含有變更元數據中的分支名稱)是其全局命名空間的誤導性設計,尤其是對於分佈式版本控制系統。 例如,讓我們看看Alice和Bob在它們的存儲庫中具有名為“for-joe”的“命名分支”的情況,這些分支沒有任何共同之處。 但是,在Joe的存儲庫中,這兩個分支將作為單個分支被虐待。 所以你不知怎麼想出了防止分支名稱衝突的慣例。 這對Git來說並不是問題,在Joe的倉庫'for-joe'分支來自Alice的時候會是'alice / for-joe',而從Bob來說,它會是'bob / for-joe'。 另請參見將分支名稱與 Mercurial wiki上引發的分支標識問題分離


這個領域是Mercurial和Git之間的主要區別之一,正如james woodyattSteve Losh在答案中所說的那樣。 默認情況下,Mercurial使用匿名輕量級代碼行,其術語稱為“頭”。 Git使用輕量級命名分支,使用內射映射將遠程存儲庫中分支的名稱映射到遠程跟踪分支的名稱。 Git“強迫”你命名分支(除了單一的未命名分支,稱為detached HEAD),但我認為這適用於分支繁重的工作流程,比如主題分支工作流,這意味著單個存儲庫範例中的多個分支。


在Git中有很多命名修訂的方式(例如在git rev-parse manpage中描述):

  • 完整的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“。

還有涉及reflog的修訂說明符,這裡沒有提到。 在Git中的每個對象,無論是承諾,標籤,樹或斑點有其SHA-1標識符; 有特殊的語法,例如'next:Documentation'或'next:README'來引用指定版本中的樹(目錄)或blob(文件內容)。


  • 一個純整數被視為修訂號。 需要記住的是修訂號對於給定的存儲庫本地的 ; 在其他存儲庫中,它們可以不同。
  • 負整數被視為從筆尖的連續偏移量,其中-1表示筆尖,-2表示筆尖之前的修訂,等等。 它們也是存儲庫本地的
  • 唯一的修訂標識符(40位十六進製字符串)或其唯一的前綴。
  • 標籤名稱(與給定修訂相關聯的符號名稱)或書籤名稱(擴展名:與給定頭相關聯的符號名稱,本地存儲庫)或“命名分支”(提交標籤;由“命名分支”給出的修訂為提交(無子提交)的所有提交的提交標籤,如果有多個提示,修訂號最大)
  • 保留名稱“tip”是一個特殊的標籤,它總是標識最新的修訂版本。
  • 保留名稱“null”表示空版本。
  • 保留名稱“。” 表示父工作目錄。

正如你可以看到比較上面的列表Mercurial提供版本號,本地存儲庫,而Git不提供。 另一方面,Mercurial僅從'tip'(當前分支)提供相對偏移量,這是本地存儲庫(至少沒有ParentrevspecExtension ),而Git允許從任何提示中指定任何後續提交。

最新版本在Git中被命名為HEAD,在Mercurial中被命名為“tip” Git中沒有null修訂。 Mercurial和Git都可以擁有許多根目錄(可以有多個無父級提交;這通常是以前獨立項目加入的結果)。


個人觀點:我認為版本號被高估了(至少對於分佈式開發和/或非線性/分支歷史)。 首先,對於分佈式版本控制系統,它們必須是存儲庫本地的,或者需要以特殊的方式將一些存儲庫作為中央編號機構處理。 其次,具有較長歷史的較大項目可以有5位數範圍內的修訂數量,所以它們僅提供略微優於6-7個字符的修訂標識符,並且暗示嚴格排序,而修訂僅部分排序(我的意思是這裡修訂版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範圍相反作為閉合間隔 。 另外範圍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)不是AB --not $(git merge-base AB) ,這意味著所有可從A或B訪問的提交,但不包括從它們兩個可訪問的提交(可從公共祖先訪問)。


Mercurial使用重命名跟踪來處理文件重命名。 這意味著在提交時保存關於文件被重命名的信息; 在Mercurial中,這些信息以文件日誌(文件revlog)元數據中的“增強型差異”形式保存。 這樣做的後果是你必須使用hg rename / hg mv ...或者你需要記住運行hg addremove來執行基於相似性的重命名檢測。

Git在版本控制系統中是獨一無二的,因為它使用重命名檢測來處理文件重命名。 這意味著文件被重命名的事實在需要的時候被檢測到:合併時或者顯示差異時(如果請求/配置)。 這具有可以改進重命名檢測算法的優點,並且在提交時不凍結。

在顯示單個文件的歷史記錄時,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。


  • “智能”協議 ,包括通過SSH訪問和通過定制git://協議(通過git-daemon ),需要在服務器上安裝git。 這些協議中的交換包括客戶端和服務器協商他們共有的對象,然後生成並發送一個包文件。 現代Git包含對“智能”HTTP協議的支持。
  • 包括HTTP和FTP(僅用於讀取)的“啞”協議和HTTPS(用於通過WebDAV推送)不需要在服務器上安裝git,但它們確實需要該存儲庫包含由git update-server-info生成的額外信息(通常從一個鉤子運行)。 交換包括客戶端在提交鏈中走動,並根據需要下載鬆散的對象和包文件。 不足之處在於它的下載量超過了嚴格要求(例如,在只有單個文件包的情況下,即使只提取一些修訂版,也會下載整個文件),並且可能需要很多連接才能完成。


Mercurial是用Python實現的,一些核心代碼用C語言編寫以提高性能。 它提供了用於編寫擴展 (插件)的API作為添加額外功能的一種方式。 某些功能(如“書籤分支”或簽名修訂版)在Mercurial分發的擴展中提供,並需要將其打開。

Git是用CPerlshell腳本實現的 。 Git提供了許多適用於腳本的低級命令( 管道 )。 引入新特性的通常方式是將其編寫為Perl或shell腳本,並且當用戶界面穩定時,為了性能,可移植性以及shell腳本避開角落案例(此過程稱為內建 ),將其重寫為C語言。

Git依賴並圍繞[資源庫]格式和[網絡]協議構建。 JGit(Java,由EGit,Eclipse Git Plugin使用),Grit(Ruby)以及其他語言(部分或全部) 重新實現了Git在其他語言中的部分或全部實現 (部分實現部分重新實現並部分包裝git命令) ,Dulwich(Python),git#(C#)。



git reset --soft HEAD~1
  • --soft表示未提交的文件應保留為與--hard相對的工作文件,這將丟棄它們。
  • HEAD~1是最後一次提交。 如果你想回滾3次提交,你可以使用HEAD~3 。 如果要回滾到特定的修訂版號,也可以使用其SHA哈希來執行此操作。



git version-control mercurial dvcs