macos-migration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

macOS 系统迁移助手

macOS System Migration Assistant

两阶段设计:旧系统采集 → 新系统还原。中间通过一个 JSON 清单文件衔接。
A two-phase design: old-system collection → new-system restoration. The two phases are connected through a JSON manifest file.

前置要求(务必先做)

Prerequisites (Do This First)

在旧系统上先做一次 Time Machine 全量备份。 这是整个迁移的安全网——新系统上 任何一步出问题,都能从这份全量备份里捞回来。还原阶段也直接从 Time Machine 挂载点 读取数据。
没有 Time Machine 全量备份,就不要开始迁移。
Make a full Time Machine backup on the old system first. This is the migration's safety net—any problem on the new system can be recovered from this full backup. The restoration phase also reads data directly from the Time Machine mount point.
Do not begin the migration without a full Time Machine backup.

运行模式检测

Runtime Mode Detection

判断「采集还是还原」只看一个信号:清单文件在不在。
bash
ls ~/migration-manifest.json 2>/dev/null
条件模式
无清单文件采集模式(在旧系统上,生成清单)
有清单文件还原模式(在新系统上,读清单还原)
Time Machine 只是还原阶段的数据来源,不参与模式判断。 用户也可以直接指定:「采集」「备份」「还原」「迁移」。

Whether to collect or restore depends on only one signal: whether the manifest file exists.
bash
ls ~/migration-manifest.json 2>/dev/null
ConditionMode
No manifest fileCollection mode (on the old system, generate a manifest)
Manifest file existsRestoration mode (on the new system, read the manifest and restore)
Time Machine is only the data source for the restoration phase and does not participate in mode detection. The user can also explicitly specify: “Collect,” “Back Up,” “Restore,” or “Migrate.”

第零步:Homebrew 检查(两种模式都必须)

Step Zero: Homebrew Check (Required in Both Modes)

bash
which brew && brew --version || echo "NOT_INSTALLED"
  • 已安装 → 继续
  • 未安装 → 引导用户在终端手动执行安装命令。这一步需要输入管理员密码(sudo), 无法静默完成:
    bash
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    装完后按提示把 brew 加入 PATH(Apple Silicon:
    eval "$(/opt/homebrew/bin/brew shellenv)"
    ), 再继续。

bash
which brew && brew --version || echo "NOT_INSTALLED"
  • Installed → Continue
  • Not installed → Guide the user to manually run the installation command in a terminal. This step requires an administrator password (sudo) and cannot be completed silently:
    bash
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    After installation, follow the instructions to add brew to PATH (Apple Silicon:
    eval "$(/opt/homebrew/bin/brew shellenv)"
    ), then continue.

模式 A:旧系统采集

Mode A: Collection on the Old System

在旧系统上运行,扫描所有软件、配置、开发环境,生成
~/migration-manifest.json
。
Run this on the old system to scan all software, configuration, and development environments and generate
~/migration-manifest.json
.

A1. Homebrew 软件包

A1. Homebrew Packages

bash
brew list --formula
brew list --cask
brew tap
bash
brew list --formula
brew list --cask
brew tap

A2. 系统 Applications

A2. System Applications

bash
ls /Applications/
ls ~/Applications/ 2>/dev/null
与 Homebrew cask 列表对比,标记哪些是 Homebrew 装的、哪些是手动/App Store 装的。
bash
ls /Applications/
ls ~/Applications/ 2>/dev/null
Compare the results with the Homebrew cask list and mark which applications were installed through Homebrew and which were installed manually or from the App Store.

A3. Mac App Store 应用

A3. Mac App Store Applications

bash
mas list 2>/dev/null || echo "mas not installed"
如果
mas
未安装,提示用户是否要安装(
brew install mas
)。
bash
mas list 2>/dev/null || echo "mas not installed"
If
mas
is not installed, ask the user whether to install it (
brew install mas
).

A4. npm 全局包

A4. npm Global Packages

bash
npm ls -g --depth=0 2>/dev/null
bash
npm ls -g --depth=0 2>/dev/null

A5. uv 工具

A5. uv Tools

bash
uv tool list 2>/dev/null
bash
uv tool list 2>/dev/null

A6. pip/pipx 包

A6. pip/pipx Packages

