Gitブランチ戦略の選び方:小規模チームに最適な運用ルール
Git Flow・GitHub Flow・Trunk-Basedを人数とリリース頻度で比較し、小規模チームにGitHub Flowを勧める理由、ブランチ保護とマージ方式、命名規則、タグ運用、IaCでの適用例まで。

Gitブランチ戦略とは、チームが「どのブランチをどこから切り、どこへどうマージし、いつリリースするか」を決めた運用ルールです。Gitの機能ではなくチームの合意事項なので、人数・リリース頻度・並行して保守するバージョン数によって最適解が変わります。
結論を先に書くと、2〜8人程度で1つの本番環境を継続的に更新するチームには、短命ブランチ + Pull Request + main保護のGitHub Flowがもっとも摩擦が少なく、複数バージョンを並行保守する場合だけGit Flowを検討します。以下でその根拠と、設定項目・命名・タグ・インフラコードへの適用まで具体化します。
この記事でわかること
- Git Flow / GitHub Flow / Trunk-Based Developmentの構造と、人数・リリース頻度による選び方
- ブランチ保護(PR必須、status check、force push禁止)で実際に有効にする項目
- Squash / Merge commit / Rebaseの使い分けと、履歴に残るものの違い
- ブランチ命名とConventional Commitsの形式、annotated tagでのリリースとホットフィックス
- Terraform・Ansibleなどインフラコードのリポジトリでの運用例(ひとり情シスの場合を含む)
ブランチ戦略が必要な理由
戦略がない状態で起きるのは次の3つです。mainに直接pushされて本番に何が入っているか分からなくなる、長く生きたブランチ同士のマージで大量の競合が出る、障害時にどのコミットへ戻せばよいか判断できない。ブランチ戦略はこの3つに対して「mainは常にデプロイ可能」「変更は小さくレビューを通す」「リリースはタグで特定できる」という不変条件を与えるものです。ルールが多いほど守られなくなるので、この3条件を満たす最小のルールを選びます。
Git Flow / GitHub Flow / Trunk-Based の比較
| 観点 | Git Flow | GitHub Flow | Trunk-Based Development |
|---|---|---|---|
| 常設ブランチ | main(リリース済み)+ develop(統合用) | mainのみ | mainのみ(trunk) |
| 作業ブランチの寿命 | feature / release / hotfixで数日〜数週間 | 数時間〜数日 | 数時間〜1日。または直接コミット |
| リリース | releaseブランチで安定化 → mainへマージしてタグ | mainへのマージがリリース候補。タグまたは自動デプロイ | 常にデプロイ可能。機能フラグで公開を制御 |
| ホットフィックス | hotfixブランチをmainとdevelopの両方へマージ | mainから修正ブランチ → PR → マージ | mainへ直接、または短命ブランチ |
| 必要な前提 | リリース担当者、複数バージョンの保守 | CIでのテスト、PRレビュー | 10分以内で終わる高速なCI、機能フラグ、厚い自動テスト |
| 向くチーム | パッケージ製品、顧客ごとのバージョン保守 | Webサービス、社内ツール、インフラコード | 1日に何度もデプロイする成熟したチーム |
Git Flowのdevelopブランチは「mainには未反映だが統合済み」という中間状態を作ります。複数バージョンを出荷する製品では必要な状態ですが、本番が1つしかないWebサービスや社内ツールでは、その中間状態が「developとmainのどちらが正か」という混乱の源になります。Trunk-Basedは理想的ですが、CIが遅い・テストが薄い状態で始めるとmainが頻繁に壊れ、結局ブランチを長生きさせることになります。
小規模チームへの推奨と理由
推奨はGitHub Flowです。常設ブランチがmainだけなので本番と一致するブランチが常に1つに定まること、PRを唯一の入口にすることでレビューとCIを強制できること、ルールが少なくオンボーディングが1日で終わることの3点が理由です。「PRを小さく、1日以内にマージする」を守れば、Trunk-Basedの利点(競合の少なさ、統合の早さ)の大半も得られます。
この推奨が当てはまらないのは、顧客ごとに異なるバージョンを保守する受託・パッケージ開発(Git Flowのrelease / hotfixブランチが必要)と、CIが整備されていないチーム(mainが壊れやすくGitHub Flowの前提が崩れる)です。後者はブランチ戦略より先にCIを用意します。GitHub Actionsで始めるCI/CDのtestジョブがあれば十分です。
| 判断の問い | はい | いいえ |
|---|---|---|
| 複数の本番バージョンを同時に保守する? | Git Flow | 次の問いへ |
| CIが自動で通り、レビューできる人が自分以外にいる? | GitHub Flow | GitHub Flow + 自己承認(後述のひとり情シス運用) |
| 1日に複数回デプロイし、機能フラグを運用できる? | Trunk-Based(短命ブランチ型) | GitHub Flowのまま、PRを小さくする |
PRとレビューの運用
GitHub Flowが機能するかどうかはブランチ保護の設定で決まります。リポジトリの Settings → Rules → Rulesets(または従来のBranch protection rules)でmainに次を設定します。
- Require a pull request before merging:承認1人以上。レビュー相手がいない場合はCI通過のみ必須にする
- Require status checks to pass:CIのジョブ名(例:
test)を指定する。指定しないと「チェックが走らなくても通る」状態になる - Require branches to be up to date before merging:古いmainを基にしたPRのマージを防ぐ
- Block force pushes と Restrict deletions:履歴の書き換えとmain削除を防ぐ
- 管理者にも適用(bypassを許可しない):例外を作ると「急ぎだから」で崩れる
マージ方式は3つあり、mainの履歴に残るものが違います。
| 方式 | mainの履歴 | 向いている場面 | 注意点 |
|---|---|---|---|
| Squash and merge | PR 1件 = コミット1件 | 小さなPRを高頻度で取り込む。作業中のWIPコミットを隠したい | PR内の細かい履歴は消える。マージ後に同じブランチを使い回すと競合する |
| Merge commit | 作業ブランチの全コミット + マージコミット | 作業過程を残したい大きな機能 | 履歴が二股になり、git bisectやgit logが読みにくい |
| Rebase and merge | 作業ブランチのコミットを直列に載せ替え | 直線的な履歴を保ちたい | コミットハッシュが変わる。共有ブランチのrebaseは禁止 |
小規模チームでは「Squash and merge」に統一し、PRタイトルをそのままコミットメッセージにする運用が管理しやすい選択です。PRの粒度は「レビューが15分で終わる」を目安にし、差分が300行を超えるなら分割を検討します。PRテンプレートを置くと、レビューに必要な情報が毎回揃います。
## 目的
(なぜこの変更が必要か。Issueがあれば #番号)
## 変更内容
-
## 確認したこと
- [ ] ローカルでテスト / lint が通る
- [ ] インフラ変更なら terraform plan / ansible --check の結果を貼った
- [ ] ロールバック手順を把握しているブランチ命名規則とコミットメッセージ
| 種別 | ブランチ名の形式 | 例 |
|---|---|---|
| 機能追加 | feature/<Issue番号>-<要約> | feature/42-backup-schedule |
| 不具合修正 | fix/<Issue番号>-<要約> | fix/57-ssh-timeout |
| 緊急修正 | hotfix/<要約> | hotfix/nginx-cert-renew |
| 雑務・依存更新 | chore/<要約> | chore/update-actions |
Issue番号を先頭に入れるのは、GitHubの「Development」欄で自動的にIssueと紐づき、ブランチ名だけで文脈が追えるようにするためです。コミットメッセージはConventional Commits(type(scope): 要約)の形式にすると、feat / fix / docs / chore / refactor / ciの種別でリリースノートを自動生成でき、互換性を壊す変更はfeat!:のように!で示せます。日常の流れは次のとおりです。
git switch main && git pull --ff-only # ローカルmainを必ずfast-forwardで更新
git switch -c feature/42-backup-schedule
# 作業
git add -A && git commit -m "feat(backup): add nightly PBS job"
git push -u origin feature/42-backup-schedule
gh pr create --fill # GitHub CLIでPR作成(タイトル・本文はコミットから)
# マージ後
git switch main && git pull --ff-only && git branch -d feature/42-backup-schedulegit pull --ff-onlyは、ローカルのmainがリモートと分岐していた場合にマージコミットを作らず失敗させる指定です。mainに直接コミットしてしまった事故がここで発覚します。git config --global pull.ff onlyにしておけば毎回のオプション指定は不要です。
Squash mergeを使うチームで、マージ済みのブランチにコミットを足して再度PRを出すと、mainには「1つに潰されたコミット」しかないため同じ変更が二重に見え、競合します。マージしたブランチは削除し、続きは新しいブランチをmainから切ってください。GitHubの「Automatically delete head branches」を有効にすると忘れません。
リリースとタグ、ホットフィックス
リリースの識別はannotated tag(git tag -a)で行います。lightweight tagと違って作成者・日時・メッセージを持ち、git describeの対象になります。バージョンはセマンティックバージョニング(vMAJOR.MINOR.PATCH)で、互換性を壊す変更でMAJOR、機能追加でMINOR、修正でPATCHを上げます。社内ツールやインフラコードのように「バージョン互換」の概念が薄い場合は、v2026.09.23のような日付ベースでも構いません。
git switch main && git pull --ff-only
git tag -a v1.4.0 -m "Release v1.4.0: nightly backup schedule"
git push origin v1.4.0
gh release create v1.4.0 --generate-notes # マージ済みPRのタイトルからリリースノートを生成タグのpushをCIのトリガー(on.push.tags: ['v*'])にすると「タグを打つ = 本番デプロイ」という運用になり、誰がいつ何を出したかがタグに残ります。ホットフィックスはGitHub Flowでは通常の修正と同じで、mainからhotfix/ブランチを切ってPR → マージ → PATCHを上げたタグを打ちます。過去バージョンへの修正が必要なときだけ、そのタグからrelease/1.3ブランチを切り、git cherry-pickでmainの修正コミットを持ち込みます。
タグは「打ち直さない」が原則です。git tag -fで同じ名前を別のコミットに付け替えると、既にそのタグでデプロイした環境やCIのキャッシュと食い違い、障害調査で「v1.4.0」が何を指すのか分からなくなります。間違えたらv1.4.1を打ってください。
情シス内製チーム(インフラコード)での適用例
TerraformやAnsibleのリポジトリでは、mainブランチが「本番の構成」を表します。アプリのコードと違って「レビューで読むべきもの」が差分そのものより実行計画(plan)である点が特徴で、運用は次のとおりです。
- 変更は必ずブランチ + PR。PRのCIで
terraform planやansible-playbook --check --diffを実行し、結果をPRのコメントに残す - レビュー担当はplanの
# forces replacementや- destroyの有無、Ansibleのchanged件数を読む。HCLの体裁はCIのfmt / validateに任せる - mainへのマージで
applyを実行する(GitHub Actionsのenvironment承認付き)。適用結果のログもPRにリンクする - 適用ごとにタグを打つ。障害時は「どのタグから壊れたか」で切り分け、
git revertのPRで戻す(git resetで履歴を消さない) - 緊急でコンソールを手で触ったら、事後に必ずコードへ反映するPRを出す。
terraform planに差分が出続ける状態を放置しない
ひとり情シスでレビュー相手がいない場合は、「PR必須 + CI通過必須 + 自己承認」にして、PRを変更記録として使うだけでも効果があります。planの結果と適用日時がPRに残るため、ISMSや内部監査で求められる変更管理の証跡になり、後任への引き継ぎ資料にもなります。Ansible側の--check運用はAnsibleでサーバー構築を自動化するで扱っています。
よくある質問
ブランチ戦略は必ず必要?
1人で使い捨ての検証をしている間は不要です。2人以上で同じリポジトリを触る、または本番環境に反映するコードを管理する時点で必要になります。最小構成は「mainを保護し、変更はPR経由」の1ルールだけで、Git Flowのような多段のブランチは複数バージョンの保守が必要になってから足せば間に合います。
mainに直接pushしてはいけない?
本番に繋がるリポジトリでは避けてください。直接pushはCIとレビューを素通りし、「何が本番にあるか」をPRの履歴から追えなくします。ブランチ保護で管理者を含めて禁止し、緊急時もhotfix/ブランチからのPRにします。承認を待つ時間が問題なら、緊急時だけ自己承認を許す運用にすれば直接pushを許す理由はなくなります。
タグはいつ打つ?
本番に出したコミットに、出したタイミングで打ちます。GitHub Flowなら「デプロイしたmainのコミット」に対してannotated tagを付け、タグのpushでデプロイを起動する形にすると打ち忘れがなくなります。インフラコードならapplyごと、アプリならリリースごとで、日付ベースかセマンティックバージョニングかはチームの互換性管理の必要性で決めます。
まとめ
小規模チームの既定解はGitHub Flowで、mainの保護(PR必須、status check、force push禁止、管理者にも適用)とSquash mergeの統一、Issue番号入りのブランチ名、Conventional Commits、annotated tagでのリリース、この5点で「mainは常にデプロイ可能」「変更は小さくレビュー済み」「リリースはタグで特定できる」が揃います。複数バージョンの保守が始まったらGit Flowのrelease / hotfixを足し、CIが10分以内で回るようになったらTrunk-Basedに寄せる、という順で進化させてください。
次に読む
- Terraformで始めるIaC:PRでplan、mainでapplyするCIの具体例
- Claude Codeとは?ターミナルで動くAIコーディングエージェント:AI生成の変更もPRとCIで受け入れる前提