ドキュメントのバージョン管理はGitかSVNか？？？

いわゆる「設計書」の世代管理をするにあたって、「Gitじゃ使いにくい！」という意見を目にする機会がありました。

そもそも、Gitにしても、SVNにしても、テキストベースの開発リソースを管理するためのものであり、バイナリファイルの管理には不向きだと僕は思っています。

開発現場にいると、「設計書」をWordやExcel、PowerPointなどで作成している現場に直面します。
「日本人はExcel方眼紙好きだね。」と揶揄されるくらい、Excelで設計書を書いている現場がほとんどなんじゃないかと思います。

先に言っておきますが、Excelのファイル形式は、バージョン管理システムでは、バイナリファイルとして扱われます。
Excelファイルの変更差分をバイナリで観たって、誰もその内容わかりません。差分をマージしたくても、バイナリでマージしてしまったらExcelファイルそのものが壊れてしまいます。


## ドキュメント管理にSVNが利用されるのはなぜか？

しかし、開発現場では「設計書」などのドキュメント管理にSVNが使われています。
理由は様々だと思いますが、主な点として次のような理由が挙げられると思います。

- 使い慣れている
- ファイルシステムのフォルダ操作と同じように、空のフォルダを先に作っておける
- 簡易的ではあるが、ロックの仕組みがある


### もし、バージョン管理システムがなかったら・・・

ExcelやWordには、変更履歴を保持する機能を独自に持っています。これらを活用する方法もあると思います。
あるいは世代番号や、更新した日時を付与したファイル名で保存するなどの方法もあると思います。

けれども、開発プロジェクトはチーム戦です。
チームで作業する以上、同じファイルを共有する必要があります。
共有したファイルを複数人で同時に更新してしまったら・・・？
単純な世代管理が、バージョン管理システムで担保できているだけで、安心感があります。


### SVNが好まれるのは、構成管理の運用に適しているからか？

厳格なリソース管理・構成管理を日常的に運用している現場では、修正対象のリソースに対して、「払出申請」「取込申請」なるものを実施しているところもあります。誰が何時、どういう目的でどのリソースを更新したかを申請ベースで書類上で管理する仕組みです。それと同時に、許可された作業者のみに更新権限を与えることができます。

SVNには、簡易的ではあるものの、ロックの仕組みがあるため、疑似的に運用できます。

いや、単純に、先祖返りとマージの負荷が耐えられなくて、ロックの仕組みが使いたいだけかもしれません。


## 開発リソースのバージョン管理は集中型か？分散型か？

結局のところ、SVNに慣れ親しんできた人が急にGitへ移行すると、これまでと勝手が違うために戸惑っているだけのようにも思います。

SVNがクライアント・サーバー方式の集中型バージョン管理システムであるのに対し、Gitは分散型バージョン管理システムです。大きな違いとして、SVNではCOMMITすればサーバーに格納されたし、CHECKOUTすればサーバーから取って来れたのに対し、GitはPUSHしなければサーバー（中央リポジトリ）へ反映されないし、FETCHしなければサーバー（中央リポジトリ）から取得されません。

この大きな違いを理解し、運用できるかどうかは各個人のスキルです。早くGitに慣れましょう。

学習コストが・・・という記事を目にしますが、SVNだって最初そうだったでしょ？と言いたい。


### Gitが使いにくいのは、分散型だから？

Gitはその特性上、SVNのようにファイル単位でユーザーが個々にロックをするのはとても難しいのです。

GitでSVNのような運用をしたいのであれば、編集を開始する前に、PULL（FETCH + MERGE）し、COMMITしたら即座にPUSHしましょう。
万が一CONFLICTしたら・・・自分の変更を戻して、最新を取り込んでから再度、編集を開始しましょう。

テキストベースのリソースであれば、SVNよりもGitの方がうまくオートマージしてくれます。
が、残念な事にExcelはバイナリファイルです。手動でExcelを立ち上げて編集するしかありません。
そこで、『Gitがロックの仕組みを持っていないからだ～！』と怒るのはナンセンスです。そもそもSVNとは考え方が異なるのですから。