bash
pip list 2>/dev/null
pipx list 2>/dev/null
bash
pip list 2>/dev/null
pipx list 2>/dev/null

A7. Go 全局工具

A7. Go Global Tools

bash
ls ~/go/bin/ 2>/dev/null
bash
ls ~/go/bin/ 2>/dev/null

A8. 配置文件扫描

A8. Configuration File Scan

扫描 home 目录下的 dotfiles 和配置目录:
bash
ls -la ~/
重点采集:
  • Shell:
    .zshrc
    ,
    .zshenv
    ,
    .zprofile
    ,
    .bashrc
    ,
    .bash_profile
  • Git:
    .gitconfig
    ,
    .gitignore_global
  • SSH:
    .ssh/
  • 编辑器:
    .claude/
    ,
    .codex/
    ,
    .config/
    ,
    .cursor/
    ,
    .zed/
  • 容器:
    .docker/
    ,
    .kube/
  • 包管理器:
    .npm/
    ,
    .nvm/
    ,
    .bun/
    ,
    .cargo/
    ,
    .rustup/
  • 其他应用配置:
    .SwitchHosts/
    ,
    .ollama/
    ,
    .syncthing/
    等
敏感数据先问后采:
.ssh/
(私钥)、dotfiles 里可能含 token、
.aws/
、
.kube/
、 钥匙串等都是密钥级信息。采集这些之前先问用户是否纳入:
  • 用户同意 → 正常采集,并明确提醒:「清单和备份里含私钥/凭据,传输、存放、 通过网盘或聊天工具发送时注意不要外泄。」
  • 用户拒绝 → 跳过这些项,在清单里记入
    "skipped_sensitive": [...]
    ,让用户日后 自行手动迁移(差异报告里也会再列一次)。
Scan dotfiles and configuration directories under the home directory:
bash
ls -la ~/
Focus on collecting:
  • Shell:
    .zshrc
    ,
    .zshenv
    ,
    .zprofile
    ,
    .bashrc
    ,
    .bash_profile
  • Git:
    .gitconfig
    ,
    .gitignore_global
  • SSH:
    .ssh/
  • Editors:
    .claude/
    ,
    .codex/
    ,
    .config/
    ,
    .cursor/
    ,
    .zed/
  • Containers:
    .docker/
    ,
    .kube/
  • Package managers:
    .npm/
    ,
    .nvm/
    ,
    .bun/
    ,
    .cargo/
    ,
    .rustup/
  • Other application configurations:
    .SwitchHosts/
    ,
    .ollama/
    ,
    .syncthing/
    , etc.
Ask before collecting sensitive data:
.ssh/
(private keys), tokens that may exist in dotfiles,
.aws/
,
.kube/
, keychains, and similar key-level information are highly sensitive. Before collecting these items, ask the user whether they should be included:
  • User agrees → Collect them normally and explicitly warn the user: “The manifest and backup contain private keys and credentials. When transferring, storing, or sending them through cloud storage or chat tools, take care not to disclose them.”
  • User declines → Skip these items and record them in the manifest as
    "skipped_sensitive": [...]
    so the user can migrate them manually later. They will also be listed again in the discrepancy report.

A9. LaunchAgents(自启动服务)

A9. LaunchAgents (Startup Services)

bash
ls ~/Library/LaunchAgents/
bash
ls ~/Library/LaunchAgents/

A10. 用户项目目录

A10. User Project Directories

列出 home 目录下非 dotfile 的顶层目录:
bash
ls -d ~/*/ 2>/dev/null
排除系统目录(Library, Applications, Public 等),记录用户自建的项目/数据目录。
List non-dotfile top-level directories under the home directory:
bash
ls -d ~/*/ 2>/dev/null
Exclude system directories such as Library, Applications, and Public, and record user-created project and data directories.

A11. Shell 环境

A11. Shell Environment

bash
undefined
bash
undefined

当前 shell 版本

当前 shell 版本

zsh --version
zsh --version

Homebrew 路径 / 版本管理器(nvm、fnm 等)配置

Homebrew 路径 / 版本管理器(nvm、fnm 等)配置

grep -iE 'brew|nvm|fnm' ~/.zshrc
undefined
grep -iE 'brew|nvm|fnm' ~/.zshrc
undefined

A12. 其他易漏项

A12. Other Easily Missed Items

