Mercurialで快適変更管理

Mercutialの勧め

Mercurialはバージョン管理システムである。

ソフトウェアをチームで開発する場合は必須のツールで、かなり長い間CVS(Concurrent Version System)が採用されることが多かった。CVSはとても便利で、今も私が参加しているチームでは主役だ。しかし、CVSには欠点が多く、決定的な事はチェンジセットをサポートしていない事だと思う。チェンジセットとは複数のファイルをひとつの変更単位として扱うことである。CVSはファイル単位でしか管理することができない。(CVSの成り立ちからそうならざるを得なかった。)だから、CVSでは案件別に変更を管理しようとすると運用が難しくなってしまうのだ

Mercurialはこのチェンジセットという更新単位をサポートする。以下はバージョン管理システムの基本的な動作をチェンジセットでどのように管理されるのかを具体例で説明する。

Mercurialでチェンジセットの恩恵にあずかろう!

始めの一歩は、リポジトリ作成から

リポジトリを作成してみる。mercurialはhgコマンドの後にサブコマンドを付けることで動作を指示する。リポジトリ作成のサブコマンドは”init”だ。

実行したディレクトリに””.hg”というフォルダができる。これがリポジトリのフォルダだ。リポジトリの管理範囲は、”.hg”ディレクトリのあるディレクトリとなる。

リポジトリ作成後でhello.cというC言語のソースコードファイルをサンプルとして作成した。このファイルは出来たてほやほやで、リポジトリにはまだ登録されていない。

ファイルを追加してみる

hello.cをリポジトリに追加する。hg addコマンドを実行したあと、hg statusで確認。”A”とマーキングされている。追加対象のファイルであることを教えてくれる。

hg commtコマンドで追加を確定させる。これでリポジトリにhello.cが追加された。hg statusで確認。追加されてしまったので今度は何も表示されない。

hg commit実行後チェンジセットID=0:a85eea9d6407が付けられる。今回の変更分はこのIDで識別できる。チェンジセットIDを確認するにはhg logコマンドを実行。

ファイルを変更してみる

hello.cを変更した後hg statusコマンドを実行すると、hello.cが変更対象として”M”マーキングされていることがわかる。

この状態で、hg diffコマンドを実行すると、変更の内容が表示される。printf(“Hello Tohru!!n”);の行がhello.cに追加されていることが分かる。hg diffコマンドはdiff形式で変更内容を表示してくれる。

変更をコミットしてhg logで確認。今回の変更にチェンジセットID=1:c3528466158aが付けられた。

サブディレクトリにあるファイルを追加してみる

当然ではあるが、サブディレクトリにあるファイルも同様に追加できる。

ファイルの削除をしてみる

チェックイン済のファイルを削除するというのは、リポジトリに「削除」という変更をすることになりる。バージョン管理システムは「削除」前のバージョンに戻る事ができなければいけないから、完全にファイルが「削除」されることはない。あるバージョンからは「削除」されたファイルの管理をしないという変更を行うことになる。

先ほど追加したcommonfunc.hファイル削除してみる。commonfunc.h自体をrmコマンドで削除すると、hg stausは”!”マーキングで管理対象ファイルが無いこと教えてくれる。この状態は単に管理されているファイルが無いだけで、実際に「削除」されてはいるわけではない。

hg remove [ファイル名]コマンドで「削除」を実行する。実行後、hg statusで確認すると対象ファイルに”R”マークが付く。

削除をチェックインしてhg logで確認。削除にもチェンジセットID=3:3d55316ca048が付けられた。

コミット前の状態を元に戻す

コミットする前の状態であれば変更内容を無かったことにすることができる。例えば、誤ってremove操作したファイルは”R”対象からはずさなければいけない。そんな時はhg revertコマンドを実行する。もちろん編集”M”や、追加”A”も取り消せる。

TAGを打ってみる