### そもそもGitに慣れないのには理由がある？（2018/07/29追記）

少々前に書かれた記事だが、「[SVN脳患者から見たGit](https://qiita.com/kaityo256/items/81e7951a1ca2706955a4)」の内容を読んでなるほどと思った。SVNとGitでは、コミット単位の管理の仕方や、ブランチそのものが全く異なる。SVNの方がよりファイルシステムに近いイメージで利用できるのかもしれない。

### Gitならではの使い方

GitHubや、GitLab、GitBucketなどの名立たるGitサーバーはPull-Request(Merge-Request)の機能を持っています。
これは、『こんな素晴らしい変更をしたから、レビューしてOKだったら取り込んでね！』と依頼する機能です。（だいぶ意訳していますが・・・）これには、（暗黙の？）ルールがあって、最新をベースに変更したものをPull-Requestしないといけません。たとえ、古いものをベースに変更したものをPull-Requestしても、レビュアーに一言『最新にREBASEしてね。』と言われるだけです。

こうして、中央リポジトリにはレビューされたものだけが、本流へマージされていくことになります。

僕は常々、GitはブランチとこのPull-Requestの機能をうまく運用してこそのものだと考えています。


### けれどもやっぱり・・・

実は、サイズの大きなバイナリファイルをGitで扱うのはあまりお勧めできません。
Gitで管理されるリソースは、圧縮されますが、既に圧縮済みのバイナリファイルはそれ以上圧縮できませんから、サイズが大きくなればなるほどPUSHやFETCHにかかる時間に影響を与えることになります。
Excelのファイル形式にしろ、Javaのライブラリ単位であるJARファイルにしろ、既に圧縮されているバイナリファイルです。

僕個人の意見としては『Gitでの管理対象リソースとして、バイナリファイルは必要最小限とし、可能であれば画像だけにする。』としたいですね。

### Git LFS（Git Large File Storage）という解決策 （2019/01/29追記）

「[Git LFS（Git Large File Storage）](https://git-lfs.github.com/)というのがあるよ。」とコメントいただきましたので、僕なりにネットの記事をあさってみました。

サイズの大きなバイナリファイルをGitで扱う手段として、Git LFSというものがあります。
Gitリポジトリとは別にLFSサーバーを運用し、サイズの大きなファイルをLFSサーバーへ置き、Gitリポジトリそのものの肥大化を抑えてパフォーマンスの低下を避けようというものです。

- [GitHub](https://help.github.com/articles/configuring-git-large-file-storage/)
- [GitLab](https://docs.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html)
- [BitBucket](https://confluence.atlassian.com/bitbucket/git-large-file-storage-in-bitbucket-829078514.html)

などが、既にサービスとして提供しているようです。

Gitを運用している実績があり、開発リソースとして大きなバイナリファイルを扱う必要がある場合は、現実的な解決策の一つとしてGit LFSの活用を検討してみるのも良いかと思います。

ただし、Qiitaの記事「[Git LFS で大きめのバイナリファイルも Git で管理する](https://qiita.com/msh5/items/582c086311d3630563bc)」にも書かれているように、Gitのメリットを一部犠牲にしてしまう部分もあり、運用に乗せるにあたっては十分に検討する必要があると思います。

いずれにせよ、目的に応じて最適なものを取捨選択できるようにしたいものです。


### SVN、Git以外の選択肢

分散型バージョン管理システムには、Gitの他に、Mercurialというものがあります。
コマンド体系がSVNに似ているので、SVNからの移行がGitと比べると少し楽かもしれません。
また、バイナリファイルも効率よく管理するように考えられているようですから、その点でもGitより良いかもしれません。


## まとめ

- 学習コストや、作業効率を優先するのであれば、慣れているものを使おう
- 将来性を考えるなら、分散型バージョン管理システム（Git、Mercurial など）を勉強しておきましょう。
    - 今まさに初めて使っているなら、良い勉強の機会ととらえて前向きに
- 設計書もそろそろ、Markdownで書く時代じゃね！？
    - ISSUESとWikiで良くないかい？