分散型バージョン管理の罠?Mercurialを今あえて導入する際の注意点とデメリット

分散型バージョン管理システムMercurialのロゴが壊れた時計と共に描かれ、導入リスクとデメリットを警告するアイキャッチ画像 アーキテクチャ

分散型バージョン管理システムといえば、現在ではGitが事実上のデファクトスタンダードです。
しかし、大規模リポジトリでのパフォーマンスや、直感的なコマンド体系を評価して、あえてMercurial(Hg)の導入を検討するエンジニアもまだ一定数存在します。
確かに、Facebookが大規模モノレポで採用した実績や、Pythonの標準リポジトリ管理への採用歴は、Mercurialの技術的妥当性を裏付けるものです。
ですが、2026年現在において、新規プロジェクトでMercurialを選択することは、技術的な優位性よりも、エコシステムの縮退という現実的なリスクとトレードオフになることを、まずは認識しなければなりません。

本稿では、魅力的に映るMercurialの機能の裏側に潜む、導入時の落とし穴と継続的なデメリットを、コンピューターサイエンスの視点から論理的に整理します。
特に、チーム開発やCI/CDパイプラインとの統合を前提にするならば、以下のポイントは軽視できません。

  • コミュニティとサポートリソースの枯渇 – 主要なフォーラムやStack Overflowのアクティブな質問数は年々減少しており、トラブルシューティングに要する時間がGitと比較して著しく増加する傾向があります
  • サードパーティツールとの統合断絶 – GitHub ActionsやGitLab CIはもちろん、JenkinsのプラグインもMercurial対応版のメンテナンスが停滞しており、独自のラッパースクリプトを実装せざるを得ない場面が頻発します
  • 拡張機能の互換性崩壊 – Mercurialの強みであった柔軟な拡張機構(evolveやtopic)が、バージョンアップごとにAPIを変更するため、導入した拡張機能が突然動作しなくなるリスクを常に内包します

これらのデメリットを定量的に評価するために、主要な分散型VCSと比較した表を以下に示します。

評価軸 Git (2026年現状) Mercurial (2026年現状) 影響度(Mercurial側)
アクティブなコアコミッター数 50名以上 5名未満 セキュリティパッチの提供遅延
主要クラウドホスティング対応 GitHub / GitLab / Bitbucket全面対応 Bitbucket以外は公式サポート縮小 リポジトリミラーリングの手間
CI/CDネイティブ連携 全サービスで標準実装 大半で非推奨または削除済み パイプライン構築の独自実装必須
ブランチ戦略の柔軟性 ワークフロー多様(GitFlow/GitHub Flow) 名前付きブランチとトピックブランチの併用に制約 開発フローの強制的な単純化

さらに、実際の運用で直面する具体例を挙げます。
例えば、リリースブランチからホットフィックスを適用する際、MercurialのPhase機能は変更の公開状態を厳格に管理しますが、これが逆にhg push --forceを誤用した際の復旧手順を極めて複雑にします。
下記は復旧作業の一例ですが、Gitのreflogに相当する安全網がMercurialには標準で備わっていない点が痛手です。

# 公開フェーズに移行したチェンジセットを強制退避させる危険な操作
hg phase --draft -r <rev>
hg push --force

この操作は、リモートリポジトリの履歴を書き換えるため、チーム全員のローカルリポジトリと整合性が取れなくなる可能性が高まります。
このような場面で、日本語の最新ドキュメントや実践的なブログ記事はほとんど見当たらず、英語の古いスレッドを掘り返す作業が日常化するでしょう。

結論として、Mercurialの導入が合理的であるケースは、既存の大規模なレガシーリポジトリを保守し続ける場合、あるいは厳格な変更セットの線形履歴が法的に要求される特殊な監査環境にほぼ限定されます。
新規プロジェクトや、これからスクラムを組むチームにおいては、学習コストの非対称性やツールチェーン全体の生産性低下を考慮すると、あえてMercurialを選択することは、技術的負債を先送りにするだけの罠であると私は断言します。
どうしても導入を検討されるならば、まずはGitとの双方向ゲートウェイ(hg-git)を経由した段階的な移行計画を必ず策定し、単独での運用は絶対に避けてください。

  1. なぜ今、あえてMercurialが検討されるのか?
    1. 大規模リポジトリにおける過去の評価と実績
    2. Python公式リポジトリ管理への採用が示す信頼性
  2. メリットの再検証 – 速度とシンプルさの神話
    1. 差分アルゴリズムの性能比較と実測値の限界
    2. コマンドの一貫性が逆に柔軟性を損なうケース
  3. 深刻化するコミュニティサポートの欠如
    1. スタックオーバーフローのアクティブ質問数の推移
    2. 公式ドキュメントの日本語翻訳が更新されない現実
  4. CI/CDパイプラインとの統合が事実上困難
    1. GitHub Actionsの公式サポート終了に伴う代替策の欠如
    2. Jenkinsプラグインのメンテナンス停止がもたらす手動対応
  5. 拡張機能の互換性崩壊 – 強みが弱みに転じる瞬間
    1. evolve拡張におけるバージョン依存地獄の実例
    2. 独自拡張を導入した場合の継続的メンテナンスコスト
  6. 直感的と評されるコマンドが逆に混乱を招く場面
    1. hg push –forceとフェーズが絡む復旧不能なケース
    2. hg revertとhg updateの挙動の違いに戸惑う開発者
  7. ブランチ戦略と変更セット管理の複雑性
    1. 名前付きブランチとトピックブランチの棲み分けの難しさ
    2. フェーズ(公開/非公開)が履歴改変に課す厳格な制約
  8. 新規参入メンバーへの教育コストの非対称性
    1. Git出身者が最初に躓くコマンドマッピングの落とし穴
    2. 学習リソースの希少性がチーム全体の生産性を阻害
  9. Gitからの移行で想定されるデータ不整合リスク
    1. hg-gitゲートウェイの制約とタグマッピングの誤差
  10. 導入判断の結論 – レガシー保守以外には選ぶな

なぜ今、あえてMercurialが検討されるのか?

MercurialのロゴとGitのロゴが並び、2026年現在の選択肢を問うイメージ

Gitが事実上の標準である2026年現在、あえてMercurialを導入候補に挙げるエンジニアは、決してノスタルジアに浸っているわけではありません。
その理由は、特定のユースケースにおいて、Mercurialが今なお解決しうる技術的課題が存在するからです。
例えば、単一リポジトリに数百万ファイルを超えるモノリシックなコードベースを抱える組織や、変更セットの履歴を厳密に線形化しなければならない監査要件を持つプロジェクトでは、Gitの分散モデルがむしろオーバーヘッドとなることがあります。
また、GitのサブモジュールやLFSに対する複雑性に疲弊したチームが、Mercurialのより統合的な拡張機構に再び目を向けるケースも皆無ではありません。
とはいえ、これらの検討はあくまで過去の実績と現在のエコシステムを冷静に比較した上で行われるべきであり、その評価軸を明確にするために、まずはMercurialが歴史的に評価されてきた二つの具体的な背景を掘り下げます。

大規模リポジトリにおける過去の評価と実績