迁移时最容易漏、丢了又很烦的几类:
bash
undefined
These categories are most commonly missed during migration and can be particularly troublesome to recover:
bash
undefined

编辑器扩展

编辑器扩展

code --list-extensions 2>/dev/null cursor --list-extensions 2>/dev/null
code --list-extensions 2>/dev/null cursor --list-extensions 2>/dev/null

自购/自装字体

自购/自装字体

ls ~/Library/Fonts/ 2>/dev/null
ls ~/Library/Fonts/ 2>/dev/null

定时任务

定时任务

crontab -l 2>/dev/null
crontab -l 2>/dev/null

手动装到非 Homebrew 路径的命令行工具

手动装到非 Homebrew 路径的命令行工具

ls /usr/local/bin/ 2>/dev/null ls /opt/ 2>/dev/null

> 系统偏好(`defaults`)量大且与机型强相关,不自动采集;如用户在意,在还原差异
> 报告里提醒手动核对(键盘、触控板、Dock、快捷键等)。
ls /usr/local/bin/ 2>/dev/null ls /opt/ 2>/dev/null

> System preferences (`defaults`) are extensive and highly dependent on the machine model, so they are not collected automatically. If the user cares about them, remind the user in the restoration discrepancy report to verify them manually, including the keyboard, trackpad, Dock, shortcuts, and other settings.

A13. 生成清单文件

A13. Generate the Manifest File

将以上所有信息写入
~/migration-manifest.json
:
json
{
  "version": 1,
  "captured_at": "2026-06-28T16:00:00+08:00",
  "source_macos": "15.5",
  "source_hostname": "old-macbook",

  "homebrew": {
    "taps": ["user/repo", "..."],
    "formulas": ["git", "node@22", "..."],
    "casks": ["1password", "docker-desktop", "..."]
  },

  "applications": {
    "homebrew_casks": ["claude", "firefox", "..."],
    "app_store": [{"id": 123456, "name": "App Name"}],
    "manual": ["Final Cut Pro.app", "..."]
  },

  "package_managers": {
    "npm_global": ["@anthropic-ai/claude-code", "..."],
    "uv_tools": ["nano-pdf", "..."],
    "pip_packages": ["package1", "..."],
    "go_binaries": ["tool1", "..."]
  },

  "configs": {
    "dotfiles": [".zshrc", ".gitconfig", ".ssh", "..."],
    "config_dirs": [".claude", ".codex", ".config", ".docker", "..."],
    "app_support_keys": ["Syncthing", "obs-studio", "..."]
  },

  "editor_extensions": {
    "vscode": ["ms-python.python", "..."],
    "cursor": ["..."]
  },

  "fonts": ["MyFont.otf", "..."],
  "crontab": "<crontab -l 原文,无则空>",
  "manual_bins": ["/usr/local/bin/foo", "/opt/bar", "..."],

  "launch_agents": ["com.github.syncthing.plist", "..."],

  "user_dirs": ["Projects", "Sites", "..."],

  "shell": {
    "shell": "zsh",
    "brew_in_path": true,
    "nvm_installed": true,
    "custom_env_vars": {}
  },

  "skipped_sensitive": []
}
Write all the information above into
~/migration-manifest.json
:
json
{
  "version": 1,
  "captured_at": "2026-06-28T16:00:00+08:00",
  "source_macos": "15.5",
  "source_hostname": "old-macbook",

  "homebrew": {
    "taps": ["user/repo", "..."],
    "formulas": ["git", "node@22", "..."],
    "casks": ["1password", "docker-desktop", "..."]
  },

  "applications": {
    "homebrew_casks": ["claude", "firefox", "..."],
    "app_store": [{"id": 123456, "name": "App Name"}],
    "manual": ["Final Cut Pro.app", "..."]
  },

  "package_managers": {
    "npm_global": ["@anthropic-ai/claude-code", "..."],
    "uv_tools": ["nano-pdf", "..."],
    "pip_packages": ["package1", "..."],
    "go_binaries": ["tool1", "..."]
  },

  "configs": {
    "dotfiles": [".zshrc", ".gitconfig", ".ssh", "..."],
    "config_dirs": [".claude", ".codex", ".config", ".docker", "..."],
    "app_support_keys": ["Syncthing", "obs-studio", "..."]
  },

  "editor_extensions": {
    "vscode": ["ms-python.python", "..."],
    "cursor": ["..."]
  },

  "fonts": ["MyFont.otf", "..."],
  "crontab": "<crontab -l 原文,无则空>",
  "manual_bins": ["/usr/local/bin/foo", "/opt/bar", "..."],

  "launch_agents": ["com.github.syncthing.plist", "..."],

  "user_dirs": ["Projects", "Sites", "..."],

  "shell": {
    "shell": "zsh",
    "brew_in_path": true,
    "nvm_installed": true,
    "custom_env_vars": {}
  },

  "skipped_sensitive": []
}

