eyecatch
Thu, Oct 6, 2016

Gitの良さが分からない? ちょっとそこに座れ

Gitの良さがいまだに分からないという人がいるようなので、Git派の一人としてSubversion(以下SVN)と比較してのGitの良さ(メリット)について語りたい。 (GitとSVNの違いについては他の人の記事に詳しいのであまり書いていない一方、勢い余ってGitのデメリットも書いた。) 本題に入る前に、冒頭にリンクを貼った記事についてひとつだけつっこんでおく。 つっこみどころは他にも沢山あるけど。 ※話の前提としてgitとSVNを採用している現場に下記のような割と違いがあるとする。 git イシューごとにブランチを切り、ローカルでコミットして、リモートブランチにpushして、GitHub・GitLab・Bitbucket経由でマージリクエスト。コードレビューの後にマージ。 SVN リモートのtrunkに個々人が直接コミット。コードレビューはあまりない。ブランチを切ることもない。 このような違いが出る背景には次のものがある。 gitを採用する現場は、猫も杓子もgit-flowというプラクティスに従う傾向がある gitを採用する現場は、コードの品質もある程度管理する傾向がある SVNは集中型でありブランチ機能などが非常に使いにくい SVNを採用する現場はコードの品質よりも「リリースに含めるならさっさとコミット」と考える傾向がある この前提には無理がある。 Gitのところに書いてあるのが、Gitというツールの枠を大きくはみだしたGitHub Flowというブランチ戦略+開発プロセスに当たるものであり、 それでGitを批判するのはお門違いであろうという点については、Gitの流行がGitHubの人気によるところが大きく、GitHubを使えることがGitの大きなメリットであるので、目をつむることにする。(マージリクエストを使う羽目になるデメリットなんて言いがかりでしかないとだけ言っておく。) 看過できないのは、SVNを使った開発がコードレビューもブランチもないという点。 どこの世界の話をしているんだろうか。 Gitが世に出る前は世間にコードレビューもブランチもあまりなかったかのような前提だが、もちろんそんなことは全くない。 60万個以上のOSSプロジェクト情報を統括するOpen HUBによれば、OSSプロジェクトの46%がSVNを使っている。この中にはGitの誕生以降にSVNを使い始めたプロジェクトも多くある。270000余りのプロジェクトの大部分がブランチすら使っていないとでも? GitHub Flowと対比するために無理やりこじつけたんだろうけど、その無理のせいで議論のスタート地点からめちゃくちゃだ。 まともな開発にはコードレビューもブランチも必要だ。 品質管理もリリース管理もしないなら要らないのかもしれないが、そんないい加減な開発現場を前提にSVNかGitかなんて議論しても意味がない。 高品質なソフトウェアを効率よく開発するために則りたい素晴らしい開発フローがあるとして、そのフローをSVNやGitやその他のツールないしひょっとしたらアナクロな日付フォルダの内どれがもっとも上手く実現してくれるか、というのがあるべき議論だ。 この「素晴らしい開発フロー」には一般的に品質管理と並行開発が含まれていて、それらにはコードレビューとブランチの利用が含まれている。 Git(+GitHub)がこんなにも急速にSVNに取って代わって流行ったのは、分散リポジトリの仕組みとブランチの軽量な実装によって効率的な並行開発が実現でき、またプルリクエストなどの機能によりコードレビューを含む快適なソーシャルコーディングが実現できるからだ。 逆に言えば、Gitが流行ったことが、人々が効率的な並行開発やコードレビューを開発フローに取り入れたかった証拠と言えるかもしれない。 Gitのメリット 前置きが長くなったが、少なくともブランチとコードレビューを活用した高品質で高効率なソフトウェア開発をしたいという前提で、SVNに対するGitのメリットを挙げてみたい。 1. リポジトリ構造がシンプル Gitリポジトリはすごくシンプルに作られているそうな。 確かに、その構造を見ると、add、commit、log、resetくらいは自前ですぐに実装できそうだ。 このシンプルな構造のおかげで、Gitリポジトリは壊れにくい。ここで壊れにくいとは、リポジトリ内部で不整合が起こりにくいということで、コマンドミスでコミット履歴が一部消えたりとかいうトラブルは壊れるに入らない。 実のところSVNリポジトリの構造を知らないので経験的なことしか言えないが、SVNリポジトリ(というより作業ディレクトリの管理情報?)はちょくちょく変な状態になり、クリーンアップしたり、酷い時には.svn内のファイルを手動でいじったりしなければならなかった。 因みに、シンプルというのはリポジトリサイズがすごく小さいということにはならず、同等の履歴を含むGitリポジトリとSVNリポジトリはだいたい同サイズなんだそうな。 2. ブランチが軽い Gitのブランチは単一のコミットを指す参照で、リポジトリ内ではSHA-1ハッシュ値が書かれただけのたった一つのファイルに過ぎない。 その為ブランチは一瞬で作成できるし、ディスクも圧迫しないので、じゃんじゃん作ってじゃんじゃん消せる。 さらに、ローカルリポジトリに過去の全ファイルの全バージョンが入っているという分散リポジトリの特長のおかげで、ブランチの切り替えも軽快にできる。 ローカルから必要なファイルを作業ディレクトリに展開するだけなので。 一方SVNはそもそもブランチをサポートする直接的な機能がないため、ブランチはリビジョンのコピーという形で実装されている。 コピーと言ってもハードリンクみたいなものでディスク上に物理的なコピーが作られるわけではなく、軽量という点ではGitと大差ないが、集中リポジトリなせいでブランチの切り替えには差が出る。 svn switchにしろsvn checkoutにしろネットワークの向こうのサーバとの通信が必要なので、それなりの時間がかかるし、通信が途切れると切り替えられなくなる。 冒頭に貼った記事にはGitはブランチを切り替える際にstashとかしないといけなくて面倒とあったが、そんなのSVNだって同じだし、stashすればいいだけだし、stashという機能があるだけSVNよりまし。Gitならコミットはあとから書き変えられるので、stashの代わりに一時的にコミットしちゃってもいい。 それも嫌ならworktree使えばよろしい。 3. バージョン間の差分取得が速い Gitは全てのファイルについて全てのバージョンのコンテンツをまるまるリポジトリに持っている。 一方SVNのリポジトリにはバージョン間の変更が記録されている。 このため、あるファイルについて任意のバージョン間の差分を取るのに、Gitはシンプルにそれぞれのバージョンのファイルを取り出して比較するだけでよいが、SVNは隣り合ったバージョンでなければバージョン間の変更を足し合わせて差分を計算しなければいけない。 さらに、Gitは比較するファイルをローカルリポジトリから取り出すだけでよいが、SVNはサーバへのアクセスが必要なので、差分取得はGitの方が大分速い。 4. ログ取得が速い Gitのコミットは常にプロジェクトの全ファイルに対するものだ。 これは変更したファイルの一部だけを対象とするコミット操作ができないという意味ではない。 Gitがひとつのコミット操作をコミットオブジェクトと呼ばれる単一のファイルに記録し、そのファイルが常にプロジェクトの全ファイルの特定のバージョンを参照しているという意味だ。(正確に言うとこのファイル自身に全ての参照が記録されているわけではないが。) このためGitのコミット履歴は実にシンプルで、ログ一覧を取得するには単にコミットをたどりながらコミットオブジェクトに書かれたログを集めればいい。 一方SVNはファイル毎にバージョンを管理するので、もう少しややこしい。 さらに、Gitはコミットオブジェクトをローカルリポジトリから持ってこれるがSVNは(以下略)。 5. オフラインでだいたいなんでもできる と、ここまで書いて、Gitのいいところはオフライン作業が捗るところではないかと思い立った。 実際Gitは、clone、fetch、pull、pushといったあからさまな操作以外はオフラインでできる。 多くの操作にネットワーク通信コストを払わなくていい上、リモートリポジトリサーバが落ちたりネットワークが落ちたり山に籠ったりしていても作業が続けられる。 ノマドに最適。 一方SVNがネットワーク通信なしでできることは、…ベースバージョンとのdiffくらい?