Chrome 迁移不是复制目录:一次 Mac mini 到 MacBook 的扩展与登录态恢复复盘

这次从 Mac mini 迁到 MacBook Pro,Chrome 给了一个很有意思的教训:Chrome 用户目录不是一个可以随便整包覆盖的普通数据目录。尤其是扩展、油猴脚本、Cookie、登录态这些东西,它们分别落在不同层里,其中一部分还被 Chrome 自己做了完整性保护。

这篇记录一下这次排障过程、踩坑点,以及以后再迁移 Chrome 时应该怎么做。

现象

新机器上 Chrome 可以打开,书签和 Profile 看起来像回来了,但扩展状态不对:

  • Chrome 插件列表一开始为空,或者只剩几个内置项;
  • Tampermonkey BETA 回来了,但界面里一度看不到旧脚本;
  • Google 账号重新登录后,商店扩展开始陆续同步回来;
  • 本地解压安装的自制插件没有自动回来;
  • Chrome 还弹过“Chrome 重置了这些设置”,里面明确提到“扩展程序”。

最迷惑的是:文件层面其实已经能看到旧数据。比如 Tampermonkey 的本地 LevelDB 目录还在,里面能搜到 ==UserScript==@namelinux.dov2ex 这类旧脚本片段。但 Chrome UI 一开始就是不认。

第一个错误假设:整目录覆盖就等于完整恢复

最直接的想法是把旧机器的:

~/Library/Application Support/Google/Chrome

完整复制到新机器同一路径。

这一步确实会把大量数据复制过来,包括:

  • Default/Extensions
  • Default/Local Extension Settings
  • Default/Sync Extension Settings
  • Default/IndexedDB
  • Default/Storage
  • Default/Cookies
  • Default/Login Data
  • Default/History
  • Default/Sessions

但问题出在 Chrome 启动后的自检。Chrome 并不只是“读取这些 JSON 和 LevelDB”,它还会校验一部分受保护偏好。

关键文件是:

Default/Preferences
Default/Secure Preferences
Local State

其中 Secure Preferences 里有 protection.macsprotection.super_mac 这样的保护结构。扩展注册信息也在这套保护范围里。

直接把旧机器的 Secure Preferences 覆盖到新机器后,Chrome 启动时认为扩展配置是在 Chrome 外部被改过的,于是触发安全重置。结果就是:扩展包和扩展数据可能还在磁盘上,但扩展注册层被清掉,只剩内置项。

这就是为什么“文件已经复制了,但插件列表是空的”。

第二个发现:油猴脚本不是立刻丢了

Tampermonkey 的脚本主要不在扩展包目录里,而在扩展自己的数据桶里。典型路径类似:

Default/Local Extension Settings/<extension-id>

这次 Tampermonkey BETA 的扩展 ID 没变,所以旧脚本数据目录可以继续被同一个扩展读取。

当 Google 账号完成验证、Chrome Sync 把 Tampermonkey BETA 重新注册回来后,旧脚本数据就正常显示了。

也就是说,油猴当时看起来像“纯新状态”,实际更像是:

  1. 旧脚本数据在磁盘上;
  2. 扩展注册层先被 Chrome 重置;
  3. Google Sync 重新装回同一个扩展 ID;
  4. 扩展重新读到原来的本地数据。

这点很关键:只要扩展 ID 不变,数据桶还在,就还有救。

第三个发现:本地解压插件不会跟着同步回来

Chrome 商店扩展能通过 Google Sync 回来,但本地解压安装的插件不行。

本地插件在 Secure Preferences 里通常是一个绝对路径注册,例如:

/Users/<user>/Documents/.../some-local-extension

Google Sync 不会帮你同步这个目录,也不会自动重新加载这个 unpacked extension。正确做法是:

  1. 把本地插件源码目录复制到新机器;
  2. 打开 chrome://extensions;
  3. 开启“开发者模式”;
  4. 点“加载已解压的扩展程序”;
  5. 选择该插件源码目录。

如果旧路径不存在,Chrome 里即使残留过注册记录也没用。

Cookie 和登录态为什么也不是普通文件

Cookie 和密码数据库看起来只是这些文件:

Default/Cookies
Default/Login Data

但它们的敏感字段是加密的。macOS 上 Chrome 会依赖钥匙串里的 Chrome Safe Storage 项。

这次检查后发现,新旧机器上的 Chrome Safe Storage 实际是一致的,所以复制旧机器的 CookiesLogin 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 迁移,我会按这个顺序来:

  1. 退出两边 Chrome,确认没有 Chrome 进程在写 Profile。
  2. 在新机器先登录 Google 并开启同步,让商店扩展和 Profile 身份先稳定。
  3. 检查旧机器和新机器的 Chrome Safe Storage 是否一致;不一致时只处理 Chrome 专用钥匙串项,不替换整套钥匙串。
  4. 从旧机器复制 Chrome 数据到 staging 目录。
  5. rsync 或等价工具覆盖数据层,但明确排除:
    • Local State
    • Default/Preferences
    • Default/Secure Preferences
  6. 校验扩展 ID、扩展数据桶大小、Cookie/Login Data 时间戳。
  7. 启动 Chrome,看是否出现“Chrome 重置了这些设置”。
  8. 手动加载本地解压插件。
  9. 最后检查 Tampermonkey、Stylus、密码管理器、Cookie 编辑器这类重数据插件。

经验教训

Chrome Profile 至少可以分成三层看:

  • 注册层PreferencesSecure PreferencesLocal State
  • 数据层:Cookie、History、Login Data、各类 IndexedDB/LevelDB
  • 外部依赖层:macOS Keychain、本地解压插件源码目录、Google Sync

迁移失败通常不是因为某一个文件没复制,而是三层状态不一致。

最重要的结论是:

不要把 Chrome 迁移理解成“复制一个文件夹”。应该理解成“让新机器生成合法注册层,再把旧机器数据层接上去”。

这次的有趣点也在这里:第一次整目录覆盖看起来最完整,反而触发了 Chrome 的自我保护;后面保留新机器注册层、只换数据层,才真正稳定。