コンテンツにスキップ

ファイルロック

Textil のファイルロック機能は、バイナリファイルの編集競合を未然に防ぎます。ブランチを跨いだ排他制御と系譜追跡により、チーム開発での競合を根本から防止します。

Unreal Engine のアセットファイル(.uasset、.umap)はバイナリ形式です。テキストファイルと異なり、Git の自動マージが効きません。2人が同じファイルを編集すると、どちらかの変更を捨てるしかなくなります。

ファイルロックは「編集前に宣言する」ことで、この問題を回避します。

なぜ Git LFS のロックではないのか

Section titled “なぜ Git LFS のロックではないのか”

Git LFS にもロック機能(git lfs lock)がありますが、ブランチ開発との相性に根本的な課題があります。

従来のロックで起きる問題
  1. 1
    Alice が main で Hero.uasset をロック
  2. 2
    Alice が編集してコミット、ロック解除
  3. 3
    Bob が feature ブランチで同じファイルをロック
    ロックは空いているので取得できてしまう
  4. 4
    Bob が編集してコミット
  5. 5
    feature を main にマージ
    競合発生、どちらかの変更を捨てるしかない

ロックが空いていても、別ブランチで編集済みかもしれない。従来のロックは「今誰が持っているか」しか見ないため、この問題を防げません。

Textil のロックは、ファイルの「系譜(Lineage)」を追跡します。ロック取得時に、申請者が最新の状態を持っているかを検証し、持っていなければロックを拒否します。

Textil のロックが防ぐ
  1. 1
    Alice が main で Hero.uasset をロック
  2. 2
    Alice が編集してコミット、ロック解除
  3. 3
    Bob が feature ブランチでロックを試みる
    「Alice の変更を取り込んでいません」と拒否
  4. 4
    Bob が git merge main で Alice の変更を取り込む
  5. 5
    Bob が再度ロックを試みる
    成功

この検証により、「ロックを取れたなら、安全に編集できる」ことが保証されます。

Textil のロックはブランチを跨いで排他的です。あるブランチでロックを取得すると、他のブランチからも同じファイルをロックできません。

バイナリファイルはマージできないため、ブランチごとに独立したロックを許可する意味がありません。グローバルスコープにすることで、マージ時の競合を根本から防ぎます。

系譜の検証には、Git LFS の oid(ファイル内容のハッシュ)を使用します。これにより、squash merge や rebase でコミット履歴が書き換わっても、ファイルの内容が同じであれば正しく追跡できます。

squash merge でも追跡できる
  1. 1
    feature ブランチで Hero.uasset を編集(oid: abc123)
  2. 2
    main に squash merge(新しいコミットだが、oid は同じ abc123)
  3. 3
    別の開発者がロックを試みる
    oid が一致するため、正しく「最新を持っている」と判定

ロック状態は WebSocket を通じてチーム全体にリアルタイムで共有されます。

  • 誰かがロックを取得すると、即座に全員に通知
  • 誰かがファイルを更新(fetch/push)すると、その情報も全員に伝播
  • 接続が切れても、再接続時に最新状態を自動復元

この仕組みにより、チーム全員が常に同じロック状態を見ることができます。

ロック機能は Git LFS で管理されているファイルのみが対象です。テキストファイル(.cpp、.h、.ini など)は Git の通常のマージで解決できるため、ロックの対象外です。

一般的な Unreal Engine プロジェクトでは、以下のファイルが LFS 管理され、ロック対象となります:

  • .uasset - アセットファイル全般
  • .umap - レベルファイル
  • テクスチャ、メッシュ、サウンドなどのインポート元ファイル