filecmp.dircmp 可秒级比对目录结构差异,仅检查路径、大小和修改时间;需设 shallow=False 避免漏判,diff_files 仅表示可能内容不同,left_only/right_only 才是真实增删文件。

用 filecmp.dircmp 快速比对目录结构与文件名差异
直接上手前先明确一点:两份庞大代码库的“内容差异”通常分三层——文件是否多/少、文件是否被改名/移动、文件内容是否真正不同。第一层最轻量,filecmp.dircmp 是标准库中唯一能无依赖完成此任务的工具。
它不读取文件内容,只检查路径存在性、大小和修改时间(可选),因此秒级完成百万级文件的结构扫描。但注意:dircmp 默认跳过软链接,且对同名但硬链接指向不同内容的文件无法识别——这类情况在 CI 构建产物中偶有出现。
- 使用时务必传入
shallow=False,否则默认只比对 mtime 和 size,会漏掉内容相同但 mtime 被重写的文件(如 git checkout 后) - 结果中的
diff_files列表只包含「同名但内容可能不同」的文件,仍需后续逐个校验;left_only/right_only才是真正的增删文件 - 避免直接递归调用
dircmp的subdirs属性做深度遍历——容易触发 RecursionError,改用队列迭代更稳
用 hashlib.blake2b 替代 md5 做高效内容指纹
当 dircmp 标出 diff_files 后,下一步必须读取文件内容生成哈希值。别用 md5 或 sha1:它们在超大文件场景下既慢又易碰撞;Python 3.6+ 自带的 blake2b 是更优解——速度比 md5 快 20%~30%,且抗碰撞性远超 MD5,还支持 streaming 分块计算。
关键点在于内存控制:单个 500MB 的源码文件若一次性 read(),极易触发 OOM。必须分块读取:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
def file_hash(path):
h = hashlib.blake2b()
with open(path, "rb") as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
- 块大小设为
8192(8KB)是实测平衡点:太小增加系统调用开销,太大占用过多内存 - 不要用
pathlib.Path.read_bytes(),它内部会尝试一次性加载整个文件 - 若代码库含大量小文件(如 Python 的
.pyc或 TS 的.d.ts),可考虑用os.stat().st_size先过滤掉明显等长的文件,跳过哈希计算
处理符号链接、.gitignored 文件和二进制噪声
真实代码库不是理想模型。常见干扰项包括:被 .gitignore 排除但物理存在的构建产物、指向同一目标的多个符号链接、编译生成的 .so/.dll 等二进制文件。它们会让哈希比对失去意义,甚至拖垮性能。
- 用
pathlib.Path.resolve()对所有路径做标准化,再用set去重,能合并重复的符号链接目标 - 提前读取
.gitignore并用pathspec库解析,比对前过滤掉匹配路径——别自己写 glob 匹配,.gitignore语法比想象中复杂(如**/build/、!src/build/main.py) - 对已知二进制扩展名(
.so,.dylib,.exe)直接跳过哈希,改用os.stat().st_mtime+st_size粗略判断——内容差异对这类文件通常不具业务意义
增量缓存哈希值避免重复计算
反复比对同一代码库(如每日 CI 检查)时,90% 以上文件往往未变。每次都重新算哈希是巨大浪费。可行方案是把路径 → 哈希 → mtime 的三元组存到本地 SQLite 数据库或 msgpack 文件中。
核心逻辑很简单:读取缓存中该文件的旧哈希和旧 mtime;若 mtime 未变,直接复用旧哈希;若 mtime 变了,再计算新哈希并更新缓存。
- SQLite 比 JSON 文件更适合——支持原子写入、索引加速查询,且 Python 内置
sqlite3模块无需额外依赖 - 缓存键必须包含文件完整路径(而非相对路径),否则跨工作目录运行时会错乱
- 别忽略缓存清理:长期运行后,被删除的文件条目应定期用
PRAGMA integrity_check+ 失效路径扫描清理
最麻烦的永远不是哈希本身,而是如何定义“差异”。比如一行空格变化是否算差异?Git diff 默认忽略空白,但代码规范检查可能要求严格一致。这些策略得在哈希之后、报告生成之前介入,而不是塞进比对流程里。

















