p4 大小写检查
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 osdef 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 Nonedrive, 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.sepfor part in parts:try:entries = os.listdir(current)except PermissionError:return Nonefor entry in entries:if entry.lower() == part.lower():actual_parts.append(entry)current = os.path.join(current, entry)breakelse:return Nonereturn actual_partsdef main():p4 = P4()try:p4.connect()depot_root = '//depot'# Recursively get all directoriesall_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 subdirsprint("Checking case consistency between depot and local filesystem:")for depot_dir in all_dirs:# Map depot dir to local pathtry: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}")continueexcept P4Exception:print(f"Error mapping: {depot_dir}")continueactual_parts = get_actual_case_on_disk(local_path)if actual_parts is None:print(f"Local path missing: {local_path} (for depot {depot_dir})")continueexpected_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 osdef 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:passdef 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:continuelocal_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")continueprint(f"成功生成仓库结构报告: {os.path.abspath(output_file)}")except P4Exception as e:print("Perforce error:", e)finally:p4.disconnect()if __name__ == "__main__":main()