# 两台 Windows 工作主机数据同步备份实施方案 > **执行边界:** 本文档只给出人工实施步骤。当前生成文档的过程不会安装软件、不会修改系统配置、不会启动同步、不会传输任何数据。 **目标:** 在笔记本 `wddsh` 与台式机 `wdd` 之间建立可长期运行的双向数据同步体系,覆盖微信数据、Git 项目仓库、开发工具配置,并保留可回滚版本。 **总体架构:** 使用 Syncthing 作为唯一持续同步引擎,承担双向、实时、冲突保留和版本留存。使用 rsync 只做微信数据的首次大体量灌入,减少 Syncthing 初次传输 30GB 海量小文件的时间。rsync 通过笔记本 WSL2 发起,SSH 连接台式机 Windows OpenSSH,再用 `wsl rsync` 调用台式机 WSL2 内的远端 rsync。实施过程先备份、再预演、再单项上线,避免一次性开放所有同步面。 **技术栈:** Windows 11、Syncthing、两端 WSL2、Windows OpenSSH、rsync、PowerShell、Windows 任务计划程序、JetBrains Settings Sync、VS Code Settings Sync。 --- ## 0. 实施前总览 ### 0.1 主机与路径 | 项目 | 笔记本 | 台式机 | |------|--------|--------| | Windows 用户 | `wddsh` | `wdd` | | 台式机局域网 IP | 不适用 | `192.168.1.194` | | 微信数据目录 | `C:\Users\wddsh\xwechat_files` | `C:\Users\wdd\xwechat_files` | | Git 项目目录 | `C:\Users\wddsh\Documents\IdeaProjects` | `C:\Users\wdd\Documents\IdeaProjects` | | Syncthing Web UI | `http://127.0.0.1:8384` | `http://127.0.0.1:8384` | ### 0.2 路径修正 原有微信 rsync 脚本中的目标路径示例是: ```bash REMOTE_DST="/c/Users/wdd/wechat_files/xwechat_files" ``` 本次实施必须改为用户已确认的台式机路径: ```bash REMOTE_DST="/mnt/c/Users/wdd/xwechat_files" ``` 如果你的 WSL 发行版能访问 `/c/Users/...`,也可以写成: ```bash REMOTE_DST="/c/Users/wdd/xwechat_files" ``` 实施前以 `ls /mnt/c/Users/wdd` 或 `ls /c/Users/wdd` 人工确认实际挂载路径,二选一固定,不要混用。 ### 0.3 上线顺序 1. 做台式机目标目录备份和空间检查。 2. 配置笔记本 WSL2 SSH 客户端、台式机 WSL2 rsync 运行时、Windows OpenSSH 免密登录。 3. 关闭两端微信,先做 rsync dry-run。 4. 执行一次微信 rsync 首次灌入。 5. 安装并配对两端 Syncthing。 6. 先上线微信目录同步并观察。 7. 再上线 Git 项目目录同步并观察。 8. 最后上线少量开发工具配置同步。 9. 配置 Syncthing 自启动、版本保留、巡检流程。 --- ## 1. Phase 0:保护现场与回滚准备 ### Task 1:确认两端不在写入关键数据 **操作位置:** 笔记本和台式机都执行。 - [ ] **Step 1:退出微信** 在两台机器上右键系统托盘微信图标,选择退出。 - [ ] **Step 2:确认微信进程已退出** 打开 PowerShell,人工执行: ```powershell Get-Process Weixin -ErrorAction SilentlyContinue ``` 预期结果:没有输出。 如果仍有输出,先在任务管理器中结束 `Weixin.exe`,再重复检查。 - [ ] **Step 3:关闭 IDE 和编辑器** 关闭 JetBrains IDE、VS Code、Cursor、Trae、终端中运行的开发服务。 - [ ] **Step 4:确认 Git 没有正在执行的操作** 在笔记本的 `C:\Users\wddsh\Documents\IdeaProjects` 下,对正在使用的仓库逐个执行: ```powershell git status ``` 预期结果:没有 rebase、merge、cherry-pick、bisect 进行中。 如果存在进行中的 Git 操作,先在原机器上完成或中止该操作后再继续。 ### Task 2:确认台式机磁盘空间 **操作位置:** 台式机。 - [ ] **Step 1:查看 C 盘可用空间** 打开 PowerShell,人工执行: ```powershell Get-PSDrive C | Select-Object Name,Used,Free ``` 判断标准: - 微信数据约 30GB,至少预留 80GB。 - Git 项目目录按实际大小再额外预留 30%。 - Syncthing 版本保留会额外占用空间,首次上线建议 C 盘剩余空间不少于 120GB。 - [ ] **Step 2:确认台式机目标目录存在** 人工执行: ```powershell New-Item -ItemType Directory -Force -Path "C:\Users\wdd\xwechat_files" New-Item -ItemType Directory -Force -Path "C:\Users\wdd\Documents\IdeaProjects" ``` 预期结果:目录存在;如果命令提示目录已存在,也属于正常。 ### Task 3:创建台式机本地保护备份 **操作位置:** 台式机。 - [ ] **Step 1:创建备份根目录** 人工执行: ```powershell New-Item -ItemType Directory -Force -Path "C:\Users\wdd\sync-preflight-backup" ``` - [ ] **Step 2:备份现有微信目录** 如果 `C:\Users\wdd\xwechat_files` 已有数据,人工执行: ```powershell robocopy "C:\Users\wdd\xwechat_files" "C:\Users\wdd\sync-preflight-backup\xwechat_files" /E /COPY:DAT /DCOPY:DAT /R:1 /W:1 /XJ ``` 预期结果:`robocopy` 退出码 `0` 到 `7` 都表示复制层面可接受;`8` 及以上表示有失败项,需要先查看输出。 - [ ] **Step 3:备份现有 Git 项目目录** 如果 `C:\Users\wdd\Documents\IdeaProjects` 已有数据,人工执行: ```powershell robocopy "C:\Users\wdd\Documents\IdeaProjects" "C:\Users\wdd\sync-preflight-backup\IdeaProjects" /E /COPY:DAT /DCOPY:DAT /R:1 /W:1 /XJ /XD node_modules .gradle build dist target .idea\system ``` 预期结果:同样以 `robocopy` 退出码 `0` 到 `7` 为可接受。 --- ## 2. Phase 1:Windows OpenSSH 与两端 WSL2 rsync 准备 ### Task 4:确认两端 OpenSSH 可用 **操作位置:** 笔记本。 - [ ] **Step 1:检查 ssh 客户端** 人工执行: ```powershell ssh -V ``` 预期结果:输出 OpenSSH 版本。 - [ ] **Step 2:测试台式机 22 端口连通** 人工执行: ```powershell Test-NetConnection 192.168.1.194 -Port 22 ``` 预期结果:`TcpTestSucceeded : True`。 - [ ] **Step 3:如果端口不通,在台式机启用 OpenSSH Server** 在台式机以管理员 PowerShell 人工执行: ```powershell Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*' ``` 如果状态不是 `Installed`,再人工执行: ```powershell Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType Automatic New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 ``` 执行后回到笔记本重复 `Test-NetConnection 192.168.1.194 -Port 22`。 ### Task 5:配置 SSH 免密登录 **操作位置:** 笔记本。 - [ ] **Step 1:检查本机 SSH 密钥** 人工执行: ```powershell Test-Path "C:\Users\wddsh\.ssh\id_ed25519.pub" ``` 预期结果:`True`。 - [ ] **Step 2:如果没有密钥,创建 ed25519 密钥** 人工执行: ```powershell ssh-keygen -t ed25519 -f "C:\Users\wddsh\.ssh\id_ed25519" -C "wddsh-r9000p-to-wdd-desktop" ``` 提示 passphrase 时,可以直接回车留空,便于自动化 rsync。 - [ ] **Step 3:把公钥追加到台式机** 优先人工执行: ```powershell type "C:\Users\wddsh\.ssh\id_ed25519.pub" | ssh wdd@192.168.1.194 "mkdir .ssh 2>NUL & type con >> .ssh\authorized_keys" ``` 如果台式机 SSH 进入的是 PowerShell 而不是 CMD,上面命令失败时改用: ```powershell $pub = Get-Content "C:\Users\wddsh\.ssh\id_ed25519.pub" -Raw ssh wdd@192.168.1.194 "powershell -NoProfile -Command `"New-Item -ItemType Directory -Force -Path `$env:USERPROFILE\.ssh | Out-Null; Add-Content -Path `$env:USERPROFILE\.ssh\authorized_keys -Value '$pub'`"" ``` - [ ] **Step 4:验证免密 SSH** 人工执行: ```powershell ssh -i "C:\Users\wddsh\.ssh\id_ed25519" wdd@192.168.1.194 "echo SSH_OK" ``` 预期结果: ```text SSH_OK ``` ### Task 6:在 WSL2 中确认 rsync 可用 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:进入 WSL2** 在笔记本 PowerShell 人工执行: ```powershell wsl ``` - [ ] **Step 2:确认 rsync 与 ssh 可用** 在 WSL2 中人工执行: ```bash rsync --version ssh -V ``` 预期结果:两个命令都输出版本。 - [ ] **Step 3:如果 rsync 不存在,在 WSL2 中安装** Ubuntu/Debian 系 WSL2 人工执行: ```bash sudo apt update sudo apt install -y rsync openssh-client ``` 执行后重复: ```bash rsync --version ``` ### Task 6a:在台式机安装 WSL2 并准备远端 rsync **操作位置:** 台式机。 **目的:** rsync 通过 SSH 工作时,远端也必须存在可执行的 `rsync`。本方案不在台式机 WSL2 内单独配置 SSH 服务,而是让 Windows OpenSSH 接收连接后执行 `wsl rsync`。 - [ ] **Step 1:检查台式机是否已有 WSL2 发行版** 在台式机 PowerShell 人工执行: ```powershell wsl -l -v ``` 预期结果:至少有一个发行版,且 `VERSION` 是 `2`。 如果看到 `Windows Subsystem for Linux has no installed distributions`,说明还没有安装发行版,继续 Step 2。 - [ ] **Step 2:安装 Ubuntu WSL2 发行版** 在台式机管理员 PowerShell 人工执行: ```powershell wsl --install -d Ubuntu ``` 如果系统提示重启,重启台式机。重启后打开 Ubuntu,按提示创建 Linux 用户名和密码。Linux 用户名可以使用: ```text wdd ``` - [ ] **Step 3:确认默认 WSL 版本是 2** 在台式机 PowerShell 人工执行: ```powershell wsl --set-default-version 2 wsl -l -v ``` 如果 Ubuntu 显示 `VERSION 1`,人工执行: ```powershell wsl --set-version Ubuntu 2 ``` 然后重复: ```powershell wsl -l -v ``` - [ ] **Step 4:在台式机 WSL2 中安装 rsync 和基础工具** 在台式机 Ubuntu/WSL2 终端人工执行: ```bash sudo apt update sudo apt install -y rsync openssh-client coreutils ``` 执行后验证: ```bash rsync --version test -d /mnt/c/Users/wdd && echo DESKTOP_WSL_CAN_ACCESS_WINDOWS_C ``` 预期结果: ```text DESKTOP_WSL_CAN_ACCESS_WINDOWS_C ``` - [ ] **Step 5:在台式机 WSL2 中创建微信目标目录** 在台式机 Ubuntu/WSL2 终端人工执行: ```bash mkdir -p /mnt/c/Users/wdd/xwechat_files test -d /mnt/c/Users/wdd/xwechat_files && echo REMOTE_WECHAT_DIR_OK ``` 预期结果: ```text REMOTE_WECHAT_DIR_OK ``` - [ ] **Step 6:从台式机 Windows 侧验证 `wsl rsync` 可被 OpenSSH 调用** 回到台式机 PowerShell,人工执行: ```powershell wsl rsync --version wsl test -d /mnt/c/Users/wdd/xwechat_files ``` 预期结果: - `wsl rsync --version` 输出 rsync 版本。 - `wsl test -d ...` 没有报错。 - [ ] **Step 7:从笔记本 Windows 侧验证远端 `wsl rsync`** 在笔记本 PowerShell 人工执行: ```powershell ssh -i "C:\Users\wddsh\.ssh\id_ed25519" wdd@192.168.1.194 "wsl rsync --version" ssh -i "C:\Users\wddsh\.ssh\id_ed25519" wdd@192.168.1.194 "wsl test -d /mnt/c/Users/wdd/xwechat_files && echo REMOTE_RSYNC_READY" ``` 预期结果: ```text REMOTE_RSYNC_READY ``` 如果第一条命令提示 `wsl: command not found` 或 `rsync: not found`,说明台式机 WSL2 或 rsync 没有配置完成,回到本 Task 的 Step 1 到 Step 6。 ### Task 7:在 WSL2 中配置 SSH Host 别名 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:创建 SSH 配置目录** 人工执行: ```bash mkdir -p ~/.ssh chmod 700 ~/.ssh ``` - [ ] **Step 2:编辑 `~/.ssh/config`** 人工执行: ```bash nano ~/.ssh/config ``` 写入以下内容: ```sshconfig Host wdd-pink-station HostName 192.168.1.194 Port 22 User wdd IdentityFile /mnt/c/Users/wddsh/.ssh/id_ed25519 IdentitiesOnly yes ServerAliveInterval 60 ServerAliveCountMax 3 StrictHostKeyChecking accept-new ``` 保存后人工执行: ```bash chmod 600 ~/.ssh/config ``` - [ ] **Step 3:验证 Host 别名** 人工执行: ```bash test -f ~/.ssh/config && echo WSL_SSH_CONFIG_OK ssh wdd-pink-station "echo Connected" ssh wdd-pink-station "wsl rsync --version" ssh wdd-pink-station "wsl test -d /mnt/c/Users/wdd/xwechat_files && echo REMOTE_WSL_RSYNC_OK" ``` 预期结果: ```text WSL_SSH_CONFIG_OK Connected REMOTE_WSL_RSYNC_OK ``` 如果出现: ```text Can't open user config file ~/.ssh/config: No such file or directory ``` 说明你当前是在笔记本 WSL2 中运行 rsync,但还没有在该 WSL 用户目录下创建 `~/.ssh/config`。回到本 Task 的 Step 1 和 Step 2,创建配置文件后再继续。Windows 侧的 `C:\Users\wddsh\.ssh\config` 不等于 WSL 侧的 `~/.ssh/config`。 --- ## 3. Phase 2:微信数据 rsync 首次灌入 ### Task 8:创建 dry-run 脚本 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:创建脚本目录** 人工执行: ```bash mkdir -p ~/sync-scripts ~/wechat_backup/logs ``` - [ ] **Step 2:创建 dry-run 脚本** 人工执行: ```bash nano ~/sync-scripts/wechat_initial_dry_run.sh ``` 写入: ```bash #!/usr/bin/env bash set -euo pipefail SRC="/mnt/c/Users/wddsh/xwechat_files" REMOTE_HOST="wdd-pink-station" REMOTE_DST="/mnt/c/Users/wdd/xwechat_files" REMOTE_RSYNC_PATH="wsl rsync" SSH_CONFIG="$HOME/.ssh/config" RSYNC_SSH="ssh -F ${SSH_CONFIG}" LOG_DIR="/mnt/c/Users/wddsh/wechat_backup/logs" LOG_FILE="${LOG_DIR}/wechat_initial_dry_run_$(date +%Y%m%d_%H%M%S).log" mkdir -p "$LOG_DIR" if tasklist.exe 2>/dev/null | grep -qi "Weixin.exe"; then echo "ERROR: Weixin.exe is still running. Exit WeChat before dry-run." | tee -a "$LOG_FILE" exit 2 fi if [ ! -d "$SRC" ]; then echo "ERROR: Source path does not exist: $SRC" | tee -a "$LOG_FILE" exit 3 fi if [ ! -f "$SSH_CONFIG" ]; then echo "ERROR: Missing WSL SSH config: $SSH_CONFIG" | tee -a "$LOG_FILE" echo "Run Task 7 first. Windows C:\\Users\\wddsh\\.ssh\\config is not the same file." | tee -a "$LOG_FILE" exit 4 fi $RSYNC_SSH "$REMOTE_HOST" "wsl test -d $REMOTE_DST" $RSYNC_SSH "$REMOTE_HOST" "wsl rsync --version" >/dev/null rsync -rvzn \ --itemize-changes \ --partial \ --inplace \ --whole-file \ --delete \ --size-only \ --no-owner \ --no-group \ --no-perms \ --no-times \ --omit-dir-times \ --rsync-path="$REMOTE_RSYNC_PATH" \ --exclude="temp/" \ --exclude="cache/" \ --exclude="apm_record/" \ --exclude="crash_report/" \ --exclude="log/" \ --exclude="*.lock" \ --exclude="*.tmp" \ -e "$RSYNC_SSH" \ "${SRC}/" \ "${REMOTE_HOST}:${REMOTE_DST}/" \ 2>&1 | tee -a "$LOG_FILE" echo "Dry-run log: $LOG_FILE" ``` - [ ] **Step 3:赋予执行权限** 人工执行: ```bash chmod +x ~/sync-scripts/wechat_initial_dry_run.sh ``` - [ ] **Step 4:检查脚本没有混入 Markdown 代码围栏** 人工执行: ```bash tail -n 5 ~/sync-scripts/wechat_initial_dry_run.sh bash -n ~/sync-scripts/wechat_initial_dry_run.sh ``` 预期结果: - `tail` 的最后一行应该是 `echo "Dry-run log: $LOG_FILE"`。 - `tail` 输出中不应该出现单独一行由三个反引号组成的 Markdown 代码围栏。 - `bash -n` 没有任何输出,并返回成功。 如果 `tail` 看到单独一行 ```` ``` ````,说明复制 Markdown 文档时把代码块结束标记也复制进了脚本。删除该行: ```bash sed -i '/^```$/d' ~/sync-scripts/wechat_initial_dry_run.sh bash -n ~/sync-scripts/wechat_initial_dry_run.sh ``` `bash -n` 通过后再继续 Task 9。 ### Task 9:执行 rsync dry-run 并审阅 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:运行 dry-run** 人工执行: ```bash ~/sync-scripts/wechat_initial_dry_run.sh ``` 预期结果: - 输出大量 `>f+++++++++` 或目录创建记录,表示将复制到台式机。 - 没有实际传输文件,因为参数包含 `-n`。 - 没有 `deleting` 大量删除台式机有效数据;如果有,先停止并检查目标目录是否选错。 如果 rsync 已输出类似下面的统计信息: ```text total size is 36,671,651,105 speedup is ... (DRY RUN) ``` 随后又报: ```text unexpected EOF while looking for matching ``' ``` 说明 dry-run 主体已经完成,但脚本文件里混入了未配对的反引号,通常是末尾复制了 Markdown 代码围栏 ```` ``` ````。处理: ```bash tail -n 10 ~/sync-scripts/wechat_initial_dry_run.sh sed -i '/^```$/d' ~/sync-scripts/wechat_initial_dry_run.sh bash -n ~/sync-scripts/wechat_initial_dry_run.sh ``` `bash -n` 没有输出后,再重新执行 dry-run。重新执行的目的只是确认脚本退出码正常;前一次 dry-run 已经证明 rsync 连接链路可用。 - [ ] **Step 2:检查 dry-run 日志** 人工执行: ```bash ls -lh /mnt/c/Users/wddsh/wechat_backup/logs/ ``` 再打开最新的 `wechat_initial_dry_run_*.log`,重点检查: - 源路径是 `/mnt/c/Users/wddsh/xwechat_files/`。 - 目标路径是 `wdd-pink-station:/mnt/c/Users/wdd/xwechat_files/`。 - 脚本包含 `--rsync-path="wsl rsync"`,表示远端 rsync 由台式机 WSL2 提供。 - 脚本使用 `-rvzn`,不要使用 `-a`。`-a` 会隐式启用权限、属主、属组、时间戳等 Linux 元数据保留行为,不适合远端目标为 Windows `/mnt/c` 的微信目录。 - 脚本包含 `--size-only`、`--no-owner`、`--no-group`、`--no-perms`、`--no-times`、`--omit-dir-times`,表示用文件大小判断增量,不在 Windows 盘上保留 Linux owner/group/permission/time 元数据。 - 脚本包含 `--inplace`、`--whole-file`,表示直接写目标文件并按整文件复制,减少 rsync 临时文件在 Windows 盘上的元数据操作。 - 没有把目标写到 `/mnt/c/Users/wdd/wechat_files/xwechat_files/`。 - [ ] **Step 3:如果 dry-run 报 SSH config 缺失** 如果输出: ```text Can't open user config file ~/.ssh/config: No such file or directory ``` 原因是当前脚本运行在笔记本 WSL2,`-e "ssh -F ~/.ssh/config"` 查找的是 WSL 用户目录下的: ```text ~/.ssh/config ``` 不是 Windows 目录: ```text C:\Users\wddsh\.ssh\config ``` 处理步骤: ```bash mkdir -p ~/.ssh nano ~/.ssh/config chmod 600 ~/.ssh/config test -f ~/.ssh/config && echo WSL_SSH_CONFIG_OK ``` `~/.ssh/config` 内容必须与 Task 7 完全一致。完成后先验证: ```bash ssh wdd-pink-station "echo Connected" ssh wdd-pink-station "wsl rsync --version" ``` 再重新运行: ```bash ~/sync-scripts/wechat_initial_dry_run.sh ``` 如果以下手动验证已经成功: ```bash test -f ~/.ssh/config && echo WSL_SSH_CONFIG_OK ssh wdd-pink-station "echo Connected" ssh wdd-pink-station "wsl rsync --version" ssh wdd-pink-station "wsl test -d /mnt/c/Users/wdd/xwechat_files && echo REMOTE_WSL_RSYNC_OK" ``` 但 dry-run 仍然报: ```text Can't open user config file ~/.ssh/config: No such file or directory ``` 说明正在运行的 `~/sync-scripts/wechat_initial_dry_run.sh` 仍是旧版本,脚本中还残留字面量 `~/.ssh/config`。检查: ```bash grep -n "ssh -F" ~/sync-scripts/wechat_initial_dry_run.sh grep -n "RSYNC_SSH" ~/sync-scripts/wechat_initial_dry_run.sh ``` 正确脚本必须包含: ```bash SSH_CONFIG="$HOME/.ssh/config" RSYNC_SSH="ssh -F ${SSH_CONFIG}" ``` 并且 rsync 命令中必须是: ```bash -e "$RSYNC_SSH" \ ``` 不能再出现: ```bash -e "ssh -F ~/.ssh/config" \ ``` - [ ] **Step 4:如果 dry-run 报远端 rsync 或 wsl 不存在** 如果输出包含: ```text wsl: command not found rsync: command not found rsync error: unexplained error (code 255) ``` 说明台式机侧还没有完成 WSL2 或 rsync 准备。处理步骤: 1. 回到 Task 6a,在台式机安装 WSL2 和 rsync。 2. 在笔记本 WSL2 中执行: ```bash ssh wdd-pink-station "wsl rsync --version" ssh wdd-pink-station "wsl test -d /mnt/c/Users/wdd/xwechat_files && echo REMOTE_RSYNC_READY" ``` 3. 看到 `REMOTE_RSYNC_READY` 后,再重新运行 dry-run。 ### Task 10:创建首次正式灌入脚本 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:创建正式脚本** 人工执行: ```bash nano ~/sync-scripts/wechat_initial_sync.sh ``` 写入: ```bash #!/usr/bin/env bash set -euo pipefail SRC="/mnt/c/Users/wddsh/xwechat_files" REMOTE_HOST="wdd-pink-station" REMOTE_DST="/mnt/c/Users/wdd/xwechat_files" REMOTE_RSYNC_PATH="wsl rsync" SSH_CONFIG="$HOME/.ssh/config" RSYNC_SSH="ssh -F ${SSH_CONFIG}" LOG_DIR="/mnt/c/Users/wddsh/wechat_backup/logs" LOG_FILE="${LOG_DIR}/wechat_initial_sync_$(date +%Y%m%d_%H%M%S).log" mkdir -p "$LOG_DIR" echo "======== WeChat initial rsync started at $(date '+%Y-%m-%d %H:%M:%S') ========" | tee -a "$LOG_FILE" if tasklist.exe 2>/dev/null | grep -qi "Weixin.exe"; then echo "ERROR: Weixin.exe is still running. Exit WeChat before initial sync." | tee -a "$LOG_FILE" exit 2 fi if [ ! -d "$SRC" ]; then echo "ERROR: Source path does not exist: $SRC" | tee -a "$LOG_FILE" exit 3 fi if [ ! -f "$SSH_CONFIG" ]; then echo "ERROR: Missing WSL SSH config: $SSH_CONFIG" | tee -a "$LOG_FILE" echo "Run Task 7 first. Windows C:\\Users\\wddsh\\.ssh\\config is not the same file." | tee -a "$LOG_FILE" exit 4 fi $RSYNC_SSH "$REMOTE_HOST" "wsl mkdir -p $REMOTE_DST" $RSYNC_SSH "$REMOTE_HOST" "wsl rsync --version" >/dev/null rsync -rvz \ --info=progress2 \ --partial \ --inplace \ --whole-file \ --delete \ --size-only \ --no-owner \ --no-group \ --no-perms \ --no-times \ --omit-dir-times \ --rsync-path="$REMOTE_RSYNC_PATH" \ --exclude="temp/" \ --exclude="cache/" \ --exclude="apm_record/" \ --exclude="crash_report/" \ --exclude="log/" \ --exclude="*.lock" \ --exclude="*.tmp" \ -e "$RSYNC_SSH" \ "${SRC}/" \ "${REMOTE_HOST}:${REMOTE_DST}/" \ 2>&1 | tee -a "$LOG_FILE" status=${PIPESTATUS[0]} echo "======== WeChat initial rsync finished with status ${status} at $(date '+%Y-%m-%d %H:%M:%S') ========" | tee -a "$LOG_FILE" exit "$status" ``` - [ ] **Step 2:赋予执行权限** 人工执行: ```bash chmod +x ~/sync-scripts/wechat_initial_sync.sh ``` - [ ] **Step 3:检查正式脚本没有混入 Markdown 代码围栏** 人工执行: ```bash tail -n 5 ~/sync-scripts/wechat_initial_sync.sh bash -n ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果: - `tail` 的最后一行应该是 `exit "$status"`。 - `tail` 输出中不应该出现单独一行 ```` ``` ````。 - `bash -n` 没有任何输出,并返回成功。 如果发现代码围栏,删除后复查: ```bash sed -i '/^```$/d' ~/sync-scripts/wechat_initial_sync.sh bash -n ~/sync-scripts/wechat_initial_sync.sh ``` ### Task 11:执行首次正式灌入 **操作位置:** 笔记本 WSL2。 - [ ] **Step 1:再次确认微信未运行** 人工执行: ```bash tasklist.exe 2>/dev/null | grep -i "Weixin.exe" || echo "WeChat is not running" ``` 预期结果: ```text WeChat is not running ``` - [ ] **Step 2:运行正式灌入** 人工执行: ```bash ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果: - 首次可能运行 1 到 2 小时,取决于文件数量、磁盘和局域网速度。 - 退出状态为 `0`。 - 日志保存在 `C:\Users\wddsh\wechat_backup\logs`。 如果输出大量类似下面的警告: ```text rsync: [generator] chgrp "/mnt/c/Users/wdd/xwechat_files/..." failed: Operation not permitted (1) ``` 根因是 `rsync -a` 默认尝试保留 Linux group 元数据,但目标目录位于台式机 Windows 盘 `/mnt/c`,Windows 文件系统不接受 WSL 的 `chgrp`。处理方式: 1. 不需要手动修改这些目标文件。 2. 暂停当前脚本或等待其结束都可以;如果已经传了很多文件,重新运行修正后的脚本会增量续传。 3. 确认 `~/sync-scripts/wechat_initial_sync.sh` 的 rsync 参数中已经包含: ```bash --size-only \ --no-owner \ --no-group \ --no-perms \ --no-times \ --omit-dir-times \ ``` 4. 语法检查: ```bash bash -n ~/sync-scripts/wechat_initial_sync.sh ``` 5. 重新执行: ```bash ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果:不再出现 `chgrp ... Operation not permitted`。如果仍有少量 `Permission denied`,优先检查微信是否正在运行,或者目标文件是否被台式机微信、杀毒软件、索引服务占用。 如果输出类似下面的警告: ```text rsync: [generator] failed to set times on "/mnt/c/Users/wdd/xwechat_files/.": Operation not permitted (1) ``` 根因是 `rsync -a` 默认保留时间戳,其中目录时间戳在 Windows `/mnt/c` 上可能无法设置。处理方式: 1. 确认 `~/sync-scripts/wechat_initial_sync.sh` 的 rsync 参数中已经包含: ```bash --omit-dir-times \ ``` 2. 同时保留前面的 Windows 元数据规避参数: ```bash --size-only \ --no-owner \ --no-group \ --no-perms \ --no-times \ --omit-dir-times \ ``` 3. 语法检查并重新执行: ```bash bash -n ~/sync-scripts/wechat_initial_sync.sh ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果:不再出现 `failed to set times on ".../."`。`--omit-dir-times` 只跳过目录时间戳;如果仍然出现临时文件或普通文件的 `failed to set times`,说明还在使用 `-a` 或仍在保留文件时间戳,继续执行下面的 code 23 完整收敛方案。 如果脚本已经传输到 100%,最后输出: ```text rsync error: some files/attrs were not transferred (see previous errors) (code 23) ``` 并且前面的错误主要是: ```text rsync: [receiver] failed to set times on "/mnt/c/Users/wdd/xwechat_files/...": Operation not permitted (1) ``` 这是 Windows `/mnt/c` 文件时间戳元数据写入失败。内容已经基本传完,但 rsync 因属性设置失败返回 `23`。完整解决步骤: 1. 不删除台式机 `C:\Users\wdd\xwechat_files`,保留已经传过去的 36GB 数据。 2. 把 `~/sync-scripts/wechat_initial_sync.sh` 中的 rsync 起始行从 `rsync -avz \` 改为: ```bash rsync -rvz \ ``` 3. 确认参数包含以下完整 Windows-safe 参数组: ```bash --partial \ --inplace \ --whole-file \ --delete \ --size-only \ --no-owner \ --no-group \ --no-perms \ --no-times \ --omit-dir-times \ ``` 4. 确认脚本中不再出现 `rsync -avz`: ```bash grep -n "rsync -" ~/sync-scripts/wechat_initial_sync.sh grep -n -- "--no-times\|--size-only\|--inplace" ~/sync-scripts/wechat_initial_sync.sh bash -n ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果: - 第一条显示 `rsync -rvz \`。 - 第二条能看到 `--size-only`、`--inplace`、`--no-times`。 - `bash -n` 没有输出。 5. 重新执行一次收敛同步: ```bash ~/sync-scripts/wechat_initial_sync.sh ``` 预期结果: - 因为已经传过 36GB,第二次主要做增量检查和少量补齐。 - 不再出现 `failed to set times`。 - 最终退出状态为 `0`。 6. 如果仍然出现 `failed to set times`,说明当前运行的还是旧脚本。直接检查: ```bash nl -ba ~/sync-scripts/wechat_initial_sync.sh | sed -n '1,90p' ``` 确认实际运行脚本与文档中的 Task 10 完全一致。 - [ ] **Step 3:验证台式机文件规模** 在台式机 PowerShell 人工执行: ```powershell (Get-ChildItem "C:\Users\wdd\xwechat_files" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object).Count ``` 在笔记本 PowerShell 人工执行: ```powershell (Get-ChildItem "C:\Users\wddsh\xwechat_files" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object).Count ``` 预期结果:两个数量接近。排除目录导致少量差异是正常的。 --- ## 4. Phase 3:安装并配对 Syncthing ### Task 12:下载并安装 Syncthing **操作位置:** 笔记本和台式机都执行。 - [ ] **Step 1:打开官方下载页** 在浏览器访问: ```text https://syncthing.net/downloads/ ``` 选择 Windows 版本。Windows 当前没有官方安装器,官方文档给出的常规方式是下载压缩包并手动放置可执行文件。 - [ ] **Step 2:创建安装目录** 以普通用户或管理员 PowerShell 人工执行: ```powershell New-Item -ItemType Directory -Force -Path "C:\Tools\Syncthing" ``` - [ ] **Step 3:解压** 把下载的 Syncthing Windows 压缩包解压,把其中的 `syncthing.exe` 放到: ```text C:\Tools\Syncthing\syncthing.exe ``` - [ ] **Step 4:首次交互启动** 双击: ```text C:\Tools\Syncthing\syncthing.exe ``` Windows 防火墙弹窗出现时,勾选专用网络,允许访问。 - [ ] **Step 5:打开 Web UI** 访问: ```text http://127.0.0.1:8384 ``` 预期结果:打开 Syncthing 管理界面。 ### Task 13:加固 Syncthing Web UI **操作位置:** 笔记本和台式机都执行。 - [ ] **Step 1:进入 GUI 设置** 在 Syncthing Web UI 中点击: ```text Actions -> Settings -> GUI ``` - [ ] **Step 2:设置用户名和密码** 填写: ```text GUI Authentication User: wddsh GUI Authentication Password: 使用本机密码管理器生成的强密码 ``` 台式机用户名可以填: ```text wdd ``` - [ ] **Step 3:限制监听地址** 确认 GUI Listen Address 保持: ```text 127.0.0.1:8384 ``` 不要改成 `0.0.0.0:8384`,除非后续明确需要远程访问 Web UI。 - [ ] **Step 4:保存并重启 Syncthing** 点击保存。提示重启时,点击重启。 ### Task 14:配对两台设备 **操作位置:** 两端 Syncthing Web UI。 - [ ] **Step 1:在笔记本复制 Device ID** 打开笔记本 Syncthing: ```text Actions -> Show ID ``` 复制完整 Device ID。 - [ ] **Step 2:在台式机添加笔记本** 打开台式机 Syncthing: ```text Add Remote Device ``` 填写: ```text Device ID: 粘贴笔记本 Device ID Device Name: R9000P-wddsh ``` 保存。 - [ ] **Step 3:在台式机复制 Device ID** 打开台式机 Syncthing: ```text Actions -> Show ID ``` 复制完整 Device ID。 - [ ] **Step 4:在笔记本添加台式机** 打开笔记本 Syncthing: ```text Add Remote Device ``` 填写: ```text Device ID: 粘贴台式机 Device ID Device Name: Desktop-wdd ``` 保存。 - [ ] **Step 5:确认直连** 在两端 Web UI 的 Remote Devices 中确认连接状态。 预期结果: ```text Connected Connection Type: TCP LAN 或 QUIC LAN ``` 如果显示 Relay,按以下顺序处理: 1. 确认 Windows 网络配置为专用网络。 2. 确认防火墙允许 `C:\Tools\Syncthing\syncthing.exe`。 3. 确认两机在同一局域网。 4. 重新启动 Syncthing。 --- ## 5. Phase 4:微信目录 Syncthing 持续同步 ### Task 15:添加微信共享文件夹 **操作位置:** 先笔记本,后台式机。 - [ ] **Step 1:在笔记本添加文件夹** 笔记本 Syncthing Web UI 点击: ```text Add Folder ``` 填写: ```text Folder Label: WeChat xwechat_files Folder ID: wechat-xwechat-files Folder Path: C:\Users\wddsh\xwechat_files Folder Type: Send & Receive ``` - [ ] **Step 2:共享给台式机** 在 Sharing 页签勾选: ```text Desktop-wdd ``` - [ ] **Step 3:设置扫描与监控** 在 Advanced 页签设置: ```text Watch for Changes: Enabled Full Rescan Interval: 300 seconds Ignore Permissions: Enabled ``` - [ ] **Step 4:启用版本控制** 在 File Versioning 页签选择: ```text Staggered File Versioning ``` 设置: ```text Maximum Age: 30 days ``` - [ ] **Step 5:保存** 点击 Save。 - [ ] **Step 6:在台式机接受共享** 台式机 Web UI 会弹出新文件夹邀请,点击 Add。 填写: ```text Folder Path: C:\Users\wdd\xwechat_files Folder Type: Send & Receive Watch for Changes: Enabled Full Rescan Interval: 300 seconds Ignore Permissions: Enabled File Versioning: Staggered, 30 days ``` 保存。 ### Task 16:配置微信 `.stignore` **操作位置:** 笔记本和台式机的微信同步目录都配置。 - [ ] **Step 1:在笔记本创建忽略文件** 用记事本打开或创建: ```text C:\Users\wddsh\xwechat_files\.stignore ``` 写入: ```gitignore // Windows noise (?i)desktop.ini (?i)thumbs.db // WeChat volatile files temp/** cache/** apm_record/** crash_report/** log/** *.lock *.tmp *.temp *.bak.tmp // Syncthing conflict review output should be handled manually *.sync-conflict-* ``` - [ ] **Step 2:在台式机创建同样的忽略文件** 用记事本打开或创建: ```text C:\Users\wdd\xwechat_files\.stignore ``` 写入同样内容。 > [!WARNING] > **db_storage 动态保护:** 高级解决方案要求微信运行时临时将 `db_storage` 排除同步(SQLite 数据库锁定状态下同步可能导致损坏)。`.stignore` 中**不要**永久添加 `db_storage`,否则聊天记录数据库将永远不会同步。下方 Task 18a 提供了自动保护脚本,可在微信运行时动态排除、关闭后自动恢复。 - [ ] **Step 3:触发重新扫描** 在两端 Syncthing Web UI 中,对 `WeChat xwechat_files` 点击: ```text Rescan ``` ### Task 17:微信同步验收 **操作位置:** 两台机器。 - [ ] **Step 1:等待状态变为 Up to Date** 在两端 Web UI 确认 `WeChat xwechat_files` 状态: ```text Up to Date ``` - [ ] **Step 2:笔记本创建测试文件** 在笔记本 PowerShell 人工执行: ```powershell Set-Content -Path "C:\Users\wddsh\xwechat_files\syncthing_probe_from_laptop.txt" -Value "from laptop $(Get-Date -Format s)" ``` - [ ] **Step 3:台式机确认收到** 在台式机 PowerShell 人工执行: ```powershell Get-Content "C:\Users\wdd\xwechat_files\syncthing_probe_from_laptop.txt" ``` 预期结果:能看到 `from laptop`。 - [ ] **Step 4:台式机创建反向测试文件** 在台式机 PowerShell 人工执行: ```powershell Set-Content -Path "C:\Users\wdd\xwechat_files\syncthing_probe_from_desktop.txt" -Value "from desktop $(Get-Date -Format s)" ``` - [ ] **Step 5:笔记本确认收到** 在笔记本 PowerShell 人工执行: ```powershell Get-Content "C:\Users\wddsh\xwechat_files\syncthing_probe_from_desktop.txt" ``` 预期结果:能看到 `from desktop`。 - [ ] **Step 6:删除测试文件** 在笔记本 PowerShell 人工执行: ```powershell Remove-Item "C:\Users\wddsh\xwechat_files\syncthing_probe_from_laptop.txt" -Force Remove-Item "C:\Users\wddsh\xwechat_files\syncthing_probe_from_desktop.txt" -Force ``` 等待删除同步到台式机。 ### Task 18:微信日常使用规则 - [ ] **Step 1:切换电脑前退出微信** 从笔记本切到台式机,或从台式机切到笔记本前,先退出微信。 - [ ] **Step 2:等待 Syncthing 同步完成** 打开 `http://127.0.0.1:8384`,确认微信文件夹是: ```text Up to Date ``` - [ ] **Step 3:再在另一台电脑登录微信** 不要在 Syncthing 显示 `Syncing`、`Scanning`、`Out of Sync` 时切换微信主力设备。 ### Task 18a:微信 db_storage 动态保护(建议配置) > [!IMPORTANT] > 高级解决方案要求:微信运行时临时排除 `db_storage` 目录(SQLite 数据库锁定状态下同步可能导致文件损坏),微信关闭后恢复同步。以下脚本自动完成这一切换,建议配合任务计划程序每 2 分钟执行一次。 **操作位置:** 笔记本和台式机都配置。 - [ ] **Step 1:获取 Syncthing API Key** 在 Syncthing Web UI 中复制: ```text Actions -> Settings -> General -> API Key ``` 然后在本机 PowerShell 中保存到当前用户目录下的受限文件: ```powershell New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\syncthing-scripts" $apiKey = Read-Host "Paste this machine's Syncthing API Key" Set-Content -Path "$env:USERPROFILE\syncthing-scripts\syncthing-api-key.txt" -Value $apiKey -NoNewline icacls "$env:USERPROFILE\syncthing-scripts\syncthing-api-key.txt" /inheritance:r icacls "$env:USERPROFILE\syncthing-scripts\syncthing-api-key.txt" /grant:r "$env:USERNAME:F" ``` 预期结果:`syncthing-api-key.txt` 只允许当前 Windows 用户读取。 - [ ] **Step 2:创建保护脚本** 人工执行创建脚本目录: ```powershell New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\syncthing-scripts" ``` 用记事本创建脚本文件: 笔记本:`C:\Users\wddsh\syncthing-scripts\wechat-db-guard.ps1` 台式机:`C:\Users\wdd\syncthing-scripts\wechat-db-guard.ps1` 写入以下内容(两端相同): ```powershell param( [Parameter(Mandatory)][string]$StIgnorePath, [Parameter(Mandatory)][string]$ApiKeyPath ) $ErrorActionPreference = "Stop" $guardComment = "// [AUTO-GUARD] db_storage excluded while WeChat is running" $guardLine = "**/db_storage/**" $wechatRunning = [bool](Get-Process Weixin -ErrorAction SilentlyContinue) if (-not (Test-Path $StIgnorePath)) { Write-Host "ERROR: .stignore not found: $StIgnorePath" exit 1 } if (-not (Test-Path $ApiKeyPath)) { Write-Host "ERROR: Syncthing API key file not found: $ApiKeyPath" exit 2 } $ApiKey = (Get-Content $ApiKeyPath -Raw).Trim() $content = Get-Content $StIgnorePath -Raw $hasGuard = $content.Contains($guardLine) if ($wechatRunning -and -not $hasGuard) { Add-Content -Path $StIgnorePath -Value "`n$guardComment`n$guardLine" Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:8384/rest/db/scan?folder=wechat-xwechat-files" -Headers @{"X-API-Key"=$ApiKey} | Out-Null Write-Host "[$(Get-Date -Format s)] GUARD ON: db_storage excluded (WeChat running)" } elseif (-not $wechatRunning -and $hasGuard) { (Get-Content $StIgnorePath) | Where-Object { $_ -ne $guardLine -and $_ -ne $guardComment } | Set-Content $StIgnorePath Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:8384/rest/db/scan?folder=wechat-xwechat-files" -Headers @{"X-API-Key"=$ApiKey} | Out-Null Write-Host "[$(Get-Date -Format s)] GUARD OFF: db_storage syncing (WeChat closed)" } else { Write-Host "[$(Get-Date -Format s)] No action needed" } ``` - [ ] **Step 3:手动验证脚本** 微信**运行时**在 PowerShell 执行(笔记本示例): ```powershell powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\syncthing-scripts\wechat-db-guard.ps1" -StIgnorePath "C:\Users\wddsh\xwechat_files\.stignore" -ApiKeyPath "$env:USERPROFILE\syncthing-scripts\syncthing-api-key.txt" ``` 台式机将 `-StIgnorePath` 替换为 `"C:\Users\wdd\xwechat_files\.stignore"`。 预期结果:输出 `GUARD ON`,检查 `.stignore` 末尾出现 `**/db_storage/**`。 关闭微信后再次执行,预期输出 `GUARD OFF`,guard 行被移除。 - [ ] **Step 4:配置定时执行** 打开任务计划程序(`taskschd.msc`),创建新任务: ```text Name: WeChat DB Guard Triggers: At log on, Repeat every 2 minutes indefinitely Action: Start a program Program/script: powershell.exe ``` 笔记本 Add arguments: ```text -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Users\wddsh\syncthing-scripts\wechat-db-guard.ps1" -StIgnorePath "C:\Users\wddsh\xwechat_files\.stignore" -ApiKeyPath "C:\Users\wddsh\syncthing-scripts\syncthing-api-key.txt" ``` 台式机 Add arguments: ```text -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Users\wdd\syncthing-scripts\wechat-db-guard.ps1" -StIgnorePath "C:\Users\wdd\xwechat_files\.stignore" -ApiKeyPath "C:\Users\wdd\syncthing-scripts\syncthing-api-key.txt" ``` Settings 页: ```text 勾选 Allow task to be run on demand 取消勾选 Stop the task if it runs longer than If the task is already running: Do not start a new instance ``` --- ## 6. Phase 5:Git 项目仓库 Syncthing 同步 ### Task 19:实施前清理 Git 工作状态 **操作位置:** 笔记本和台式机。 - [ ] **Step 1:关闭 IDE** 关闭所有可能写入项目目录的 IDE、编辑器、构建工具、终端。 - [ ] **Step 2:检查常用仓库状态** 在每个活跃仓库执行: ```powershell git status ``` 预期结果: - 没有 rebase、merge、cherry-pick 进行中。 - 如果有重要未提交改动,先在原机器上 commit 或 stash。 - [ ] **Step 3:删除临时锁文件** 只在确认没有 Git 命令运行时,人工检查并删除: ```powershell Get-ChildItem "C:\Users\wddsh\Documents\IdeaProjects" -Recurse -Force -Filter "index.lock" ``` 如果确认是残留锁文件,删除: ```powershell Get-ChildItem "C:\Users\wddsh\Documents\IdeaProjects" -Recurse -Force -Filter "index.lock" | Remove-Item -Force ``` 台式机同样检查: ```powershell Get-ChildItem "C:\Users\wdd\Documents\IdeaProjects" -Recurse -Force -Filter "index.lock" ``` ### Task 20:添加 Git 项目共享文件夹 **操作位置:** 先笔记本,后台式机。 - [ ] **Step 1:在笔记本添加文件夹** Syncthing Web UI 点击: ```text Add Folder ``` 填写: ```text Folder Label: IdeaProjects Folder ID: ideaprojects Folder Path: C:\Users\wddsh\Documents\IdeaProjects Folder Type: Send & Receive ``` - [ ] **Step 2:共享给台式机** Sharing 页签勾选: ```text Desktop-wdd ``` - [ ] **Step 3:设置高级参数** Advanced 页签设置: ```text Watch for Changes: Enabled Full Rescan Interval: 600 seconds Ignore Permissions: Enabled ``` - [ ] **Step 4:启用版本控制** File Versioning 页签选择: ```text Staggered File Versioning Maximum Age: 30 days ``` - [ ] **Step 5:台式机接受共享** 台式机 Web UI 收到邀请后点击 Add,填写: ```text Folder Path: C:\Users\wdd\Documents\IdeaProjects Folder Type: Send & Receive Watch for Changes: Enabled Full Rescan Interval: 600 seconds Ignore Permissions: Enabled File Versioning: Staggered, 30 days ``` 保存。 ### Task 21:配置 Git 项目 `.stignore` **操作位置:** 笔记本和台式机的 `IdeaProjects` 根目录都配置。 - [ ] **Step 1:在笔记本创建忽略文件** 创建或编辑: ```text C:\Users\wddsh\Documents\IdeaProjects\.stignore ``` 写入: ```gitignore // Windows noise (?i)desktop.ini (?i)thumbs.db // Dependency directories **/node_modules/** **/.pnpm-store/** **/.yarn/cache/** **/.gradle/** **/.m2/repository/** **/vendor/** // Build outputs **/build/** **/dist/** **/out/** **/target/** **/.next/** **/.nuxt/** **/.vite/** **/coverage/** // Python caches **/__pycache__/** **/*.pyc **/.pytest_cache/** **/.mypy_cache/** **/.ruff_cache/** // IDE volatile files **/.idea/workspace.xml **/.idea/tasks.xml **/.idea/usage.statistics.xml **/.idea/shelf/** **/*.iws **/.vscode/.ropeproject/** // Logs and temp **/*.log **/*.tmp **/*.temp **/.DS_Store // Git transient lock files; do not ignore .git itself **/.git/index.lock **/.git/HEAD.lock **/.git/config.lock // Syncthing conflicts require manual resolution **/*.sync-conflict-* ``` - [ ] **Step 2:在台式机创建同样的忽略文件** 创建或编辑: ```text C:\Users\wdd\Documents\IdeaProjects\.stignore ``` 写入同样内容。 - [ ] **Step 3:触发重新扫描** 在两端 Web UI 对 `IdeaProjects` 点击: ```text Rescan ``` ### Task 22:Git 项目同步验收 **操作位置:** 两台机器。 - [ ] **Step 1:等待状态为 Up to Date** 在两端确认 `IdeaProjects` 文件夹状态: ```text Up to Date ``` - [ ] **Step 2:笔记本创建测试文件** 在笔记本 PowerShell 人工执行: ```powershell Set-Content -Path "C:\Users\wddsh\Documents\IdeaProjects\syncthing_project_probe.txt" -Value "from laptop $(Get-Date -Format s)" ``` - [ ] **Step 3:台式机确认收到** 在台式机 PowerShell 人工执行: ```powershell Get-Content "C:\Users\wdd\Documents\IdeaProjects\syncthing_project_probe.txt" ``` 预期结果:能看到 `from laptop`。 - [ ] **Step 4:台式机创建反向测试文件** 在台式机 PowerShell 人工执行: ```powershell Set-Content -Path "C:\Users\wdd\Documents\IdeaProjects\syncthing_project_probe_desktop.txt" -Value "from desktop $(Get-Date -Format s)" ``` - [ ] **Step 5:笔记本确认收到** 在笔记本 PowerShell 人工执行: ```powershell Get-Content "C:\Users\wddsh\Documents\IdeaProjects\syncthing_project_probe_desktop.txt" ``` - [ ] **Step 6:删除测试文件** 在笔记本 PowerShell 人工执行: ```powershell Remove-Item "C:\Users\wddsh\Documents\IdeaProjects\syncthing_project_probe.txt" -Force Remove-Item "C:\Users\wddsh\Documents\IdeaProjects\syncthing_project_probe_desktop.txt" -Force ``` ### Task 23:Git 日常使用规则 - [ ] **Step 1:同一时间只在一台机器开发同一个仓库** 如果笔记本正在编辑某个仓库,台式机只做查看或等待同步完成。 - [ ] **Step 2:大改动先 commit** 开始跨机器切换前,在当前机器执行: ```powershell git status git add -A git commit -m "wip: save work before machine switch" ``` 如果不想产生 commit,使用: ```powershell git stash push -u -m "machine switch" ``` - [ ] **Step 3:切换机器前等待 Up to Date** 确认 `IdeaProjects` 在 Syncthing 中为: ```text Up to Date ``` - [ ] **Step 4:另一台机器先检查状态再开发** 在目标机器仓库中执行: ```powershell git status ``` 确认没有异常冲突文件后再打开 IDE。 --- ## 7. Phase 6:开发工具配置同步 ### Task 24:优先启用内置配置同步 **操作位置:** 两台机器。 - [ ] **Step 1:JetBrains IDE 启用 Settings Sync** 在 IntelliJ IDEA、WebStorm 或其他 JetBrains IDE 中打开: ```text File -> Settings -> Settings Sync ``` 登录同一个 JetBrains 账号,启用同步。 - [ ] **Step 2:VS Code 启用 Settings Sync** 在 VS Code 中执行: ```text Manage -> Settings Sync is On ``` 登录同一个 GitHub 或 Microsoft 账号。 - [ ] **Step 3:不要用 Syncthing 同步 JetBrains 整个配置目录** 不要同步整个: ```text %APPDATA%\JetBrains ``` 原因:其中包含缓存、索引、机器路径和可能的平台特定状态,跨机器直接同步容易导致 IDE 异常。 ### Task 25:同步 Windows Terminal 配置 **操作位置:** 如果两端都使用 Microsoft Store 版 Windows Terminal。 - [ ] **Step 1:确认配置路径** 笔记本路径: ```text C:\Users\wddsh\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState ``` 台式机路径: ```text C:\Users\wdd\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState ``` - [ ] **Step 2:在 Syncthing 添加独立文件夹** 笔记本添加: ```text Folder Label: Windows Terminal Folder ID: windows-terminal-settings Folder Path: C:\Users\wddsh\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState Folder Type: Send & Receive ``` 共享给 `Desktop-wdd`。 - [ ] **Step 3:台式机接受共享** 填写: ```text Folder Path: C:\Users\wdd\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState Folder Type: Send & Receive ``` - [ ] **Step 4:配置 `.stignore`** 两端的 `LocalState\.stignore` 写入: ```gitignore *.tmp *.log *.bak ``` ### Task 26:一次性迁移 Git 全局配置 **操作位置:** 笔记本导出,台式机导入。 - [ ] **Step 1:在笔记本查看 Git 全局配置** 人工执行: ```powershell git config --global --list --show-origin ``` - [ ] **Step 2:在笔记本复制全局配置文件** 全局配置文件通常为: ```text C:\Users\wddsh\.gitconfig ``` 人工复制到台式机: ```text C:\Users\wdd\.gitconfig ``` - [ ] **Step 3:台式机审阅绝对路径** 在台式机 PowerShell 执行: ```powershell notepad "C:\Users\wdd\.gitconfig" ``` 检查并修正包含 `C:\Users\wddsh` 的路径。 ### Task 27:一次性迁移 SSH 配置 **操作位置:** 手动操作,不建议 Syncthing 长期同步私钥。 - [ ] **Step 1:复制 SSH config,不复制私钥前先评估** 可复制: ```text C:\Users\wddsh\.ssh\config ``` 到: ```text C:\Users\wdd\.ssh\config ``` - [ ] **Step 2:私钥按需迁移** 如果确实需要同一套私钥,在台式机创建: ```powershell New-Item -ItemType Directory -Force -Path "C:\Users\wdd\.ssh" ``` 然后手动复制: ```text C:\Users\wddsh\.ssh\id_ed25519 C:\Users\wddsh\.ssh\id_ed25519.pub ``` 到: ```text C:\Users\wdd\.ssh\ ``` - [ ] **Step 3:修正权限** 在台式机 PowerShell 执行: ```powershell icacls "C:\Users\wdd\.ssh\id_ed25519" /inheritance:r icacls "C:\Users\wdd\.ssh\id_ed25519" /grant:r "$env:USERNAME:F" ``` --- ## 8. Phase 7:Syncthing 自启动与运行参数 ### Task 28:优先用任务计划程序随用户登录启动 **操作位置:** 笔记本和台式机都执行。 - [ ] **Step 1:打开任务计划程序** 按 `Win + R`,输入: ```text taskschd.msc ``` - [ ] **Step 2:创建任务** 点击: ```text Task Scheduler Library -> Create Task ``` General 页填写: ```text Name: Syncthing Run only when user is logged on ``` - [ ] **Step 3:设置触发器** Triggers 页点击 New: ```text Begin the task: At log on Specific user: 当前用户 ``` - [ ] **Step 4:设置动作** Actions 页点击 New: ```text Action: Start a program Program/script: C:\Tools\Syncthing\syncthing.exe Add arguments: --no-console --no-browser --logfile="C:\Tools\Syncthing\syncthing.log" --logflags=3 Start in: C:\Tools\Syncthing ``` > [!TIP] > `--logfile` 将 Syncthing 运行日志持久化到文件,便于事后排查同步异常。`--logflags=3` 输出日期+时间前缀。日志文件会随运行时间增长,可在每月巡检时检查大小或清空。 - [ ] **Step 5:设置条件** Conditions 页: ```text 取消勾选 Start the task only if the computer is on AC power 取消勾选 Stop if the computer switches to battery power ``` - [ ] **Step 6:设置运行策略** Settings 页: ```text 勾选 Allow task to be run on demand 勾选 Run task as soon as possible after a scheduled start is missed 取消勾选 Stop the task if it runs longer than ``` - [ ] **Step 7:保存并测试** 保存任务后,右键任务选择: ```text Run ``` 打开: ```text http://127.0.0.1:8384 ``` 预期结果:Syncthing Web UI 可访问。 ### Task 29:不优先使用 Windows 服务模式 本方案不把 NSSM 服务模式作为默认做法。原因: - 当前是个人工作站场景,不是无人值守服务器。 - Syncthing 官方文档也更推荐终端用户随登录启动。 - 服务模式需要额外处理运行用户、GUI 密码、文件权限和安全边界。 只有在台式机长期无人登录但仍需同步时,才考虑 NSSM 服务模式。 ### Task 29a:配置 Syncthing 全局安全参数 **操作位置:** 笔记本和台式机都执行。 - [ ] **Step 1:设置最小磁盘剩余空间** 在 Syncthing Web UI 中打开: ```text Actions -> Settings -> General ``` 找到 `Minimum Free Disk Space`,设置为: ```text 5 % ``` 保存。此参数确保磁盘空间不足时 Syncthing 自动暂停同步,防止写满系统盘导致系统异常。 - [ ] **Step 2:确认日志文件已生成** Task 28 中已通过启动参数 `--logfile` 启用日志持久化。确认日志文件存在: ```powershell Test-Path "C:\Tools\Syncthing\syncthing.log" ``` 预期结果:`True`。 如日志文件增长过大,可在巡检时清空: ```powershell Clear-Content "C:\Tools\Syncthing\syncthing.log" ``` --- ## 9. Phase 8:监控、巡检与冲突处理 ### Task 30:每日快速巡检 **操作位置:** 任意一台机器。 - [ ] **Step 1:打开 Syncthing Web UI** 访问: ```text http://127.0.0.1:8384 ``` - [ ] **Step 2:检查三个状态** 确认: ```text Remote Device: Connected WeChat xwechat_files: Up to Date IdeaProjects: Up to Date ``` 如果 Windows Terminal 配置也纳入同步,确认: ```text Windows Terminal: Up to Date ``` - [ ] **Step 3:检查 Recent Changes** 点击: ```text Recent Changes ``` 确认没有异常的大批量删除。 ### Task 31:每周冲突文件检查 **操作位置:** 笔记本和台式机。 - [ ] **Step 1:查找冲突文件** 笔记本 PowerShell: ```powershell Get-ChildItem "C:\Users\wddsh\xwechat_files","C:\Users\wddsh\Documents\IdeaProjects" -Recurse -Force -Filter "*sync-conflict*" -ErrorAction SilentlyContinue ``` 台式机 PowerShell: ```powershell Get-ChildItem "C:\Users\wdd\xwechat_files","C:\Users\wdd\Documents\IdeaProjects" -Recurse -Force -Filter "*sync-conflict*" -ErrorAction SilentlyContinue ``` - [ ] **Step 2:处理 Git 冲突文件** 如果冲突文件出现在 Git 仓库中: 1. 打开原文件和 `*.sync-conflict-*` 文件。 2. 使用 IDE diff 工具比较内容。 3. 把需要保留的内容合并到原文件。 4. 删除 `*.sync-conflict-*` 文件。 5. 执行: ```powershell git status git diff git add -A git commit -m "fix: resolve syncthing conflict" ``` - [ ] **Step 3:处理微信冲突文件** 微信数据冲突不要手动改数据库文件。处理规则: 1. 如果冲突文件是图片、视频、语音等媒体文件,保留较新的或两者都保留。 2. 如果冲突文件是数据库文件,先停止两端微信和 Syncthing。 3. 从 `.stversions` 中找出最近正常版本。 4. 复制出备份后,再决定恢复哪一份。 5. 不确定时,不删除任何数据库冲突文件。 ### Task 32:每月容量检查 **操作位置:** 两台机器。 - [ ] **Step 1:检查 C 盘空间** PowerShell: ```powershell Get-PSDrive C | Select-Object Name,Used,Free ``` - [ ] **Step 2:检查 Syncthing 版本目录** 笔记本: ```powershell Get-ChildItem "C:\Users\wddsh\xwechat_files\.stversions","C:\Users\wddsh\Documents\IdeaProjects\.stversions" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum ``` 台式机: ```powershell Get-ChildItem "C:\Users\wdd\xwechat_files\.stversions","C:\Users\wdd\Documents\IdeaProjects\.stversions" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum ``` - [ ] **Step 3:空间不足时先降低版本保留天数** 在 Syncthing Web UI 中进入对应文件夹: ```text Edit -> File Versioning -> Maximum Age ``` 从: ```text 30 days ``` 临时调整为: ```text 14 days ``` 不要直接删除正在使用中的同步目录。 --- ## 10. Phase 9:手动触发 Syncthing 重新扫描 ### Task 33:从 Web UI 手动扫描 **操作位置:** 任意一端。 - [ ] **Step 1:打开文件夹菜单** 在 Syncthing Web UI 中点击目标文件夹。 - [ ] **Step 2:点击 Rescan** 等待状态从 `Scanning` 变为: ```text Up to Date ``` ### Task 34:准备 PowerShell API 扫描脚本 **操作位置:** 两台机器,可选。 - [ ] **Step 1:获取 API Key** Syncthing Web UI: ```text Actions -> Settings -> General -> API Key ``` 复制 API Key。 - [ ] **Step 2:创建脚本目录** PowerShell 人工执行: ```powershell New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\syncthing-scripts" ``` - [ ] **Step 3:创建 `rescan-wechat.ps1`** 用记事本创建: ```text %USERPROFILE%\syncthing-scripts\rescan-wechat.ps1 ``` 写入: ```powershell $ErrorActionPreference = "Stop" $apiKey = Read-Host "Paste this machine's Syncthing API Key" $headers = @{ "X-API-Key" = $apiKey } $uri = "http://127.0.0.1:8384/rest/db/scan?folder=wechat-xwechat-files" Invoke-RestMethod -Method Post -Uri $uri -Headers $headers ``` - [ ] **Step 4:创建 `rescan-ideaprojects.ps1`** 用记事本创建: ```text %USERPROFILE%\syncthing-scripts\rescan-ideaprojects.ps1 ``` 写入: ```powershell $ErrorActionPreference = "Stop" $apiKey = Read-Host "Paste this machine's Syncthing API Key" $headers = @{ "X-API-Key" = $apiKey } $uri = "http://127.0.0.1:8384/rest/db/scan?folder=ideaprojects" Invoke-RestMethod -Method Post -Uri $uri -Headers $headers ``` - [ ] **Step 5:手动触发扫描** PowerShell 人工执行: ```powershell powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\syncthing-scripts\rescan-wechat.ps1" powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\syncthing-scripts\rescan-ideaprojects.ps1" ``` 预期结果:返回 JSON 或空响应,Web UI 中对应文件夹进入扫描状态。 --- ## 11. 回滚方案 ### Task 35:暂停 Syncthing 同步 **操作位置:** 两端 Syncthing Web UI。 - [ ] **Step 1:暂停远程设备** 对 Remote Device 点击: ```text Pause ``` - [ ] **Step 2:暂停问题文件夹** 对问题文件夹点击: ```text Pause ``` ### Task 36:从预实施备份恢复台式机目录 **操作位置:** 台式机。 - [ ] **Step 1:恢复微信目录** 确认 Syncthing 已暂停后,人工执行: ```powershell robocopy "C:\Users\wdd\sync-preflight-backup\xwechat_files" "C:\Users\wdd\xwechat_files" /MIR /COPY:DAT /DCOPY:DAT /R:1 /W:1 /XJ ``` - [ ] **Step 2:恢复 Git 项目目录** 确认 Syncthing 已暂停后,人工执行: ```powershell robocopy "C:\Users\wdd\sync-preflight-backup\IdeaProjects" "C:\Users\wdd\Documents\IdeaProjects" /MIR /COPY:DAT /DCOPY:DAT /R:1 /W:1 /XJ ``` - [ ] **Step 3:删除 Syncthing 文件夹配置** 在 Syncthing Web UI 中对问题文件夹选择: ```text Edit -> Remove ``` 只移除 Syncthing 配置,不手动删除实际数据目录。 ### Task 37:从 Syncthing 版本目录恢复单个文件 **操作位置:** 文件所在机器。 - [ ] **Step 1:打开版本目录** 微信版本目录通常在: ```text C:\Users\wddsh\xwechat_files\.stversions C:\Users\wdd\xwechat_files\.stversions ``` Git 项目版本目录通常在: ```text C:\Users\wddsh\Documents\IdeaProjects\.stversions C:\Users\wdd\Documents\IdeaProjects\.stversions ``` - [ ] **Step 2:复制目标版本到临时目录** 在资源管理器中打开 `.stversions`,按文件名和修改时间找到要恢复的版本,先复制到: ```powershell New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\restore-review" ``` 不要剪切,不要直接覆盖原文件。 - [ ] **Step 3:人工 diff 后恢复** 确认内容正确后,再复制回原路径。不要直接在 `.stversions` 中编辑文件。 --- ## 12. 最终验收清单 - [ ] 两台机器 Syncthing 都能访问 `http://127.0.0.1:8384`。 - [ ] 两台机器互相显示 `Connected`,并且不是 Relay 优先连接。 - [ ] `WeChat xwechat_files` 两端都是 `Up to Date`。 - [ ] 微信测试文件能笔记本到台式机、台式机到笔记本双向同步。 - [ ] `IdeaProjects` 两端都是 `Up to Date`。 - [ ] Git 项目测试文件能双向同步。 - [ ] `.stignore` 已存在于微信目录和 `IdeaProjects` 根目录。 - [ ] 微信和 Git 文件夹都启用了 Staggered File Versioning,保留 30 天。 - [ ] Syncthing 已通过任务计划程序随用户登录启动。 - [ ] 切换微信登录设备前,已经形成“退出微信 -> 等待 Up to Date -> 另一端登录”的习惯。 - [ ] 切换 Git 开发机器前,已经形成“commit/stash -> 等待 Up to Date -> 另一端 git status”的习惯。 --- ## 13. 参考依据 - 原始方案:`35-黑苹果DELL/7-数据同步备份-高级解决方案.md` - 微信 rsync 基础脚本:`35-黑苹果DELL/6-微信数据备份.md` - Syncthing 下载页:`https://syncthing.net/downloads/` - Syncthing Windows 自启动说明:`https://docs.syncthing.net/users/autostart.html` - Syncthing 防火墙与直连排查:`https://docs.syncthing.net/users/firewall.html` - Syncthing 忽略规则:`https://docs.syncthing.net/users/ignoring.html` - Syncthing 文件版本控制:`https://docs.syncthing.net/users/versioning.html` - Syncthing 手动扫描 API:`https://docs.syncthing.net/rest/db-scan-post.html`