I am LAZY bones?
AN ancient AND boring SITE

代码库放 iCloud 文件夹会怎样?

之前我有个习惯,会把代码(整个repo)放到 iCloud 管理的 ~/Documents 下。一直觉得挺方便的,因为即使没有 commit+push,我到另一台电脑上也能接着干活,登了同一个iCloud账号的目录会自动同步。

但最近遇到一个特别邪门的问题。一个用 uv 管理的 Python 项目,editable 方式安装,前一个小时还好好的,突然就:

邪门在哪呢?同一个 venv、同一个解释器,pytest 跑起来一千六百多个用例全绿。测试说包在,入口脚本说包没了。去 site-packages 里看,editable 安装落下的 .pth 文件好好躺在那,内容就是一行指向源码目录的路径,权限 644,路径也真实存在。文件在、内容对、权限对,但它就是不生效。

排查:谁把我的 .pth 吞了

先补一句背景:editable 安装的机制,是往 site-packages 里放一个 .pth 文件,Python 启动时 site 模块会读它,把里面的路径追加进 sys.path。这个文件失效,包就从解释器眼里消失。

做了三个对照实验,结果非常有意思:

1. 自己写一个探针 .pth(内容随便指个存在的目录)放进同一个目录——生效;
2. 把出问题的 .pth 原样 cp 成另一个文件名——生效;
3. 出问题的那个文件本身——死活不生效。

同目录、同内容、同权限,副本能用原件不能用,那唯一的区别只剩下文件的元数据了。ls 加个 -O 把 BSD 文件标志打出来:

flags 一栏赫然写着 hidden——macOS 的 UF_HIDDEN 文件标志。cp 不会复制 BSD flags,所以副本是干净的,这就解释了实验 2。而 CPython 从 3.11 起,site.py 处理 .pth 时加了一条安全加固(防止恶意软件用隐藏 .pth 做隐蔽注入):

带隐藏标志的 .pth,静默跳过,一个字的错都不报。两个各自都算合理的行为叠在一起,效果就是:文件在,import 没了。

那谁给文件打的 hidden?chflags nohidden 清掉,几秒钟之后再看,又变回 hidden 了——有个进程在实时跟我对抗。答案到这基本明了:这个仓库在 ~/Documents 底下,而这台 Mac 开着 iCloud 的“桌面与文稿”同步。把仓库挪出 Documents、重建 venv,flags 干干净净,问题再没复发。

iCloud Drive 到底在做什么

开了”桌面与文稿”同步之后,~/Documents 就不再是普通目录,而是由 fileproviderd 守护进程托管的同步空间。它主要干三类事:

1. 监听并上传每一次文件变更——它假设文件是”偶尔被人编辑的文档”;
2. 冲突消解——当它认为同一个文件出现两个竞争版本,不丢弃任何一边,而是生成”xxx 2″”xxx 3″这样的冲突副本;
3. 元数据管理——给托管文件打 xattr 和文件标志(上面那个 hidden 就是它干的),开了”优化 Mac 存储”还会把冷文件驱逐成无数据的占位符,读取时才按需下载。

对文档来说这些设计都挺好。但代码库不是文档。

代码库为什么全中

第一,写入模式冲突。开发工具链的写入是高频、批量、依赖原子性的:包管理器一次重写上万个小文件,Python 用”临时文件 + rename”做原子写,git 靠锁文件和 rename 更新引用。同步进程和这些写入异步竞争,竞争输了就落冲突副本。我这次在 site-packages 里就看到了 “_editable_impl_mycli 2.pth”、”3.pth”、”4.pth” 一窝副本,主 .pth 的内容更是被拼接成了四份路径首尾相连的乱码:

第二,元数据篡改。就是前面 UF_HIDDEN 那一段,而且它是主动维护的,你清掉它还会打回来。

第三,驱逐。.git/objects 里的 packfile、venv 里的二进制,都可能被”优化存储”驱逐成占位符——在线时表现为构建随机卡顿,离线时表现为仓库”损坏”。

第四,git 自己的风险。.git 里的 index、refs 一旦出现冲突副本,仓库状态可能真损坏。git 本身就是分布式同步工具,外面再套一层文件级同步,等于双重同步,语义必然打架。

这次踩到的坑

1. chflags nohidden 修不好——几秒内被 fileproviderd 顶回来,别在这条路上浪费时间;
2. 症状自相矛盾极具误导性:pytest 全绿(它靠 rootdir 机制自己把源码目录塞进了路径),入口脚本却挂了,两个工具对”包是否存在”给出相反答案,直觉上会先怀疑一万个别的东西,最后才怀疑文件系统在说谎;
3. 损坏是异步、随机发生的:同一个 venv 一小时内坏了两次,中间检查全都正常——因为坏不坏取决于同步进程什么时候追上来;
4. 诊断口诀:文件明明在但 import 不到,先 ls -lO 看 flags 列。

结论

iCloud(以及 Dropbox、OneDrive 这些文件级同步盘,机制细节不同但三板斧一样)适合放终态文档:文稿、图片、表格。任何带衍生状态的目录——代码库、venv、node_modules、.git、构建缓存——都不该放进去。代码的跨机同步交给 git,这本来就是它的职责,而且语义正确。仓库放个非托管路径(比如 ~/dev)就好;实在有目录必须留在 iCloud 里又想排除,macOS 没有官方排除项,只有给目录名加 .nosync 后缀这个 hack。

一个按文档假设设计的同步器,遇上一堆违反它全部假设的文件,双方都没有 bug,组合在一起就是灾难。就此,完毕。

最后修改时间: 2026年07月17日 23:15

本文章发表于: 2026年07月17日 23:15 | 所属分类:经验技巧. | 您可以在此订阅本文章的所有评论. | 您也可以发表评论, 或从您的网站trackback.

一个评论 关于: “代码库放 iCloud 文件夹会怎样?”

  1. 依云 在 2026年07月18日 01:29 说:回复

    哈哈,Dropbox流行那会儿,我就把自己的Python脚本放进去了,结果冲突惨了~

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注