Chienomi

(VSCodeのために) MercurialよりGitのほうがマージしやすい問題を解消する

Live With Linux::software

マージとthree-way merge

まず、我々Mercurialユーザーはひとつの事実を受け入れなければならない。

それは、「世の中ではGitのほうが使われている」という事実だ。

Gitのほうが使われているということは、Gitのほうが有力なツールが多く揃い、Gitのほうが使いやすい世の中になっているということを意味する。

その問題のひとつにマージがある。

この「マージ」という概念が、GitとMercurialでは大きく違う。

Gitではそもそもワーキングツリーを持つリポジトリに対するpushというのができないようになっている。 また、ワーキングツリー上でpullした場合、conflictするならばpullに失敗するという仕様だ。

Mercurialはそもそもワーキングツリーを持たないリポジトリというものがなく、pull操作は基本的に妨げられることがない (push操作はremoteのHEADが進んでいる場合はpullしてからでないとpushできない)。 Gitと違いこの操作によってワーキングツリーが変更されるということはなく、ワーキングツリーの更新にはupdateを行う必要がある。

このときconflictが発生するなら自動的に(強制的に)mergeを行うことになる。

また、既に変更がcommitされており、remoteにないcommitによってconflictが発生する場合、 Mercurialでは複数のHEADを持つ状態になる 。 複数のHEADをひとつにまとめるのがmergeである。これはGitでも見られるマージ操作だ。

Gitの場合は競合したファイルにマークをつけた状態になる。 一方、Mercurialではマージツールがある場合はThreeWayMergeというのが行われる。

MercurialのThreeWayMergeは専用のソフトウェアを使って行う高度な競合解消作業だ。 「普通はマージツールなんてないから」と思うかもしれないが、Vimにはvimdiffというものが含まれているため、Vimがあるだけでマージツールを利用したThreeWayMergeになる。 また、KDEコンポーネントのkdiff3も使えたりする。

Vimdiffがマージツールとして使いやすいかというと、「慣れによる」というのが私の回答である。 個人的にはMeldのほうが直感的で使いやすいと思うし、Vimdiffを使うのならMeldをインストールしてマージするというのも考えて良いと思う。

なお、意図せずconflictが発生してしまい、マージツールがない場合は、hg rollbackによって競合を発生させたremoteのリビジョンを外しておき、もう一度pullし直すという方法がとれる。

Git-styleでマージしたい

もちろん、何の機能もないエディタを使ってマークをつけられただけのファイルを編集するのは大変である。

だが、Gitユーザーが多いという事実は、それをそのまま放置するようなことはない、ということを意味している。 つまり、VSCodeのマージ機能が便利過ぎるのだ。

VSCodeでマークがついたファイルを操作する

さすがにこれはMeldよりも使いやすい。

じゃあMercurialでもVSCodeでマージしたいんじゃ! と思ったときにその情報がない。 というか、今Googleで検索すると、関連情報にすらたどり着けない。

というわけで書いたのが今回の記事だ。

マージの際に何を使うか、というのはマージツールの設定として決めることができる。 これは、.hgrc[merge-tools]セクションである。 現在どのようなマージツールが定義されているかは、hg config merge-toolsで知ることができる。

ちなみに、Vimdiffのpriorityが -10, Gvimdiffが -9 なのに対し、Meldはpriorityの設定がないのでMeldがあればMeldが使われる。

そもそもどうやってマージするか、ということについては、 [ui] セクションの merge という項目にある。 この詳細はhg help merge-toolsで見ることができる。

さて、Mercurialでは同一のファイルが変更された場合、問答無用で競合状態にする。 機械的に統合できる場合でも勝手に統合したりはしない。

Gitの場合、変更箇所が競合している場合にだけ手動での解決を要求する。そうでなければ同一ファイルに対する変更を取り込む。

:merge はGitと同様の挙動になる。解決できない競合マークがつけられる。 場合によっては :other:local も便利なオプションである。

finally, GitみたいにマージしてVSCodeでいい感じに競合解消したい場合、 .hgrc に次のように書けば良い。

[ui]
merge = :merge