A14. 采集完成

A14. Collection Complete

输出清单摘要,提示用户:
✅ 采集完成!清单已保存到 ~/migration-manifest.json

采集内容:
- Homebrew: X taps, Y formulas, Z casks
- 应用: X 个 Homebrew, Y 个 App Store, Z 个手动安装
- npm 全局包: X 个
- 配置文件: X 个 dotfiles, Y 个配置目录
- 编辑器扩展: X 个 / 字体: Y 个 / 定时任务: 有/无
- 自启动服务: X 个
- 用户目录: X 个
- 跳过的敏感项: X 个(用户选择不采集)

⚠️ 若采集了 .ssh / dotfiles 中的凭据,清单与备份含私钥,传输存放注意不要外泄。

下一步:确认已有 Time Machine 全量备份,将 migration-manifest.json 复制到新系统,
再次运行此 skill。

Output a manifest summary and prompt the user:
✅ 采集完成!清单已保存到 ~/migration-manifest.json

采集内容:
- Homebrew: X taps, Y formulas, Z casks
- 应用: X 个 Homebrew, Y 个 App Store, Z 个手动安装
- npm 全局包: X 个
- 配置文件: X 个 dotfiles, Y 个配置目录
- 编辑器扩展: X 个 / 字体: Y 个 / 定时任务: 有/无
- 自启动服务: X 个
- 用户目录: X 个
- 跳过的敏感项: X 个(用户选择不采集)

⚠️ 若采集了 .ssh / dotfiles 中的凭据,清单与备份含私钥,传输存放注意不要外泄。

下一步:确认已有 Time Machine 全量备份,将 migration-manifest.json 复制到新系统,
再次运行此 skill。

模式 B:新系统还原

Mode B: Restoration on the New System

在新系统上运行,读取
migration-manifest.json
并逐步还原。
Run this on the new system to read
migration-manifest.json
and restore the system step by step.

B0. 获取清单

B0. Obtain the Manifest

bash
ls ~/migration-manifest.json
如果不存在:
找不到 migration-manifest.json。

请将旧系统上采集的清单文件复制到新系统的 home 目录:
  scp old-mac:~/migration-manifest.json ~/migration-manifest.json

或者手动指定路径。
bash
ls ~/migration-manifest.json
If it does not exist:
找不到 migration-manifest.json。

请将旧系统上采集的清单文件复制到新系统的 home 目录:
  scp old-mac:~/migration-manifest.json ~/migration-manifest.json

或者手动指定路径。

B1. Homebrew 软件安装

B1. Homebrew Software Installation

前提:第零步已确认 Homebrew 装好(需用户手动输密码安装)。装好之后这一步才能跑。
读取清单中的
homebrew
字段。
bash
undefined
Prerequisite: Homebrew has been confirmed as installed in Step Zero. The user must manually enter the password to install it. This step can run only after Homebrew is ready.
Read the
homebrew
field from the manifest.
bash
undefined

添加 taps(逐个)

添加 taps(逐个)

brew tap <user>/<repo>
brew tap <user>/<repo>

信任第三方 taps

信任第三方 taps

brew trust <user>/<repo>
brew trust <user>/<repo>

安装 formulas(批量,失败不回滚整批)

安装 formulas(批量,失败不回滚整批)

brew install <formula1> <formula2> ...
brew install <formula1> <formula2> ...

安装 casks(逐个,避免回滚)

安装 casks(逐个,避免回滚)

for cask in <cask1> <cask2> ...; do brew install --cask "$cask" 2>&1 | tail -3 done

