マージコンフリクト、メソッド、レポジトリ保護
「マージリクエストが通らない」「誰かが保護ブランチに直接プッシュしてしまった」「コンフリクトの解決方法がわからない」。チーム開発では、こうした課題が日常的に発生します。
GitLabは、これらの課題に対処するための機能を体系的に提供しています。本記事では、マージコンフリクトの解決、マージ戦略の選択、リポジトリ保護の3つの観点から、実践的な開発ワークフローを構築する方法を解説します。
1. マージコンフリクトの解決
1.1. コンフリクトが発生する仕組み
マージリクエストにおいて、ソースブランチとターゲットブランチが同じ行に異なる変更を加えた場合、マージコンフリクトが発生します。GitLabは多くのケースで自動的にマージを行いますが、コンフリクトが発生した場合は、開発者が適切な変更を選択する必要があります。
Gitがコンフリクトを検出すると、以下のようなマーカーでコンフリクトブロックを示します。
<<<<<<< HEAD
あなたの変更内容
=======
ターゲットブランチの最新の変更内容
>>>>>>>
コンフリクトを解決するには、保持したくないバージョンの行と、3つのコンフリクトマーカー(開始、終了、区切りの=======)を削除します。
1.2. 解決方法の選び方
GitLabは、コンフリクトの複雑さに応じて3つの解決方法を提供しています。状況に応じて最適な方法を選択することで、効率的に作業を進められます。
| 解決方法 | 適している状況 | 作業の複雑さ |
|---|---|---|
| インタラクティブモード | どちらかのバージョンを選ぶだけで済む場合 | 低 |
| インラインエディタ | 両方の変更を組み合わせる必要がある場合 | 中 |
| コマンドライン | 複数ファイルにまたがる複雑なコンフリクト | 高 |
1.2.1. インタラクティブモード
使用条件:
以下の条件を満たすファイルは、GitLab UIで直接コンフリクトを解決できます。
- バイナリファイルではないテキストファイル
- コンフリクトマーカーを含めて200KB未満
- UTF-8互換エンコーディングを使用
- コンフリクトマーカーを含まない
- 両方のブランチで同じパスに存在
手順:
- マージリクエストの概要ページで「コンフリクトを解決」を選択
- 各コンフリクトに対して「Use ours」または「Use theirs」を選択
- コミットメッセージを入力して「ソースブランチにコミット」
1.2.2. インラインエディタ
複雑なコンフリクトで、手動編集が必要な場合に使用します。
- コンフリクトファイルのリストから対象ファイルを選択
- 「Edit inline」を選択してエディタを開く
- コンフリクトブロックを手動で編集
- コミットメッセージを入力して保存
1.2.3. コマンドライン
最も複雑なコンフリクトに対して完全な制御を提供します。複数ファイルにまたがるコンフリクトや、バイナリファイルのコンフリクトはコマンドラインでの解決が必要です。
1.3. リベースによる事前対処
マージリクエストが「マージ可能性を確認中」のメッセージで停止している場合、リベースを実行することでコンフリクトを事前に解決できます。
GitLab UIでのリベース:
前提条件:
- マージコンフリクトが存在しない
- ソースプロジェクトに対して少なくともDeveloperロールを持つ
- フォークの場合、上流プロジェクトのメンバーからのコミットを許可している
手順:
- マージリクエストの概要タブでマージウィジェットまで移動
- 「ソースブランチをリベース」を選択、またはコメントで
/rebaseを入力
GitLabは、デフォルトブランチに対してブランチのリベースをスケジュールして実行します。
2. マージ方法の選択
プロジェクトで選択するマージ方法によって、コミット履歴の見え方と管理のしやすさが大きく変わります。チームのワークフローに合わせて適切な方法を選択することが重要です。
2.1. 3つのマージ方法の比較
| マージ方法 | 履歴の見え方 | 使用するケース | リベースの必要性 |
|---|---|---|---|
| マージコミット | すべてのブランチとマージポイントが見える | ブランチの履歴を完全に保持したい場合 | 不要 |
| 準線形履歴 | クリーンだが、ブランチの起点は確認できる | ビルドの成功を保証しつつ履歴を整理したい場合 | 必要な場合あり |
| 早送りマージ | 完全に線形な履歴 | 最もシンプルな履歴を維持したい場合 | 常に必要 |
2.2. マージコミット方式
デフォルトでは、GitLabはブランチがmainにマージされる際にマージコミットを作成します。この方式は、ブランチの履歴を完全に保持できるため、「いつ、どのブランチから、何がマージされたか」を明確に追跡できます。
2.2.1. 基本的なマージコミット
以下の例では、mainブランチにコミットA、C、Eがあり、featureブランチにコミットBとDがある状態を想定します。
マージ前:
マージ後:
これはgit merge --no-ff <feature>コマンドと同等です。
2.2.2. スカッシュマージ
スカッシュマージでは、featureブランチのすべてのコミット(BとD)を1つのコミットに結合してからマージします。細かいコミット履歴を整理しつつ、マージポイントは明確に保持できます。
使い分けのポイント:
- 通常のマージコミット: 開発プロセス全体を追跡したい場合
- スカッシュマージ: 機能単位でコミットを整理したい場合
2.3. 準線形履歴を持つマージコミット
すべてのマージに対してマージコミットが作成されますが、ブランチは早送りマージが可能な場合にのみマージされます。これにより、マージリクエストのビルドが成功した場合、マージ後のターゲットブランチのビルドも成功することが保証されます。
メリット:
- ビルドの成功を保証
- クリーンな履歴を維持
- ブランチの起点とマージポイントが明確
デメリット:
- 早送りマージが不可能な場合、リベースが必要
2.4. 早送りマージ
ワークフローポリシーによっては、マージコミットのないクリーンなコミット履歴が求められる場合があります。早送りマージは、完全に線形な履歴を維持できます。
重要な制約: 早送りマージは、ターゲットブランチ(例:main)がソースブランチのベースコミットから分岐していない場合にのみ可能です。ターゲットブランチに新しいコミットがある場合は、必ずリベースが必要です。
2.4.1. スカッシュなしの早送りマージ
ソースブランチのすべてのコミットがターゲットブランチに直接追加され、個々のコミット履歴が維持されます。
マージ前:
マージ後:
これはgit merge --ff-only <source-branch>と同等です。
2.4.2. スカッシュありの早送りマージ
ソースブランチのすべてのコミットがまず1つのコミットに結合され、その後ターゲットブランチに早送りされます。
マージ前:
マージ後:
これはgit merge --squash <source-branch>に続いてgit commitを実行することと同等です。
2.5. プロジェクトへの設定方法
- プロジェクトの「設定」>「マージリクエスト」を選択
- 以下から希望する「マージ方法」を選択:
- マージコミット: ブランチ履歴を完全に保持
- 準線形履歴を持つマージコミット: ビルド成功を保証しつつ履歴を整理
- 早送りマージ: 完全に線形な履歴を維持
- 「マージ時にコミットをスカッシュ」で、コミット処理のデフォルト動作を選択:
- 許可しない: スカッシュは実行されず、ユーザーは動作を変更できません
- 許可: スカッシュはデフォルトでオフですが、ユーザーは動作を変更できます
- 推奨: スカッシュはデフォルトでオンですが、ユーザーは動作を変更できます
- 必須: スカッシュは常に実行され、ユーザーは動作を変更できません
- 「変更を保存」を選択
2.6. CI/CDパイプラインなしでのリベース
頻繁なリベースが必要な準線形ワークフローを持つプロジェクトでは、CI/CDパイプラインをトリガーせずにリベースすることでリソースを節約できます。
マージリクエストレポートセクションから「パイプラインなしでリベース」を選択します。
利用可能な条件:
- 早送りマージが不可能だが、コンフリクトのないリベースが可能な場合
- 「パイプラインは成功する必要があります」オプションが有効になっていない場合
3. リポジトリの保護
マージ方法を適切に設定しても、保護されていないブランチには誰でも直接プッシュできてしまいます。リポジトリ保護は、開発ワークフローを維持しながら、コードベースへの不正な変更を防ぐための仕組みです。
3.1. 解決できる課題
リポジトリ保護により、以下のような一般的な開発課題を解決できます。
- 本番環境や保護されたブランチへの誤ったコミット
- コミット履歴における機密データの露出
- コードレビュープロセスのバイパス
- 重要なファイルへの不正な変更
- 検証されていないコミット作成者
- メインブランチへの非準拠コードの混入
3.2. 5つの保護方法
GitLabは、異なるセキュリティニーズに対応する5つの保護方法を提供しています。これらを組み合わせることで、包括的な保護を実現できます。
3.2.1. 保護されたブランチ
目的: コードの安定性と品質を確保するためのブランチ権限制御
できること:
- プッシュとマージの権限制御
- 誤削除の防止
- レビューの強制
- 強制プッシュ権限の規制
適用範囲: グループ、プロジェクト
使用するタイミング: すべてのプロジェクトで最初に設定すべき基本的な保護
3.2.2. マージリクエスト承認
目的: 変更がマージされる前に承認を必要とするレビュープロセス
できること:
- コードレビューの要求
- 承認ルールの作成
- 承認設定の構成
適用範囲: グループ、プロジェクト
使用するタイミング: チーム開発でコードレビューを必須にしたい場合
3.2.3. プッシュルール
目的: コミット、ファイル、タグがリポジトリに入る前に検証するpre-receive Gitフック
できること:
- コミット内容の評価
- ブランチ名ルールの強制
- タグ削除の防止
- 署名付きコミットの要求
適用範囲: インスタンス、グループ、プロジェクト
使用するタイミング: コミットの品質基準を組織全体で統一したい場合
3.2.4. Code Owners
目的: コードベース内の特定のファイルとディレクトリに対する専門知識を持つ人を定義
できること:
- 特定のファイルへの変更に専門家の承認を要求
- コードメンテナンスの責任者を特定
適用範囲: プロジェクト
使用するタイミング: 特定の領域に対して専門家のレビューが必要な場合
3.2.5. ステータスチェック
目的: マージリクエストのステータスを検証する外部システムへのAPI呼び出し
できること:
- サードパーティワークフローツールとの統合
- 外部品質要件に対する検証
適用範囲: プロジェクト
使用するタイミング: 外部ツールとの連携が必要な場合
3.3. ブランチルールによる一元管理
複数の保護方法を管理しやすくするため、GitLabは保護されたブランチ、承認ルール、ステータスチェックのための統一されたブランチルールインターフェースを提供しています。
プロジェクト設定の「ブランチルール」ページを使用して、1つの場所からすべてのブランチ保護を設定し、ブランチ全体の保護状態を表示し、複雑な保護の組み合わせを管理できます。
注意: グループ保護の場合は、グループ設定で保護されたブランチとプッシュルールを設定します。「ブランチルール」ページはプロジェクトでのみ利用可能です。グループルールはグループ内のすべてのプロジェクトに適用され、プロジェクト固有のルールと併用できます。
3.4. 段階的な保護戦略
プロジェクトの成熟度に応じて、段階的に保護を強化していくことをお勧めします。
3.4.1. フェーズ1: ベースライン保護
目的: すべてのリポジトリで一貫したセキュリティ基準を確立
実施内容:
- グループのデフォルトブランチ保護を設定して、新しいプロジェクトを自動的に保護
- 保護されたブランチを設定して、プッシュとマージの権限を制御
- マージリクエスト承認を要求して、ピアレビューを強制
対象: すべてのプロジェクト
3.4.2. フェーズ2: 包括的保護
目的: 重要なプロジェクトを多層保護で保護
実施内容:
- 保護されたブランチと承認ルールを設定して、プッシュとマージの権限を制御
- 機密ロジックを含むファイルにCode Owner承認を要求
- 署名付きコミットを強制して、作成者のIDを検証
- ステータスチェックを追加して、自動テストに対する検証を実施
- グループにプッシュルールを適用して、すべてのプロジェクトで基準を強制
対象: 本番環境に影響するプロジェクト
3.4.3. フェーズ3: ターゲット保護
目的: 特定のセキュリティ要件に対処
実施内容:
- ファイルにドメイン専門知識のレビューが必要な場合、Code Owner承認を要求
- コミット基準とコンテンツ制限を維持するためにプッシュルールを強制
- 外部検証が必要な場合、ステータスチェックを追加
- ワークフロー固有の要件に対して承認ルールを設定
対象: 特殊な要件を持つプロジェクト
3.5. 実装チェックリスト
前提条件:
- プロジェクトに対して少なくともMaintainerロール、またはグループに対してOwnerロールを持つ
- 保護が必要なブランチを特定
- コンプライアンスとセキュリティ要件を決定
ステップ1: スコープを選択
- グループルールの場合: グループの「設定」>「リポジトリ」
- プロジェクト固有のルールの場合: プロジェクトの「設定」>「リポジトリ」>「ブランチルール」
ステップ2: ベースライン保護を設定
-
デフォルトブランチとその他の重要なブランチに対して保護されたブランチを作成
- グループ設定の場合: 「設定」>「リポジトリ」>「保護されたブランチ」
- プロジェクト設定の場合: 「設定」>「リポジトリ」>「ブランチルール」
- 「設定」>「マージリクエスト」>「マージリクエスト承認」でマージ権限と承認要件を設定
ステップ3: レビュー要件を追加
-
特定のファイルに対して
CODEOWNERSファイルでCode Ownersを定義 - 「設定」>「マージリクエスト」で承認ルールを設定
ステップ4: セキュリティ制御を有効化
-
プッシュルールを設定:
- グループの場合: 「設定」>「リポジトリ」>「プッシュルール」
- プロジェクトの場合: 「設定」>「リポジトリ」>「プッシュルール」
- 「設定」>「リポジトリ」>「プッシュルール」>「署名されていないコミットを拒否」で署名付きコミットを有効化
ステップ5: 設定をテスト
- テストマージリクエストを作成
- 保護ルールが正しくトリガーされることを確認
- 結果に基づいて設定を調整
4. まとめ: 実践的なワークフロー構築
GitLabのマージコンフリクト解決、マージ戦略、リポジトリ保護の3つの機能は、それぞれ独立して機能しますが、組み合わせることで真価を発揮します。
推奨される導入順序:
- まず保護を設定 - 保護されたブランチとマージリクエスト承認で基本的なガードレールを構築
- マージ方法を選択 - チームのワークフローに合わせて適切なマージ戦略を決定
- コンフリクト解決を習得 - チームメンバーが適切な解決方法を選択できるようにする
チーム規模別の推奨設定:
小規模チーム(5人以下):
- マージ方法: マージコミット(スカッシュ推奨)
- 保護: 保護されたブランチ + マージリクエスト承認(1名)
中規模チーム(5-20人):
- マージ方法: 準線形履歴を持つマージコミット
- 保護: 保護されたブランチ + マージリクエスト承認(2名) + Code Owners
大規模チーム(20人以上):
- マージ方法: 準線形履歴または早送りマージ
- 保護: すべての保護方法を組み合わせ、グループレベルでプッシュルールを適用
これらの機能を適切に活用することで、開発速度を損なうことなく、コードの品質とセキュリティを確保する開発環境を実現できます。まずは小さく始めて、チームの成熟度に応じて段階的に保護を強化していくことをお勧めします。