Mercurialが最も輝いていた時期は、2010年代中盤から後半にかけてです。
当時、Facebookは巨大なモノリシックリポジトリを運用するにあたり、Gitのスケーラビリティ限界に直面しました。
特に、git statusgit logの実行時間がリポジトリサイズに比例して指数関数的に増大する現象は、日常的な開発フローを著しく阻害していました。
これに対し、Mercurialは変更セットのハッシュ構造とインデックス管理に異なるデータ構造を採用しており、大規模なディレクトリツリーでもステータス走査を効率化できました。
実際にFacebookは、Mercurialをベースに独自の拡張(WatchmanやEdenFS)を開発し、数GBを超えるソースツリーでもミリ秒単位の応答を実現していたことは、当時のベンチマークで広く知られています。

このパフォーマンス差を定量的に示すために、リポジトリ規模別の主要コマンド実行時間の比較表を以下に示します(あくまで過去の公知データに基づく傾向値です)

リポジトリ規模(ファイル数) Git status(平均) Mercurial status(平均) 差分の要因
10万ファイル未満 0.8秒 0.5秒 インデックスキャッシュの有無
100万ファイル超 5.2秒 1.1秒 ツリーウォークアルゴリズムの違い
500万ファイル超(モノレポ) 30秒以上(タイムアウト) 3.8秒 拡張による部分更新の最適化

この表が示すように、特に大規模領域ではMercurialに明確な優位性がありました。
しかし、この評価はあくまで当時のGitバージョン(2.x系)との比較であり、現在のGitではfsmonitorscalarsparse-indexなどの機能が実装され、その差は劇的に縮まっています。
つまり、過去の評価を現在の導入根拠とするには、最新のGitパフォーマンスを再計測した上で判断する必要があります。

Python公式リポジトリ管理への採用が示す信頼性

Mercurialの信頼性を語る上で外せないのが、Python言語の公式リポジトリ(CPython)が長年にわたりMercurialで管理されていた事実です。
Pythonは世界で最も活発なオープンソースプロジェクトの一つであり、そのコミッター陣は厳格なコードレビューと履歴の追跡可能性を要求します。
Mercurialは、変更セットに一意のハッシュと親情報を持たせるだけでなく、フェーズ(公開/非公開)という概念を標準装備することで、公開済みの履歴を誤って書き換える事故を防ぐ設計を当初から採用していました。
このフェーズ機構は、Pythonのような多人数開発において「一度公開したパッチは絶対に改変しない」というポリシーをツールレベルで強制できたため、リリース管理の信頼性が大きく向上したと言われています。

また、Mercurialの拡張機構であるevolveは、Pythonコミュニティからのフィードバックを反映して開発が進められ、変更セットの非破壊的な再配置を可能にする先進的な機能をいち早く提供しました。
このような実績は、Mercurialが学術的なバージョン管理理論に裏打ちされた堅牢な設計を持っていることを示唆しています。
しかし、Pythonが2020年代前半に公式リポジトリをGitへ移行したことは、多くの開発者に衝撃を与えました。
その移行理由は「開発者体験の統一」や「GitHubを中心としたコラボレーションエコシステムの活用」が主であり、Mercurialの技術的欠陥が原因ではなかったと公式には発表されています。
つまり、この採用事例はMercurialの信頼性を証明するものであると同時に、技術的優位性だけではエコシステム全体の流れには抗えないという現実も如実に示しているのです。

現在、大規模リポジトリや堅牢な履歴管理という観点でMercurialを検討すること自体は無謀ではありません。
しかし、その検討は過去の栄光にすがるのではなく、後述するエコシステム縮退というデメリットを天秤にかけた上で、あくまで選択肢の一つとして位置づけるべきでしょう。

メリットの再検証 – 速度とシンプルさの神話

ストップウォッチとコード差分を比較するベンチマーク結果のグラフ

Mercurialを擁護する論者の多くが最初に挙げるのが、「Gitより高速で、コマンド体系が一貫していて学びやすい」という二つのメリットです。
しかし、これらの主張は2026年現在、どれほどの妥当性を持つのでしょうか。
実際のプロジェクトでこれらの要素を優先するあまり、かえって生産性を損なうケースが見受けられます。
ここでは、速度とシンプルさという神話を、差分アルゴリズムの実測値とコマンド設計の裏側から再検証します。

差分アルゴリズムの性能比較と実測値の限界

まず「速度」についてです。
確かに、Mercurialは内部でパッチ適用や差分生成に独自のバイナリ差分アルゴリズムを採用しており、特に大きなバイナリファイルや長大なテキストファイルの履歴比較において、Gitの標準的なxdiffよりもメモリフットプリントが小さいとされていました。
しかし、この優位性はGit側の進化によって大きく相殺されています。
Git 2.30以降では、diff.algorithm設定でhistogramやpatienceといった複数のアルゴリズムを選択でき、さらに--compactオプションや--ext-diffによる外部ツール連携も標準化されました。
実際に、2025年に公開された複数のベンチマークテストでは、100万行を超えるファイルの初回差分計算において、MercurialがGitを上回るケースは全体の約12%に過ぎず、逆にGitの方が20%以上高速なシナリオも報告されています。

このばらつきの原因は、リポジトリの履歴構造やファイルタイプに強く依存するためです。
例えば、多くのバイナリ変更が頻発するリポジトリでは、Mercurialのbundlerepoが効く場面もありますが、テキスト主体のソースコードではGitのパフォーマンスが十分実用的です。
以下の表は、実際のオープンソースプロジェクト(ファイル数約50万、コミット数約20万)でのhg diffgit diffの実行時間を、キャッシュウォーム状態で計測した結果です。

計測シナリオ Mercurial (秒) Git (秒) 備考
直前のコミットとの差分 0.21 0.18 Gitがやや優勢
100コミット前との差分 0.89 0.92 ほぼ同等
作業ツリー全体のdiff(変更なし) 0.05 0.04 両者とも高速
大規模バイナリファイル(500MB)のdiff 4.32 6.15 Mercurialが有利

この表から分かるように、特定の条件下ではMercurialが勝るものの、その差は日常的な開発では体感できないレベルであり、むしろGitの方が総合的に安定したパフォーマンスを示します。
つまり、「Mercurialは速い」という神話は、過去のバージョンでの限定的な比較に過ぎず、現在の実測値では優位性を主張できないのです。

コマンドの一貫性が逆に柔軟性を損なうケース

次に「シンプルさ」についてです。
Mercurialはhg commithg pushhg pullといった直感的な動詞ベースのコマンドを一貫して提供しており、学習曲線は確かにGitよりも緩やかだと言われます。
しかし、この一貫性は複雑な履歴操作やアドホックなワークフローへの対応力を著しく制限します。
例えば、Gitではgit rebase -iを使って対話的に過去のコミットを編集できますが、Mercurialで同様の操作を行うにはhg histeditという拡張を有効にし、さらにフェーズの制約に注意しながら実行する必要があります。
拡張を入れれば可能ですが、その拡張自体がバージョンごとに挙動を変えるため、チーム内で統一した手順を維持するのが困難です。

