Git実務ワークフロー #1 ブランチ戦略 — GitHub Flowとtrunk-based
Git基礎 #3 ブランチとマージ — fast-forwardと3-wayではブランチを作るコストが実質0であることを確認し、Git基礎 #6 GitHubと初めてのPull RequestではブランチをPRで合流させる手順を実習しました。ところがチームに入ると、すぐに次の質問にぶつかります。ブランチをどんな名前でどこから作り、どこにマージし、デプロイはどの時点で行うのか。この質問に対するチームの合意がブランチ戦略です。このシリーズはGit基礎の続編として、コマンドではなくチームでGitを運用する方法を扱います。
全7回で構成します。
- #1 ブランチ戦略 — GitHub Flowとtrunk-based ← この記事
- #2 rebase vs merge — 判断基準と禁則
- #3 interactive rebase — squash・fixupでコミット整理
- #4 コンフリクト解決 — 構造とmergetool・rerere
- #5 PR運用 — レビュー単位・コミットメッセージ・draft PR
- #6 stash・cherry-pick・bisect — 日常ツールセット
- #7 モノレポとGit — sparse-checkout・サブモジュール・LFS
今回は、実務で出会う代表的な3つの戦略、Git Flow、GitHub Flow、trunk-based developmentの構造を順に見て、最後に選択基準を表に整理します。
戦略がないと起きること #
ブランチ戦略なしに各自がブランチを作り始めると、数日のうちに同じ問題が繰り返されます。デプロイしようとするコードにまだ検証されていない変更が混ざり、急ぎのバグ修正をどこから分岐すべきか誰も答えられず、長く生き残ったブランチがmainと大きく離れて、マージのたびに大きなコンフリクトが起きます。
戦略が答える質問は3つに要約できます。
- どこから作るのか — 新しい作業ブランチの起点になるブランチ
- どこにマージするのか — 作業を終えたブランチの合流先
- 何をデプロイするのか — 本番に出るコードがあるブランチ
3つの戦略は、この質問にそれぞれ違う答えを出します。
Git Flow — 5種類のブランチ #
Git Flowは2010年に提案された、最も歴史が長く最も構造化された戦略です。ブランチを5種類に分けます。
main ──●─────────────────●────────▶ リリースごとにタグ (v1.0, v1.1)
\ /
release \ ●──● リリース準備、バグ修正のみ
\ /
develop ──●───●──●──●───●───●──▶ 次のリリースに向けた統合地点
\ / \ /
feature ●──● ●───● 機能単位の作業
hotfix: mainから分岐してmainとdevelopの両方にマージ- main — リリースされたコードだけが存在します。コミットごとにバージョンタグが付きます。
- develop — 次のリリースに向けて機能が集まる統合ブランチです。
- feature — developから分岐してdevelopに戻ります。
- release — リリース直前の安定化期間のためのブランチです。バグ修正だけを受け付けます。
- hotfix — 本番の緊急修正用で、mainから分岐してmainとdevelopの両方にマージされます。
この構造は「数か月に一度バージョンを切ってリリースするソフトウェア」を前提に設計されました。インストール型の製品やモバイルアプリのように、リリースがイベントである環境ではいまも有効です。一方、1日に何度もデプロイするWebサービスでは、developとreleaseが形式的な通過点になり、ブランチ管理のコストだけが残るケースが多くなります。原作者自身も10年後の追記で、継続的にデプロイするWebサービスならもっとシンプルなフローを検討するよう勧めています。
GitHub Flow — mainと短命ブランチ #
GitHub Flowは構造を極端に減らした戦略です。ブランチは2種類だけです。
main ──●────────●────────●──▶ 常にデプロイ可能な状態を維持
\ / /
feature ●──● ●──● 作業単位で短く、PRで合流ルールも短いです。
- mainは常にデプロイ可能な状態に保ちます。
- 作業はmainから分岐した名前付きブランチで行います。
- PRを開いてレビューを受け、通過したらmainにマージします。
- マージされたmainはすぐに、またはできるだけ早くデプロイします。
気づいた方もいると思いますが、Git基礎 #6で実習したブランチ作成、プッシュ、PR、マージのサイクルがまさにGitHub Flowです。追加の学習コストなしにPRのレビュー文化とそのままつながるため、継続的にデプロイするWebサービスの事実上の標準になりました。developもreleaseもないので、「次のリリースに何が入るのか」といった質問には、ブランチではなくPRとデプロイ記録が答えます。
trunk-based development — mainへ直行 #
trunk-based developmentはさらに一歩進みます。すべての作業をmain(trunk)に直接コミットするか、ブランチを作るとしても1〜2日以内に合流させる超短命ブランチだけを許容します。
main ──●──●──●──●──●──●──●──▶ 全員が毎日mainに統合
\ /
ごく短いブランチ ●● 寿命は1〜2日以内核心の目標は統合の遅延をなくすことです。ブランチが長く生きるほどmainと離れてコンフリクトが大きくなりますが、毎日統合すればコンフリクトが小さいうちに解消されます。代わりに前提条件が重いです。
- 堅牢な自動テストとCI — 人が止めなくても、壊れたコードがmainに入れない状態が必要です。
- feature flag — 未完成の機能もmainにマージし、コード内のスイッチでオフにしておきます。デプロイと機能公開を分離する手法で、未完成の機能がmainにあってもユーザーには見えません。オンとオフを切り替えるのはデプロイではなく設定です。
この2つが揃っていないチームが形だけ真似すると、壊れたmainを全員で共有する最悪の状態になります。GoogleやMetaのような大規模組織がこの方式を使えるのは、それだけのインフラが支えているからです。
選択基準 #
3つの戦略を判断軸で整理します。
| 基準 | Git Flow | GitHub Flow | trunk-based |
|---|---|---|---|
| 合うデプロイモデル | バージョンリリース(インストール型、モバイル) | 継続デプロイのWebサービス | 1日複数回のデプロイ |
| ブランチ寿命 | 長い(週単位) | 短い(日単位) | ごく短い(1〜2日以内) |
| 複数バージョンの並行保守 | 強い | 弱い | 弱い |
| 前提条件 | リリース管理体制 | CIとレビュー文化 | 強力なCI、feature flag |
| 構造の複雑さ | 高い | 低い | 低い(運用難度は高い) |
判断が必要なときに投げかける質問は2つです。第一に、リリースはイベントか日常か。バージョンを切ってリリースし、旧バージョンも保守する必要があるなら、Git Flow系はいまも有効です。第二に、毎日の統合を支えるCIがあるか。あるならブランチ寿命を縮める方向、つまりtrunk-basedに近づくほど利益が大きくなります。
戦略はツールではなく合意です #
多くのチームにとって無難な出発点はGitHub Flowです。ルールが少なく、PRレビュー文化と自然に結びつき、必要になればtrunk-based側に調整できます。どの戦略を選ぶにせよ、重要なのはチーム全員が同じルールを共有することです。半分はdevelopへ、半分はmainへマージするチームは、どの戦略を選んでも整理されません。戦略は短くてもいいのでリポジトリのCONTRIBUTINGドキュメントに書いておくと、新しいメンバーが最初のブランチを作る前に答えを見つけられます。
まとめ #
この記事の要点は3つです。
- ブランチ戦略とは、ブランチの起点、合流先、デプロイ地点に関するチームの合意です。
- Git Flowはバージョンリリースの製品に、GitHub Flowは継続デプロイのWebサービスに、trunk-basedは強力なCIを備えた高頻度デプロイ組織に合います。
- 無難な出発点はGitHub Flowで、ブランチ寿命を縮めていくほどtrunk-basedに近づきます。
次回の「Git実務ワークフロー #2 rebase vs merge — 判断基準と禁則」では、分岐した履歴を合流させるもう1つの方法であるrebaseを扱います。mergeと何が違うのか、そして絶対にrebaseしてはいけない状況はいつなのかを確認します。