没装上的(需 sudo、网络失败等)先记下,最后在 B7 差异报告统一列给用户。
for cask in <cask1> <cask2> ...; do brew install --cask "$cask" 2>&1 | tail -3 done

Record anything that could not be installed, such as packages requiring sudo or those that failed due to network issues, and present it to the user in the discrepancy report in B7.

B2. 其他包管理器

B2. Other Package Managers

读取清单中的
package_managers
字段。
bash
undefined
Read the
package_managers
field from the manifest.
bash
undefined

npm

npm

npm install -g <pkg1> <pkg2> ...
npm install -g <pkg1> <pkg2> ...

uv

uv

uv tool install <pkg1> <pkg2> ...
uv tool install <pkg1> <pkg2> ...

pip(如果需要)

pip(如果需要)

pip install <pkg1> <pkg2> ...
pip install <pkg1> <pkg2> ...

go binaries

go binaries

go install <pkg1>@latest <pkg2>@latest ...
undefined
go install <pkg1>@latest <pkg2>@latest ...
undefined

B3. 配置文件恢复

B3. Configuration File Restoration

读取清单中的
configs
字段。
有两种数据来源,优先级:
  1. Time Machine 直接复制(如果有挂载)
  2. 用户手动提供(如果清单是从别的方式获取的)
bash
TM="<Time Machine 用户目录路径>"
LOCAL="/Users/<username>"
Read the
configs
field from the manifest.
There are two possible data sources, in priority order:
  1. Copy directly from Time Machine if it is mounted
  2. Provided manually by the user if the manifest was obtained through another method
bash
TM="<Time Machine 用户目录路径>"
LOCAL="/Users/<username>"

dotfiles

dotfiles

rsync -av --ignore-existing "$TM/.zshrc" "$LOCAL/" rsync -av --ignore-existing "$TM/.gitconfig" "$LOCAL/" rsync -av --ignore-existing "$TM/.ssh/" "$LOCAL/.ssh/"
rsync -av --ignore-existing "$TM/.zshrc" "$LOCAL/" rsync -av --ignore-existing "$TM/.gitconfig" "$LOCAL/" rsync -av --ignore-existing "$TM/.ssh/" "$LOCAL/.ssh/"

配置目录

配置目录

for dir in .claude .codex .config .docker .SwitchHosts ...; do rsync -av --ignore-existing "$TM/$dir/" "$LOCAL/$dir/" done

> 恢复 `.ssh/` 后确认权限:`chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_*`。
> 如无 Time Machine,提示用户提供配置文件的来源路径。
for dir in .claude .codex .config .docker .SwitchHosts ...; do rsync -av --ignore-existing "$TM/$dir/" "$LOCAL/$dir/" done

> After restoring `.ssh/`, verify the permissions: `chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_*`.
> If Time Machine is unavailable, ask the user to provide the source path for the configuration files.

B4. Library 关键子目录

B4. Key Library Subdirectories

从 Time Machine 恢复(如果有):
子目录命令说明
Preferences
cp -rn
应用偏好设置
Keychains
cp -rn
钥匙串
Containers
cp -rn
沙盒应用数据
Group Containers
cp -rn
共享容器
Mail
cp -rn
邮件数据
Safari
cp -rn
书签/历史
rsync 在 Time Machine APFS 快照上会报
io_read_flush
错误,Library 用
cp -rn
。
Restore these from Time Machine if available:
SubdirectoryCommandDescription
Preferences
cp -rn
Application preferences
Keychains
cp -rn
Keychains
Containers
cp -rn
Sandboxed application data
Group Containers
cp -rn
Shared containers
Mail
cp -rn
Mail data
Safari
cp -rn
Bookmarks/history
rsync
reports
io_read_flush
errors on Time Machine APFS snapshots. Use
cp -rn
for Library subdirectories.

B5. LaunchAgents(自启动服务)

B5. LaunchAgents (Startup Services)

读取清单中的
launch_agents
字段。
逐个检查并创建对应的 plist 文件,然后
launchctl load
。
Read the
launch_agents
field from the manifest.
Check and create the corresponding plist files one by one, and then run
launchctl load
.

B6. 其他易漏项还原

B6. Restoration of Other Easily Missed Items

对应采集阶段的 A12:
bash
undefined
These correspond to A12 in the collection phase:
bash
undefined

编辑器扩展(逐个装)