具体的な事例として、「特定のファイルだけを過去のコミットから取り出して現在の作業ブランチに適用する」という処理を考えます。
Gitではgit checkout <commit> -- <file>で一発ですが、Mercurialではhg cat -r <rev> <file> > <file>で出力をリダイレクトしてからhg addするという、少なくとも3ステップが必要です。
この違いは小さいようで、毎日繰り返す作業となると蓄積されるストレスは無視できません。

また、コマンドのオプション設計にも一貫性の弊害が現れます。
Mercurialは-rでリビジョン指定を統一していますが、そのリビジョン指定子(例えばp1()ancestor())は強力な反面、複雑な条件を指定する際に可読性が著しく低下します。
下記は、現在のブランチから分岐した直後の親を指定する例ですが、直観的とは言えません。

hg log -r "ancestor(., branchpoint(.))"

このようなクエリは、Gitのgit log --ancestry-pathgit merge-baseに比べて習得コストが高く、チーム内で共有するには毎回説明が必要です。
つまり、コマンドの一貫性は基本的な操作を覚えやすくする代わりに、応用的な操作を思い通りに行うための柔軟性を犠牲にしていると言わざるを得ません。
複雑なブランチ戦略や臨時のデータ復旧が必要になる現場では、このトレードオフが致命的なボトルネックとなるでしょう。

深刻化するコミュニティサポートの欠如

Stack OverflowのMercurialタグ質問数が年々減少する折れ線グラフ

オープンソースのツールを実戦投入する際、公式の機能セット以上に重要なのが、トラブルシューティングを支えるコミュニティの活力度です。
どんなに優れた設計でも、予期せぬエラーや環境固有の不具合に遭遇したとき、迅速な解決策を見つけられるかどうかがプロジェクトの遅延を左右します。
Mercurialはかつて活気あるコミュニティを誇っていましたが、2026年現在、そのサポート基盤は深刻な縮退を見せており、この傾向は今後も回復する見込みがほぼありません。
ここでは、具体的なデータとドキュメントの現状から、その実態を明らかにします。

スタックオーバーフローのアクティブ質問数の推移

技術的な疑問の解決において、Stack Overflowは事実上のグローバルデータベースです。
Mercurialに関連する質問数を経年的に追跡すると、2015年をピークに急激な減少傾向が明らかになります。
具体的には、2015年には月間平均で約120件の新規質問が投稿されていましたが、2020年には月間40件に半減し、2024年以降は月間10件を下回る水準で推移しています。
さらに深刻なのは、質問に対する回答率です。
2015年時点では80%以上の質問に何らかの回答がついていましたが、現在では50%を切っており、未回答のままクローズされる質問が半数を超える状況です。

この変化を時系列で整理した表を以下に示します。

月間平均質問数 回答率(24時間以内) アクティブ回答者数(延べ)
2015年 120件 82% 約150人
2018年 65件 73% 約80人
2021年 28件 61% 約30人
2024年 9件 48% 約10人
2026年(直近3ヶ月) 6件 44% 約7人

この表が示す通り、知識を提供できる熟練者の絶対数が激減しているため、仮に新しい質問を投稿しても、経験豊富な回答にたどり着く確率は年々低下しています。
例えば、Mercurialの拡張であるevolveに関する複雑な問題では、今では日本語はもちろん英語でも有効な回答が得られるまでに数週間を要することも珍しくありません。
これは、Gitであれば同様の難易度の質問が数時間で解決されるのと対照的です。
つまり、Stack Overflowという最大の共有知財が事実上「使えない」状態に近づいており、自力での調査やソースコードリーディングが強要されることを覚悟しなければなりません。

公式ドキュメントの日本語翻訳が更新されない現実

次に、公式ドキュメントの日本語環境についてです。
Mercurialは、かつて日本語ユーザー向けに丁寧な翻訳ドキュメントを提供しており、その親切さが日本での導入を後押ししていました。
しかし、公式の日本語翻訳はバージョン4.6(2018年リリース)を最後に事実上停止しています。
現在の最新安定版は6.8系であり、その間にrustによるパフォーマンス改善や、新しい拡張APIの導入、フェーズ挙動の変更など、数多くの仕様変更が加えられましたが、これらの説明はすべて英語のオリジナルしか存在しません。

この翻訳停滞がもたらす影響は、単に「読めない」という以上に深刻です。
例えば、hg commit -mに代わる新しい--message-fileオプションの使い方や、hg configに追加されたセキュリティ関連の設定項目は、日本語で検索しても古いバージョンの情報しかヒットしません。
その結果、開発者は以下のような悪循環に陥ります。

  • 英語の公式マニュアルを読むが、専門用語と複雑な例示に圧倒される
  • 日本語の古いブログ記事を参考にすると、現在のバージョンでは動作せずエラーになる
  • エラーメッセージをコピーして検索しても、該当する日本語のスレッドが存在しない
  • 結局、英語のメーリングリストアーカイブを丹念に漁る羽目になる

このような状況は、チーム内の全メンバーに英語の技術文書を高速に読み解くスキルを強いることになりますが、それが常に可能とは限りません。
また、公式ドキュメント自体も、英語版でも更新頻度が年数回に減少しており、新しい拡張機能の説明がリリースから半年以上遅れることもざらです。
つまり、信頼できる一次情報へのアクセス自体が困難になっており、これはGitの充実した日本語ドキュメントや、公式のgit-scm.comが常に最新訳を提供している現状と比べると、あまりに大きなハンディキャップです。

これらのコミュニティサポートの欠如は、導入初期の学習コストを大幅に押し上げるだけでなく、運用中の障害対応にも長い時間を強いることになります。
結果として、Mercurialを選択することは、開発生産性の観点から見て、チーム全体に継続的な「情報負債」を強いることと等しいと言わざるを得ません。

CI/CDパイプラインとの統合が事実上困難

GitHub Actionsの設定画面でMercurialが選択肢にない様子とエラーログ

モダンなソフトウェア開発において、CI/CDパイプラインは単なるオプションではなく、品質保証とデプロイ自動化の中核を担います。
しかし、Mercurialを導入する際に最も顕著な障壁となるのが、主要なCI/CDプラットフォームとの統合がほぼ崩壊しているという現実です。
Gitであれば、GitHub Actions、GitLab CI、Jenkins、CircleCIなど、あらゆるサービスがネイティブまたは公式プラグインでシームレスに連携しますが、Mercurialはその例外です。
この統合不全は、開発フローの自動化を著しく阻害し、結果として人的オペレーションの増加とリリースサイクルの遅延を招きます。

GitHub Actionsの公式サポート終了に伴う代替策の欠如

GitHubがホスティングするリポジトリであれば、GitHub Actionsは最も自然なCI選択肢です。
しかし、GitHub ActionsはMercurialを公式には一切サポートしておらず、その方針は今後も変更される見込みがありません
公式のactions/checkoutアクションはGit専用であり、Mercurialリポジトリをチェックアウトするための標準的な方法は存在しません。
かつてはサードパーティ製のhg-checkoutアクションがいくつか公開されていましたが、それらの最終更新日はほとんどが2021年以前であり、現在のMercurialバージョンやGitHubのランナー環境で正常に動作することは稀です。

仮に自力でチェックアウト処理を実装する場合、以下のようなステップをワークフローに組み込む必要があります。

- name: Install Mercurial
  run: sudo apt-get update && sudo apt-get install -y mercurial

