MCL(99)

インデックスに戻る

前のMCLへ  次のMCLへ

▼ My Computer Life (99)

99. バージョン管理

 私も、素人プログラマから、職業プログラマとなり、職場で複数人開発をするようになった。
 アマチュアでやっていた頃は、一人で組んでいたのであまり気にしなかったが、さすがに仕事でやるとなると、明確なバージョン管理のシステムが無いと、やっていられなかった。

 バージョン管理とは、簡単に言うと、過去の変更履歴を保存しておいて、いつでも昔のバージョンに戻せるような仕組みを言う。…ざっくり過ぎるな…。ま、下で説明していく。
 そういうわけで、バージョン管理システムは、世の中にたくさんあったりする。

 初期に利用されていたバージョン管理システムは、RCS(Revision Control System)だ。これは、本当にファイルの過去の変更を保存するだけのシステムだった。
 例えば、テキストファイルを作成する。このテキストを、RCSの管理下に置くと、以後改変するごとにバージョンが上がる。一番最初にバージョン1だったとして、適当に文章を追加して登録すると(この登録をコミットと言う)、バージョン2となり、1と2の差分が保存される。同様に、バージョン3、4と変更を加えるたびに数字が上がっていく。
 このようにしておくと、例えばいついつの状態に戻したいとか、そういうのが可能になる。プログラムだと、それが凄く大切だったりする。不具合が出たら、出る前に戻せるからね。「不具合出る前はどうだったっけ…」とか記憶に頼るのは怖すぎる。
 もろちん、バージョン管理システムを採用していなくても、皆そういうのを経験するうちに、手動でのバージョン管理をするようになる。例えばファイル名とかフォルダ名に日付つけたり、番号つけたりして、節目節目で別に保存していくという仕組みだ。こうすれば履歴が手元に残る。
 このような手動管理システムに対し、アプリケーションとして提供されている管理システムは、それを自動的にやってくれるのと、各バージョンで保存するのが差分なので、全バージョンでファイル丸ごとを保存しておくより効率良い、というメリットがある。
 あとは、一つ前、あるいはもっと前のバージョンと、今手元のバージョンではどこが違うんだっけ、というのを差分抽出ツールとかで見れるので、関係ないところを間違って変更しちゃってないか、などをチェックできる。

 さて、バージョン管理システムとは、その名の通り、バージョンを管理するシステムなので、まあ上記のRCSがやっている事で大体問題はない。
 ただ、時代が過ぎるに従って、バージョン管理システムには、もう一つ「共同開発のためのツール」という役割が与えられるようになった。

 バージョン管理自体は、プログラム開発に非常に役立つ…と言うか実質不可欠なので、RCSであっても、無いよりはずっとマシである。事実、昔はRCSで開発をしていたらしい。
 例えば、メインになるマシン(今で言うところのサーバ)に、RCSとその管理下に置かれたマスターのファイル群を置いておく。各開発者は、そのサーバにアクセスして、目的のファイルを更新する。これで、一応バージョン管理+共同開発は可能になる。
 ただ、このやり方を見ても分かるとおり、衝突が常に問題になる。共同開発している別々の人が、同じファイルを更新しようとした時、問題が起きる(根本的には、この問題自体は解決しようがない部分があるけど)。
 RCSは、そもそも1人で操作する事を想定していた部分があるため、ある人がファイルを編集しようとする時は、他の人が編集するのは基本的に禁止していた。一応これで、衝突でメチャクチャになる事は防げる。
 ただ、実際のプログラム開発というのは、そんな1ファイルずつ更新するものではない。対象の機能を開発するのに、いくつものソースファイルを更新する事になる。でもそんなに複数のファイルをロックしたら、他の人は全然開発できなくなる!

 そこを改善したのが、CVS(Concurrent Version System)というツールだ。
 CVSは、誰かがファイルを編集しようとしても、特にロックはしない。だから、「誰が編集しようとしている」という情報を送信したり登録したりする仕組みがない。では衝突にはどう対処するのだろうか。

 衝突とはこのような事だ。
 一つのファイルを二人の人がいじろうとする。Aさんがまずそのファイルを編集し、それをコミットする。その後Bさんがそれを取得し、編集し、コミットする。このような順番なら問題がない。RCSは、それを強制する形で解決している。
 ただ、Aさんが編集し、それをまだコミットしないうちに、Bさんがそのファイルを取得して編集しようとした時に、衝突が起きる。この後Aさんがコミットしたとしても、Bさんの手元にあるファイルはそのAさんの更新が入っていない。なので、更にその後Bさんがコミットを行うと、Aさんの変更が消え去ってしまう。
 このような時、CVSはBさんのコミットを止める。あなたの持っているファイルは、今最新のファイルよりも古いのをベースにしていますよ、という警告を出す。この衝突の事をコンフリクトと言う。そしてコンフリクトが発生した場合、マージを試みさせる。マージとは、テキストファイルを混ぜて、うまくAさんとBさんの両方の更新が入るようにする。ただ、機械的に混ぜるだけなので、うまく行かない事もある。まあそれならそれで、後からコミットする人が責任を持ってちゃんとした形にすればいい。

 まあ、さっきも書いたが、本質的には人と人の作業の衝突は、どこかで起きてしまう事で、根本的には避けようがない。
 ただ、RCSの方針が、最初から衝突が起きないように、編集するファイルをロックしてしまうのに対し、CVSはロックはしない代わり、万が一衝突が起きた時にそれを警告し、遅かった方の人に対して解決を迫るというやり方になる。衝突の可能性があるので全部ロックするよりもはるかに柔軟なやり方で、現実的だ。

 それ以外の、過去バージョンの履歴を残すとか、その過去のバージョンに戻す機能を用意するとかは、RCSから引き続きサポートしている。

 私は会社ではCVSをしばらく使ってたが、ちょうどその頃Subversion(SVN)という新しいバージョン管理システムが出始めた頃で、その記事を見つけて、社長に「これいいっすよ」みたいに進言した記憶がある。そのあとしばらくしてSVNに変えたんだっけな。変えなかったんだっけな。記憶が曖昧だけど、世間的にはほとんどSVNに乗り換えたんじゃないだろうか。

 CVSとSVNの違いは色々あるが、私が最初にSVNを発見した時に「いいな」と思ったのが、バイナリファイルでも差分形式で保存する、という事だ。
 RCSやCVSは、テキストファイルについては、各バージョンが作られるたびに差分を保存するようにしている。だからサーバ容量的にも、それを落としてくる速度的にも有利になる。ただ、バイナリファイルに対しては差分を作成せず、毎回全体を保存する。ただ、やっぱりバイナリファイルの方が大きい(ゲーム開発で言えば、絵だったり音だったり動画だったりするので)ので、そのやり取りに時間がかかるのはきつい。
 SVNはそこが改善されている。

 もう一つ、一般的に言われているSVNとCVSの違いとして、CVSはファイルごとにバージョンをつけるのに対し、SVNはリポジトリ(まあ、プロジェクト全体みたいなもの)に対して番号を振る。そのリポジトリに1000個のファイルがあったとして、そのどれか一つでも更新されたら、全体のバージョン番号をあげる。だから、SVNではバージョンの数字がかなり大きくなる。ただ、例えば一度にファイルを10個コミットしたとしても、バージョン番号が10上がるのではなく、1しか上がらない。コミットの回数でバージョンを上げる、みたいなイメージだ。ここがポイントだ。
 この利点は明白だ。先ほども書いたが、一般的にプログラムにある機能を追加する時、更新しなければいけないファイルは複数になる。で、誰かがその複数ファイルをコミットして、それに不具合があったとしよう。そうしたら、一つ前のバージョンに戻してそこから開発を再開したいものだが、CVSでは、バージョン番号はファイルごとなので、その「複数ファイル」というのを、タイムスタンプなどから見つけ出して、一つ一つ戻さないといけない。それに対し、SVNではリポジトリ全体に対してバージョン番号がつけられるので、その不具合のある機能の複数ファイルがコミットされる一つ前のバージョン番号に戻せば、その後何百個ファイルがコミットされていようと、それらを無かった事にできる。ファイル一つ一つ探して戻すCVSに対して、明らかに楽だ。

 他にもいくつか、SVNの利点はあるが、まあそんなこんなで、新しい分優れているので、大体SVNに置き換えられていった。

 少し先の話になるが、これを書いている2015年現在だと、バージョン管理システムの覇権はGitに移りつつあるように思える。
 Gitは、あのLinuxのリーナス・トーバルズが、そのLinuxの開発のために作ったバージョン管理システムらしい。先行するCVS、SVNと違うところは、Gitは分散型のバージョン管理システムというところだ。
 CVSやSVNは、集約型のバージョン管理システムとなる。中央にサーバがあり、バージョンがカウントされるのはそのサーバ上の話である。だから、例えば実験的な機能を入れてみるとか、そのためのブランチ(機能を平行開発するための分岐)を作るにしても、その中央サーバ上でやるので、他の全員に影響は波及する。
 Gitでは、ローカルリポジトリを作れる。つまり、自分の手元にコピーとしてリポジトリを作れる。これはこれで一つのバージョン管理システムとなっているので、ここで独自にファイルを追加してテストする事もできるし、ブランチを作って平行開発をする事も可能だ。バージョン管理システムなので、元に戻す事も好きにできる。
 ローカルリポジトリをいじる分には、中央のサーバには影響しないので、従って他の人に影響を与えない。最終的に動作が確認できたら、そのローカルリポジトリの内容を、中央に「プッシュ」する事で、更新を反映できる。
 集約型の場合、バージョン更新のためには中央にコミットするしかないので、「自分が作業中の途中の状態」に戻すのが難しい。そういう事をするためには、中央にコミットをして、要は全作業メンバーに変更を波及させないといけない。Gitはプッシュをするまでは全部手元なので、気軽にコミットができるし、気軽にブランチを切れる。これが便利なのだ。

 私はもうGitが主流になる頃には、エンジニアとしては卒業気味だったので、いまいちその細かい仕組みを理解していないが、まあそんな感じで今後も新しいテクノロジが出てきて覚えなきゃいけなくなるんだろうな〜、とか思うとちょっと気が重い。


前のMCLへ  次のMCLへ

戻る

TOM 2019