编辑器扩展(逐个装)

for ext in <清单 editor_extensions.vscode>; do code --install-extension "$ext"; done
for ext in <清单 editor_extensions.vscode>; do code --install-extension "$ext"; done

字体

字体

rsync -av --ignore-existing "$TM/Library/Fonts/" "$LOCAL/Library/Fonts/"
rsync -av --ignore-existing "$TM/Library/Fonts/" "$LOCAL/Library/Fonts/"

定时任务:先看新机器有没有现成的,再决定是否合并写入,避免覆盖

定时任务:先看新机器有没有现成的,再决定是否合并写入,避免覆盖

crontab -l 2>/dev/null

> `/usr/local/bin`、`/opt` 里的手动安装工具无法自动还原,列入 B7 差异报告提醒用户。
crontab -l 2>/dev/null

> Manually installed tools in `/usr/local/bin` and `/opt` cannot be restored automatically. List them in the discrepancy report in B7 and remind the user about them.

B7. 未安装项目处理

B7. Handling Items That Could Not Be Installed

对比清单和实际安装结果,输出差异:
undefined
Compare the manifest with the actual installation results and output the differences:
undefined

还原差异报告

还原差异报告

❌ 安装失败(需手动处理)

❌ 安装失败(需手动处理)

  • docker-desktop: 需要 sudo → 手动执行
    brew install --cask docker-desktop
  • tailscale-app: 需要 sudo → 手动执行
    brew install --cask tailscale-app
  • android-platform-tools: 网络问题 → 稍后重试
  • docker-desktop: 需要 sudo → 手动执行
    brew install --cask docker-desktop
  • tailscale-app: 需要 sudo → 手动执行
    brew install --cask tailscale-app
  • android-platform-tools: 网络问题 → 稍后重试

⚠️ 清单中有但无 Homebrew cask

⚠️ 清单中有但无 Homebrew cask

  • Final Cut Pro: App Store 应用
  • DaVinci Resolve Studio: 手动下载 → blackmagicdesign.com
  • Final Cut Pro: App Store 应用
  • DaVinci Resolve Studio: 手动下载 → blackmagicdesign.com

ℹ️ App Store 应用(需手动安装或用 mas)

ℹ️ App Store 应用(需手动安装或用 mas)

  • GarageBand
  • Infuse
  • GarageBand
  • Infuse

🔧 需手动核对(非软件)

🔧 需手动核对(非软件)

  • 系统偏好 defaults:键盘 / 触控板 / Dock / 快捷键
  • /usr/local/bin、/opt 手动工具:<manual_bins 列表>
  • 拒绝采集的敏感项:<skipped_sensitive 列表>

---
  • 系统偏好 defaults:键盘 / 触控板 / Dock / 快捷键
  • /usr/local/bin、/opt 手动工具:<manual_bins 列表>
  • 拒绝采集的敏感项:<skipped_sensitive 列表>

---

踩坑记录

Troubleshooting Notes

1.
brew bundle
一个失败全部回滚

1.
brew bundle
Rolls Back the Entire Batch When One Item Fails

问题:
brew bundle --file=Brewfile
中任何一个 cask 下载失败,整批已安装的包全部回滚。
解决:不用
brew bundle
。taps 逐个添加,formulas 批量安装,casks 逐个安装。
Problem: If any cask download fails in
brew bundle --file=Brewfile
, the entire batch of installed packages is rolled back.
Solution: Do not use
brew bundle
. Add taps one by one, install formulas in a batch, and install casks one at a time.

2. 第三方 taps 未信任

2. Third-Party Taps Are Not Trusted

问题:Homebrew 6+ 要求显式信任第三方 taps,否则报
Refusing to load cask from untrusted tap
。
解决:
brew trust <user>/<repo>
Problem: Homebrew 6+ requires third-party taps to be explicitly trusted; otherwise, it reports
Refusing to load cask from untrusted tap
.
Solution:
brew trust <user>/<repo>

3. rsync 在 Time Machine APFS 快照上报错

3.
rsync
Reports an Error on Time Machine APFS Snapshots

问题:
rsync -av --ignore-existing
复制 Library 子目录时报
io_read_flush
错误。
解决:Library 子目录改用
cp -rn
。
Problem:
rsync -av --ignore-existing
reports an
io_read_flush
error when copying Library subdirectories.
Solution: Use
cp -rn
for Library subdirectories.