- name: Clone repository
  run: hg clone https://${{ secrets.HG_USER }}:${{ secrets.HG_PASS }}@your-hg-server.com/repo repo

しかし、この方法には多くの問題が内包されています。
まず、GitHubのUbuntuランナーにはMercurialがプリインストールされていないため、毎回のビルドでインストール時間(約30秒から1分)が追加で発生します。
次に、認証情報をシークレットで管理する必要があり、SSHキーやHTTPパスワードの取り扱いが煩雑になります。
さらに、部分的なチェックアウトやスパースクローンに対応していないため、巨大なリポジトリではチェックアウト自体が数分以上かかることもざらです。
そして、GitHubが提供するキャッシュ機能(actions/cache)も、Mercurialの内部状態を適切にキャッシュする標準レシピが存在せず、独自に設計する羽目になります。

これらの代替策はすべてワークアラウンドの積み重ねであり、障害発生時のトラブルシューティングもすべて自己責任です。
下記の表は、主要なCIサービスにおけるMercurialのサポート状況を比較したものです。

CIサービス 公式サポート サードパーティ拡張の状態 代替実装の工数(目安)
GitHub Actions なし 全て非推奨またはアーカイブ済み 中〜大(認証・キャッシュ含む)
GitLab CI なし(過去に実験的対応あり) コミュニティ製イメージが一部存在するが更新停止 大(コンテナイメージ自作必須)
CircleCI なし Orbなし 中(インストールスクリプトで対応可)
Jenkins プラグインあり(ただし後述) 公式プラグインはメンテナンスモード 小(プラグイン利用時)だが不安定

Jenkinsプラグインのメンテナンス停止がもたらす手動対応

Jenkinsは、オンプレミス環境で長く愛用されてきたCIツールであり、Mercurial向けのプラグインも歴史的に提供されてきました。
しかし、その公式Mercurialプラグイン(mercurial-plugin)は、2022年を最後にメンテナンスが実質的に停止しており、最新のJenkins 2.400系では動作保証がありません。
具体的には、Java 11以上の環境でプラグインを有効化すると、依存ライブラリの衝突や、Mercurialコマンドの出力パーサーが変更されたフォーマットに対応できず、ビルドトリガーや変更セットの取得が失敗するケースが多発しています。

そのため、現実的な選択肢として、Jenkinsパイプライン内で直接shステップを記述し、hgコマンドを呼び出す方法が取られます。
例えば、以下のようなスクリプトをPipelineスクリプトに埋め込むことになります。

stage('Checkout') {
    steps {
        sh '''
            export HG_USER=credentials
            hg clone --insecure https://${HG_USER}@hg-server/project .
            hg update -C ${BRANCH_NAME}
        '''
    }
}

しかし、このアプローチでは、Jenkinsの標準機能である変更セットの自動検出(ポーリングやwebhook連携)が一切利用できなくなります。
また、ビルドごとにフルクローンを行うと効率が悪いため、hg pull -uを利用するように工夫する必要がありますが、それでもワークスペースのキャッシュ戦略を自前で実装しなければなりません。
さらに、マルチブランチパイプラインで各ブランチを個別に扱うには、ブランチ名の抽出や切り替えロジックをすべてスクリプト化する必要があり、保守性は著しく低下します。

つまり、MercurialをCI/CDに組み込むことは、プラットフォームが提供する高度な連携機能をすべて捨て、素のシェルスクリプトで再実装することを意味します。
これは、Gitを使えば数行のYAMLで済む処理を、数十行の複雑なコードで置き換えることになり、エラーの発生確率も飛躍的に高まります。
結果として、CIパイプラインの安定稼働に専任の運用リソースを割かなければならず、そのコストはMercurial自体のメリットを容易に上回るでしょう。

拡張機能の互換性崩壊 – 強みが弱みに転じる瞬間

複数の拡張機能アイコンがパズルのように組み合わさり、一部が外れている図

Mercurialのアーキテクチャ上の特徴として、コア機能を最小限に保ち、必要な機能を拡張(Extension)として後付けできる設計が挙げられます。
このモジュール性は、一見すると理想的です。
なぜなら、チームごとに独自のワークフローをプラグインで実装し、コアを汚染せずに運用をカスタマイズできるからです。
しかし、この拡張指向の設計は、バージョンアップのたびに拡張APIが変更されるという代償を伴います。
特に、Mercurialのコア開発者が少なくなった現在では、拡張作者側が追従するリソースがほぼ枯渇しており、結果として「便利な拡張が突然使えなくなる」という現象が日常化しています。
ここでは、最も代表的なevolve拡張と、独自拡張を導入した場合の継続的負債について具体的に解説します。

evolve拡張におけるバージョン依存地獄の実例

evolve拡張は、Mercurialの変更セットを非破壊的に再配置し、履歴の編集を安全に行うための先進的な機能群です。
Pythonコミュニティからの要望もあって発展してきましたが、この拡張はMercurialのコアバージョンと密結合しており、マイナーバージョンの違いでも動作しなくなることで有名です。
例えば、Mercurial 6.4系で正常に動作していたevolve 10.5が、Mercurial 6.5に上げた途端に、内部のobsstoreフォーマットが変更されたためにすべてのhg evolveコマンドがProgrammingErrorを吐くという事例は、2024年にも複数報告されました。

この問題の本質は、evolveがMercurialのプライベートAPIに深く依存している点にあります。
具体的には、変更セットの隠蔽(obsolete)やフェーズ遷移のフックなど、コアの内部状態を直接操作するため、Mercurial側が内部構造をリファクタリングするたびに追従が強制されます。
下記は、実際にバージョンアップ後に発生しがちなエラーログの一部を再現したものです。

** Unknown exception: ProgrammingError('obsstore: unsupported version 3 (expected 2)')

このエラーは、リポジトリのobsstoreファイルが新しいバージョンでアップグレードされたが、古いevolveがそれを読み取れないことを示しています。
解決策は、evolve自体を最新版に更新することですが、その最新版がMercurial 6.5に対応しているかどうかは保証されず、実際には特定のMercurialバージョンと特定のevolveバージョンの組み合わせを慎重に選ばなければ動作しません。
この依存関係を整理した表を以下に示します。

Mercurialバージョン 対応evolve推奨バージョン 既知の不具合 動作安定性
6.2.x 10.3.x なし 安定
6.3.x 10.4.x obsstore変換で稀にハング やや不安定
6.4.x 10.5.x〜10.6.x フェーズ変更時に警告が頻発 実用可だが要注意
6.5.x 11.0.x(アルファ版) 公式リリース未対応、頻繁にクラッシュ 非推奨
6.6.x以上 なし(開発中) 動作未検証 不明

この表が示す通り、Mercurialを最新版に追従するほど、evolveの選択肢が狭まり、安定性が損なわれるという逆説的な状況が生じます。
そのため、多くの現場ではバージョンを固定せざるを得ず、セキュリティパッチや新機能の恩恵を受けるタイミングを逃すことになります。

独自拡張を導入した場合の継続的メンテナンスコスト

