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種類に分けます。

Git Flowのブランチ構造
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種類だけです。

GitHub Flowのブランチ構造
main     ──●────────●────────●──▶   常にデプロイ可能な状態を維持
            \      /        /
feature      ●──●      ●──●         作業単位で短く、PRで合流

ルールも短いです。

  1. mainは常にデプロイ可能な状態に保ちます。
  2. 作業はmainから分岐した名前付きブランチで行います。
  3. PRを開いてレビューを受け、通過したらmainにマージします。
  4. マージされたmainはすぐに、またはできるだけ早くデプロイします。

気づいた方もいると思いますが、Git基礎 #6で実習したブランチ作成、プッシュ、PR、マージのサイクルがまさにGitHub Flowです。追加の学習コストなしにPRのレビュー文化とそのままつながるため、継続的にデプロイするWebサービスの事実上の標準になりました。developもreleaseもないので、「次のリリースに何が入るのか」といった質問には、ブランチではなくPRとデプロイ記録が答えます。

trunk-based development — mainへ直行 #

trunk-based developmentはさらに一歩進みます。すべての作業をmain(trunk)に直接コミットするか、ブランチを作るとしても1〜2日以内に合流させる超短命ブランチだけを許容します。

trunk-basedのブランチ構造
main     ──●──●──●──●──●──●──●──▶   全員が毎日mainに統合
                 \  /
ごく短いブランチ    ●●                  寿命は1〜2日以内

核心の目標は統合の遅延をなくすことです。ブランチが長く生きるほどmainと離れてコンフリクトが大きくなりますが、毎日統合すればコンフリクトが小さいうちに解消されます。代わりに前提条件が重いです。

  • 堅牢な自動テストとCI — 人が止めなくても、壊れたコードがmainに入れない状態が必要です。
  • feature flag — 未完成の機能もmainにマージし、コード内のスイッチでオフにしておきます。デプロイと機能公開を分離する手法で、未完成の機能がmainにあってもユーザーには見えません。オンとオフを切り替えるのはデプロイではなく設定です。

この2つが揃っていないチームが形だけ真似すると、壊れたmainを全員で共有する最悪の状態になります。GoogleやMetaのような大規模組織がこの方式を使えるのは、それだけのインフラが支えているからです。

注記
GitHub Flowとtrunk-basedの境界は、思ったより薄いものです。機能ブランチの寿命を1日以内に縮めてfeature flagを導入すると、GitHub Flowは自然にtrunk-basedに近づきます。2つの戦略を対立関係ではなく、ブランチ寿命のスペクトラムとして見るほうが実務の感覚に合います。

選択基準 #

3つの戦略を判断軸で整理します。

基準Git FlowGitHub Flowtrunk-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してはいけない状況はいつなのかを確認します。

X