Perforce の排他ロックの挙動を細かく検証してみた

Perforce の排他ロックの挙動を細かく検証してみた

Perforce の排他ロック (binary+l) について、デッドロックの発生・管理者による解除・非排他 (binary+w) との違い・typemap による自動付与といった実際の運用で押さえておきたい仕様を確かめました。
2026.07.19

はじめに

バイナリ資産を多人数で扱う開発現場では、Perforce の排他ロックは避けて通れません。誰かがファイルを編集のために開くと、他の人は開けなくなるという仕組みは強力ですが、Git に慣れた方には少し馴染みの薄い挙動もあります。

本記事では、2 人のユーザーを用意した Perforce に対して、排他ロックによるデッドロック・管理者による解除・非排他ロックとの違い・typemap による自動付与を試し、挙動を確かめます。

バイナリ向けのファイルタイプには、ロックのかかり方が異なる 2 種類があります。binary+l は排他ロック付きのファイルタイプで、1 人が編集のために開くと、他のユーザーは開けません。binary+w は非排他で、複数人が同時に開けます。本記事はこの 2 つを軸に検証していきます。手元で再現できるよう、手順を本文に示します。

Perforce とは

Perforce (Helix Core) は、大容量のバイナリを多人数で扱う開発現場で広く使われる、中央集権型のバージョン管理システムです。ゲーム開発での採用が多く、マージできないバイナリを守るための排他ロックの仕組みを備えます。サーバープロセスは p4d、クライアントは p4 と呼びます。

検証環境

  • macOS
  • Helix Core p4d 2025.2
  • tester_a と tester_b の 2 ユーザー

対象読者

  • Perforce のロック運用を設計したい管理者
  • 大容量のバイナリを多人数で扱う開発現場の方

参照

背景と問題提起

なぜ Perforce はロックを使うのでしょうか。主戦場はゲーム開発で、扱うファイルの多くはテクスチャや 3D モデルといったバイナリです。バイナリはテキストと違い、意味のあるマージができません。 マージできない以上、同時編集を許すと片方の作業が失われます。だからこそ、編集を 1 人に限る排他ロックが合理的な保護策になります。

一方で、ロックの挙動には、実際に運用してみないと気づきにくい仕様もあります。ブロックされたコマンドの成否・管理者が解除したときにファイルがどうなるか・typemap を後から足したときの適用範囲について、実際に確かめていきましょう。

検証方法

p4d を手元に起動し、2 人のユーザー tester_a と tester_b、それぞれのワークスペースを用意します。検証対象として、排他ロックを表す binary+l を付けたバイナリファイルを 2 つ登録します。

p4d の起動

Perforce の p4d と p4 は公式のダウンロードページから取得できます。

start-p4d.sh
mkdir -p p4root
p4d -r "$PWD/p4root" -p 1666 -L "$PWD/p4d.log" -J "$PWD/p4root/journal" -d
export P4PORT=1666
# 検証用に認証を無効化する。本番では設定しない
p4 configure set security=0

ユーザーとファイルの準備

tester_a と tester_b のワークスペースを作り、binary+l を付けたバイナリ 2 つ (assetX.bin と assetY.bin) を登録します。

ユーザーとファイルの準備スクリプト
setup-users.sh
# tester_a のワークスペースを作り、排他ロックのバイナリを登録する
export P4USER=tester_a P4CLIENT=ws_a
p4 client -i <<EOF
Client: ws_a
Owner: tester_a
Root: $PWD/ws_a
View:
	//depot/... //ws_a/...
EOF
mkdir -p ws_a && cd ws_a
head -c 1048576 /dev/urandom > assetX.bin
head -c 1048576 /dev/urandom > assetY.bin
p4 add -t binary+l assetX.bin assetY.bin
p4 submit -d "baseline: assetX/Y.bin (binary+l)"
cd ..
# tester_b のワークスペースを作り、同期する
p4 -u tester_b -c ws_b client -i <<EOF
Client: ws_b
Owner: tester_b
Root: $PWD/ws_b
View:
	//depot/... //ws_b/...
EOF
p4 -u tester_b -c ws_b sync

検証結果

排他ロックによるデッドロック

tester_a が assetX.bin を、tester_b が assetY.bin を、それぞれ編集のために開きます。続けて、互いに相手のファイルも開こうとします。

deadlock.sh
# tester_a が X を、tester_b が Y を開く
p4 -u tester_a -c ws_a edit //depot/assetX.bin
p4 -u tester_b -c ws_b edit //depot/assetY.bin
# 互いに相手のファイルを開こうとする
p4 -u tester_a -c ws_a edit //depot/assetY.bin
p4 -u tester_b -c ws_b edit //depot/assetX.bin

binary+l は、編集のために開いた時点で他のユーザーを締め出します。そのため、tester_b が assetX.bin を開こうとした行は次のように拒否されます。

//depot/assetX.bin - can't edit exclusive file already opened for edit
... //depot/assetX.bin - also opened by tester_a@ws_a