4.
cp -rn
的 "Operation not permitted"

4.
cp -rn
Reports "Operation not permitted"

问题:macOS SIP/TCC 保护的文件无法直接复制(系统 Preferences、Mail 数据库等)。
影响:不影响使用。系统或应用在首次启动时自动重建。
Problem: Files protected by macOS SIP/TCC cannot be copied directly, including system Preferences and Mail databases.
Impact: This does not affect usage. The system or applications rebuild these files automatically on first launch.

5.
cp -rn
的 "Not a directory" 错误

5.
cp -rn
Reports "Not a directory"

问题:Containers 中系统容器使用符号链接指向标准目录。
影响:不影响使用。用户应用的容器数据已正确复制。
Problem: Some system containers in Containers use symbolic links that point to standard directories.
Impact: This does not affect usage. User application container data has been copied correctly.

6. 需要 sudo 的 casks

6. Casks That Require sudo

问题:
docker-desktop
、
tailscale-app
、
adobe-creative-cloud
安装时需要 sudo。
解决:记录在差异报告中,让用户手动执行。
Problem: Packages such as
docker-desktop
,
tailscale-app
, and
adobe-creative-cloud
require sudo during installation.
Solution: Record them in the discrepancy report and have the user run the commands manually.

7. node 版本冲突

7. node Version Conflict

问题:
brew install node@22
后
brew link
失败。
解决:
brew link --overwrite node@22
Problem:
brew link
fails after running
brew install node@22
.
Solution:
brew link --overwrite node@22

8. Homebrew 检测不到非 Homebrew 安装的软件

8. Homebrew Cannot Detect Software Not Installed Through Homebrew

问题:旧系统上通过 DMG/PKG/App Store 安装的软件,Homebrew 无法感知。
解决:采集阶段扫描
/Applications/
并与
brew list --cask
对比,标记出手动安装的应用,还原时单独处理。
Problem: Software installed on the old system through DMG, PKG, or the App Store is invisible to Homebrew.
Solution: During collection, scan
/Applications/
and compare it with
brew list --cask
, mark manually installed applications, and handle them separately during restoration.

原则

Principles

  1. 先有 Time Machine 全量备份再开始:这是出错时唯一的兜底,也是还原的数据来源
  2. Homebrew 由用户手动安装:需要输密码(sudo),引导用户在终端执行,装好再继续
  3. 两阶段分离:采集和还原是独立的步骤,通过 JSON 文件衔接
  4. 敏感数据先问后采:私钥/凭据采不采由用户决定;采了就提醒外泄风险
  5. 绝不覆盖已有文件:所有 rsync/cp 都带
    --ignore-existing
    或
    -n
  6. 先小后大:按目录大小排序复制,方便快速验证
  7. 先做计划再动手:还原前输出清单摘要,让用户确认
  8. 记录差异:还原后输出差异报告,列出需要手动处理的项目
  9. 逐个安装 casks:避免
    brew bundle
    的回滚问题
  10. 验证每一步:安装后
    brew list
    确认,复制后
    du -sh
    验证大小
  11. 跳过缓存:Library/Application Support 中的缓存数据不恢复
  1. Make a full Time Machine backup before starting: It is the only fallback in case of failure and the data source for restoration
  2. Have the user install Homebrew manually: It requires a password through sudo, so guide the user to run the command in a terminal and continue only after installation is complete
  3. Separate the two phases: Collection and restoration are independent steps connected through a JSON file
  4. Ask before collecting sensitive data: The user decides whether private keys and credentials should be collected; if they are collected, warn the user about the risk of disclosure
  5. Never overwrite existing files: Every rsync and cp operation includes
    --ignore-existing
    or
    -n
  6. Start small and work up: Copy directories in ascending order of size to make validation easier
  7. Make a plan before taking action: Before restoration, output a manifest summary and ask the user for confirmation
  8. Record discrepancies: After restoration, output a discrepancy report listing items that require manual handling
  9. Install casks one by one: Avoid the rollback problem caused by
    brew bundle
  10. Verify every step: Confirm installations with
    brew list
    and verify copied sizes with
    du -sh
  11. Skip caches: Do not restore cache data under Library/Application Support