1. ホーム
  2. ギット

[解決済み】GitとMercurial - 比較と対比

2022-06-27 17:16:41

質問

ここしばらくの間、私は個人的なプロジェクトのためにsubversionを使用しています。

Git と Mercurial、そして一般的な DVCS について、ますます多くのことを耳にするようになりました。

DVCSの全体的なことを試してみたいのですが、どちらのオプションにもあまり慣れていません。

MercurialとGitの違いは何ですか?

注:私は ではない がベストであるか、あるいは、どれから始めるべきかを見つけようとしているのです。 私は主に、両者が似ている部分と異なっている部分を探しています。

どのように解決するのですか?

免責事項です。 私は Git を使っており、Git の開発を git メーリングリストでフォローし、Git に少し貢献しています(主に gitweb)。Mercurial はドキュメントと FreeNode の #revctrl IRC チャンネルでの議論からいくつか知っています。

この記事を書くにあたり、#mercurial IRC チャンネルで Mercurial についてのヘルプを提供してくれたすべての人に感謝します。



概要

ここで、PHPMarkdown / MultiMarkdown / Maruku extension of Markdownのようなテーブルのための構文があると良いでしょう。

  • リポジトリ構造。 Mercurial は (2 つ以上の親を持つ) タコ足マージや、非コミットオブジェクトのタグ付けを許しません。
  • タグです。 Mercurial はバージョン管理された .hgtags ファイルを使用し、リポジトリごとのタグのための特別なルールもあります。 .hg/localtags Git のタグの参照は refs/tags/ 名前空間に存在し、デフォルトでは取得時に自動フォローされ、明示的なプッシュが必要です。
  • ブランチです。 Mercurial では、基本的なワークフローは 匿名ヘッド Git では軽量な名前付きブランチを使い、特殊なブランチ ( リモート追跡ブランチ ) があり、リモートリポジトリのブランチに従います。
  • リビジョンの命名と範囲。 Mercurial は リビジョン番号 を提供し、相対リビジョン(先端から数える、つまり現在のブランチ)とリビジョン範囲を、この ローカル Git はブランチの先端から相対的なリビジョンを参照する方法を提供し、リビジョン範囲はトポロジカル(リビジョンのグラフに基づく)です。
  • Mercurial は リネームトラッキング を使用し、Gitでは リネーム検出 を使用して、ファイルのリネームを処理します。
  • ネットワークの Mercurial は SSH と HTTP "smart" プロトコル、および静的 HTTP プロトコルをサポートし、最新の Git は SSH、HTTP、GIT "smart" プロトコル、および HTTP(S) "dumb" プロトコルをサポートしています。 どちらも、オフライン転送のためのバンドルファイルをサポートしています。
  • Mercurial は エクステンションを使用します。 (プラグイン) と確立された API を使用します。Git は スクリプト可能 と確立されたフォーマットがあります。

Mercurial と Git の違いはいくつかありますが、似ているところもあります。 どちらのプロジェクトも、互いのアイデアを拝借しています。 例えば hg bisect コマンドは、Mercurial (旧 bisect 拡張 ) に触発され git bisect コマンドに触発されたもので、一方 git bundlehg bundle .

リポジトリ構造、リビジョンの保存

Gitでは、オブジェクト・データベースには4つのタイプのオブジェクトがあります。 ブロブ ファイルの内容を含むオブジェクト、階層的な ツリー ディレクトリ構造を格納するオブジェクトで、ファイル名やファイルのパーミッションの関連部分(ファイルの実行可能パーミッション、シンボリックリンクであること)を含みます。 コミット オブジェクトがあります。このオブジェクトには、作者情報、コミットで表されるリビジョンにおけるリポジトリの状態のスナップショットへのポインタ(プロジェクトのトップディレクトリのツリーオブジェクトを経由して)、0個以上の親コミットへの参照が含まれます。 タグ 他のオブジェクトを参照するオブジェクトで、PGP / GPG を使用して署名することができます。

Gitはオブジェクトを保存するために2つの方法を使います。 緩い 形式であり、各オブジェクトは別々のファイルに保存されます(これらのファイルは一度書き込まれると、決して変更されません)。 パック フォーマットでは、多くのオブジェクトが一つのファイルに差分圧縮されて保存されます。 操作の原子性は、オブジェクトを書き込んだ後に新しいオブジェクトへの参照が (create + rename トリックを使用して原子的に) 書き込まれるという事実によって提供されます。

Git リポジトリでは、定期的なメンテナンスが必要です。 git gc (を使った定期的なメンテナンスが必要です(ディスク容量を減らし、パフォーマンスを向上させるため)。 (この方法は、リポジトリの圧縮をより良くします)。

Mercurialは(私が理解する限り)ファイルの履歴を ファイルログ (というフラットな構造を使っています。 マニフェスト と呼ばれるフラット構造を使用し、ディレクトリ構造を格納するために チェンジログ と呼ばれる、コミットメッセージや0、1、2の親を含むチェンジセット(リビジョン)に関する情報を格納する構造体があります。

Mercurial は トランザクションジャーナル を使い、操作のアトミック性を提供します。 切り捨て ファイルに依存しています。 Revlog は追記のみです。

Git と Mercurial のリポジトリ構造を見てみると、Git はオブジェクトデータベース(またはコンテントアドレス付きファイルシステム)、Mercurial は伝統的な固定フィールドリレーショナルデータベースに似ていることがわかります。

違いです。

Git では ツリー オブジェクトを形成します。 階層的な 構造を形成します。Mercurial では マニフェスト ファイルは フラット 構造になっています。 Gitでは ブロブ オブジェクトストア 1バージョン ファイルの内容の。Mercurialでは ファイルログ が格納されます。 単一のファイルの全履歴 (を保存します(ここでリネームの複雑さを考慮しない場合)。つまり、Git が Mercurial よりも速い操作領域(マージやプロジェクトの履歴表示など)と、Mercurial が Git よりも速い領域(パッチの適用や単一ファイルの履歴表示など)があることになります。 この問題は、エンドユーザーにとって重要ではないかもしれません。

Mercurial の固定記録構造のために 変更履歴 の構造上、Mercurial のコミットには 親を二つまで Gitのコミットは2つ以上の親を持つことができます(いわゆるquot;octopus merge")。 (理論的には) octopus merge を 2 つの親を持つ一連のマージで置き換えることができますが、Mercurial と Git のリポジトリ間で変換するときに複雑な問題が発生する可能性があります。

私の知る限り、Mercurial には同じように アノテーションタグ (タグオブジェクト) がありません。注釈付きタグの特殊なケースは 署名入りタグ (PGP / GPG 署名付き) です。Mercurial でこれと同じことをするには、次のようにします。 GpgExtension を使うことができます。このエクステンションは Mercurial と一緒に配布されています。 このエクステンションは Mercurial と一緒に配布されています。 タグで非コミットオブジェクト ができませんが、これはあまり重要ではないと思います (一部の git リポジトリでは、タグ付き blob を使って公開 PGP 鍵を配布し、署名付きタグを検証するために使っています)。

参考: ブランチとタグ

Git では、参照 (ブランチ、リモート追跡ブランチ、タグ) はコミットの DAG の外に置かれます (そうあるべきです)。 リファレンスは refs/heads/ 名前空間 ( ローカルブランチ ) はコミットを指し、通常 "git commit" で更新されます。ブランチの先端 (head) を指すので、このような名前になっています。 での参照は refs/remotes/<remotename>/ 名前空間 ( リモートトラッキングブランチ ) はコミットを指し、リモートリポジトリのブランチを追跡します。 <remotename> を指しており、"git fetch"または同等の方法で更新されます。 のリファレンスは refs/tags/ 名前空間 ( タグ ) は通常コミット (軽量タグ) もしくはタグオブジェクト (注釈付きタグと署名付きタグ) を指し、変更されることを意図していません。

タグ

Mercurial では、リビジョンに永続的な名前を付けることができます。 タグを使います。 タグは無視パターンと同じように保存されます。 つまり、グローバルに見えるタグは、リビジョン管理された .hgtags ファイルに保存されることを意味します。第一に、 Mercurial はこのファイルに対して特別なルールを使って、すべてのタグの現在のリストを取得し、ファイルを更新しなければなりません(例えば、現在チェックアウトされたバージョンではなく、直近にコミットされたリビジョンを読みます); 第二に、他のユーザーや他のリポジトリから新しいタグを表示するには、このファイルに変更をコミットしなければなりません(私の理解では、)。

Mercurial はまた ローカルタグ に格納されている hg/localtags に格納され、他の人からは見えません (もちろん、譲渡もできません)。

Git のタグは、他のオブジェクト (通常はタグ・オブジェクトで、そのオブジェクトはコミットを指す) への固定 (定数) の名前付き参照であり、その参照は refs/tags/ 名前空間に格納されています。 デフォルトでは、リビジョンのセットを取得したりプッシュしたりするとき、git は自動的に取得・プッシュされるリビジョンを指すタグを取得・プッシュします。 それにもかかわらず、あなたは を制御することができます。 をある程度制御することができます。 どのタグが取得されるか またはプッシュされます。

Git は軽量タグ (コミットを直接指す) と注釈付きタグ (タグオブジェクトを指す。オプションで PGP 署名を含むタグメッセージを含み、それがコミットを指す) をわずかに異なって扱います。たとえばデフォルトでは、 "git describe" でコミットを記述するときに注釈付きタグだけを考慮します。

Git には Mercurial のローカルタグに相当する厳密なものはありません。とはいえ、git のベストプラクティスでは、公開リポジトリを別に用意し、そこに自分の変更をプッシュし、他の人がそこからクローンしたりフェッチしたりすることを推奨しています。これは、あなたがプッシュしないタグ(とブランチ)は、あなたのリポジトリでは非公開であることを意味します。一方、名前空間として heads , remotes または tags のように、例えば local-tags はローカルタグの場合です。

個人的な意見です。 私の意見では、タグはリビジョングラフの外側にあるべきです。なぜなら、タグはリビジョングラフの外部だからです(リビジョングラフへのポインタです)。 タグはバージョン管理されませんが、転送可能であるべきです。 Mercurial がファイルを無視するのと同じようなメカニズムを使うということは、タグをリビジョングラフの外側に置くか、リビジョングラフの外側に置くかしなければならないことを意味します。 .hgtags を特別に扱うか(ツリー内のファイルは転送可能だが、通常のファイルはバージョン管理される)、ローカルにしかないタグを持つ必要があります ( .hg/localtags はバージョン管理されていませんが、転送はできません)。

ブランチ

Git では ローカルブランチ (branch tip, or branch head) はコミットへの名前付き参照で、新しいコミットを成長させることができます。 ブランチは、アクティブな開発ライン、すなわちブランチチップから到達可能なすべてのコミットを意味することもあります。 ローカルブランチは refs/heads/ 名前空間に存在します。したがって、たとえば 'master' ブランチの完全修飾名は 'refs/heads/master' となります。

Git における現在のブランチ (チェックアウトされたブランチ、および新しいコミットが行われるブランチを意味します) は、HEAD によって参照されるブランチとなります。シンボリック参照ではなく、コミットを直接指している HEAD を持つこともできます。 分離されたHEAD と呼ばれます ("git branch" は「(ブランチなし)」であることを示します)。

Mercurial では、匿名ブランチ (ブランチヘッド) があり、ブックマーク (via. ブックマークエクステンション ). このような ブックマーク分岐 は純粋にローカルなものであり、これらの名前は (バージョン 1.6 までは) Mercurial を使って転送することができませんでした。rsync や scp を使って .hg/bookmarks ファイルをリモートリポジトリにコピーできます。また hg id -r <bookmark> <url> を使って、ブックマークの現在のティップのリビジョン ID を取得することもできます。

1.6以降、ブックマークはプッシュ/プルすることができるようになりました。そのため ブックマークエクステンション のページには リモートリポジトリを利用する . Mercurialのブックマークは、以下のような違いがあります。 グローバル であるのに対し、Git における 'リモート' の定義では ブランチ名のマッピング を、リモートリポジトリの名前からローカルのリモート追跡ブランチの名前へのマッピングとして記述します。 refs/heads/*:refs/remotes/origin/* のマッピングは、リモートリポジトリの 'master' ブランチ ('refs/heads/master') の状態を 'origin/master' リモートトラッキングブランチ ('refs/remotes/origin/master') で見つけることができることを意味します。

Mercurial には、いわゆる 名前付きブランチ というものがあり、ブランチ名は 埋め込み である場合、ブランチ名はコミット内(チェンジセット内)に埋め込まれます。 そのような名前はグローバルです(フェッチ時に転送されます)。 それらのブランチ名は、チェンジセットのメタデータの一部として永久に記録されます。 最近の Mercurial では、 "named branch" を閉じると、ブランチ名の記録を止めることができます。 この仕組みでは、ブランチのチップはその場で計算されます。

Mercurialの"named branches"は、私の意見では コミットラベル の代わりに、それがそうであるためです。 名前付きブランチが複数の先端を持ち(複数の子なしコミット)、リビジョンのグラフのいくつかの不連続な部分から構成されることがある、という状況があります。

Git には Mercurial の "embedded branches" に相当するものはありません。さらに Git の哲学は、ブランチがあるコミットを含むと言うことはできても、あるコミットがあるブランチに属するということは意味しないということです。

Mercurial のドキュメントでは、少なくとも長期間のブランチ(リポジトリごとに1つのブランチというワークフロー)については、別々のクローン(別々のリポジトリ)を使うことが提案されていることに注意してください、別名 クローンによるブランチ作成 .

プッシュ時のブランチ

Mercurial はデフォルトでプッシュする すべてのヘッド . もし、単一のブランチをプッシュしたい場合 ( 単一ヘッド ) を使用する場合、プッシュしたいブランチの tip リビジョンを指定する必要があります。 ブランチの tip は、リビジョン番号(リポジトリにローカル)、リビジョン識別子、ブックマーク名(リポジトリにローカル、転送されない)、埋め込みブランチ名(名前付きブランチ)などで指定できます。

私が理解する限り、Mercurial の用語で "名前付きブランチ" にあるとマークされたコミットを含むリビジョンの範囲をプッシュすると、プッシュ先のリポジトリにこの "名前付きブランチ" が存在することになります。 つまり、このような埋め込みブランチの名前("named branches")は グローバル (指定されたリポジトリ/プロジェクトのクローンに関して) です。

デフォルトでは、( push.default 設定変数に従う) "git push" または "git push < リモート >" Git は以下のようにプッシュします。 一致するブランチ つまり、ローカルブランチのうち、プッシュ先のリモートリポジトリに同等のものがすでに存在しているものだけをプッシュします。 このとき --all オプションを使用すると ("git push --all") すべてのブランチ を使用すると、"git push < リモート > < ブランチ >" をプッシュします。 与えられたシングルブランチ で、"git push < を使うことができます。 リモート > HEAD"を使ってプッシュします。 現在のブランチ .

上記はすべて、Git がどのブランチをプッシュするのかを remote.<remotename>.push 設定変数でプッシュするブランチを設定していないことを前提としています。

フェッチにおけるブランチ

注意してください。 ここでは Git の用語を使っています。 がない場合です。 その変更をローカルの作業と統合することです。 これが、" git fetch "および" hg pull "しています。

もし私が正しく理解しているなら、デフォルトで Mercurial がフェッチするのは 全頭 をリモートリポジトリから取得しますが、 " で取得するブランチを指定することができます。 hg pull --rev <rev> <url> または、" hg pull <url>#<rev> "を取得するために シングルブランチ . リビジョン識別子、quot;named branch"(変更履歴に含まれるブランチ)名、ブックマーク名で <rev> を指定することが可能です。 ただし、ブックマーク名は(少なくとも現時点では)転送されません。 取得したすべての "名前付きブランチ" リビジョンは転送されます。 "hg pull" は取得したブランチの先端を、匿名、無名のヘッドとして保存します。

Git ではデフォルトで("git clone" で作成された 'origin' リモート、および "git remote add" で作成されたリモートに対して) " git fetch "(あるいは " git fetch <remote> ")を取得します。 すべてのブランチ をリモートリポジトリから ( refs/heads/ 名前空間) に格納し、それらを refs/remotes/ 名前空間に保存されます。 これは、たとえばリモート 'origin' にある 'master' という名前のブランチ (完全な名前は 'refs/heads/master') は 'origin/master' として保存されることを意味します。 リモート追跡ブランチ (フルネーム: 'refs/remotes/origin/master') として保存されることを意味します。

を取得することができます。 シングルブランチ を使うことで、Git で git fetch <remote> <branch> - Git は要求されたブランチを FETCH_HEAD に格納します。これは Mercurial の unnamed head と似たようなものです。

これらは、強力な refspec Git 構文:refspecs を使うと、どのブランチを取得し、どこに保存するかを指定したり設定したりすることができます。 例えば、デフォルトの "fetch all branches" の場合は '+refs/heads/*:refs/remotes/origin/*' ワイルドカード refspec で表され、 "fetch single branch" は 'refs/heads/<branch>' と省略されています。 Refspecは、リモートリポジトリのブランチ(ref)の名前をローカルのrefの名前にマップするために使用されます。 しかし、(主に "git remote" コマンドのおかげで)Git で効果的に作業するために refspecs について知る必要は(あまり)ないのです。

個人的な意見です。 Mercurial の "named branches" (チェンジセットメタデータにブランチ名を埋め込む) は、グローバル名前空間による誤ったデザインだと個人的には思っています。 分散型 バージョン管理システムにとっては特にそうです。 たとえば、アリスとボブの二人が、それぞれのリポジトリに 'for-joe' という名前の "named branch" を持っている場合を考えてみましょう。しかし、Joeのリポジトリでは、この2つのブランチは1つのブランチとして扱われます。そこで、ブランチ名の衝突を防ぐための規約を作ることになりました。これはGitでは問題ありません。Joeのリポジトリでは、Aliceからの'for-joe'ブランチは'alice/for-joe'となり、Bobからのそれは'bob/for-joe'となります。以下もご覧ください。 ブランチ名とブランチ ID の分離 という Mercurial wiki で提起された問題も参照してください。

Mercurial の "bookmark branches" は現在、インコアディストリビューションメカニズムを欠いています。

相違点

このあたりは、MercurialとGitの大きな違いのひとつである james woodyatt スティーブ・ロッシュ はその答えの中でこう述べています。Mercurialは、デフォルトで、匿名の軽量コードラインを使用します。 Gitは、軽量な名前付きブランチを使い、リモート・リポジトリのブランチの名前をリモート・トラッキング・ブランチの名前に対応させるための射影マッピングを使います。 Git は、ブランチに名前を付けることを強制します(ただし、デタッチド HEAD と呼ばれる単一の無名ブランチは例外です)が、これはトピックブランチワークフローのようなブランチを多用するワークフロー、つまり単一のリポジトリパラダイムに複数のブランチを持つ場合にうまく機能すると私は考えています。

リビジョンの命名

Git では、リビジョンに名前をつける方法がたくさんあります(たとえば git rev-parse のマニュアルページで説明されています)。

  • 完全な SHA1 オブジェクト名 (40 バイトの 16 進文字列)、またはその部分文字列で、リポジトリ内で一意であるもの。
  • シンボリックな参照名、例えば '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' などの特別な構文が用意されています。

Mercurial はまた、チェンジセットの命名方法をたくさん持っています(例えば hg のマニュアルページで説明されています)。

  • 単純な整数はリビジョン番号として扱われます。 リビジョン番号は 与えられたリポジトリにローカル であり、他のリポジトリでは異なる場合があることを覚えておく必要があります。
  • 負の整数は、先端からの連続したオフセットとして扱われ、-1 は先端、-2 は先端より前のリビジョン、といった具合になります。また、それらは ローカル にもなります。
  • 一意のリビジョン識別子(40桁の16進数文字列)またはその一意の接頭辞。
  • タグ名(与えられたリビジョンに関連するシンボリック名)、またはブックマーク名(拡張子:与えられたヘッドに関連するシンボリック名、リポジトリにローカル)、または "named branch" (コミットラベル、 "named branch" によって与えられたリビジョンは与えられたコミットラベルのすべてのコミットの先端(子なしコミット)、そのような先端が複数ある場合は最大のリビジョン番号を持つもの)です。
  • 予約名 "tip" は、常に最新のリビジョンを識別する特別なタグです。
  • 予約名 "null"は、null リビジョンを表します。
  • 予約名 "."は作業ディレクトリの親を表します。

差分

上のリストを見比べてわかるように、Mercurial はリポジトリにローカルなリビジョン番号を提供しますが、Git はそうではありません。 一方、Mercurial は相対オフセットを 'tip' (現在のブランチ) からのみ提供し、これはリポジトリにローカルなものです (少なくとも ParentrevspecExtension がない場合)、Git ではどの tip からでも続くコミットを指定することができます。

最新のリビジョンは、Git では HEAD、Mercurial では "tip" と名付けられます; Git では null リビジョンは存在しません。Mercurial と Git はどちらも多くのルートを持つことができます(複数の親なしコミットを持つことができます; これは通常、以前は別々のプロジェクトが結合した結果です)。

こちらもご覧ください。 多くの異なる種類のリビジョン指定子 の記事は、Elijah's Blog (newren's)に掲載されています。

個人的な意見です。 私が思うに 改訂数 は過大評価されていると思います (少なくとも分散開発および/または非線形/枝分かれした歴史のために)。 まず、分散バージョン管理システムでは、リポジトリにローカルであるか、中央の番号付け機関として特別な方法でいくつかのリポジトリを扱う必要があります。 第二に、より大きなプロジェクト、より長い歴史、5桁の範囲のリビジョン数を持つことができるので、6-7文字のリビジョン識別子に短縮するよりもわずかな利点しかなく、リビジョンが部分的にのみ順序付けられる一方で、厳格な順序付けを意味します(ここでは、リビジョンnとn+1が親と子でなくてもいいという意味です)。

リビジョンの範囲

Git では、リビジョンの範囲は トポロジカル . よく見られる A..B 構文は、線形履歴の場合、A から始まり (ただし A を除く)、B で終わる改訂範囲を意味します (すなわち、範囲は 以下から開く の略語(構文解析)です。 ^A B の略語で、履歴をたどるコマンドでは A から到達可能なものを除き、B から到達可能なすべてのコミットを意味します。 A..B の範囲の挙動は、AがBの祖先でない場合でも完全に予測可能(かつ非常に有用)であることを意味します。 A..B は、AとBの共通の祖先(マージベース)からリビジョンBまでのリビジョンの範囲を意味します。

Mercurial では、リビジョン範囲は リビジョン番号 . 範囲指定は A:B 構文で指定し、Gitとは逆にrangeは 閉じた間隔 . また、範囲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 という記法があります。これは、リビジョンの対称的な差分を意味します。 A B --not $(git merge-base A B) を意味します。これは、AまたはBのどちらかから到達できるすべてのコミットを意味しますが、両方から到達できるすべてのコミット(共通の祖先から到達できるもの)は除きます。

名前の変更

Mercurial の使用法 リネームトラッキング を使っています。 これは、ファイル名が変更されたという情報がコミット時に保存されることを意味します。Mercurial では、この情報は "enhanced diff" 形式で ファイルログ (file revlog) メタデータに &qu;enhanced diff" という形で保存されます。 この結果、ファイル名を変更するために hg rename / hg mv ... あるいは hg addremove を実行して類似度ベースのリネーム検出を行う必要があります。

Git はバージョン管理システムの中ではユニークなものです。 リネーム検出 を使うという点です。 これは、ファイル名が変更されたという事実が、必要な時に検出されることを意味します:マージを行う時や、(要求/設定されている場合)差分を表示する時です。 これは、リネーム検出アルゴリズムの改良が可能で、コミット時に凍結されないという利点があります。

Git と Mercurial はどちらも --follow オプションを使ってリネームを追跡する必要があります。 どちらも、ファイルの行単位の履歴を表示する際に、リネームに従うように git blame / hg annotate .

Git では git blame コマンドはコードの動きを追うことができます。たとえコードの動きが健全なファイル名の変更に含まれないとしても、あるファイルから別のファイルへコードを移動(あるいはコピー)することもできます。 私が知る限り、この機能はGitにしかありません(執筆時、2009年10月)。

ネットワークプロトコル

Mercurial と Git はどちらも、同じファイルシステム上のリポジトリからの取得と、リポジトリへのプッシュをサポートしています。 また、両者とも バンドルファイル .

Mercurial は SSH と HTTP プロトコルを使っての取得とプッシュをサポートしています。 SSH の場合は、接続先のマシンにアクセス可能なシェルアカウントと、hg のコピーがインストールされている/利用可能である必要があります。 HTTP アクセスの場合は hg-serve または Mercurial CGI スクリプトの実行が必要で、Mercurial がサーバーマシンにインストールされている必要があります。

Git は、リモートリポジトリにアクセスするために使われる 2 種類のプロトコルをサポートしています。

  • スマートプロトコル SSH 経由のアクセスや、カスタム git:// プロトコル (by git-daemon による)、サーバーに git がインストールされていることが必要です。 これらのプロトコルでのやり取りは、クライアントとサーバーが、どのようなオブジェクトを共通に持っているかについて交渉し、パックファイルを生成して送信することで成り立っています。 最近の Git には、HTTP プロトコルの "smart" に対するサポートが含まれています。
  • dumb" プロトコル は、HTTP や FTP (取得のみ)、HTTPS (WebDAV によるプッシュ) など、サーバーに git がインストールされている必要はありませんが、リポジトリに git update-server-info (通常はフックから実行されます) によって生成された余分な情報がリポジトリに含まれている必要があります。 クライアントはコミットチェーンをたどり、必要に応じてルースオブジェクトやパックファイルをダウンロードする、というやりとりをします。 欠点は、厳密に必要なものよりも多くダウンロードすることです(たとえば、単一のパックファイルしかないコーナーケースでは、わずかなリビジョンをフェッチするときでも全体をダウンロードすることになります)、そして、終了するために多くの接続を必要とする可能性があることです。

拡張: スクリプト可能性と拡張機能 (プラグイン) の比較

Mercurial は Python で実装されていますが、性能のために一部のコアコードは C で書かれています。 を書くための API を提供しています。 拡張機能 (プラグイン) を書くための API を提供しています。 ブックマークブランチやリビジョンへの署名など、一部の機能は Mercurial と一緒に配布されるエクステンションで提供され、それを有効にする必要があります。

Git は C , Perl そして シェルスクリプト . Git には、多くの低レベルのコマンド ( 配管 ) を提供しています。これは、スクリプトで使用するのに適しています。 新しい機能を導入する通常の方法は、Perlやシェルスクリプトで書き、ユーザーインターフェースが安定したら、パフォーマンスや移植性、シェルスクリプトの場合はコーナーケースの回避のためにCで書き直すことです(この手続きは ビルトイン化 ).

Gitは、[リポジトリ]フォーマットと[ネットワーク]プロトコルに依存し、それを中心に構築されています。 言語バインディングの代わりに、(部分的または完全な) 再実装 があります(一部は部分的な再実装で、一部はgitコマンドのラッパーです)。JGit (Java, Eclipse Git Plugin の EGit で使用), Grit (Ruby), Dulwich (Python), git# (C#).


TL;DR