互いに相手のファイルを待つ形になり、どちらも 2 つ目のファイルを取れません。これがデッドロックです。

自動化のときに注意したい仕様があります。コマンドが失敗すれば終了コードは非ゼロになるのが一般的ですが、この排他ロックによるブロックでは、p4 edit の終了コードは 0 となりました。

$ p4 -u tester_b -c ws_b edit //depot/assetX.bin ; echo "exit=$?"
//depot/assetX.bin - can't edit exclusive file already opened for edit
... //depot/assetX.bin - also opened by tester_a@ws_a
exit=0

スクリプトや自動化で終了コードだけを見ていると、ロックに阻まれたことに気づけません。なお、後述する submit の衝突では終了コードは 1 になります。

管理者による解除

他のユーザーが保持したままのロックは、admin 権限を持つユーザーが解除できます。今回の最小構成は protections を設定しておらず、この場合 Perforce はすべてのユーザーに super 権限を与えるため、実際にはどのユーザーでも解除できます。本番では p4 protect で管理者に限定します。

まず、誰が何を開いているかを確認します。

$ p4 opened -a
//depot/assetY.bin#2 - edit default change (binary+l) by tester_b@ws_b *exclusive*

*exclusive* の印が、排他ロックであることを示します。ここで、tester_b がこのファイルに未コミットの編集を加えているとします。管理者が解除すると、この編集はどうなるでしょうか。強制解除すると相手の作業が失われる、あるいは元の状態に戻る、と思うかもしれませんが、検証するとどちらでもありませんでした。

編集前後とファイルのハッシュ値を取りながら、管理者が解除します。

$ md5 -q ws_b/assetY.bin        # 編集前
a8470858dab3f9774e473bb0babeb582
# tester_b がローカルを書き換える (未コミットの編集)
$ md5 -q ws_b/assetY.bin        # 編集後
6fdb30a7d9d8faffb47a6efaf409e0ea

$ p4 -u admin -c ws_b revert -C ws_b //depot/assetY.bin
//depot/assetY.bin#2 - was edit, cleared

$ md5 -q ws_b/assetY.bin        # 解除後
6fdb30a7d9d8faffb47a6efaf409e0ea

解除後のハッシュ値は編集後のままで、デポの内容には戻っていません。これは p4 revert の仕様で説明できます。

$ p4 help revert
...
The -C flag allows a user to specify the workspace that has the file
opened rather than defaulting to the current client workspace. When
this option is used, the '-k' flag is also enabled ...

The -k flag marks the file as reverted in server metadata without
altering files in the client workspace.

他人のワークスペースを指定する -C-k を暗黙的に有効にします。-k は、サーバー側のメタデータ上で編集中の状態を解除するだけで、クライアントのファイルには手を触れません。つまり管理者の解除は、サーバー側の編集中の状態とロックを解除しますが、保持者のワークスペースのファイルは変更しません。 ロックは実際に解け、待っていた側はそのファイルを編集できるようになります。

$ p4 -u tester_a -c ws_a edit //depot/assetY.bin
//depot/assetY.bin#2 - opened for edit

保持者の編集内容はワークスペースに残りますが、サーバー側では編集中の状態が解除されます。

ここまでは、保持者が何も備えていない場合でした。保持者が作業を確実に残したいときは、解除の前に shelve でサーバーへ退避しておきます。編集を番号付きの changelist にまとめてから shelve します (p4 change で changelist を作成し、p4 reopen -c で編集をそこへ移します)。

$ p4 -u tester_b -c ws_b shelve -c 15
... //depot/assetY.bin - warning: shelve of +l file
Change 15 files shelved.

ここで注意したいのは、shelve してもロックは解除されないことです。 shelve のあとも p4 opened -a では *exclusive* のままで、他の人のブロックは解けません。shelve は作業を退避するだけで、ロックそのものを解除するにはやはり管理者の操作が必要です。管理者が解除しても、退避した shelve は残ります。

$ p4 -u admin -c ws_b revert -C ws_b //depot/assetY.bin
//depot/assetY.bin#2 - was edit, cleared
$ p4 changes -s shelved
Change 15 on 2026/07/19 by tester_b@ws_b *pending* 'WIP assetY'

保持者はあとで unshelve すれば、退避した編集をそのまま取り戻せます。

$ p4 -u tester_b -c ws_b unshelve -s 15 //depot/assetY.bin
//depot/assetY.bin#2 - unshelved, opened for edit

解除の前に本人が shelve しておけば作業は守れます。逆に何も備えていなければ、サーバー側の編集中の状態が解除され、退避していない編集は手元に残るだけになります。だからこそ、管理者が解除する前に本人へ連絡することが大切です。

非排他 (binary+w) との違い

排他ロックだけが選択肢ではありません。ファイルタイプを binary+w にすると非排他になり、2 人が同じバイナリを同時に開けます。待たされないぶん手軽ですが、衝突の扱いには注意が要ります。

