这次从 Mac mini 迁到 MacBook Pro,Chrome 给了一个很有意思的教训:Chrome 用户目录不是一个可以随便整包覆盖的普通数据目录。尤其是扩展、油猴脚本、Cookie、登录态这些东西,它们分别落在不同层里,其中一部分还被 Chrome 自己做了完整性保护。
这篇记录一下这次排障过程、踩坑点,以及以后再迁移 Chrome 时应该怎么做。
现象
新机器上 Chrome 可以打开,书签和 Profile 看起来像回来了,但扩展状态不对:
- Chrome 插件列表一开始为空,或者只剩几个内置项;
- Tampermonkey BETA 回来了,但界面里一度看不到旧脚本;
- Google 账号重新登录后,商店扩展开始陆续同步回来;
- 本地解压安装的自制插件没有自动回来;
- Chrome 还弹过“Chrome 重置了这些设置”,里面明确提到“扩展程序”。
最迷惑的是:文件层面其实已经能看到旧数据。比如 Tampermonkey 的本地 LevelDB 目录还在,里面能搜到 ==UserScript==、@name、linux.do、v2ex 这类旧脚本片段。但 Chrome UI 一开始就是不认。
第一个错误假设:整目录覆盖就等于完整恢复
最直接的想法是把旧机器的:
~/Library/Application Support/Google/Chrome
完整复制到新机器同一路径。
这一步确实会把大量数据复制过来,包括:
Default/ExtensionsDefault/Local Extension SettingsDefault/Sync Extension SettingsDefault/IndexedDBDefault/StorageDefault/CookiesDefault/Login DataDefault/HistoryDefault/Sessions
但问题出在 Chrome 启动后的自检。Chrome 并不只是“读取这些 JSON 和 LevelDB”,它还会校验一部分受保护偏好。
关键文件是:
Default/Preferences
Default/Secure Preferences
Local State
其中 Secure Preferences 里有 protection.macs 和 protection.super_mac 这样的保护结构。扩展注册信息也在这套保护范围里。
直接把旧机器的 Secure Preferences 覆盖到新机器后,Chrome 启动时认为扩展配置是在 Chrome 外部被改过的,于是触发安全重置。结果就是:扩展包和扩展数据可能还在磁盘上,但扩展注册层被清掉,只剩内置项。
这就是为什么“文件已经复制了,但插件列表是空的”。
第二个发现:油猴脚本不是立刻丢了
Tampermonkey 的脚本主要不在扩展包目录里,而在扩展自己的数据桶里。典型路径类似:
Default/Local Extension Settings/<extension-id>
这次 Tampermonkey BETA 的扩展 ID 没变,所以旧脚本数据目录可以继续被同一个扩展读取。
当 Google 账号完成验证、Chrome Sync 把 Tampermonkey BETA 重新注册回来后,旧脚本数据就正常显示了。
也就是说,油猴当时看起来像“纯新状态”,实际更像是:
- 旧脚本数据在磁盘上;
- 扩展注册层先被 Chrome 重置;
- Google Sync 重新装回同一个扩展 ID;
- 扩展重新读到原来的本地数据。
这点很关键:只要扩展 ID 不变,数据桶还在,就还有救。
第三个发现:本地解压插件不会跟着同步回来
Chrome 商店扩展能通过 Google Sync 回来,但本地解压安装的插件不行。
本地插件在 Secure Preferences 里通常是一个绝对路径注册,例如:
/Users/<user>/Documents/.../some-local-extension
Google Sync 不会帮你同步这个目录,也不会自动重新加载这个 unpacked extension。正确做法是:
- 把本地插件源码目录复制到新机器;
- 打开
chrome://extensions; - 开启“开发者模式”;
- 点“加载已解压的扩展程序”;
- 选择该插件源码目录。
如果旧路径不存在,Chrome 里即使残留过注册记录也没用。
Cookie 和登录态为什么也不是普通文件
Cookie 和密码数据库看起来只是这些文件:
Default/Cookies
Default/Login Data
但它们的敏感字段是加密的。macOS 上 Chrome 会依赖钥匙串里的 Chrome Safe Storage 项。
这次检查后发现,新旧机器上的 Chrome Safe Storage 实际是一致的,所以复制旧机器的 Cookies 和 Login Data 后可以继续使用。否则,即使数据库文件复制成功,Chrome 也可能无法解密里面的值。
所以完整恢复登录态时要同时考虑两层:
- 数据库文件有没有复制;
- Chrome 用来解密这些数据的钥匙串项是否一致。
不要直接替换整套系统钥匙串。更稳的办法是只检查或迁移 Chrome 需要的那一个钥匙串项。
最终有效方案
这次最后稳定下来的流程不是“整目录覆盖”,而是分两阶段:
1. 先让 Chrome 自己恢复注册层
在新机器上登录 Google 账号并开启同步,完成身份验证,让 Chrome Sync 重新建立扩展注册。
这一步会让 Secure Preferences 重新变成新机器认可的状态。之后不要再用旧机器的 Secure Preferences 覆盖它。
2. 再覆盖数据层
退出 Chrome 后,从旧机器复制数据层,但排除这些文件:
Local State
Default/Preferences
Default/Secure Preferences
保留新机器已通过 Chrome 自己生成的注册层,然后覆盖:
Default/Cookies
Default/Login Data
Default/History
Default/Sessions
Default/Extensions
Default/Local Extension Settings
Default/Sync Extension Settings
Default/Managed Extension Settings
Default/Extension State
Default/Extension Rules
Default/Extension Scripts
Default/DNR Extension Rules
Default/IndexedDB
Default/Storage
Default/Local Storage
Default/Session Storage
这样扩展注册不会再次被 Chrome 判定为外部篡改,数据也能回到旧机器状态。
以后迁移 Chrome 的计划
以后如果再做 Chrome 迁移,我会按这个顺序来:
- 退出两边 Chrome,确认没有 Chrome 进程在写 Profile。
- 在新机器先登录 Google 并开启同步,让商店扩展和 Profile 身份先稳定。
- 检查旧机器和新机器的
Chrome Safe Storage是否一致;不一致时只处理 Chrome 专用钥匙串项,不替换整套钥匙串。 - 从旧机器复制 Chrome 数据到 staging 目录。
- 用
rsync或等价工具覆盖数据层,但明确排除:Local StateDefault/PreferencesDefault/Secure Preferences
- 校验扩展 ID、扩展数据桶大小、Cookie/Login Data 时间戳。
- 启动 Chrome,看是否出现“Chrome 重置了这些设置”。
- 手动加载本地解压插件。
- 最后检查 Tampermonkey、Stylus、密码管理器、Cookie 编辑器这类重数据插件。
经验教训
Chrome Profile 至少可以分成三层看:
- 注册层:
Preferences、Secure Preferences、Local State - 数据层:Cookie、History、Login Data、各类 IndexedDB/LevelDB
- 外部依赖层:macOS Keychain、本地解压插件源码目录、Google Sync
迁移失败通常不是因为某一个文件没复制,而是三层状态不一致。
最重要的结论是:
不要把 Chrome 迁移理解成“复制一个文件夹”。应该理解成“让新机器生成合法注册层,再把旧机器数据层接上去”。
这次的有趣点也在这里:第一次整目录覆盖看起来最完整,反而触发了 Chrome 的自我保护;后面保留新机器注册层、只换数据层,才真正稳定。