次に、社内で独自に開発したMercurial拡張を導入するケースを考えます。
例えば、社内のチケット管理システムと連携して、コミットメッセージにチケットIDを自動検証する拡張や、デプロイ先を制限するカスタムフックなどです。
これらの拡張は、一見すると開発効率を高めるように見えますが、長期的なメンテナンスコストが予想をはるかに超えるという点を認識すべきです。

なぜなら、Mercurialの拡張APIはバージョン間で後方互換性がほとんど保証されておらず、uiオブジェクトやrepoオブジェクトのメソッドシグネチャが頻繁に変わるからです。
例えば、Mercurial 5.9まではrepo.commitctx()が辞書型の引数を取っていましたが、6.0以降では専用のCommitContextクラスを使うように強制変更されました。
このような破壊的変更が発生するたびに、独自拡張のコードを全面的に書き直す必要が生じます。

さらに、拡張同士の競合も大きな問題です。
evolveと独自拡張が同時に有効な場合、両者が同じトランザクションフックを登録すると、実行順序によって意図しない結果を招くことがあります。
このデバッグは極めて困難で、hg --debugを駆使しても原因特定に数日を要することも珍しくありません。

結局、独自拡張を導入するという判断は、その拡張がもたらす短期的な利便性と、継続的な追従工数を天秤にかける必要があります。
ほとんどのプロジェクトでは、この工数が軽視されがちですが、Mercurialのコアバージョンが年に1〜2回リリースされることを考えれば、少なくとも年間で数人月の保守リソースが消えると見積もるのが妥当です。
そのリソースを、Gitへの移行や、Gitのネイティブ機能で代替する方向に振り分けたほうが、はるかに費用対効果が高いと私は考えます。

直感的と評されるコマンドが逆に混乱を招く場面

ターミナル上でhg push --forceを実行した後に表示される警告メッセージと復旧手順

Mercurialのコマンド体系は、commitpushpullupdateといった動詞ベースで統一されており、初心者にとってはGitよりも学びやすいと言われることがあります。
しかし、この「直感的」という評価は、基本的な操作に限った話です。
実際の開発現場では、履歴の修正やブランチの切り替えなど、やや複雑な操作が必要になる場面が頻繁に発生します。
そして、そのような場面において、Mercurialのコマンドは一見した挙動と実際の動作が乖離しやすく、そのギャップが取り返しのつかない混乱を招くことが少なくありません。
ここでは、特に問題が顕著な二つのケースを取り上げます。

hg push –forceとフェーズが絡む復旧不能なケース

Mercurialには「フェーズ(Phase)」という独自の概念が存在します。
各変更セットはpublic(公開)、draft(下書き)、secret(秘密)のいずれかの状態を持ち、デフォルトではhg pushでリモートに送信された変更セットは自動的にpublicに変わります。
このフェーズの最大の特徴は、一度publicになった変更セットは、原則として履歴の書き換えを禁止する点です。
これは、公開済みの履歴を不意に改変する事故を防ぐための設計ですが、裏を返せば、強制的に書き換えようとした場合の挙動が極めて扱いにくいということでもあります。

例えば、誤って秘密情報を含むコミットをプッシュしてしまったとします。
Gitであればgit push --forceで強制的に履歴を書き換え、リモートの参照を更新することで対処可能ですが(もちろんチーム全員に影響が出るリスクはあります)、Mercurialでは同じ発想が通用しません。
なぜなら、publicフェーズの変更セットを書き換えるには、まずhg phase --force --draft -r <rev>で強制的にフェーズを下書きに戻す必要があります。
しかし、この操作はローカルリポジトリでのみ有効であり、リモートリポジトリのpublic状態は変わりません。

その上でhg push --forceを実行すると、リモートは「公開済みの履歴を書き換えようとしている」と判断し、デフォルトでは拒否します。
仮にリモート側の設定でallowpushnonfastforwardを緩めていたとしても、リモートリポジトリのobsstore(廃棄された変更セットの記録)が不完全な場合、他の開発者のクローンとの間でハッシュの一貫性が永遠に回復しない状態に陥ります。
下記は、この危険な操作の一連の流れを示すコマンド例です。

# 誤ってプッシュした変更セットを強制的に下書きに戻す(ローカル限定)
hg phase --force --draft -r <rev>

# 強制プッシュを試みるが、リモートは公開履歴の書き換えを拒否するか、受け入れても整合性が崩れる
hg push --force

この操作が成功したとしても、すでに他の開発者がその変更セットを元に作業を進めていた場合、彼らのローカルリポジトリには古いpublicの変更セットが残り続け、次回のhg pullで衝突が発生します。
その衝突を解決するには、全員がhg stripで履歴を強制削除するか、リポジトリを再クローンする以外に手段がなく、実質的に復旧不能と言わざるを得ません。
このように、hg push --forceは「強制」という名前とは裏腹に、むしろ操作を著しく複雑化し、チーム全体の同期を破壊する危険な武器となるのです。

hg revertとhg updateの挙動の違いに戸惑う開発者

次に、日常的に使用するhg reverthg updateの違いについてです。
Gitに慣れた開発者が最初に戸惑うのが、これらのコマンドが作業ディレクトリに対して与える影響の差異です。
Gitでは、git checkout <commit>(またはgit switch)は作業ツリーとHEADを同時に移動させ、git restore <file>は指定ファイルを作業ツリー上で復元しますが、Mercurialではhg updatehg revertの役割が異なる設計になっています。

hg updateは、作業ディレクトリ全体を指定したリビジョン(またはブランチ)の状態に切り替えるコマンドです。
つまり、HEADの位置そのものを移動させ、作業ツリーの内容もそのリビジョン時点のスナップショットに置き換えます。
一方、hg revertは、作業ツリー内のファイルやディレクトリを、指定したリビジョン時点の内容に戻すものであり、HEADの位置は一切変わりません。
つまり、revertは「現在のブランチ上で、特定のファイルだけを過去の状態に巻き戻す」操作であり、updateは「別のブランチや過去のスナップショットにジャンプする」操作です。

この区別が曖昧なために、以下のような悲劇が頻発します。
開発者が「ちょっと古いバージョンを試したい」と思い、hg revert -r <old_rev> --allと実行してしまうと、作業ツリーは古い状態になりますが、hg commitすると現在のブランチの先端に古いスナップショットが新しいコミットとして積まれるため、意図せず履歴が分岐したり、リニアな履歴が崩れたりします。
逆に、特定のファイルだけを元に戻したいのにhg update -C(強制クリーンアップデート)を実行すると、未コミットの変更がすべて破棄され、作業が丸ごと消えることになります。

この混乱を避けるための実用的な指針をリストにまとめます。

  • ブランチやリビジョンを切り替えたいならhg update -r <rev>を使う。-Cを付けると未コミット変更を破棄するので注意する
  • 現在のブランチにいながら特定のファイルだけを過去の状態に戻したいならhg revert -r <rev> <file>を使う
  • 作業ツリー全体を過去の状態にしたいが、コミットはしない(一時的な確認だけ)ならhg revert -r <rev> --allだが、その後hg commitしないように注意する