TAGはチェンジセットに対する名前である。チェンジセットは通常英数文字の羅列であるIDで管理されているので識別しずらいという方もいるだろう。TAGを打つことでわかりやすくすることができるし、独自のバージョンNoで管理するという運用も可能となる。TAGを打ってあると、TAGでその時点のソースコードを取り出すことができる。

TAGを表示するコマンドはhg tagsだ。まだTAGを打っていない状態だが、”tip”というTAGが表示される。”tip”というのは特別で、常に最新のチェンジセットに付けられる名前だ。だから”tip”というTAGを打つことはできない。TAGを打つコマンドはhg tag [TAG名]だ。

TAG1というTAGを打ってみる。4:1267ec260381チェンジセットに”TAG1″という名前を付けることができた。TAGを打つと新たにチェンジセットIDが付けられる。hg tagsを見ると、”tip”タグが新しいチェンジセットIDになっているのが分かる。TAGを管理するために”.hgtags”というファイルがリポジトリに追加されるので、チェンジセットがひとつ増えることになるのだ。次からTAGを作成する度にこのファイルが更新され、チェンジセットもその都度増えていく。(しかたがないが、これは運用上あまり嬉しくないことだ。)

ブランチ(コピー)してみる

開発のために現在のソースコードはそのままにしておいて、別の場所で更新内容を管理したいことはよくある。たとえば、「安定版」系、「開発版」系などに役割を分担し、それぞれの更新内容を管理したい場合だ。

CVSではブランチを作成することで実現している。ブランチとは更新対象となる系列から分岐して新たに設けた系列のこと。同じファイルを2系列で更新することが可能となる。もちろん、相互に更新内容をマージすることも可能である。

Mercurialでも同様の事ができる。ただ、cvsに慣れている方は理解するのに少し時間がかかるかもしれない。

cvsではファイル単位の管理となっていて、ブランチもファイル単位のリビジョン番号で管理されている。ファイル単位で全ての更新を串刺しして表現することが可能なのだ。1.0のブランチは1.0.0、1.1のブランチは1.1.0という具合だ。これって変更管理の立場からはとってもわかりやすい。

さてさて、Mercurialの分岐はリポジトリのコピーである。リポジトリが2つになるので、それぞれに更新管理が可能という事だ。これってあとで難しいことにならないだろうか?

CVSの恩恵にどっぷり浸っていた方は心配になると思う。かくいう私も心配だった、うまく管理できるのだろうかと。しかし、Mercurialはリポジトリ関でマージができるので、良く考えれば何の問題もない。実際の現場では、おそらく「マスタ」リポジトリと、それから分岐した「開発用」リポジトリという具合で運用となるのではないだろうか。

分岐してみる

では、分岐してみる。分岐にはhg clone [元] [先]コマンドを実行する。例ではmercurialディレクトリをmercurila_cloneディレクトリにコピーしている。

hg logを確認すると、コピー元と全く同じ表示結果となる。

分岐先リポジトリでファイルを更新する

分岐先リポジトリで、hello.cファイルの更新を行います。

今回の変更内容の親チェンジセットを確認しておく。hg parentsコマンドはチェンジセットの親チェンジセットを確認できる。

分岐元リポジトリでファイルを更新する

分岐元でも同じhello.cファイルを更新する。

最新のチェンジセットIDは分岐先と元で異なる。けれど、分岐するまえのチェンジセット、つまり、親チェンジセットは両リポジトリとも同じ物が表示されている。つまり、元は同じ物だ。

分岐元の変更内容を、分岐先に反映する

分岐先リポジトリでの更新内容を分岐元に反映するためには、まず、分岐先の変更内容を分岐元に反映して変更内容の同期をとることが必要だ。想定シチュエーションは開発が済んだのでリリース用のマスタリポジトリにマージ作業が必要な状態だ。

分岐元のリポジトリで、分岐先の変更内容を取得する。hg pull [リポジトリ]コマンドでリポジトリの変更内容を持ってくる事ができる。

