バージョン間でPrefabをコンフリクトさせないシステム

バージョン間でPrefabをコンフリクトさせないシステム

バージョン間でPrefabをコンフリクトさせないシステム

はじめに

株式会社QualiArtsでUnityエンジニアをしている中辻です。

私たちのプロジェクトでは大型施策により8バージョン先の並行開発が必要になりました。Unityプロジェクトにおいてバージョンが増えるほど恐ろしいのがPrefabのコンフリクトです。Prefabはバイナリに近い性質を持つため、コンフリクトすると手動での解消が極めて困難で、最悪の場合は作業のやり直しが発生します。

本記事では、この問題を GitHub ActionsUnity エディタ拡張 で解決したシステムを紹介します。

既存フローの課題

本記事での「バージョン」はリリースラインごとのブランチ(例:version/2.11.0)を指し、「未来バージョン」は現在のPR先よりも先行しているバージョンブランチのことです。

従来のフローでは、バージョン間のマージ時に初めてコンフリクトが判明し、Prefab編集の作業やり直しが発生していました。理想は、個人のPR時点で未来バージョンとのコンフリクトに気づき、対応バージョンの調整やコンフリクト前提の効率的な作業ができる状態です。

従来フローと理想のフローの比較

システム全体像

各バージョンブランチで変更された Prefab を管理し、チェックするシステムです。5つのGitHub Actionsと1つのUnityエディタ拡張で構成されています。

Prefabコンフリクト防止システム 全体図

Actionタイミング役割
1. Tracker個人ブランチPR時変更Prefabを記録
2. CreateVersion新バージョンブランチ作成時記録を全消去
3. BranchMergeToolバージョンマージ時リストを新バージョン側に強制リセット
4. Cleanupバージョン反映マージPR時解決済みを整理
5. Checker個人ブランチPR時コンフリクトを予知

Action 1: Tracker — 変更Prefabの記録

タイミング

バージョンブランチへの個人PRが作成された時に発動します。

処理

PR内の変更差分から Prefab ファイルを抽出し、.github/data/prefab-paths.json にファイルパスをリストとして追加してコミットします。このJSONは、そのバージョンで編集済み(=将来コンフリクトの起点になりうる)Prefab一覧を保持する台帳です。

{
  "paths": [
    "Assets/Campus/OutGame/Home/Prefabs/HomeScreen.prefab",
    "Assets/Campus/InGame/Produce/Prefabs/ProduceScreen.prefab"
  ]
}

パフォーマンスの工夫

actions/checkout@v4fetch-depth: 0ではなくfetch-depth: 50 を指定し、必要な分だけfetchすることで処理速度を大幅に改善しました(5分 → 1分)。 個人の対応PRは追いコミットをする場合もあるので作業の邪魔にならないようにこのActionでは速度を重視しました。差分のデータさえあれば良くプロジェクト全体のデータは必要ない状況だからできることではあります。

Action 2: CreateVersion — 新バージョンでのリスト初期化

タイミング

新規バージョンブランチの作成時に発動します。本プロジェクトでは必ずGitHub Actions経由でバージョンブランチを作成するルールになっています。

処理

バージョンブランチ作成Actionの最後に、prefab-paths.json のリストを空にしてコミットします。

考え方: 前のバージョンから切ったブランチ=その時点での Prefab 変更がコンフリクトすることはない

バージョンマージでの prefab-paths.json の保護

バージョン間マージでは、prefab-paths.json を新しいバージョン側で正しく維持するために2つの仕組みが連携しています。

Action 3: BranchMergeTool — 古いバージョンのリストを流し込まない

バージョン間の自動マージを担う BranchMergeTool では、prefab-paths.json が新しいバージョン側を常に優先するよう設定されています。

  • コンフリクト時: AutoMergeConflictRegexFormats により、マージ先(新しいバージョン)の内容を自動採用
  • コンフリクトしない時: ForceBaseBranchRegexFormats により、マージ後に強制リセット

コンフリクトしないケースへの対策が重要です。2回目以降のバージョンマージでは prefab-paths.json がコンフリクトしない場合があるのです。

1回目と2回目のマージでの3-way mergeの違い

1回目のマージでは、新バージョン側でリストがクリアされているため、古いバージョン側の追加とコンフリクトして正しく解消されます。しかし2回目以降は、新バージョン側が前回のマージ以降変更されていないため、Gitの3-way mergeでは「片方だけが変更した」と判断され、古いバージョンのリストがそのまま反映されてしまいます

これを防ぐため、マージ後に対象ファイルをマージ先の内容に強制リセットする仕組みを導入しました。

