#GitHub Flow
31 Aug 2011
##git-flowの問題点 (Issues with git-flow)
最近Gitについての講義などで色々な所へよく行きますが、ほとんどどこへ行ってもgit-flow
について意見を聞かれ、そういう時は「まあ、良いと思いますよ」と答えています。git-flow
は想定されるあらゆる作業フローで運用でき、ドキュメントも揃っていてテストも良くされているGitシステムを使っていて、Gitの柔軟な作業フローは単純明快であらゆる開発者に使われています(訳注:git-flow
が良いとは思わないが、とても素晴らしいGitを使っているって事は評価するよって感じの皮肉)。git-flow
自体は一応一部業界での標準になっているようで、開発者がプロジェクトや会社を移動したとしてもこの標準化されたフローに素早く順応出来るようです。
ですが、git-flow
にはやはり問題があります。小さいところで言うと、新しい機能用のブランチを master
ではなく develop
ブランチから作らないといけない事とか、hotfixのやり方が気に入らないなど結構な数の人から聞いた事があります。
しかしより重要な問題はgit-flow
は一般開発者や開発チームが必要とする知識以上に複雑だと言う事です。どのぐらい複雑かと言うと、git-flow
では独自の作業フローを間違えないようにする為に、大量のヘルパースクリプトが開発されている程です。これはこれで良いとしても、このヘルパースクリプトはコマンドラインでのみ実行可能でGitGUI版では実行出来ず、コマンドラインからではこのシステムを満足に使えない人が、この複雑怪奇なgit-flow
の作業フローを完全に理解しないといけない(実際、全ステップを手作業でやるしかない訳で)という皮肉があり、これが最大の問題です。
git-flow
ではこの2つの問題がありますがこれらの問題を解決する方法は簡単で、ただ単純により簡単な手順でやれば良いだけなんです。GitHubではgit-flow
は使ってませんし、より簡単なGitの作業フロー(訳注:git-flow
をもじってGit workflow
と書いてある)をずっと使い続けています。
簡単だからこそさまざまなメリットがあるのです。一つは簡単に理解出来ると言う事で、これはつまり理解するまでに時間がかからず、何をやってるか分からなくなったり、間違った作業をやり直したりする事もほとんどありません。もう一つは複雑な作業フローを強制するラッパースクリプトが必要ない為、GUI版を使う事も全然問題ありません。
##GitHubフロー
GitHubでgit-flow
を使わない理由は、簡単に言うとGitHubでは常にデプロイをしているからです。git-flow
のプロセスは"リリース"と言う事を中心にデザインされていますが、GitHubでは常に本番環境へデプロイしている為(日に5,6回程度)、"リリース"と言う概念はありません。チャットルームロボット(CIからの結果が表示される場所と同じ場所にあります)なんかも使ってますし、テストや本番環境へのファイル適用などの工程を出来るだけ簡単にしている事もあり、これだけ頻繁であっても従業員は皆ストレスなくデプロイが行えています。
これだけ頻繁にデプロイする事のメリットも実はあって、数時間毎にデプロイしているからこそ重大なバグを埋め込んでしまう事がほぼありません。このやり方であれば小さなバグなどは入ってしまうかも知れませんが、次のデプロイですぐに直せます。リリース単位で本番環境へデプロイするような通常の場合であれば、このような小さなバグは"hotfix"と称してや、通常の作業の流れから外して修正していると思います。ですが、GitHubではこのような小さな修正は通常作業の一部で、hotfixも小さい機能追加も区別は特にないんです。
常にデプロイする他のメリットとして、全体の問題を簡単に把握出来ると言う事もあります。全体渡って同じ流れで且つシンプルにしている為、見つけたセキュリティに関わるバグにすぐ反応出来たり、小さくても良い機能追加を超迅速に行えたり、その上通常行うこのような変更や、もっと大きな機能追加であっても全く同じやり方で確認する事が出来るんです。
GitHubフローとはなんなのか?
- masterブランチにあればそれはつまりデプロイ出来ると言う事
- 何か新しい事を始める時は、名前から見て意味が分かるブランチ名をmasterから作る事(例:new-oauth2-scopes)
- そしてローカル上でそのブランチへコミットをしていき、定期的に同じ名前のブランチをサーバーにもプッシュする事
- みんなの意見や助言が要る時や、マージしても良い頃かなと思えばプルリクエストをする事
- レビューがされ、この変更に許可が出た後でのみmasterにマージする事
- masterにマージされた後には、デプロイ出来る状態のはずなので、速やかにデプロイする事
これで全部です。とても簡素ですがそれなりに大きいチームでも効率よく機能しています。(GitHubは現在従業員35人で、大体15-20人ぐらいが同時に同じプロジェクト(github.com)で作業していますし、このぐらいの人数が一般的な開発チーム(同じメソッドをいじっててコンフリクトしてしまうような程の人数)のサイズなのではないでしょうか? いずれにしてもこのぐらいの人数が活気があって素早く継続的な開発が出来る人数だと思います。
順を追って各ステップを見ていきましょう。
masterと名付けた詳細にわたるあらゆるアイデアが盛り込まれている唯一のブランチを持つ事。これが基本的にこのシステムにおいて一番制約のあるルールです。GitHubにおいては、masterブランチはすでにデプロイされている、または少なくとも数時間の内にはデプロイされると言う事意味しています。コミットの巻き戻しが起きる事はほぼあり得ず、問題が起きてもコミットをrevert
したり問題を修正する新しいコミットをしたりし、masterブランチ自体はほぼロールバックされる事はありません。
masterブランチは安定していて、常時、デプロイしても新しいブランチを作っても問題が無い状態であるべきです。もし、masterに未テストのものや、ビルドが失敗するようなものをプッシュした場合、開発チームとの対話をしてないと言う事でありこれはあってはいけません。GitHubでプッシュされる全てのブランチはテスト済みでチャットルームで報告されます。ローカルでテスト出来ない場合などはサーバーにトピックブランチなどとしてプッシュして、Jenkinsが結果を報告させるようにしています。
デプロイする時だけに使うデプロイ済みのブランチを用意する事も出来ますが、GitHubではしていません。GitHubでは現デプロイ済みのSHAをWebアプリで通知しており、ソースの比較が必要ならcurlして見れば良いだけです。
何か始めるにはまず、見て意味の分かる名前の付いたブランチを先程からの説明通り安定しているmasterから作ります。現在のGitHubのソースコードから例を出すと user-content-cache-key、 submodules-init-task、 redis2-transition などがあります。これにはfetchした時にみんなの作業している課題が分かったり、自分が作ったブランチを放置して戻ってきた時に、何をやってたのか簡単に分かるなどのメリットがあります。
GitHubのブランチリストのページを見てみると分かるように最近どんなブランチで作業があったか簡単に分かるし、大体どのぐらいの作業量があったのかも分かります。
おおざっぱに言えば現在の開発状況込みで、近々盛り込まれる機能のリストが出来ているとも言えます。まだこのページを使った事がなかったら見てみてください。とっても便利なんです。このページでは現在選択中のブランチから派生した、それぞれ個々の機能改善のみを含むブランチが表示され、ソートもされているので最終更新の降順で並んでいます。ブランチの内容が気になったら、「compare」ボタンを押して実際のunified diff
やこのブランチにだけ関連するコミットのリストを見たりも出来ます。
なので今書いている途中でも、まだマージされていない44ものブランチがGitHub自身のリポジトリにあり、この1週間で9から10個程度のブランチがmasterにプッシュされた事も分かります。
git-flow
とのもう1つの大きな違いとして、GitHubでは名前付きブランチを常にサーバーにプッシュしていると言う事です。これは開発するに当たってmasterブランチだけが重要なので、サーバーに他のブランチをプッシュしたからと言って何も壊しませんし、変にもなりません。master以外のものはただ単に何かの作業中だと言う事です。
これをするからこそ、パソコンを無くしたり、ハードディスクが壊れたりしてもバックアップがある事が保証されます。さらに重要な事は、git fetch
するだけでみんなの作業している事がTODOリストのように表示され、開発チームで常にコミュニケーションを取る事が出来ると言う事にあります。
$ git fetch
remote: Counting objects: 3032, done.
remote: Compressing objects: 100% (947/947), done.
remote: Total 2672 (delta 1993), reused 2328 (delta 1689)
Receiving objects: 100% (2672/2672), 16.45 MiB | 1.04 MiB/s, done.
Resolving deltas: 100% (1993/1993), completed with 213 local objects.
From github.com:github/github
* [new branch] charlock-linguist -> origin/charlock-linguist
* [new branch] enterprise-non-config -> origin/enterprise-non-config
* [new branch] fi-signup -> origin/fi-signup
2647a42..4d6d2c2 git-http-server -> origin/git-http-server
* [new branch] knyle-style-commits -> origin/knyle-style-commits
157d2b0..d33e00d master -> origin/master
* [new branch] menu-behavior-act-i -> origin/menu-behavior-act-i
ea1c5e2..dfd315a no-inline-js-config -> origin/no-inline-js-config
* [new branch] svg-tests -> origin/svg-tests
87bb870..9da23f3 view-modes -> origin/view-modes
* [new branch] wild-renaming -> origin/wild-renaming
誰でもGitHubのブランチリストページをを見るだけでみんなが何をしているのか分かるし、内容を見て一緒に作業を手伝う事も可能です。
GitHubではプルリクエストという素晴らしいコードレビューの仕組みがありますが、悲しい事にあんまり知られていません。使われている場合でもほとんどの人はオープンソース開発などで使っていて、あるプロジェクトをフォークして、改良して、メンテナンスしている人にプルリクエストするみたいな感じだけです。でも、GitHubの開発メンバーでもやっているように、自分たちの専用のコードレビューとして使う事も簡単に出来るんです。
実際にGitHubでは実質的な「プルリクエスト」としてより、ブランチ用の会話ビューとして使っています。GitHubサイトでは公開、非公開を問わず単一のプロジェクト内でもブランチからブランチへプルリクエスト出来るようになっています。だから「分からない所があるのですが、誰か見てくれませんか?」や「マージしてください」と言う事だけでも使って良いのです。
ここで分かるようにジョシュがブライアンに見てもらう為にCCしています。その後ブライアンが来てコードに助言をしています。下の方に行くと、ジョシュがブライアンの懸念に気づきコードを改善していっています。
最終的にまだこの段階でコードは試作中だと見て取れます。まだデプロイ出来る状態のブランチではないため、実際にmasterにデプロイするまでには何度もプルリクエストを行っていきます。
もし自分がやっている機能改善やブランチの課程で分からない事があったり、誰かの助言が必要になった場合、もしくは自身が開発担当でデザイナーに確認してもらいたいとか(その反対でも)、さらにはほぼ全くコードが無いにしてもスクリーンショットで見せれたり、ただ単に良いアイデアを思いついた時でも、いつでもプルリクエストして良いんです。GitHubでは@username
にCCを足して連絡できるので、特定の誰かに見てもらいたい場合は単純に最初のメッセージ(PRメッセージ)でCCすれば良いんです(上のジョシュの例参照)。
これはとても便利で、プルリクエスト機能はunified diff
の個々の行、それぞれのコミット、プルリクエスト自体にコメント出来るし、これらの全ては単一で一連の流れで表示されます。さらにはこのブランチにプッシュする事も続けて出来るので、忘れていた事について誰かがコメントしても、バグがあってもこれらを修正してブランチにプッシュでき、GitHubのプルリクエストページではこれらの最新のコミットが表示されて、と言ったようにブランチ内で繰り返し作業出来ます。
そうすればプルリクエストページやコミットリストで、いつこのブランチが最新のmasterブランチを取り込んだのかもわかり便利になります。
ブランチで全部が本当に確実に完成して、デプロイ出来る状態になったと思えば次のステップに進みましょう。
GitHubでは直接masterで作業したり、ブランチが完成したと思ってもそう簡単にmasterへマージしません。事前に社内の他の誰かに許可を得てから行います。通常これは +1 とか絵文字とか「:shipit:
」などの単純なコメントだけですが、とにかく事前に誰かに見てもらうようにしています。
上のようなコメントがあってから、且つブランチがCIで問題なければデプロイすべくmasterへマージします(これで同時に自動でプルリクエストが閉じられます)。
これでやっと作業が終わり、masterブランチにマージされました。これが意味するところは今すぐにデプロイしないにしても、他のみんなはこの最新のmasterの状態を元に作業を始めます。さらに次のデプロイが(とは言っても数時間は空きますが)新たなmasterとなっていきます。せっかく書いたコードを他の誰かに変に上書きされたりしたくは無いでしょうから、大体みんなは自分のマージがうまくいったか確認したり、それぞれが思うところの変更をプッシュしたりします。
当社の campfire ボットから(ヒューボットと言う名前ですが)社員が誰がやってもデプロイできるようになっています。なので簡単な
hubot depoy github to production
と言うコマンドがコードをデプロイしダウンタイムゼロで必要な全プロセスを再起動させます。これがどのぐらいGitHubで当たり前な事か下を見てください。
一日の内にサポート担当やデザイナーを含めて6人の別々な人が24回ほどデプロイしているのが分かると思います。
自分自身もたった一行の変更のみのコミットをしたブランチを何度かデプロイした事があります。見ての通りこの処理は簡単、直感的、スケーラブルで且つとても便利です。だからこそ2週間もかけて作ったそれぞれで50コミットもした複数のブランチであっても、10分で作った1コミット分であっても、これがこれだけ簡単なコマンドだからこそ、たったの1コミット変更だけだったとしても、ほぼ無視出来るような小さな変更で無い限りは誰もわざわざやらないでおく事が無いんです。
ヒューボットにコマンドを打ち、デプロイ出来るような環境はとても簡単ですし、とにかく便利です。たぶんほとんどの人はGitHubは安定したプラットフォーム上にあると感じていると思いますが、これには背景に上記のような簡単な作業でデプロイまでできると言う事があり、問題があればすぐに提示され新機能は次々と盛り込まれ、当社では質や安定性に妥協が無いからこそより開発スピードが増し、より簡素に、より少ないプロセスで開発ができている事が結果として表れていると思います。
Gitはそれ自体が全体を把握する事が結構難しい為、必要以上に複雑な作業フローでGitを使うと言う事は(訳注:要はgit-flow
を皮肉ってる)、関わる全ての人へ日々、多大な心労をさせてしまいます。だからこそGitHubではチームで使うシステムは、それ自体では動かなくなってしまう程簡素な構造から絶対的に必要な構造を追加していくような、可能な限りシンプルなものになるようにと常に言っています。
比較的長めの周期(リリース間が数週間から数ヶ月)で行う昔ながらのリリースをし、hotfix、ブランチのメンテナンスや、リリースの間で起こるその他の事などを、即時では無く非定期的にすればいいような開発チーム(訳注:GitHubとは対極にあるような開発体制を相当皮肉ってる)であれば、git-flow
は良いと思いますし、この場合ならば本当にgit-flow
を使った方が良いと思っています。
反対に継続的なテスト、デプロイをし、日々本番環境にプッシュするような事に抵抗のない開発チームにとっては、上記までに説明のGitHubフロー
のようなより簡単な工程で開発する事をお勧めします。