ブランチとマージ
1. はじめに
GitHubのブランチ保護機能やマージ戦略は、チーム開発における品質保証の要です。本記事では、GitHubが提供する最新のブランチ管理機能を体系的に解説します。単なる機能紹介ではなく、実際のプロジェクトでどう活用すべきかという観点から、実践的な知見をお届けします。
2. マージ方法の選択:プロジェクトの性質に応じた最適解
GitHubは3つの主要なマージ方法を提供しています。それぞれの特性を理解することで、プロジェクトに最適な戦略を選択できます。
3つのマージ方法の比較:
| 項目 | 標準マージ | Squashマージ | Rebaseマージ |
|---|---|---|---|
| コミット履歴 | すべて保持 | 1つに統合 | すべて保持 |
| マージコミット | あり | あり | なし |
| 履歴の形 | 分岐あり | 分岐あり | リニア |
| 元のSHA | 保持 | 失われる | 変更される |
| ロールバック | PR単位 | PR単位 | コミット単位 |
| 適用場面 | 詳細な履歴追跡 | クリーンな履歴 | 完全なリニア履歴 |
2.1. 標準マージ(Merge Commit)
すべてのコミットを保持したまま、マージコミットで統合します。
適用場面:
- 各コミットの詳細な履歴を保持したい場合
- コードレビューで個別コミットの文脈が重要な場合
- 複雑な機能開発で、開発プロセスを追跡可能にしたい場合
設定方法:
リポジトリの Settings > Pull Requests で「Allow merge commits」を有効化します。
# マージコミットメッセージのフォーマット選択可能
# - デフォルト: PR番号とタイトル(例: Merge pull request #123 from patch-1)
# - PRタイトルのみ
# - PRタイトルと説明
2.2. Squashマージ
複数のコミットを1つにまとめてマージします。
適用場面:
- クリーンなGit履歴を維持したい場合
- 作業中のコミット(WIP、typo修正など)を本番履歴に含めたくない場合
- 機能単位でのロールバックを容易にしたい場合
注意点:
- 元のコミットのSHA IDが失われるため、
git rerereなどの機能が効果的に使えない場合があります - squash後にヘッドブランチで作業を続けると、次のPRで重複コミットが表示される可能性があります
2.3. Rebaseマージ
コミットを1つずつベースブランチに追加し、完全にリニアな履歴を作成します。
適用場面:
- 完全なリニア履歴が必要な場合
- マージコミットを排除したい場合
- 各コミットの内容を保持しつつ、クリーンな履歴を維持したい場合
重要な注意点:
- GitHubのrebase and mergeは、コミッター情報を更新し、新しいSHAを生成します
- コミット署名の検証が行われません(GitHub側で再署名できないため)
- コントリビューターがコマンドラインでrebaseし、強制プッシュする必要がある場合があります
回避策:
ローカルでrebaseとマージを行い、PRのベースブランチにプッシュすることで、署名を保持できます。
3. マージキュー:高頻度マージ環境での救世主
マージキューは、1日に多数のPRがマージされる活発なブランチで真価を発揮します。
3.1. マージキューの動作原理
3.2. マージキューの主要機能
1. ビルド並行数の制御
# Settings > Branches > Branch protection rules
Build concurrency: 5 # 1-100の範囲で設定可能
# 同時に実行されるmerge_groupウェブフックの最大数
# マージ速度とCI負荷のバランスを調整
2. マージグループの設定
| 設定項目 | 説明 |
|---|---|
| 最小PR数 | この数に達するまで待機してからマージ実行 |
| 最大PR数 | 一度にマージする最大PR数(デプロイへの影響を制限) |
| 待機時間 | 最小数に達しない場合のタイムアウト |
3. 失敗時の動作制御
「Only merge non-failing pull requests」オプション:
- 有効: すべてのPRが必須チェックをパスする必要がある
- 無効: グループ内の最後のPRがパスすれば、他のPRに失敗があってもマージ可能(断続的なテストの失敗に対処)
3.3. GitHub ActionsとCI/CDの統合
# .github/workflows/merge-queue.yml
name: マージキューチェック
on:
pull_request:
branches: [main]
merge_group: # マージキュー用のトリガー
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: テスト実行
run: |
npm install
npm test
- name: ビルド確認
run: npm run build
サードパーティCI連携の場合:
gh-readonly-queue/{base_branch}という特殊なプレフィックスで始まるブランチへのプッシュに対してCIを実行するよう設定します。
3.4. キューのジャンプ機能
緊急のホットフィックスなどで、キューの先頭に移動できます。
注意: キューのジャンプはコミットグラフの中断を引き起こし、進行中のすべてのPRの再ビルドが必要になります。頻繁な使用はマージ速度を低下させる可能性があります。
4. 保護されたブランチ:コード品質の砦
ブランチ保護ルールは、コードの品質と整合性を保証する重要な機能です。
4.1. 必須レビューの設定
主要設定項目:
- 必須承認数: 1-6の範囲で設定可能
- 古い承認の却下: 新規コミットプッシュ時に承認をリセット
- コードオーナー承認: CODEOWNERSファイルで指定された所有者の承認が必須
- 最新プッシュの承認: 最後にプッシュした人以外の承認が必要
実践的な推奨設定:
# 小規模チーム(2-5人)
required_approving_review_count: 1
dismiss_stale_reviews: true
require_code_owner_reviews: true
# 理由: 少人数でもレビューの形式を保ちつつ、開発速度を維持
# 中規模チーム(6-20人)
required_approving_review_count: 2
dismiss_stale_reviews: true
require_code_owner_reviews: true
require_last_push_approval: true
# 理由: 複数の視点でのレビューを確保し、最終変更の見落としを防止
# 大規模チーム/クリティカルなコード
required_approving_review_count: 3
dismiss_stale_reviews: true
require_code_owner_reviews: true
require_last_push_approval: true
# 理由: 高い品質基準を維持し、複数の専門家の承認を必須化
4.2. ステータスチェックの要件
Strictモード vs Looseモード
| モード | 「最新化必須」 | マージ要件 | ビルド数 | 使用場面 |
|---|---|---|---|---|
| Strict | ✓ | ベースブランチと同期必須 | 多い | 互換性が重要 |
| Loose | ✗ | 同期不要 | 少ない | 開発速度重視 |
# Strictモードの例
require_status_checks:
strict: true # ベースブランチと同期必須
contexts:
- "test-suite"
- "build"
- "lint"
ステータスチェックのソース指定:
特定のGitHub Appからのステータスのみを受け入れることができます。これにより、不正なステータス更新を防止できます。
4.3. 署名付きコミットの強制
# ローマ字名でのGit設定
git config --global user.signingkey YOUR_GPG_KEY_ID
git config --global commit.gpgsign true
# コミット署名
git commit -S -m "署名付きコミット"
# 過去のコミットに署名を追加(rebase)
git rebase -i HEAD~3 --exec "git commit --amend --no-edit -S"
Vigilantモード対応:
GitHubのVigilantモードが有効な場合、「Partially verified」とマークされたコミットも署名付きブランチで許可されます。
4.4. リニア履歴の強制
リニア履歴を強制すると、マージコミットがブロックされ、変更の巻き戻しが容易になります。
前提条件: リポジトリでsquashマージまたはrebaseマージを有効にする必要があります。
5. ルールセット:次世代のブランチ保護
ルールセットは、ブランチ保護ルールの進化版で、より柔軟で強力な管理が可能です。
5.1. ルールセットの利点
| 機能 | ブランチ保護ルール | ルールセット |
|---|---|---|
| 複数ルールの同時適用 | ✗(1つのみ) | ✓(最大75個) |
| ステータス表示 | ✗ | ✓(Active/Disabled) |
| 読み取り専用表示 | ✗ | ✓(全員が閲覧可能) |
| ルール集約 | ✗ | ✓(最も厳格なルールを適用) |
| メタデータ制御 | 限定的 | ✓(コミットメッセージ、作成者など) |
5.2. ルールのレイヤリング
複数のルールセットが同じブランチに適用される場合、すべてのルールが集約され、最も厳格なバージョンが適用されます。
例:
# ルールセット1: feature-branch-rules
必須レビュー数: 2
署名付きコミット: 不要
# ルールセット2: security-rules
必須レビュー数: 1
署名付きコミット: 必須
# 適用される実際のルール(集約結果)
必須レビュー数: 2 # より厳格
署名付きコミット: 必須 # より厳格
5.3. プッシュルールセット
プッシュルールセットは、リポジトリとそのフォークネットワーク全体に適用される特殊なルールセットです。
制限可能な項目:
- ファイルパス制限
# 例: テストディレクトリへのプッシュを制限
restrict_file_paths:
- "test/demo/**/*"
- "config/production.yml"
- ファイル拡張子制限
# 例: 実行ファイルのプッシュを防止
restrict_file_extensions:
- ".exe"
- ".dll"
- ".so"
- ファイルサイズ制限
# 例: 大きなバイナリファイルを防止
max_file_size: 5MB
- ファイルパス長制限
# 例: Windows互換性を確保
max_file_path_length: 260
重要な制約事項:
- プッシュルールセットのバイパス権限は、ルートリポジトリで設定された権限のみが有効です。フォークでは独自のバイパス権限を設定できません
- 1回のプッシュで最大1000個の参照更新まで対応。これを超えるとプッシュが拒否されます
- REST APIの「Create a blob」「Create a tree」「Create or update file contents」エンドポイントにも適用されます
5.4. メタデータ制限
コミットメッセージやブランチ名に規則を適用できます。メタデータ制限は一貫性を高めるためのもので、セキュリティ対策としてのコードレビューの代替にはなりません。
重要な注意事項:
ブランチをsquashマージする場合、そのブランチ上のすべてのコミットがベースブランチのメタデータ要件を満たす必要があります。
コミットメッセージのパターン例:
# 課題番号必須
commit_message_pattern: "^(feat|fix|docs|style|refactor|test|chore)\\([A-Z]+-[0-9]+\\): .+"
# 例: "feat(PROJ-123): 新機能を追加"
# コミッター名の制限
committer_email_pattern: ".*@company\\.com$"
ブランチ名のパターン例:
# 機能ブランチの命名規則
branch_name_pattern: "^(feature|bugfix|hotfix)/[a-z0-9-]+$"
# 許可: feature/add-login, bugfix/fix-crash
# 拒否: Feature/AddLogin, add-login
5.5. fnmatch構文の活用
ルールセットでは、fnmatch構文を使用してブランチやタグをパターンマッチできます。
基本パターン:
# すべてのreleaseブランチ
*release*
# qa/配下の単一階層ブランチ
qa/*
# qa/配下のすべてのブランチ(多階層対応)
qa/**/*
# 特定のプレフィックスを含むすべて
release**/**/*
制限事項:
- バックスラッシュ(
\)はクォート文字として使用不可 -
[^charset]形式の文字セット補完は未対応 -
File::FNM_PATHNAMEはサポート、File::FNM_EXTGLOBは未対応
5.6. バイパス権限の管理
「PRのみバイパス」の利点:
- 変更の追跡可能性を維持
- 監査ログに記録
- コードレビューの透明性を確保
5.7. ルールセットインサイト
Settings > Rules > Insights で、ルールセットの効果を確認できます。
表示される情報:
- Pass: ルールをパスしたアクション
- Fail: ルールで失敗したアクション
- Bypass: ルールをバイパスしたアクション
- Evaluate: 評価モードでの結果(本番適用前のテスト)
6. ブランチ管理のベストプラクティス
6.1. ブランチの命名規則
# 機能開発
feature/[課題番号]-[簡潔な説明]
例: feature/PROJ-123-add-user-authentication
# バグ修正
bugfix/[課題番号]-[簡潔な説明]
例: bugfix/PROJ-456-fix-memory-leak
# ホットフィックス
hotfix/[課題番号]-[簡潔な説明]
例: hotfix/PROJ-789-critical-security-patch
# リリース
release/[バージョン]
例: release/v2.5.0
6.2. 古いブランチの整理
Settings > Branches でブランチの状態を確認できます:
- Your branches: 自分がプッシュしたブランチ
- Active branches: 過去3ヶ月以内にコミットがあるブランチ
- Stale branches: 3ヶ月以上コミットがないブランチ(削除候補)
自動削除の設定:
# Settings > Pull Requests
Automatically delete head branches: 有効
# 注意: ブランチ保護ルールやルールセットで保護されている
# ブランチは自動削除されません
6.3. デフォルトブランチの変更
# ローカルでの対応
git branch -m old-branch-name new-branch-name
git fetch origin
git branch -u origin/new-branch-name new-branch-name
git remote set-head origin -a
# オプション: 古いブランチの追跡参照を削除
git remote prune origin
7. トラブルシューティング
7.1. ルールセットのデバッグ
アクションが失敗した場合、以下の手順で原因を特定できます:
-
対象ブランチのルールセット確認
- ブランチ名の横の🛡️アイコンをクリック
- 適用されているルールセットを表示
-
Rules Insights で詳細確認
- Settings > Rules > Insights
- 失敗したアクションの詳細を展開
- どのルールが失敗したかを確認
-
評価モードでのテスト
- ルールセットを「Evaluate」ステータスに変更
- 本番適用前に影響を確認
- インサイトで「would have failed」を確認
7.2. ステータスチェックの問題
ワークフロー名とジョブ名の形式:
# 単一ワークフロー
チェック名: <job name>
# 再利用可能ワークフロー
チェック名: <job name> / <reusable job name>
# その他のチェック
チェック名: <check name>
注意: ステータスチェックは、ワークフロー、マトリックス、イベントトリガーの種類を考慮しません。複数のワークフローで同じジョブ名を使用すると、曖昧なステータスチェック結果が発生し、PRのマージがブロックされる可能性があります。
7.3. コミット履歴の修正
署名やメタデータ要件を満たすために、ローカルで履歴を書き換える場合:
# 署名を追加(対話的rebase)
git rebase -i HEAD~3 --exec "git commit --amend --no-edit -S"
# コミットメッセージの修正
git rebase -i HEAD~3
# エディタで'reword'を選択し、メッセージを修正
# 強制プッシュ(慎重に実行)
git push --force-with-lease origin feature-branch
8. まとめ
GitHubのブランチ・マージ管理機能は、単なる技術的な制約ではなく、チーム開発の品質を支える基盤です。
選択のポイント:
- マージ方法: プロジェクトの性質と履歴管理の方針に応じて選択
- マージキュー: 高頻度マージ環境で開発速度を維持
- ブランチ保護: コード品質の最低基準を設定
- ルールセット: 複雑な要件に対応する柔軟な管理
これらの機能を適切に組み合わせることで、開発速度を犠牲にすることなく、コードの品質と安全性を確保できます。まずは小規模な設定から始め、チームの成熟度に応じて段階的に厳格化していくアプローチを推奨します。