Action 4: Cleanup — 解決済みPrefabのリスト整理

Cleanup は、バージョン反映マージPR(jenkins/version/X.X.Xversion/* からのPR)で発動します。

マージ先のPrefabリストに存在するパスのうち、PR内で変更があるものをリストから削除してコミットします。変更があるのにマージできるということは、コンフリクトの可能性がない(解決済み)と判断できるためです。

強制リセットでリストを保護し、Cleanupで解決済みのパスを除外する——この2つが連携することで、各バージョンの prefab-paths.json は常にそのバージョン固有の編集済みPrefabリストとして正しく機能します。

あとはそれを使ってチェックと可視化機能を作るだけです。

Action 5: Checker — コンフリクトの予知

タイミング

バージョンブランチへの個人PRが作成された時に発動します。

処理

PRで変更されたPrefabが未来のバージョンprefab-paths.json にも存在する場合、最も近いバージョンとJSONへのリンクをPRコメントとして警告します。

Checkerの警告PRコメント

GitHub の Blame 機能で変更 PR を追跡できるため、リンクは JSON の対象パス行の Blame へのリンクになっています。これにより、どの PR で変更が入ったか、誰が変更したかを確認できます。

なお、この仕組みは「同一Prefabが編集済み」であることを検知するもので、実際にコンフリクトするかどうかはケースバイケースです。あくまでコンフリクトの”可能性”の検知であり、最終判断はBlameリンクから変更内容を確認して行います。

Checkerは全ての未来バージョンの prefab-paths.json を参照しますが、各JSONは git show で取得するだけなのでチェックアウト不要で軽量です。8バージョン並行でもPR作成時のCI時間への影響はほぼありません。

Unityエディタ拡張 — 作業前に気づく

PR作成時の警告だけでは、Prefab 編集作業が終わった後に気づくことになり、作業コストを支払った後になってしまいます。

可視化

EditorApplication.projectWindowItemOnGUI を使用し、コンフリクトの危険があるPrefabファイルの背景を赤く描画します。さらに、直近でコンフリクトするバージョンをタグとして表示します。

Projectウィンドウでの赤い表示とバージョンタグ

Prefabアイコンにコンフリクト警告が表示されている様子

非同期データ取得

EditorApplication.FocusChanged でリストを更新し、ブランチ切り替えやFetchを考慮しています。Gitから未来のバージョンの prefab-paths.json を非同期で取得してキャッシュすることで、エディタの作業を阻害しません。

現在のバージョン判定

コミットを遡り、バージョンブランチにも含まれているコミットを探すことで、バージョンブランチ自体にいなくても現在のバージョンを正しく自動判定しています。

今後の展開

  • 対象ファイルの拡大: Prefabだけでなく、他のコンフリクトしやすいバイナリファイルやアセットも監視対象にする
  • 厳密なコンフリクト判定: 変更の有無だけでなく「実際にマージコンフリクトが発生するか」まで判定する(計算時間とのトレードオフ)
  • 横展開: 独立したライブラリとして他プロジェクトからも利用できる形に整備する

まとめ

バージョンの並行開発が増えるほど、Prefabコンフリクトのリスクは指数的に増大します。本システムでは以下の多層防御でこの問題に対処しています。

  1. 記録 — Trackerが変更Prefabを自動追跡
  2. 予知 — CheckerがPR時にコンフリクトを警告
  3. 可視化 — Unityエディタ拡張で作業前に気づける
  4. 保護 — バージョンマージ時にリストを強制リセットし、Cleanupで解決済みを整理

なお、このシステムの導入にはバージョンブランチの作成やマージ周りが手動ではなくGitHub Actions経由で統制されていることが前提になります。

「コンフリクトしてから直す」のではなく「コンフリクトさせない」仕組みを作ることで、開発者の心理的安全性と開発効率の両方を向上させることができました。

過去は3バージョン開発になっただけでとてもマージがタスクがつらい状態でしたが、Actionsでの自動化やコンフリクトを回避するシステムなどがあればそこまで苦には思わない環境が現実になってきています。 Unity開発にプロジェクトにおいて同じような苦労を感じてる方の参考になれば幸いです。

著者

中辻 智裕のプロフィール画像

(Nakatsuji Tomohiro)

2016年にサイバーエージェントに新卒入社。QualiArtsの前身であるAmebaゲームズに配属され、現在は運用プロジェクトでUnityチームのエンジニアリーダーを務めている。UI/UXや音関連にちょっとうるさい。

この記事をシェア

目次