情報システム部の中嶋です。弊社では以前よりバージョン管理システム(VCS)としてGitを採用していますが、Subversion歴が長い*1おっさんとしては、ここで一度ちゃんと比較をしておきたいと思い、4つの論点で比較してみました。(まぁ、控えめに言ってポエムです。)
なお、「チェンジセット記録型」とか「タグ型ブランチ」とかの用語はこの文書のために作った造語なので、あまり気にせずに読み飛ばしていただけると幸いです。
TL;DR
悲観的排他かつ集中リポジトリ | 楽観的排他かつ集中リポジトリ | 楽観的排他かつ分散リポジトリ | |
---|---|---|---|
チェンジセット記録型かつコピー型ブランチ | VSSなど | Subversion、TFVCなど | (たぶんない) |
スナップショット記録型かつタグ型ブランチ | (たぶんない) | (たぶんない) | Gitなど |
「SubversinよりGit」と言う人は、超巨大プロジェクトを扱う機会がなく、マージ先を柔軟に選択できるからGitを選んでいるのではないかと思う。
論点1:悲観的排他と楽観的排他
悲観的排他とは
悲観的排他とは、ファイルの更新をそのファイルの更新権(ロック)を取得してから行うことにより、同時に複数人が同じファイルを更新してしまうことを抑止する方法である。ロックを取得することを「チェックアウト」、更新内容をリポジトリに格納しロックを手放すことを「チェックイン」と呼ぶことが多い。有名なVCSとしては、Visual SourceSafe(VSS)などがある。
楽観的排他とは
楽観的排他とは、「同時に複数人が同じファイルを更新することは稀なので、コンフリクトしたら(自分が修正したファイルを他人も修正してすでにコミットしていたら)その時に考える」という方法である。通常、コンフリクトした場合には、マージを行なってから再度コミットする。一般的に、更新内容をリポジトリに格納することを「コミット」と呼ぶが、リポジトリに格納した更新内容自体をコミットと呼ぶVCSもある。また「チェックアウト」という用語も「作業場の内容を最新に更新する」的な意味で用いられるが、詳細はVCSによって異なる。
近年の楽観的排他を用いたVCSは、コンフリクトが発生した場合にも自動的にマージすることができるようになってきたため、編集者がコンフリクトを解決する場面は少なくなってきている。有名なVCSには、Subversion*2やTeam Foundation Version Control(TFVC)、Gitなどがある。
なお、Subversion*3やTFVCは悲観的排他もサポートしている。
楽観的排他に対する悲観的排他の利点
絶対にコンフリクトが起きないので、マージの必要がない。よって、マージミスは絶対に発生しない。また、Excelファイルなどテキスト形式でないファイルはマージが行えない(もしくは難しい)ため、初めからコンフリクトが起きないようにしておくことは有効な方法である。
悲観的排他に対する楽観的排他の利点
ロックの取得が不要であるため、「他人が更新中であるため、自分が更新できない」という事態が発生しない。また、ロックを取得したまま長期間放置する不心得者への対処が不要である。
論点2:集中と分散
集中リポジトリ型とは
集中リポジトリ型とは、中央にある1つのリポジトリを複数の編集者が共有する方法である。有名なVCSには、VSS、Subversion、TFVCなどがある。
分散リポジトリ型とは
分散リポジトリ型とは、編集者個々がそれぞれリポジトリを所有し、必要に応じてリポジトリ同士を同期させる方法である。一般的には、中央リポジトリを1つ用意し、各編集者はそのリポジトリのコピー(クローン)を手元に作成して使用する。「プッシュ」や「プル」などクローン元のリポジトリと同期するための操作がある。有名なVCSには、Git、Mercurial、BitKeeperなどがある。
分散リポジトリ型に対する集中リポジトリ型の利点
リポジトリをクローンする必要がない。極端に大規模なプロジェクトでなければ問題となることはないが、実際にWindows OSやOfficeなどはリポジトリが巨大であるために分散リポジトリ型への移行が行えずにいた。Microsoftはこの問題を解決するためにGit専用のファイルシステムまで開発することになった*4。また、原理的に悲観的排他は集中リポジトリ型でなければ実現できない*5。
集中リポジトリ型に対する分散リポジトリ型の利点
リポジトリは必ず編集者の手元にあるため、オフラインな環境であってもリポジトリにアクセスできる。また、試行錯誤の過程をリポジトリに格納しながら他人には公開しないといったことも行える。
ファイルの更新履歴の表示なども、リポジトリが手元にあるため高速に行うことができる。ただし、ここで表示されるファイルの更新履歴は編集者の手元のリポジトリに記録されているものであるため、必ずしも中央リポジトリと同じとは限らない。
論点3:チェンジセットとスナップショット
チェンジセット記録型とは
チェンジセット記録型とは、コミットの際、リポジトリに現在の状態と直前の状態との変更差分を記録する方法である。リポリトリに格納する操作を「コミットする」、コミットする対象を「チェンジセット」と呼ぶ場合が多い。有名なVCSに、SubversionやTFVCなどがある。
なおVSSも変更差分を記録しているが、同時に行われた複数のファイルに対する変更を1つのものとしては認識せず、ファイル単位で管理している。
スナップショット記録型とは
スナップショット記録型とは、コミットの際、リポジトリに管理対象ファイル全ての現在の状態を記録する方法である。リポジトリに格納する操作も格納する対象も「コミット(する)」と呼ぶ。この方式を採用している代表的なVCSにはGit*6がある。
スナップショット記録型に対するチェンジセット記録型の利点
スナップショット記録型のVCSではファイル名の変更を記録することができない*7。Gitではこの問題を回避するために、「削除されたファイルと追加されたファイルの内容が非常に似ている場合、それはファイル名が変更されたものだ」と判断しUIに表示している。
また、スナップショット記録型は必ず管理対象ファイル全てを同時に扱うため管理対象ファイルの一部だけをチェックアウトすることができないが、チェンジセット記録型では1つのリポジトリのうち必要な部分だけをチェックアウトし作業を行うことができる。このため、チェンジセット記録型はスナップショット記録型よりも大規模プロジェクトに向いていると言える。
さらにスナップショット記録型は各時点でのファイルのスナップショットを保持しているため、チェンジセット記録型よりも格納しているデータ量が多く、特に大きなファイルが存在する場合、リポジトリの肥大化が顕著である。
チェンジセット記録型に対するスナップショット記録型の利点
チェンジセット記録型のVCSでは1つ1つのコミットが変更差分しか持っていないため、1つのコミットだけを独立して操作することはできない。しかしスナップショット記録型のVCSは、そのリポジトリが管理対象としている全てのファイルを1つのコミットの中に保持しているため、1つのコミットだけを独立して操作することができる。これによりコミット順序の変更やコミット単位の調整、分岐元/分岐先以外へのマージが行えるようになっている。
論点4:コピーとタグ
コピー型ブランチとは
コピー型ブランチとは、ブランチの作成を「リポジトリ内でのファイルコピー」として扱い記録する方法である。有名なVCSに、Subversion*8やTFVCなどがある。
なおVSSもコピー型ブランチを採用していると言えるが、ブランチを作成するという行為自体が悲観的排他の利点を損なう行為であるため、利用場面は限られる*9。
タグ型ブランチとは
タグ型ブランチとは、ブランチを「コミット指し示すポインタ(ここではタグと表現する)」として扱い記録する方法である。この方式を採用している代表的なVCSには、Git*10がある。
タグ型ブランチに対するコピー型ブランチの利点
コピー型ブランチでは、1つの作業領域に1度に複数のブランチを展開することができる。このため、あるブランチで作業中に他のブランチの作業を行う際、作業中の修正をコミット/退避する必要がない。
また、試験的な実装を行なっている複数ブランチのプログラムを同時に実行するなど、柔軟な対応が行える。これに対し、タグ型ブランチでは1つのブランチしか展開することができない。1つのファイルシステム上に複数のブランチを同時に展開するためには、リポジトリ自体をクローンする必要がある。
コピー型ブランチに対するタグ型ブランチの利点
タグ型ブランチを採用しているVCSにおいて「ブランチの作成」とは、すなわち「タグの作成」であるため、その処理は非常に高速である*11。また、ブランチの内容が作成時に固定化されないため、ブランチの分岐位置を作成後に容易に変更することができる。さらにマージに関しても、マージ先のブランチが指すコミットがマージ元のコミット履歴に含まれる場合はマージ先ブランチが指すコミットを変更するだけで高速かつ確実にマージを行うことができる。
ということで、一通りまとめてみました。後発のシステムほどそれまでのシステムが抱えている問題を解決した良いものになっているわけですが、「人は何かの犠牲なしに何も得ることはできない。」ので多かれ少なかれトレードオフがあったりします。
「じゃぁ、どれを採るか」ということですが、コードレビューをしっかりやるとどうしても変更履歴が無駄に増えてしまうので、ここを変更できるGitが今の所一番使いやすいのかなぁと思っています。
2017年9月8日
公開当初、Mercurialも比較対象に含めておりましたが、内容に誤りがございましたので削除いたしました。
*1:自分は2017年4月にアットホームにJoinしましたが、それまではSubversionやTFVCを使用していました。
*2:http://jtdan.com/vcs/svn/svn-book/svn.basic.vsn-models.html#svn.basic.vsn-models.copy-merge
*3:http://jtdan.com/vcs/svn/svn-book/svn.advanced.locking.html
*4:https://www.infoq.com/jp/news/2017/02/GVFS
*5:Mercurialにはその名もズバリ「Lock Extension」という分散なのに悲観的排他が実現できるエクステンションがあるそうなので、「原理的に〜実現できない」は大げさな表現かもしれません。
*6:https://git-scm.com/book/ja/v1/使い始める-Gitの基本
*7:Subversionも、リポジトリがVer.1.8になるまでは変更が追跡できないことが多かった記憶があります。Ver.1.8になってから、変更の追跡もマージも一気に優秀になりました。
*8:http://jtdan.com/vcs/svn/svn-book/svn.branchmerge.using.html
*9:VSSでも「ファイルの共有」を使用することでバージョン別のブランチを悲観的排他で運用することが可能ですが、マージが前提となるトピックブランチのような運用は想定されていないと思われます。
*10:https://git-scm.com/book/ja/v2/Git-のブランチ機能-ブランチとは
*11:実際にはSubversionにおけるブランチ作成も「このリビジョンからこのブランチを作成した」という情報だけをコミットしているので、そこまでの速度差はないと思われます。