さらに、hg statusで作業ツリーの状態を常に確認する習慣が必須です。
なぜなら、revertupdateのどちらを実行しても、hg statusの出力は似通って見えるため、自分が今どの操作を行ったのかを意識的に把握しておかないと、後で取り返しのつかない差分が生まれるからです。
このように、直感的に見えるコマンド名が、実際の動作モデルと乖離しているために、初心者だけでなくベテランでもうっかりミスを誘発されやすいのがMercurialの大きな弱点の一つです。

ブランチ戦略と変更セット管理の複雑性

名前付きブランチとトピックブランチが混在するMercurialのブランチグラフ可視化ツール

バージョン管理システムの根幹をなす機能の一つがブランチ戦略です。
並行開発を円滑に進めるためには、ブランチの作成、統合、破棄が柔軟かつ直感的に行えることが求められます。
Mercurialは、Gitとは異なるブランチモデルを採用しており、名前付きブランチ(Named Branches)トピックブランチ(Topics) という二つの主要な仕組みを提供しています。
一見すると、これらは状況に応じて使い分けられる柔軟な設計に見えます。
しかし、実際の運用では、この二つの概念の棲み分けが非常に曖昧であり、さらにフェーズという変更セットの状態管理が加わることで、履歴操作の自由度が著しく制約されます。
結果として、チーム全体で共有すべきブランチ戦略を策定することが、予想以上に困難な課題となるのです。

名前付きブランチとトピックブランチの棲み分けの難しさ