pullコマンドを実行すると分岐元の更新の情報が得られるが、更新情報は分岐先で新しいチェンジセットとなる.
hg statusで確認するとparentは分岐時のチェンジセットになっている。

この状態はまだ分岐元の更新内容がhello.cファイルに反映されていない。単に更新情報があるだけだ。hello.cに対してhg mergeコマンドでマージ作業を行う。

マージを行うと、hello.cファイルは”M”マーキングされる。チェックインすれば、分岐元の変更が分岐先に反映する。

マージしたファイルは新たなチェンジセットとしてコミットされる。マージ元のチェンジセットが2つあるので、parentが2つ表示される。例は、チェンジセット8:06846c7cc667が、分岐先の更新内容6:79715a23ef0fと、分岐元の更新内容7:18a85a40a048のマージ結果であることを表示している。

分岐先の変更内容を、分岐元に反映する

同期をとった後、分岐先リポジトリの変更内容を、分岐元リポジトリにも反映する。hg push [リポジトリ]コマンドを実行する。

では、分岐元に行って確認してる。分岐先の更新内容がチェンジセット: 7:79715a23ef0fとして登録され、マージの内容がチェンジセット: 8:06846c7cc667として登録されている。これで、両リポジトリ間の同期は完了だ。

以上で具体例は終わりである。チェンジセットによってMercurialがどう変更管理しているか理解できたと思う。バージョン管理システムの基本部分を備えている事はCVSもMercurialも同じであるけれど、ソースコードの変更管理を運用していくからには、チェンジセットを扱えるMercurialの方に分がある。

チェンジセットを扱えるバージョン管理システムとして普及しつつあるもうひとつのシステムがSubversionである。MercurialとSubversionの違いを次で説明する。

Mercurilは「分散型」である

CVSに替わるバージョン管理システムが幾つか存在している。それらは大きく2つに分類できる。リポジトリを一所に置いて管理する「集中型」と、開発者が個々にリポジトリを持ちリポジトリ間で変更を調整できる「分散型」である。

「集中型」はリポジトリからソースコードをチェックアウトする必要がある。チェックアウトした瞬間からリポジトリから切り離され、あとの変更管理はそれぞれの方法で行う。変更が完了したらチェックイン(コミット)し、この時点でリポジトリが更新される。リポジトリが更新されれば、他の開発者が変更内容をチェックアウトできるようになる。

「分散型」はリポジトリからソースコードを切り離す必要が無い。リポジトリ自体をコピーするからだ。変更者は手元にリポジトリを置いておくことができるので、コピーされたリポジトリに変更内容をいつでもチェックインできる。そして、変更完了後にコピー元のリポジトリと、手元のリポジトリ間で変更内容を同期する。(必要がなければそのままでも良い)。

「分散型」のアドバンテージは、開発者間で変更内容を同期できることである。ひとつの開発案件を複数の開発者で担当し、それぞれの変更が完了した時点で結合テストを行いたい事もあるだろう。そのケースでは、開発者間でリポジトリをマージしあって同期すれば良い。結合テスト完了後コピー元のリポジトリと同期することになる。

The Internetが当たり前の世の中、開発チームは一所に集まる必要がなかったり、それによって、開発を行う時間帯も様々であったりというケースがある。そんな開発環境に「集中型」は向かない様である。開発者が分散していると「集中型」は取扱いに苦労するのだろう。特に開発者間で変更内容をマージできるのは便利なのだと思う。

なので、最近は「分散型」のバージョン管理システムが採用されることが多くなってきている。例えば、Linuxのソースコード管理はgitで行われているが、gitは「分散型」に分類される。

「分散型」であるというのもMercurialをお勧め重要な要素だ!

参考リンク

ご意見、ご指摘はこちらへ

この文章はbaeが書いています。有用な情報となるよう心がけていますが、利用される方は個人の責任でお願いします。お気付きの点、また、ご意見ありましたらご一報ください。公開鍵サーバに宛先を登録していますのでそちらを参照ください。

公開鍵情報

コメントを残す