ここでは assetW.bin を binary+w で登録しておきます。

p4 add -t binary+w assetW.bin

これを 2 人が同時に開いてみます。

$ p4 -u tester_a -c ws_a edit //depot/assetW.bin
//depot/assetW.bin#3 - opened for edit
$ p4 -u tester_b -c ws_b edit //depot/assetW.bin
//depot/assetW.bin#3 - opened for edit
... //depot/assetW.bin - also opened by tester_a@ws_a

binary+l のようなブロックはなく、警告は出ますが、2 人目も同じファイルを開けます。問題は submit のタイミングで現れます。それぞれが編集したうえで、まず tester_a が submit し、続いて tester_b が submit すると、後から submit する tester_b は解決を求められます。

$ p4 -u tester_a -c ws_a submit -d "A edits assetW"
Change 13 submitted.
$ p4 -u tester_b -c ws_b submit -d "B edits assetW"
//depot/assetW.bin - must resolve before submitting
Out of date files must be resolved or reverted.

$ p4 -u tester_b -c ws_b resolve -am //depot/assetW.bin
Non-text diff: 0 yours + 0 theirs + 0 both + 1 conflicting
//ws_b/assetW.bin - resolve skipped.

バイナリはマージできないため、自動マージ (-am) はスキップされます。残る選択は、自分の版を採る (-ay) か相手の版を採る (-at) かの二択で、採らなかった側の版はこのファイルには反映されません。binary+w は待ち時間をなくす代わりに、衝突時の判断を submit のタイミングに持ち越します。

typemap による自動付与

排他ロックの付け忘れを防ぐには、typemap で拡張子ごとにファイルタイプを決めておく方法があります。適用範囲を確かめるため、まず typemap を設定する前に art_old.psd を 1 つ追加しておきます。この時点では typemap が無いので、通常の binary になります。

そのうえで、typemap を設定します。

typemap.sh
p4 typemap -i <<EOF
Typemap:
	binary+l //....uasset
	binary+l //....umap
	binary+l //....psd
EOF

これで、設定後に追加する .psd ファイルは、-t を付けなくても binary+l になります。

$ p4 add art_new.psd
$ p4 opened //depot/art_new.psd
//depot/art_new.psd#1 - add default change (binary+l) *exclusive*

typemap の適用は追加 (p4 add) の時点に限られ、設定前に追加済みのファイルには及びません。 設定前から存在する .psd は binary のままです。

$ p4 fstat -T "depotFile, headType" //depot/art_old.psd
... depotFile //depot/art_old.psd
... headType binary

既存のファイルを binary+l にするには、p4 edit -t binary+l で型を変えて submit する移行が別に必要です。typemap を導入するときは、この移行を忘れると古いファイルだけロックが付かない状態が残ります。

考察

binary+l と binary+w の選び方

観点 binary+l (排他) binary+w (非排他)
同時編集 2 人目の edit をブロック 2 人が同時に開ける
衝突のタイミング 発生しない (edit 時に止める) submit 時に resolve
バイナリの衝突解消 不要 自分か相手の版の二択
向いているファイル 1 人ずつ触るアセット (.uasset、.psd、音源など) 並行編集を運用で捌けるもの

マージできないバイナリの多くは、既定を binary+l にして typemap で自動付与し、明確に並行してよいものだけ binary+w にすると分かりやすいでしょう。

予防とロック競合への備え

ロック競合は、事後の解除よりも事前の設計で減らせます。typemap で付け忘れを防ぎ、p4 opened -a で長時間 *exclusive* のまま動かないファイルを見つけて早めに連絡するという運用が有効です。管理者の解除はサーバー側の編集中の状態を解除するだけなので、保持者の作業を守るには、解除の前に本人へ連絡し、必要なら shelve してもらう手順が要ります。

なお、本記事で扱った binary+l の排他オープンとは別に、p4 lock コマンドによる勧告ロックもあります。こちらは edit は許して submit の段階で止めるもので、p4 opened -a では *locked* と表示され、edit を止める *exclusive* とは動作の仕方が異なります。

まとめ

Perforce の排他ロックについて、デッドロックの発生、管理者による解除、非排他との違い、typemap の適用範囲を確認しました。ブロックされても終了コードは 0、管理者の解除はワークスペースのファイルを変更しない、typemap は既存ファイルに遡及しない、といった点は、実際の運用で押さえておきたい仕様です。Git に慣れた方にはとくに、少し馴染みの薄い挙動かもしれません。本記事が、Perforce のロックを運用する際の判断材料になれば幸いです。


ゲーム開発・運用環境の効率化を支援します

Classmethodの専門家による包括的なクラウド活用とデジタル化支援で、ゲーム開発の効率を最大化しましょう。AWSの導入から運用、最適化まで、最新技術と豊富な経験であらゆる課題を解決します。株式会社CAPCOM様、株式会社SNK様などの事例もご覧いただけます。

ゲーム業界のサービス詳細を見る

この記事をシェアする

関連記事