P4 服务器存储文件的路径不一致
背景:p4服务器没有开启文件大小写 + Windows 磁盘上不路径大小写
使用p4 where 本地磁盘目录, 获取Depot中的目录, workspace中的目录,本地磁盘中存储的目录
得到Depot中的目录
//depot/Game/Content/Textures/.png
//depot/Game/Content/textures/T.png
而在P4V中或者控制台中使用p4 dirs 即p4 dirs //depot/Game/Content/* 看到的是:
//depot/Game/Content/Textures
因此,这里遍历本地磁盘中文件,查找本地文件在P4中的Depot中的位置,用P4 dirs 获取Depot中最开始存储的文件夹, 这样就能比较出哪些文件被记录在Depot中文件路径是错误的。
以下是python代码
ChatGPT给的方案
from P4 import P4, P4Exception
import os
def get_actual_case_on_disk(path):
"""
Given a path, returns the actual case of each part as found on disk.
On Windows, this is necessary to detect case mismatches.
Returns None if the path does not exist.
"""
if not os.path.exists(path):
return None
drive, rest = os.path.splitdrive(os.path.abspath(path))
parts = rest.strip(os.sep).split(os.sep)
actual_parts = []
current = drive + os.sep if drive else os.sep
for part in parts:
try:
entries = os.listdir(current)
except PermissionError:
return None
for entry in entries:
if entry.lower() == part.lower():
actual_parts.append(entry)
current = os.path.join(current, entry)
break
else:
return None
return actual_parts
def main():
p4 = P4()
try:
p4.connect()
depot_root = '//depot'
# Recursively get all directories
all_dirs = []
stack = [depot_root]
while stack:
curr = stack.pop()
try:
subdirs = p4.run('dirs', f'{curr}/*')
for d in subdirs:
all_dirs.append(d)
stack.append(d)
except P4Exception:
pass # no subdirs
print("Checking case consistency between depot and local filesystem:")
for depot_dir in all_dirs:
# Map depot dir to local path
try:
where = p4.run('where', depot_dir)
if where and 'path' in where[0]:
local_path = where[0]['path']
else:
print(f"Cannot map depot dir: {depot_dir}")
continue
except P4Exception:
print(f"Error mapping: {depot_dir}")
continue
actual_parts = get_actual_case_on_disk(local_path)
if actual_parts is None:
print(f"Local path missing: {local_path} (for depot {depot_dir})")
continue
expected_parts = os.path.normpath(local_path).split(os.sep)
depot_parts = depot_dir.lstrip('/').split('/')[1:] # remove leading //depot
# Compare only last N parts (to match depot dir depth)
n = len(depot_parts)
compare_expected = expected_parts[-n:]
compare_actual = actual_parts[-n:]
for exp, act, dep in zip(compare_expected, compare_actual, depot_parts):
if exp != act:
print(f"Case mismatch: depot '{dep}' vs local '{act}' in {local_path}")
except P4Exception as e:
print("Perforce error:", e)
finally:
p4.disconnect()
if __name__ == "__main__":
main()
Deep Seek给的代码
from P4 import P4, P4Exception
import os
def list_dirs_recursive(p4, depot_path, all_dirs):
try:
subdirs = p4.run('dirs', f'{depot_path}/*')
for d in subdirs:
all_dirs.append(d)
list_dirs_recursive(p4, d, all_dirs)
except P4Exception:
pass
def main():
p4 = P4()
output_file = "p4_depot_structure.txt" # 输出文件名
try:
p4.connect()
root = '//depot'
all_dirs = []
list_dirs_recursive(p4, root, all_dirs)
# 确保输出目录存在
os.makedirs(os.path.dirname(output_file), exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as f:
# 写入目录结构
f.write("Folders on depot:\n")
for d in all_dirs:
f.write(f"{d}\n")
# 写入文件列表
f.write("\nFiles under each folder:\n")
for depot_dir in all_dirs:
try:
# 获取本地映射路径
where_result = p4.run('where', depot_dir)
if not where_result:
continue
local_path = where_result[0].get('path', '')
if not local_path or not os.path.exists(local_path):
continue
# 获取工作区根路径以正确转换路径
client_root = p4.fetch_client()['Root'].replace('\\', '/')
if not client_root.endswith('/'):
client_root += '/'
# 遍历本地文件系统
for root_dir, _, files in os.walk(local_path):
for file in files:
full_path = os.path.join(root_dir, file)
# 将本地路径转换为depot路径
# 方法:移除工作区根路径,添加depot根路径
rel_path = os.path.relpath(full_path, p4.fetch_client()['Root'])
depot_path = f"//{p4.client}/{rel_path.replace(os.sep, '/')}"
f.write(f"{depot_path}\n")
except (P4Exception, OSError) as e:
f.write(f"\n[Error] Skipping {depot_dir}: {str(e)}\n")
continue
print(f"成功生成仓库结构报告: {os.path.abspath(output_file)}")
except P4Exception as e:
print("Perforce error:", e)
finally:
p4.disconnect()
if __name__ == "__main__":
main()