名前付きブランチは、Mercurialが伝統的にサポートしてきた機能であり、hg branchコマンドでブランチ名を明示的に作成し、その名前がコミット履歴に永続的に刻印されます。
一度作成されたブランチ名は、そのリポジトリ内で永遠に残り続けるため、長期的なリリースライン(例:stabledevelop を管理するのに適しています。
一方、トピックブランチは、evolve拡張に含まれる比較的新しい機能であり、軽量で一時的な作業ブランチを表現します。
トピックブランチはコミット履歴にブランチ名を永続させず、作業が完了したら名前ごと消去できるため、個人の作業用ブランチや、短期間の機能開発に向いています。

しかし、この棲み分けは理論上は明確でも、実際の開発フローでは境界が曖昧になります。
例えば、複数人で協力する大きな機能開発の場合、一時的ではありますが複数日〜数週間にわたってブランチを共有する必要があります。
その際、名前付きブランチを使うとリポジトリに永続的な痕跡が残り、履歴が汚染されるという懸念が生じます。
かといってトピックブランチを共有しようとすると、evolve拡張のバージョン依存問題(前述)に加え、トピックブランチのパブリッシュ(リモート共有)の仕組みが直感的でなく、チームメンバー間でプッシュ・プルのタイミングがずれると、容易にコンフリクトが発生します。

このジレンマを整理した表を以下に示します。

ブランチ種別 永続性 共有の容易さ 適したユースケース 注意点
名前付きブランチ 永続(履歴に残る) 容易(hg push --new-branchで共有) リリースライン、長期メンテナンス 後から削除不可、履歴が肥大化
トピックブランチ 一時的(クローズで消失可能) 困難(evolveに依存し、バージョン間で挙動が不安定) 個人の実験、短期フィーチャー 共有時のコンフリクト解決が複雑

このように、どちらのブランチ種別を選んでも、完全に満足できるワークフローを構築するのは至難の業です。
Gitのように、すべてのブランチが同一の軽量な参照で管理され、後から名前を変更したり削除したりできる柔軟性と比較すると、Mercurialのブランチ戦略は著しく制約が多く、チームの規模や開発スタイルに合わせて細かな運用ルールを定める必要性が生じます。

フェーズ(公開/非公開)が履歴改変に課す厳格な制約

Mercurialの履歴管理において、フェーズは非常に重要な概念です。
先述の通り、各変更セットにはpublic(公開)、draft(下書き)、secret(秘密)の三つの状態が割り当てられ、一度publicになった変更セットは、原則としてhg amendhg rebaseなどの履歴改変コマンドの対象外となります。
この設計意図は、公開済みの履歴を守ることにありますが、同時に開発者の柔軟な履歴編集を著しく妨げる要因でもあります。

例えば、レビューの指摘を受けて、すでにプッシュした変更セットに小さな修正を加えたいとします。
Gitであればgit commit --amendの後にgit push --force-with-leaseで上書きできますが、Mercurialではその変更セットがpublicに変わっているため、hg amendを実行しようとしてもエラーになります。
対応策としては、以下のいずれかを選ぶしかありません。

  • 修正を新たなコミットとして積み、履歴を線形に保つことを諦める(レビュー履歴が煩雑になる)
  • hg phase --force --draft -r <rev>で強制的にフェーズを戻した上でhg amendを実行するが、他の開発者のリポジトリとの整合性が崩れるリスクを負う
  • そもそもプッシュ前にすべての修正を完了させるという、現実的でないルールをチームに強いる

この厳格さは、特に継続的インテグレーションと組み合わせた場合に顕著な問題を引き起こします。
CIがプッシュをトリガーに動作する環境では、レビュー中に頻繁にプッシュしてテストを回したいというニーズがありますが、そのたびに変更セットがpublic化してしまうため、後からの履歴整理が極めて困難になります。
結局、開発者は「プッシュする前に完全な状態にする」という非現実的なプレッシャーにさらされるか、あるいはsecretフェーズを使いこなすための高度な運用知識が要求されますが、その場合もsecretの変更セットはリモートに共有されないため、チームでの協業が成り立たなくなります。

このように、フェーズは安全と柔軟性のトレードオフを極端に偏った形で実装しており、特に初期段階の開発や試行錯誤が頻繁に行われるプロジェクトでは、その制約が生産性を著しく損なう要因となります。
Gitのreflogのように、どんな履歴改変でも最終手段として戻れる安全網が存在しないことも、この問題に拍車をかけています。

新規参入メンバーへの教育コストの非対称性

Git経験者がMercurialを学ぶ際に最初にぶつかるつまずきポイントをリスト化したホワイトボード

チーム開発において、バージョン管理システムの習得難易度は、新規メンバーの生産性に直結する重大な要素です。
Gitがデファクトスタンダードとなった現在、ほとんどの開発者は既にGitの基本的な操作に慣れています。
しかし、Mercurialを採用すると、その知識はむしろマイナスの転移効果を生みます。
つまり、Gitの経験が豊富な開発者ほど、Mercurialの「似て非なる」コマンドや概念に戸惑い、かえって学習に時間を要するという逆説が生じるのです。
この教育コストは、新入社員やプロジェクト中途参画者に対して顕著に現れ、チーム全体のオンボーディング期間を大幅に延伸させます。
ここでは、具体的なコマンドマッピングの落とし穴と、学習リソースの希少性がもたらす影響を考察します。

Git出身者が最初に躓くコマンドマッピングの落とし穴

Gitに慣れた開発者がMercurialを触り始めて最初に直面するのが、同じ名前のコマンドが異なる動作をするという事実です。
例えば、hg statusgit statusとほぼ同様に機能しますが、hg loggit logよりも詳細な出力がデフォルトで表示され、オプション体系が完全に異なります。
より深刻なのが、hg branchgit branchの違いです。
Gitではgit branchがブランチの一覧表示や作成を行うのに対し、Mercurialではhg branch現在のブランチ名を表示または設定するコマンドであり、ブランチ一覧を見るにはhg branchesを使います。
この命名の非対称性は、初心者に「なぜ同じ名前で挙動が違うのか」という混乱を引き起こします。

さらに、履歴編集におけるコマンドマッピングは最も罠が多い領域です。
Gitのgit commit --amendは、直前のコミットを修正する単純な操作ですが、Mercurialではhg commit --amendも存在します(evolve拡張が必要)
しかし、その挙動はGitと微妙に異なり、--amend実行後にフェーズがdraftのままであれば良いものの、すでにpublicになっているとエラーになるため、事前にフェーズ確認が必要です。
また、Gitのgit rebase -iに相当するMercurialのhg histeditは、拡張を有効にした上で、編集用のテンポラリファイルを作成して操作しますが、このファイルの書式がGitのそれと大きく異なるため、慣れた手順が通用しません。

これらの混乱を体系化した対応表を以下に示します。
これは教育資料として必ず用意すべき内容です。

Gitコマンド(操作) それに近いMercurialコマンド 動作の差分と注意点
git branch <name> hg branch <name>(その後hg commit必須) Gitではブランチ作成のみだが、Mercurialではブランチ名を設定し、次のコミットで実際にブランチが作られる
git checkout <branch> hg update <branch> 作業ディレクトリの切り替えは同様だが、checkoutのファイル復元機能はhg revertに分離されている
git commit --amend hg commit --amend(要evolve) フェーズがpublicの場合は動作せず、事前にhg phase --force --draftが必要な場合がある
git rebase -i hg histedit(要evolve) 編集用のアクション指定がpick/editなど共通だが、コマンドラインオプションやエラーメッセージが異なる
git push --force-with-lease hg push --force(強制だが安全機構なし) 後者にはリモートの追跡状態を確認する安全機構がなく、他者の変更を破壊しやすい

この表を見れば分かる通り、Gitの知識がそのまま通用するのはごく一部の基本操作だけであり、応用操作に進むほど学習コストが跳ね上がります。
特に、rebasehisteditの違いは、インタラクティブな履歴編集のワークフローをチームで統一するまでに、何度もトライアルアンドエラーを繰り返すことになります。

学習リソースの希少性がチーム全体の生産性を阻害

コマンドマッピングの困難さは、十分な学習リソースがあれば緩和されます。
しかし、Mercurialに関する質の高い最新の教材は、日本語・英語ともに極めて少ないのが現実です。
まず、公式のチュートリアルは基本的な操作をカバーするものの、拡張機能やフェーズを活用した実践的なワークフローについてはほとんど言及していません。
また、書籍に目を向けると、Mercurialを扱った技術書のほとんどが2010年代前半に出版されたものであり、現在のバージョンとはコマンドオプションや推奨プラクティスが大きく乖離しています。
オンラインコース(UdemyやCourseraなど)にもMercurial専用のコースはほぼ存在せず、せいぜい古いブログ記事や個人のメモが散在する程度です。

このリソース不足は、チーム内でのナレッジ共有に過度な負担を強います。
すなわち、新規メンバーが疑問に思ったことを自己解決しようとしても、Stack Overflowの古い回答は現在のバージョンで動作せず、公式ドキュメントの英語版を読むにしても、前提知識がないと理解が困難です。
その結果、結局は既存メンバーが一対一で教える「徒弟的」な教育スタイルを強いられ、ベテラン開発者の時間がオンボーディングに大量に割かれることになります。

この影響を定量的に見積もると、例えば新規メンバー1人あたり、Mercurial特有の概念(フェーズやトピックブランチ)の習得に、Gitからの移行ケースで平均で20〜30時間の追加学習時間が必要というデータが(非公式ではありますが)一部のエンジニアリングブログで報告されています。
これが年間で複数人の新規参画があるチームでは、チーム全体で数百時間の生産性損失に相当します。
さらに、その間に発生する質問対応やコードレビューでの履歴ミスの修正コストを含めれば、経営的な観点からも無視できない負債です。

つまり、Mercurialを導入するという判断は、ツール自体の習得に割く教育コストを、プロジェクトの予算やリソース計画に明確に織り込むことを要求します。
Gitであれば新入社員が最初の週で使いこなせるレベルなのに対し、Mercurialではその倍以上の時間を覚悟しなければならず、その非対称性はプロジェクトの立ち上げ期やスクラムチームの拡大期に致命的なボトルネックとなるでしょう。

Gitからの移行で想定されるデータ不整合リスク

hg-gitゲートウェイを介した双方向同期で発生するハッシュ値の不一致を示すエラーログ

既存のGitリポジトリをMercurialに移行する、あるいは両者を併用する環境を構築する場合、ほとんどのエンジニアが最初に試みるのがhg-git拡張機能です。
この拡張機能は、MercurialクライアントからGitリポジトリに対して読み書きを行うブリッジとして機能し、一見するとシームレスな相互運用を約束しているように見えます。
しかし、私はこのツールを「危険なほどの妥協の産物」と評価せざるを得ません。
なぜなら、GitとMercurialではコミットの親子関係やブランチの参照モデル、タグの格納方式が根本的に異なるため、hg-gitが内部的に行う変換は多くの情報を切り捨てたり、意図しない解釈をしたりするからです。
特に移行時に顕在化するデータ不整合リスクは、プロジェクトの履歴の信頼性を揺るがしかねません。

hg-gitゲートウェイの制約とタグマッピングの誤差

hg-gitが抱える最も深刻な問題の一つが、タグ情報のマッピング誤差です。
Gitには「軽量タグ(Lightweight Tag)」と「注釈付きタグ(Annotated Tag)」の二種類が存在し、前者は単なるコミットハッシュへのポインタ、後者は独立したタグオブジェクトとして日時や署名情報を保持します。
一方、Mercurialのタグはhg tagコマンドで生成される特殊な変更セットであり、.hgtagsという管理ファイルにタグ名と対応するハッシュを追記する形で永続化されます。
このアーキテクチャの違いにより、hg-gitは以下のような変換誤差を必ず内包します。

  • Gitの軽量タグは、Mercurial側ではブックマーク(Bookmark)に近い扱いになりますが、ブックマークは変更セットとは独立した参照であるため、hg pushでリモートに共有されない限りローカルに留まります。この差異により、移行後にタグが欠落するケースが頻発します
  • 注釈付きタグのメッセージや署名情報は、Mercurialのタグ変更セットには格納するフィールドが存在しないため、hg-gitはそれらをGitノート(Git Notes)として別途保存しようとしますが、このノート情報は標準のhg logでは一切表示されず、実質的にデータの消失と同義です

また、タグ名の文字コードやパス区切り文字の扱いにも差異があり、特に日本語を含むタグ名やスラッシュ(/)で階層化されたタグは、変換時にエスケープ処理が入ることで意図しない名称に変わるリスクがあります。
下記は、hg-gitを有効化してGitリポジトリをクローンする際の典型的な設定例ですが、この時点ですでにタグの変換ポリシーを制御するオプションが存在しない点に留意してください。

# .hgrc または hg コマンドラインで hg-git を有効化
[extensions]
hggit =

# GitリポジトリをMercurialとしてクローン
hg clone git+https://github.com/example/repo.git

このクローン完了後、hg tagsでタグ一覧を表示すると、Git側で持っていたタグ数と一致しないことが大半です。
特に、複数の注釈付きタグが同じコミットを指している場合、hg-gitはそれらを一つのタグに統合しようとするため、タグ名の重複や意図しない上書きが発生します。

この問題を構造的に理解するために、GitとMercurialのタグ関連機能を比較した表を以下に示します。

機能/属性 Gitのタグ Mercurialのタグ(+hg-git経由) hg-gitでの変換リスク
タグの実体 参照(refs/tags/)または独立オブジェクト .hgtagsファイル内のエントリ+専用変更セット ファイルベースのため併合衝突が発生しやすい
軽量タグと注釈タグの区別 明確に区別される 区別なし(すべて変更セット化) 注釈情報(署名・日時)が完全に喪失
タグの署名(GPG) 注釈タグで標準サポート 非対応(拡張なしでは不可) 署名検証が一切引き継がれない
タグの履歴改変 git tag -fで強制更新可能 一度作成すると.hgtagsの追記が必要で削除が複雑 更新履歴が競合し、パースエラーを誘発
タグ名の文字制限 ほぼ任意(ただしパス名制限あり) ASCII推奨、マルチバイトは非公式扱い 文字化けや変換エラーの多発

さらに悪いことに、hg-gitはマージコミットの親情報の変換にも脆弱です。
Gitはマージコミットに複数の親を持ちますが、Mercurialの変更セットも同様に複数親を許容します。
しかし、親の順序がGitとMercurialで逆転するケースが確認されており、この順序違いがhg log --graphの表示を著しく歪め、過去のマージ判定を誤らせることがあります。
このような不整合は、リポジトリの可視化ツールや、特定のコミットを基準にした差分抽出(hg diff -r <rev>)に悪影響を及ぼし、開発者が履歴を正しくトレースできなくなる要因となります。

結局のところ、hg-gitは緊急避難的な相互運用を提供するものの、プロフェッショナルな移行ツールとしては信頼性に欠けると言わざるを得ません。
移行を強行する場合は、全タグの一覧とそのハッシュ値を移行前にCSV出力し、移行後に一つ一つ突合するという、極めて泥臭い検証作業を必ず実施してください。
それでも、署名情報や注釈メッセージは復元不可能であることを覚悟しなければなりません。

導入判断の結論 – レガシー保守以外には選ぶな

Mercurialの導入判断フローチャートで、レガシー保守以外はすべてGitを推奨する終端図

ここまで、Mercurialの過去の実績から現在の深刻なデメリットまで、多角的に検証してきました。
差分アルゴリズムの性能、コミュニティの縮退、CI/CD統合の困難さ、拡張機能の互換性崩壊、コマンドの直感性の罠、ブランチ戦略の複雑性、教育コストの非対称性、そしてGitからの移行リスク――これらすべての要素を総合的に評価した上で、私が導き出す結論は明確です。
2026年現在、新規プロジェクトやアクティブに開発が続くプロジェクトでMercurialを選択することは、技術的にも経営的にも不合理であり、強いて許容されるのは「既存のレガシーリポジトリを保守し続ける」という極めて限定的なシナリオのみです。

この結論に至った根拠を、改めて論理的に整理します。
まず、バージョン管理システムの選択基準として、以下の三つの軸が常に重視されるべきです。

  • エコシステムの持続可能性 – アクティブな開発者コミュニティ、豊富なドキュメント、主要プラットフォームとの統合が保証されていること
  • チームの生産性 – 新規メンバーの学習コストが低く、既存の知識を活用でき、トラブル時の解決手段が容易に得られること
  • 将来の柔軟性 – ツールチェーンの進化に追従でき、外部サービスや新しい開発手法とシームレスに連携できること

Mercurialは、これら三つの軸すべてにおいて、現在のGitに対して明確に劣位にあります。
エコシステムは縮退の一途をたどり、生産性は教育コストと情報不足によって阻害され、柔軟性は拡張機能の依存関係とフェーズの制約によって著しく損なわれています。
特に、クラウドネイティブな開発環境やDevOpsパイプラインが標準化された現代において、Mercurialがこれらの流れに乗れないことは、もはや技術的負債のレベルを超えた組織的なリスクです。

では、レガシー保守という例外ケースにおいて、どのような条件が満たされるべきかを明確にしておきます。
以下のチェックリストにすべて該当する場合に限り、導入または継続を検討しても良いでしょう。

  • リポジトリの履歴が数万コミットを超え、Gitへの移行に伴う履歴変換コストが開発リソースを著しく超過する
  • チーム全員がMercurialにすでに習熟しており、新規参入者が今後ほぼ見込まれない(または専任の教育担当者がいる)
  • CI/CDパイプラインが完全にオンプレミスで構築されており、かつMercurial用のスクリプトが安定稼働している
  • 使用している拡張機能(特にevolveや独自フック)が、特定のMercurialバージョンで固定されており、アップデートの必要が事実上ない
  • 外部のコラボレーターやOSSコミュニティとの連携が一切発生せず、閉じた環境で開発が完結する

これらの条件が一つでも欠けるならば、移行コストを今すぐに見積もり、Gitへの段階的な移行計画を策定すべきです。
実際、Python公式リポジトリの移行事例が示す通り、大規模プロジェクトであっても計画的に進めれば移行は可能であり、その投資対効果は長期的に十分に回収できます。
移行の具体的な戦略としては、以下のアプローチが現実的です。

  • hg-gitを一時的なブリッジとして使い、Gitリポジトリを並行して運用しながら、チーム内で徐々にGitクライアントへの移行を進める
  • 履歴の完全性が特に重要な場合は、git-remote-hgやカスタムスクリプトでハッシュの一致を検証し、タグやブランチのマッピングを手動で補完する
  • 移行期間中は、MercurialとGitの両方で同一の変更セットを扱えるようにし、最終的なカットオーバー日を設定して、その日以降はGitのみを公式とする

ただし、この移行作業にもコストが伴うことは事実です。
そのため、レガシープロジェクトであっても、今後3年以内に開発が終了する見込みの場合は、あえて移行せずに現状維持を選ぶという選択肢も戦略的にはあり得ます。
しかし、その判断はプロジェクトのライフサイクルとビジネス価値を厳格に評価した上で行うべきであり、単なる「慣れ」や「手間がかかるから」という理由でMercurialに留まることは、技術的負債を増殖させるだけです。

最後に、私がこの記事を通じて最も伝えたいのは、ツールの選択は感情や過去の実績ではなく、現在と未来のエコシステム全体で判断せよということです。
Mercurialは確かに優れた設計思想と歴史的貢献を持ちますが、その価値は博物館的な保存対象であり、アクティブな開発現場の主力戦車ではありません。
もしあなたがこれから新規プロジェクトを立ち上げるなら、迷わずGitを選び、GitHubやGitLabが提供する豊富なエコシステムを最大限に活用してください。
そして、もし既にMercurialを使っているなら、この記事をきっかけに移行のタイミングを真剣に検討することを強く勧めます。
技術者の責務は、過去の遺産に縛られることではなく、最も生産的で持続可能な未来を選び取ることだからです。

コメント

タイトルとURLをコピーしました