workspace マッピング、サーバーの have list、ローカル内容は一致しないことがある。P4PORT・P4USER・P4CLIENT を確認してから同期、変更登録、shelve、submit を行う。パスと changelist は例。
強制同期の前にマッピングを確認
サーバー・認証主体・workspace を確認する
p4 info
Server address・User name・Client name・Client root を確認する。接続・認証エラーは sync 前に解決する。
p4 login -s
期限切れなら p4 login で対話認証して再確認する。パスワードを引数に書かない。設定元が不明なら p4 set を別途読む。
p4 client -o
読取専用。p4 client で変更する前に仕様を保存し、既存作業の所属を確認する。
p4 where //depot/project/file.txt
実際の depot ファイルを指定する。not in client view はマッピングの問題で、強制 sync では直らない。
p4 have //depot/project/...
... は範囲を再帰的に含める。have はディスク内容を検証しない。
同じ範囲をプレビューしてから同期する
p4 sync -n //depot/project/...
件数・パス・対象 revision を確認し、必要な変更を保存してから進む。
p4 sync //depot/project/...
同じ file spec に @CHANGE / #REV を付けて固定できる。その指定で先にプレビューする。出力と have を確認し、開いているファイルや上書き保護のエラーでは部分成功を見落とさない。
p4 where で depot・client・ローカルのパスを照合する。Root・View・Stream が違えば同期先も変わる。client 名は論理名でホスト名と同じでなくてよい。have は同期版の記録であり現在の内容検査ではない。
図を準備しています
have list と disk 内容は別の状態sync -k は server 記録のみ、disk は不変。sync -f は再転送。#none は管理 file と have 記録を削除する。
{"id":"ops-p4-sync","title":"have list と disk 内容は別の状態","summary":"sync -k は server 記録のみ、disk は不変。sync -f は再転送。#none は管理 file と have 記録を削除する。","height":600,"html":"<main><p class=\"quiet\" data-i18n=\"scope\"></p><div class=\"cards\"><section class=\"card \"><h3 data-i18n=\"disk\"></h3><strong id=\"disk\"></strong></section><section class=\"card \"><h3 data-i18n=\"have\"></h3><strong id=\"have\"></strong></section><section class=\"card \"><h3 data-i18n=\"head\"></h3><strong id=\"head\">#3</strong></section></div><div class=\"controls\"><button type=\"button\" data-action=\"sync\" data-i18n=\"sync\"></button><button type=\"button\" data-action=\"metadata\" data-i18n=\"metadata\"></button><button type=\"button\" data-action=\"force\" data-i18n=\"force\"></button><button type=\"button\" data-action=\"none\" data-i18n=\"none\"></button><button type=\"button\" data-action=\"reset\" data-i18n=\"reset\"></button></div><div class=\"explain\"><div class=\"stable \"><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"same\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"different\"></p><p id=\"message\" aria-live=\"polite\"></p></div></div></main>\n","css":"main {\n display: grid;\n gap: 18px;\n min-width: 0;\n}\np {\n overflow-wrap: anywhere;\n}\n.controls {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n.presets {\n display: flex;\n flex-wrap: wrap;\n}\n.presets button {\n flex: 1 1 85px;\n font-size: 15px;\n}\n.cards {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n}\n.card {\n border: 1px solid var(--rule);\n border-radius: 10px;\n padding: 14px;\n min-width: 0;\n background: var(--surface);\n}\n.card h3 {\n font-size: 15px;\n color: var(--muted);\n margin-bottom: 8px;\n}\n.card strong {\n font-size: 23px;\n font-weight: 500;\n overflow-wrap: anywhere;\n}\n.code {\n font-family: var(--viz-font-mono);\n font-size: 15px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.stable {\n display: grid;\n min-width: 0;\n}\n.stable > * {\n grid-area: 1/1;\n margin: 0;\n}\n.stable > .reserve {\n visibility: hidden;\n pointer-events: none;\n}\n.explain {\n border-left: 3px solid var(--accent);\n padding: 12px 14px;\n background: var(--accent-soft);\n}\n.arrow {\n text-align: center;\n color: var(--accent);\n font-size: 25px;\n}\n.row {\n padding: 12px;\n border: 1px solid var(--rule);\n border-radius: 8px;\n min-width: 0;\n}\n.row.on {\n border-color: var(--accent);\n background: var(--accent-soft);\n}\n.row.off {\n color: var(--muted);\n}\n.bar {\n height: 18px;\n background: var(--surface);\n border: 1px solid var(--rule);\n border-radius: 5px;\n overflow: hidden;\n margin-top: 8px;\n}\n.bar > i {\n display: block;\n height: 100%;\n background: var(--accent);\n width: 0;\n transition: width 0.22s;\n}\n.checks {\n display: grid;\n gap: 8px;\n}\n.checks label {\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 9px;\n border: 1px solid var(--rule);\n border-radius: 7px;\n font-size: 16px;\n}\n.checks input {\n width: 18px;\n height: 18px;\n accent-color: var(--accent);\n flex-shrink: 0;\n}\nlabel.range {\n display: block;\n font-size: 16px;\n}\nlabel.range output {\n float: right;\n font-variant-numeric: tabular-nums;\n}\n.pair {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.flow {\n display: grid;\n gap: 6px;\n}\n.quiet {\n font-size: 14px;\n color: var(--muted);\n}\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 5px;\n background: var(--accent-soft);\n color: var(--accent);\n}\nbutton {\n overflow-wrap: anywhere;\n}\n.large {\n font-size: 32px;\n color: var(--accent);\n font-weight: 500;\n}\n.layers {\n display: grid;\n gap: 12px;\n}\n.layer {\n border-left: 3px solid var(--rule);\n padding-left: 12px;\n}\n.layer.on {\n border-color: var(--accent);\n}\n@media (max-width: 520px) {\n .cards,\n .pair {\n grid-template-columns: 1fr;\n }\n .card {\n padding: 12px;\n }\n .card strong {\n font-size: 22px;\n }\n}\n","js":"const $ = (s) => document.querySelector(s);\nconst $$ = (s) => [...document.querySelectorAll(s)];\nconst t = (k) => viz.t(k);\nconst put = (id, v) => ($(\"#\" + id).textContent = v);\nconst say = (k) => put(\"message\", t(k));\nconst state = (v) => ($(\"main\").dataset.state = JSON.stringify(v));\nconst press = (key, v) =>\n $$(\"[data-\" + key + \"]\").forEach((b) =>\n b.setAttribute(\"aria-pressed\", String(b.dataset[key] === v)),\n );\nlet disk = 2,\n have = 2;\nfunction draw() {\n put(\"disk\", disk === null ? \"∅\" : \"#\" + disk);\n put(\"have\", have === null ? \"∅\" : \"#\" + have);\n say(disk === have ? \"same\" : \"different\");\n state({ disk, have });\n}\n$$(\"[data-action]\").forEach(\n (b) =>\n (b.onclick = () => {\n let a = b.dataset.action;\n if (a === \"sync\" && have !== 3) {\n disk = 3;\n have = 3;\n }\n if (a === \"metadata\") have = 3;\n if (a === \"force\") {\n disk = 3;\n have = 3;\n }\n if (a === \"none\") {\n disk = null;\n have = null;\n }\n if (a === \"reset\") {\n disk = 2;\n have = 2;\n }\n draw();\n }),\n);\ndraw();\n","audio":false,"strings":{"different":"have は#3だが disk は違う。通常 sync は最新と判断し得る。-k は download ではない。","disk":"ローカル内容","force":"sync -f #3","have":"server have list","head":"depot head","metadata":"sync -k #3","none":"sync #none","reset":"#2 に reset","same":"disk と have は一致。ただし have 単独は内容検証ではない。","scope":"未 open file 一つ。disk/have は#2、head は#3。上書き可・編集なし。自由に操作して差を見る。","sync":"sync #3"}}| 操作 | ローカルファイル | have list |
| p4 sync PATH | open 状態と上書き保護に従って更新 | 更新 |
| p4 sync -f PATH | 強制転送。保持する変更を先に確認 | 更新 |
| p4 sync -k PATH | 転送・変更なし | 記録のみ変更。flush 相当で不一致を作り得る |
| p4 sync "//depot/project/...#none" | 範囲内の管理対象ローカルファイルを削除。open 状態の制約あり | 対応する記録を削除 |
"#none" はメタデータだけの削除ではない。引用符で shell の特殊解釈も避ける。同範囲を sync -n で確認し、マッピング不良を -f で直そうとしない。
ファイルを open して changelist を確認
変更種別に応じて open 操作を選ぶ
edit/add/delete/move は異なる変更向けで、すべて順番に実行するものではない。確認済み workspace の相対パスを使う。
p4 edit file.txt
編集後、p4 diff file.txt で確認する。
p4 add new-file.txt
内容は作らない。p4 opened で add を確認する。
p4 delete obsolete-file.txt
ローカルに影響し、submit 後に depot の最新から削除される。opened で対象を確認し、誤指定なら revert の影響を確認して戻す。
p4 move old-name.txt new-name.txt
必要に応じて元を edit で開き、未使用の移動先を使う。move/delete と move/add の対、および新パスの内容を確認する。
オフライン編集は検出してから登録する
p4 reconcile -n ...
追加・変更・削除候補から生成物を除き、範囲を絞った p4 reconcile path/to/file で登録する。未 open の差分は p4 diff -f file.txt。ここでの -f は強制同期ではない。
p4 change
Description と Files を編集し、保存後の番号を記録する。以下の 12345 はその例であり置き換える。
任意のファイル編集が自動的に changelist に入るわけではない。オフライン変更は reconcile -n で add/edit/delete を確認し、範囲を絞って登録する。未 open ファイルは diff -f。番号付き changelist を作り reopen で移す。
リスト整理・競合解決・submit の順で進む
p4 reopen -c 12345 file.txt
-c は changelist 選択。未 open のファイルを自動登録するものではない。
p4 opened -c 12345
p4 diff -du file.txt で内容を読み、追加ファイルは直接確認する。describe -s の概要だけでは不十分。
p4 resolve -n -c 12345
必要なら p4 resolve -c 12345 で個別に確認し、diff・テスト・再プレビューを行う。一方を無条件で採用しない。
p4 submit -c 12345
成功時の番号を記録し、p4 describe -s NUMBER で確認する。失敗時はエラーと opened を調べる。
ローカル変更の破棄は別途確認する
p4 revert -n file.txt
必要な内容を保存してから p4 revert file.txt。edit の変更は破棄され、add/delete はローカルへの影響が異なる。未変更だけなら範囲確認とプレビュー後に revert -a を選ぶ。
12345 は pending changelist の例。resolve 対象と競合を確認し、内容・テスト・stream を確認して submit する。revert は作業変更を破棄する。revert -a も対象を確認して使う。
Shelve はサーバー上の未提出内容
shelve 後にサーバー上の内容を確認する
p4 shelve -c 12345
作成済み pending の番号を使う。submit せず、ローカル作業も消さない。
p4 describe -S 12345
受取側は p4 changes -s shelved -u YOUR_USER で探せる。確認済みのユーザー名を使う。
受取 workspace の新しいリストへ復元する
受取側のマッピングと作業を確認し、p4 change で対象 12346 を作る。元の 12345 とは別番号。
p4 unshelve -s 12345 -c 12346
opened -c 12346・diff・resolve -n -c 12346 を確認し、必要なら解決・テストする。元 shelf は残る。stream をまたぐ場合は対応関係も確認する。
p4 shelve -d -c 12345
ローカルの revert や submit 済み履歴削除とは別。削除前に describe -S で番号と内容を再確認する。
履歴・行の由来・stream 仕様を必要に応じて調べる
p4 filelog file.txt
行ごとの由来は別途 p4 annotate file.txt で調べ、対応 changelist を読む。ファイルを変更する手順ではない。
p4 stream -o //streams/main
実際の stream 名を使い、client -o の Stream と照合する。
shelve は depot に submit せず、ローカル編集も消さない。unshelve は対象 changelist に復元し resolve が必要な場合がある。既存 shelf の置換は利用者と版を確認する。shelf 削除とローカル revert は別操作。
p4 sync · p4 reconcile · p4 shelve