P4 命令速查与运维脚本
P4 命令速查与运维脚本
平时用 P4 做引擎/资源仓库的运维,命令行比 P4V 顺手得多。这里把自己反复用到的命令、批处理和 PowerShell 脚本攒在一起,按场景分组。depot 路径都换成了占位符 //depot/project/...、//MyProject/...,服务器地址写成 <P4SERVER>:1666,实际用的时候替换成自己的。
环境与配置
p4 set P4PORT=<P4SERVER>:1666 # 配置服务器地址
p4 set P4USER=<user>
p4 set P4PASSWORD=<password>
p4 set P4CLIENT=<workspace>
p4 set # 查看本机 P4 配置
p4 info # 显示服务器信息
p4 configure show # 显示服务器配置(如最大并行数)
p4 monitor show # 当前服务器正在执行的所有命令
p4 protect # 当前连接的 P4 保护表
p4 login # 当前用户登录
不想改环境变量,也可以在单条命令里直接传连接参数:
p4 -C UTF-8 -p <P4SERVER>:1666 -u <user> -P <password> -c <workspace> <cmd>
命令行登录(脚本里常用):
p4 set P4PORT=<P4SERVER>:1666
p4 set P4USER=test
p4 set P4CLIENT=test_client
echo <password>|p4 login
用户与组管理
p4 user -f <user> # 新增或修改用户配置(打开 spec 编辑)
p4 user -d <user> # 删除用户
p4 password <user> # 设置用户密码
p4 group <group> # 新建组(打开 spec 编辑)
p4 group -o <group> # 输出某个组的成员名单
删除某个用户、清掉它在组里的记录、并 revert 它 checkout 的所有服务器文件:
p4 revert -y -D <user>
日常 checkout / 提交
p4 opened # 查看所有 checkout 状态的文件
p4 edit/add/delete/reopen <FileSpec> # 编辑/添加/删除/移动到另一个 pending CL
p4 change # 生成一个 changelist
p4 submit -c <CL> # 提交指定 changelist
p4 submit -d <description> --parallel=<N> -f revertunchanged -c <CL> # 并发提交并 revert 未改动文件
p4 describe -ds <CL> | grep '^\.\.\.' # 查看某个 CL 涉及哪些文件
p4 resolve # 检查并解决冲突
p4 resolve -f -am # 自动解冲突,采用"接受 Merge"的方式
p4 integrate <source> <target> # merge,从 source 到 target
注意 p4 delete 会直接删掉本地文件,加 -k 可以保留本地副本。
向指定 changelist 添加文件或文件夹:
p4 add -c <CL> <dir_path>\... # 整个文件夹
p4 add -c <CL> <file_path> # 单个文件
批量提交(高并发,顺便丢掉未改动文件):
p4 submit --parallel=12 --noretransfer=1 -f revertunchanged -c <CL>
关联 / 取消关联 Job:
p4 fix -c <CL> <Job>
p4 fix -d -c <CL> <Job>
复制某一类型的文件(这里是把所有 .wav 从一个分支复制到另一个):
p4 copy -c default //MyProject/main/....wav //MyProject/dev/....wav
revert(保留本地修改)
很多时候只想把文件从 pending CL 里摘出来、状态恢复成未 checkout,但不动文件内容:
p4 revert -k //... # revert pending CL,保留本地修改
p4 revert -k -C <Client> //... # 管理员替任意 client revert,保留本地修改
p4 revert -k -c default d:\game\... # revert default 下全部文件
p4 revert -k -a -c default d:\game\... # 只 revert default 下没改动的文件
-k 是 keep 本地文件,-a 限定"只处理未变更的文件"。
同步与版本
查当前 workspace 同步到的 changelist:
p4 changes -m 1 //...#have
p4 changes -m1 //MyProject/main/...#have # 本地同步到的最高 CL
p4 changes -m1 -s resolved //MyProject/main/...#have
-m1 里的 1 是数字,表示只取一条。
查指定文件(夹)的最新 changelist:
p4 changes -m 1 //depot/project/Version_config/Asia.txt
同步到指定版本:
p4 sync //depot/...@123 # 同步到 revision 123
走代理缓存同步:
p4 -Zproxyload sync //depot/project/CHAR/...
并发同步:
p4 sync --parallel=<N> <FileSpec>
检测并修复同步状态
p4 flush 用在本地已有数据、但 workspace 没记录的情况——更新 have 列表让其对应上 depot:
p4 flush
文件曾经 sync 到本地、但被手动误删,workspace 仍以为文件存在时,可以这样检测出丢失文件并强制重新 sync。注意:从来没 sync 过的文件检测不出来,那种直接 p4 sync 即可。
# 列出 workspace 缺失、但 depot 里存在的未打开文件
p4 diff -sd //MyProject/main/...@CL
p4 diff -sd //MyProject/main/...@CL | p4 -x - sync -f # 强制同步这些文件
# 列出 workspace 里与 depot 版本不一致的未打开文件
p4 diff -se //MyProject/main/...@CL
p4 diff -se //MyProject/main/...@CL | p4 -x - sync -f
强制更新本地缺失的文件,用 PowerShell 逐个比对路径:
$P4Branch="//test/main/Certs"
$LocalRoot="E:\test\Certs"
$CL="" # 留空或写成 @123 限定版本
p4 files "${P4Branch}/..." | foreach-object {
$DepotFile = ($_ -split "#")[0]
$filename = $DepotFile -replace $P4Branch,$LocalRoot -replace "/","\"
if(!(Test-Path $filename)){
p4 sync -f $DepotFile$CL
}
}
批量编辑/添加/删除(按目录或后缀)
Windows 命令行下,把整个目录树喂给 p4 -x -:
dir /b /s /a-d | p4 -x - add # 添加当前及子目录所有文件
dir /b /s /a-d | p4 -x - edit # checkout 当前及子目录所有文件
dir /b /s /a-d | p4 -x - delete # 删除
dir /b /s /a-d | p4 -x - edit -t +w # checkout 并加可写属性
dir /b /s /a-d | p4 -x - add -t +w # 添加并加可写属性
给指定目录或后缀的文件统一加可写属性:
p4 edit -c default -t +w //MyProject/Engine/.../DotNET/...
p4 edit -c default -t +w //MyProject/Engine/.../*.dll
按后缀过滤提交(default CL 里只提交匹配的,其余移到下一个 default CL):
p4 submit *.txt # 只提交 .txt
p4 submit -d "header files" *.h # 只提交 .h,带描述
统计文件大小
p4 sizes 统计数量和大小:
p4 sizes -h -s <FileSpec> # 文件数量与总大小
p4 files <FileSpec> # depot 文件夹下文件信息
用 p4 fstat 把指定大小区间的文件分桶输出成列表(以 byte 为单位)。下面把 art 目录按数量级切成几档,各自落到一个 txt:
p4 fstat -Ol -F "fileSize >1024 & fileSize <= 102400" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 1024filelist.txt
p4 fstat -Ol -F "fileSize >102400 & fileSize <= 1048576" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 102400filelist.txt
p4 fstat -Ol -F "fileSize >1048576 & fileSize <= 10485760" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 1048576filelist.txt
p4 fstat -Ol -F "fileSize >10485760 & fileSize <= 104857600" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 10485760filelist.txt
p4 fstat -Ol -F "fileSize >104857600 & fileSize <= 1073741824" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 104857600filelist.txt
p4 fstat -Ol -F "fileSize >1073741824 & fileSize <= 107374182400" -T "depotFile" //depot/project/art/... | sed "s/... depotFile //" | sed "/^[[:space:]]$/d" > 1073741824filelist.txt
列出两个 CL 之间所有改动文件以及每个文件大小(PowerShell,-e 踢掉被删除的文件):
p4 files -e "//MyProject/main/...@279273,@279348" | foreach {
$string = ($_ -split "#")[0]
$values = (((p4 sizes $string) -split "#")[1] -split " ")
[int]$size = $values[1] # 字符串转 int
$size = $size/1024
echo "$string $size M"
} | Out-File test.txt utf8
Shelved 文件
获取指定 shelved CL 的内容,不要加路径,否则会变成获取整个路径下所有 shelved CL:
p4 changes -s shelved @CL,@CL # 正确
p4 changes -s shelved //depot/test/...@CL,@CL # 加路径后 CL 范围失效
用 print 规避 unshelve 锁定二进制文件
直接 p4 unshelve 二进制文件会被锁,改用 p4 print 把 shelved 文件落到本地就能绕开。print 时同步生成一个文件列表(避免后续 shelved CL 内容变动导致撤销出错),方便之后还原。
param (
[int]$unshlevedNumber
)
$listFilePath = "shelved_${unshlevedNumber}_file_list.txt"
# 拿到 shelve 文件详情,找出文件路径
$shelveFileList = & p4 describe -s -S $unshlevedNumber
if (Test-Path $listFilePath) {
Remove-Item $listFilePath -Force
}
foreach ($line in $shelveFileList) {
if ($line -match "^\.\.\.\s(.+)\#\d+\s") {
$depotFilePath = $matches[1]
$fileAction = $matches[2].ToLower()
# p4 where 找对应的本地路径
$whereOutput = & p4 where $depotFilePath
$localFilePath = ($whereOutput -split '\s+')[2]
if (-not $localFilePath) {
Write-Warning "无法获取本地路径: $depotFilePath"
continue
}
$localDir = [System.IO.Path]::GetDirectoryName($localFilePath)
if (-not (Test-Path $localDir)) {
New-Item -ItemType Directory -Path $localDir -Force | Out-Null
}
# depotPath@=changeNum,等号指定该 changelist 的版本
$fileAtChange = "$depotFilePath@=$unshlevedNumber"
# -o 输出到本地路径
& p4 print -o $localFilePath $fileAtChange
Write-Host "Downloaded $depotFilePath to $localFilePath"
# 把 depot 路径和文件状态写进列表
"$depotFilePath $fileAction" | Out-File -FilePath $listFilePath -Encoding utf8 -Append
}
}
在 workspace 下执行:
powershell -ExecutionPolicy Bypass -File .\P4Print_shelvedFiles.ps1 -unshlevedNumber 10968
还原 print 出来的文件
因为文件没有 checkout 状态,没法直接 revert,只能根据上一步的列表强制 sync 或本地删除来还原。add/delete/move 状态的文件要单独考虑——add 的文件本地删掉,其余强制 sync。
param (
[int]$unshlevedNumber
)
$listFilePath = "shelved_${unshlevedNumber}_file_list.txt"
if (-not (Test-Path $listFilePath)) {
Write-Error "File list '$listFilePath' not found."
exit
}
$fileEntries = Get-Content $listFilePath
foreach ($entry in $fileEntries) {
# 格式: <depot_file_path> <file_action>
$parts = $entry -split "\s+"
if ($parts.Count -lt 2) { continue }
$depotFilePath = $parts[0]
$action = $parts[1]
$whereOutput = & p4 where $depotFilePath
$localFilePath = ($whereOutput -split '\s+')[2]
if (-not $localFilePath) {
Write-Warning "Cannot find local file path for $depotFilePath"
continue
}
switch ($action) {
"add" {
if (Test-Path $localFilePath) {
try {
Remove-Item -Path $localFilePath -Force -ErrorAction Stop
Write-Host "Deleted local added file: $localFilePath"
} catch {
Write-Warning "Failed to delete local file $localFilePath. $_"
}
}
}
default {
Write-Host "Force syncing file: $depotFilePath"
& p4 sync -f $depotFilePath | Out-Null
}
}
}
限制 changelist 可见性
P4 server 默认 defaultChangeType=public,任何人都能看 pending 和 submitted 的 changelist。想限制就改成 restricted 再重启 server:
p4 configure set defaultChangeType=restricted
改成 restricted 后:
- Pending changelist:只有 owner 可见,跟别人是否有文件权限无关。
- 含 shelved 文件的 pending changelist:对其中某些 shelved 文件有 list(及以上)权限的用户能看到那些文件,但看不到 changelist 描述。
- Submitted changelist:对其中某些文件有 list(及以上)权限的用户能列出那些文件、读到 changelist 描述。
参考:Restrict access to changelists。
改文件名大小写(Windows)
Linux 这种大小写敏感的文件系统,改大小写就是一次普通 rename。Windows 的 NTFS 大小写不敏感(case-aware 但 case-insensitive),P4 server 跑在 Windows 上时,只差大小写的文件名引用会被忽略。要真正改掉,必须先删除、再用正确大小写重新 add,并把 have 列表里旧大小写的引用清掉。
把文件 test(假设有 3 个 revision)改成 Test:
# 1. 删除现有文件
p4 delete test
p4 submit -d "deleting file"
# 2. sync 到删除前的最后一个 revision(假设是 #3)
p4 sync test#3
# 3. p4 flush(即 sync -k)去掉 have 列表里的旧大小写引用,但不动本地文件
p4 flush test#none
# 4. 用正确大小写重新 add(add 用命令行给的大小写)
p4 add Test
# 5. 提交
p4 submit -d "New file in mixed case"
在 Jenkins SCM pipeline 里拿到 sync 后的 CL 号
p4.run("sync") 之后想知道同步到的最高 changelist 号。在 tagged 模式下,p4 sync 的输出里就带 change 字段:
p4 -Ztag sync ...
... depotFile //stream/test/merge/foo.txt
... rev 2
... action updated
... change 226
tagged 输出会被 P4Python 转成字典,所以可以直接:
changelist_number = p4.run("sync")[0]["change"]
但这有边界情况:被删除的文件不会被 sync,删除 revision 不算进这个 CL 号。更稳妥的做法是先拿当前 depot 的最高 submitted CL 号、再 sync 到这个确切的号,保证一致性——两条命令之间就算有人提了新 CL,你记录的号也仍然对得上你 sync 到的内容:
changelist_number = p4.run("changes", "-m1", "-ssubmitted")[0]["change"]
p4.run("sync", "@{changelist_number}")
任何 client sync 到这个 CL 号,都能拿到同一组 revision(受各自 View 限制)。
参考:Determine changelist number after p4.run("sync")。
杂项
设置仓库只保留某文件最近 n 个版本(n 取 1-10 或 16/32/64/128/256/512):
p4 edit -t +Sn //.../test.txt
# 或在 typemap 里设:
text+Sn //....txt
清空 stream 机器仓库(清 workspace、清 stream 分支、删 depot):
p4 stream --obliterate -y //MyProject/<BranchName>
p4 depot -d -f <DepotName>
合并分支:在 P4V 里 merge | Integrate...。创建 depot:P4Admin 里 Tools > Administration > File > New > Depot。
Workspace 文件只读,改不了但能 add/delete?
checkout(create new changelist)之后再改即可。