[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"post-利用mutagen实现win与linux远程秒级双向工作区文件同步":3},{"post":4,"prev_post":28,"next_post":29},{"id":5,"title":6,"slug":7,"summary":8,"cover_url":9,"status":10,"is_pinned":11,"view_count":12,"published_at":13,"created_at":13,"updated_at":14,"author":15,"category":21,"tags":24,"content_md":25,"content_html":26,"comment_count":27},5,"利用Mutagen实现Win与Linux远程秒级双向工作区文件同步","利用mutagen实现win与linux远程秒级双向工作区文件同步","一种本地Win与远程Linux服务器的工作区文件双向秒级同步方法，远程部署的Harness工具如DSH工作区可用，本文章根据经验讲述教程与潜在问题解决方法。","\u002Fapi\u002Fdownloads\u002Ffiles\u002F124\u002Fthumbnail?size=800","published",false,59,"2026-09-09T10:57:44","2026-09-11T08:41:29.338954+00:00",{"id":16,"username":17,"display_name":18,"avatar_url":19,"bio":20},2,"FwindEmi","狐风轩汐","\u002Fuploads\u002Favatars\u002F2_4c71188069e24466897fa1b0fdb6a48b.png","我是屑狐狸",{"id":22,"name":23,"slug":23},1,"技术教程",[],"# Mutagen 实现 Windows ↔ Linux 远程双向文件同步：完整教程与踩坑实录\n\n> 本文记录了我在实际项目中使用 Mutagen 搭建 Windows 本地开发机与远程 Linux VM 之间双向文件同步的完整过程，包括方案选型、安装配置、开机自启、踩过的坑、与 AI Agent 框架的协同工作流，以及最终稳定的运维方案。目前该方案已在多个项目目录、超过 5GB 文件的规模下持续稳定运行。\n\n## 为什么选择 Mutagen\n\n我的工作流是这样的：本地 Windows 上用 IDE 写代码，远程 Linux VM 上跑构建、测试和部署。两边的文件需要实时保持一致——本地改了代码，远程要立刻看到；有时候也在远程改配置，本地也要同步回来。\n\n传统方案有几个：\n\n- **SSHFS \u002F NFS 挂载远程目录**：网络延迟直接变成每次文件读写的延迟，IDE 打开一个项目要等十几秒。在 Windows 上 SSHFS 的稳定性也堪忧。\n- **rsync 手动推送**：每次改完手动 `rsync` 一下，忘了推就白干。而且不支持反向同步。\n- **Git 做中转**：改一下就 commit + push + pull，太重了，而且不想提交的调试改动也得同步。\n- **VS Code Remote \u002F JetBrains Gateway**：它们确实能用，但有些构建工具链必须跑在远程，有些编辑器不支持远程模式，而且这种方案把编辑器和远程强绑定了。\n\n**Mutagen** 的思路完全不同：它是一个独立的文件同步守护进程，在本机和远程各跑一个 agent，自动监测文件变化并双向同步。不依赖任何编辑器，不改变你的工作习惯，文件系统操作延迟始终是本地级别的。\n\n核心特点：\n\n- 增量同步（基于内容哈希，只传变化的部分）\n- 支持双向、单向多种同步模式\n- 内置冲突检测和暂停机制\n- SSH 原生传输，无需额外端口\n- 跨平台：Windows \u002F macOS \u002F Linux 互通\n\n---\n\n## 环境概况\n\n先看一下示例环境，方便后面的命令对照：\n\n\n| 角色              | 系统           | 说明                                        |\n| ----------------- | -------------- | ------------------------------------------- |\n| **Alpha（本地）** | Windows 11     | 开发机，IDE 在这里                          |\n| **Beta（远程）**  | Debian 13 (VM) | 构建和运行环境，通过 WireGuard VPN 内网访问 |\n\n远程 VM 的网络地址是 WireGuard 内网 IP（示例中用 `10.0.0.50`），SSH 可达即可，Mutagen 不需要额外开端口。\n\n假设我们同步以下几个项目目录：\n\n\n| 会话名      | 本地路径（示例）     | 远程路径（示例）                            | 忽略规则                                       |\n| ----------- | -------------------- | ------------------------------------------- | ---------------------------------------------- |\n| `backend`   | `D:\\dev\\my-backend`  | `root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-backend`  | `node_modules target build dist .gradle`       |\n| `frontend`  | `D:\\dev\\my-frontend` | `root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-frontend` | `node_modules`                                 |\n| `docs`      | `D:\\dev\\docs-site`   | `root@10.0.0.50:\u002Froot\u002Fprojects\u002Fdocs-site`   | `node_modules`                                 |\n| `workspace` | `D:\\dev\\workspace`   | `root@10.0.0.50:\u002Froot\u002Fworkspace`            | `node_modules target build dist .gradle *.jar` |\n\n所有会话都启用了 `--ignore-vcs`（忽略 `.git` 目录——版本控制的东西不该靠文件同步来传）。\n\n---\n\n## 安装 Mutagen\n\n### Windows（本地）\n\n从 [Mutagen 官方 Release 页面](https:\u002F\u002Fgithub.com\u002Fmutagen-io\u002Fmutagen\u002Freleases) 下载对应平台的压缩包，解压后把 `mutagen.exe` 放到 PATH 里的某个目录。\n\n比如放在 `C:\\Users\\\u003C用户名>\\.local\\bin\\`，然后把这个目录加到系统 PATH：\n\n```powershell\n# PowerShell 中执行\n$userPath = [Environment]::GetEnvironmentVariable(\"PATH\", \"User\")\nif ($userPath -notlike \"*\\.local\\bin*\") {\n    [Environment]::SetEnvironmentVariable(\"PATH\", \"$userPath;C:\\Users\\$env:USERNAME\\.local\\bin\", \"User\")\n}\n```\n\n验证：\n\n```bash\nmutagen version\n# 应输出类似：0.18.1\n```\n\n### Linux（远程）\n\n远程 VM 不需要手动安装 Mutagen——当你创建同步会话时，本机的 Mutagen 会自动通过 SSH 把对应版本的 agent 上传到远程并启动。这是 Mutagen 的一个很贴心的设计。\n\n**前提条件**：远程 SSH 可达，且登录用户有读写目标目录的权限。\n\n---\n\n## 创建同步会话\n\n### 基本命令\n\n```bash\nmutagen sync create \\\n  -n \u003C会话名> \\\n  -m two-way-safe \\\n  --ignore-vcs \\\n  -i node_modules \\\n  -i target \\\n  -i build \\\n  -i dist \\\n  -i .gradle \\\n  \"\u003C本地路径>\" \\\n  \"\u003C用户名>@\u003C远程地址>:\u003C远程路径>\"\n```\n\n参数说明：\n\n- `-n`：会话名称，后续管理用这个名字引用\n- `-m`：同步模式（**关键参数，下面详细讲**）\n- `--ignore-vcs`：忽略 `.git` \u002F `.hg` \u002F `.svn` 等版本控制目录\n- `-i`：忽略规则，每条一个 `-i` 参数，支持 glob 模式\n- 最后两个位置参数：Alpha（本地）和 Beta（远程）的路径\n\n### 示例\n\n以一个后端项目为例：\n\n```bash\nmutagen sync create \\\n  -n backend \\\n  -m two-way-safe \\\n  --ignore-vcs \\\n  -i node_modules -i target -i build -i dist -i .gradle \\\n  \"D:\\dev\\my-backend\" \\\n  \"root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-backend\"\n```\n\n创建后，Mutagen 会立即开始首次对账（reconcile）——把两端文件差异同步一致。根据规模不同，首次对账可能需要几十秒到几分钟。之后进入 `Watching for changes` 状态，只同步增量变化。\n\n### 验证会话状态\n\n```bash\nmutagen sync list\n```\n\n正常状态长这样：\n\n```\nName: backend\nAlpha:\n    URL: D:\\dev\\my-backend\n    Connected: Yes\n    Synchronizable contents:\n        1200 directories\n        8500 files (280 MB)\nBeta:\n    URL: root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-backend\n    Connected: Yes\n    Synchronizable contents:\n        1200 directories\n        8500 files (280 MB)\nStatus: Watching for changes\n```\n\n`Status: Watching for changes` = 正常工作中。Alpha 和 Beta 的 `Connected` 都应该是 `Yes`。\n\n---\n\n## 同步模式选择：最关键的一步\n\nMutagen 提供三种同步模式。**选错模式可能导致数据静默丢失**，这是我在实际使用中踩过的最大的坑。\n\n### 三种模式对比\n\n\n| 模式               | 冲突行为                                                       | 适用场景                 |\n| ------------------ | -------------------------------------------------------------- | ------------------------ |\n| `two-way-safe`     | 双方都改了同一个文件 → **暂停同步，列出冲突，等人裁决**       | **推荐默认**，双向工作区 |\n| `two-way-resolved` | 双方都改了同一个文件 → **Alpha（本地）永远赢**，静默覆盖 Beta | 仅当确定本地永远权威     |\n| `one-way-replica`  | Beta 变成 Alpha 的精确镜像，Beta 上多余的文件直接删除          | 单向分发，如部署静态资源 |\n\n### 为什么推荐 two-way-safe\n\n`two-way-resolved` 看起来很方便——冲突自动解决，不会有冲突暂停的烦恼。但它有个致命问题：**当 daemon 长时间没运行（比如笔记本合盖一晚上），重新启动后会立即开始全量对账。此时如果某个文件两端都改过，Alpha 的版本会静默覆盖 Beta 的版本——没有任何提示**。\n\n这意味着你在远程辛辛苦苦改了一晚上的文件，本地一开机就被旧版覆盖了，而且你完全不知道发生了什么。\n\n`two-way-safe` 在同样的场景下会**暂停同步并列出冲突**，你看到冲突后可以手动决定保留哪个版本。不会丢数据。\n\n**结论：凡是双向都可能修改的工作区，一律用 `two-way-safe`。**\n\n> 这一条建议是用血泪换来的——下文踩坑部分会讲这个事故的完整经过。\n\n### 冲突出现后怎么处理\n\n使用 `two-way-safe` 时，如果两端同时修改了同一个文件，Mutagen 会暂停该会话。`mutagen sync list` 会显示类似：\n\n```\nConflicts:\n    path\u002Fto\u002Ffile.yaml (both modified)\nStatus: Watching for changes\n```\n\n此时你需要手动决定保留哪个版本：\n\n```bash\n# 1. 暂停会话\nmutagen sync pause \u003C会话名>\n\n# 2. 比较两端文件内容，决定保留哪个\n#    可以 SSH 到远程 diff，或在本地用工具对比\n\n# 3. 把\"输\"的版本手动覆盖成\"赢\"的版本\n#    比如决定用远程版本：scp 远程版本到本地\n#    或者决定用本地版本：scp 本地版本到远程\n\n# 4. 恢复会话\nmutagen sync resume \u003C会话名>\n\n# 5. 验证冲突已消失\nmutagen sync list \u003C会话名>\n```\n\n冲突处理是手动的，但这正是 `two-way-safe` 的价值——它给你选择权，而不是自动帮你做一个可能错误的覆盖决定。\n\n---\n\n## Daemon 开机自启（Windows）\n\nMutagen 的同步依赖一个后台 daemon 进程。在 Windows 上，这个 daemon 不会自动启动，需要你手动 `mutagen daemon start` 或者配置开机自启。\n\n### 自启脚本\n\n在 Windows 启动目录放一个 `.bat` 脚本：\n\n**路径**：`C:\\Users\\\u003C用户名>\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mutagen_autostart.bat`\n\n**内容**：\n\n```bat\n@echo off\nREM Mutagen daemon 自启脚本\n\"C:\\Users\\\u003C用户名>\\.local\\bin\\mutagen.exe\" daemon start\n```\n\n就这么简单。`daemon start` 会让 Mutagen 自我后台化（detach 到后台），不依赖命令行窗口。\n\n### 一个致命的陷阱：start \u002FB\n\n如果你在网上搜 Windows 开机自启的写法，很多教程会用 `start \u002FB`：\n\n```bat\n@echo off\nstart \u002FB \"\" \"C:\\Users\\\u003C用户名>\\.local\\bin\\mutagen.exe\" daemon\n```\n\n**这是错的。** `start \u002FB` 启动的进程会挂在当前控制台上。当启动脚本执行完毕、控制台窗口关闭时，Windows 会发送 `CTRL_CLOSE_EVENT`，daemon 在启动几秒后就被杀死了。\n\n现象：开机后看似\"自启了\"，但 `mutagen sync list` 输出 `Attempting to start Mutagen daemon...`（说明 daemon 根本没在运行）。在你发现并手动拉起 daemon 之前，所有远程的改动都积压着——而当你手动启动 daemon 时，积压的改动会一次性对账，如果模式是 `two-way-resolved`，覆盖事故就发生了。\n\n**正确的写法只有一种：直接调用 `mutagen.exe daemon start`。** Mutagen 内部会处理自我后台化。\n\n### 安全软件拦截\n\n部分安全软件（如火绒）会对启动目录的文件写入进行拦截。如果你发现脚本写不进去或内容被回滚，可以改用先写到临时目录再复制的方式：\n\n```bash\n# 在 bash \u002F git-bash 中执行\ncat > \"$LOCALAPPDATA\u002FTemp\u002Fmutagen_autostart.bat\" \u003C\u003C 'EOF'\n@echo off\n\"C:\\Users\\\u003C用户名>\\.local\\bin\\mutagen.exe\" daemon start\nEOF\ncp -f \"$LOCALAPPDATA\u002FTemp\u002Fmutagen_autostart.bat\" \\\n  \"$APPDATA\u002FMicrosoft\u002FWindows\u002FStart Menu\u002FPrograms\u002FStartup\u002Fmutagen_autostart.bat\"\n```\n\n写完后读回验证内容没被篡改。\n\n---\n\n## 忽略规则配置\n\n### 命令行 vs .mutagenignore\n\n忽略规则有两个来源，叠加生效：\n\n1. **创建会话时的 `-i` 参数**：写入会话配置，之后一直生效\n2. **同步根目录下的 `.mutagenignore` 文件**：动态读取，随时改随时生效，不需要重建会话\n\n推荐的做法：**通用的、项目类型固定的忽略规则**（如 `node_modules`、`target`、`build`）在创建会话时用 `-i` 写死；**特定环境产生的临时目录**（如浏览器调试 profile、缓存目录）用 `.mutagenignore` 补充。\n\n### 常见项目的忽略规则\n\n\n| 项目类型       | 推荐忽略                                                              |\n| -------------- | --------------------------------------------------------------------- |\n| Java \u002F Maven   | `target` `build` `dist` `.gradle`                                     |\n| Node.js \u002F 前端 | `node_modules` `.nuxt` `.output` `.vitepress\u002Fcache` `.vitepress\u002Fdist` |\n| Python         | `__pycache__` `.venv` `*.pyc`                                         |\n| 通用           | `.git`（用 `--ignore-vcs` 更优雅）                                    |\n\n### .mutagenignore 示例\n\n在同步根目录下创建 `.mutagenignore` 文件：\n\n```\n# 浏览器调试 profile（会持续写文件 + 锁文件，不能同步）\n.chrome-debug\u002F\n\n# 本地缓存 \u002F 日志\n.cache\u002F\nlogs\u002F\n\n# 大体积二进制\n*.jar\n```\n\n修改后不需要重启 daemon 或重建会话，Mutagen 会自动重新读取。\n\n---\n\n## 与 IDE 远程开发的配合\n\nMutagen 不排斥 IDE 的远程开发功能，两者可以互补使用：\n\n- **VS Code Remote-SSH \u002F JetBrains Gateway**：适合直接在远程编辑单个项目。但如果你的工作流涉及多个工具链交叉（比如同时打开多个项目、使用远程终端构建），Mutagen 的纯文件同步方式更灵活。\n- **Mutagen + 本地 IDE**：文件始终在本地，IDE 索引、搜索、跳转都是本地速度。远程只负责构建和运行。这种方式在 Windows + Linux 混合开发环境中体验最好。\n\n我个人的选择是后者：全部使用本地 IDE 编辑，Mutagen 负责把文件实时推到远程，远程跑构建和部署。这种方式下编辑体验完全没有远程延迟，而且可以随时切换 IDE（今天用 IntelliJ，明天用 VS Code，不受影响）。\n\n---\n\n## 实战：与 AI Agent 框架的协同工作流\n\n文件同步本身只是基础设施。它的真正价值体现在具体的工作流上——下面以我在远程 VM 上运行 DSH（DeepSeek Harness）的实际场景为例，讲讲 Mutagen 是怎么把\"改远程配置\"这件事变得优雅的。\n\n### 背景\n\nDSH 是一个跑在远程 Linux VM 上的 AI Agent 框架（基于 Cordis 插件体系），通过 systemd 管理服务。它的核心配置文件散落在几个位置：\n\n- `~\u002F.dsh\u002Fsettings.yaml` — 全局设置\n- `~\u002F.dsh\u002Fprofiles\u002Fweb\u002Fpackage.json` — profile 的依赖声明\n- `~\u002F.dsh\u002Fprofiles\u002Fweb\u002Fcordis.patch.yml` — 插件加载与 LLM 路由配置\n- `~\u002F.dsh\u002Fprofiles\u002F\u003C插件名>\u002F` — 自定义插件的源码\n\n日常运维包括：调整 LLM 路由、升级插件版本、修改自定义插件代码、调整 nginx 反代配置等。\n\n### 痛点：远程编辑体验极差\n\n这些配置文件如果直接在远程用 `vim` 或 `nano` 编辑，体验非常糟糕——尤其 `cordis.patch.yml` 这种嵌套很深的 YAML，在终端里改简直是折磨。你想在本地用 VS Code 打开？以前要么 SSHFS 挂载（卡顿），要么手动 scp 来回传（繁琐）。\n\n### 解决方案：Mutagen 同步 DSH 配置目录\n\n把 DSH 的配置目录纳入 Mutagen 同步范围，一切就简单了：\n\n```bash\nmutagen sync create \\\n  -n dsh-config \\\n  -m two-way-safe \\\n  --ignore-vcs \\\n  -i node_modules -i .pnpm \\\n  -i sessions \\\n  \"D:\\dev\\dsh-remote\" \\\n  \"root@10.0.0.50:\u002Froot\u002F.dsh\"\n```\n\n**关键忽略规则**：\n\n- `node_modules` \u002F `.pnpm`：这些是 pnpm 管理的依赖目录，体积大且平台相关，必须在远程用 `pnpm install` 安装，不能同步\n- `sessions`：DSH 的会话数据（zstd 压缩的 jsonl），属于运行时状态，不需要在本地编辑\n\n同步建立后，`settings.yaml`、`cordis.patch.yml`、`package.json`、自定义插件源码全部可以在本地 IDE 中编辑，保存即同步到远程。\n\n### 典型工作流：升级 DSH 插件\n\n以升级一个插件版本为例，完整流程：\n\n```\n┌─────────────────────────────┐         ┌─────────────────────────────┐\n│       本地 (Windows)         │         │       远程 VM (Linux)        │\n│                             │         │                             │\n│  1. IDE 打开 package.json   │         │                             │\n│  2. 改版本号 0.3.5 → 0.3.6  │         │                             │\n│  3. Ctrl+S 保存             │──┐      │                             │\n│                             │  │      │                             │\n│  8. IDE 查看远程文件        │  │      │  4. Mutagen 收到新文件      │\n│     确认同步到位            │←─┼──────│  5. SSH 进去 pnpm install   │\n│                             │  │      │  6. systemctl restart dsh   │\n│                             │  │      │  7. journalctl 查日志确认   │\n└─────────────────────────────┘  │      └─────────────────────────────┘\n                           Mutagen 自动同步\n```\n\n步骤展开：\n\n1. **本地改 `package.json`**：把插件版本号从 `0.3.5` 改成 `0.3.6`，保存\n2. **Mutagen 自动同步**：几秒内远程的 `package.json` 就更新了\n3. **SSH 进远程执行安装**：\n\n   ```bash\n   cd ~\u002F.dsh\u002Fprofiles\u002Fweb\n   rm -f pnpm-lock.yaml\n   pnpm install --no-frozen-lockfile\n   ```\n4. **重启服务**：\n\n   ```bash\n   systemctl restart dsh-web\n   journalctl -u dsh-web --no-pager -n 50\n   ```\n5. **验证**：看 journalctl 输出有没有报错。如果一切正常，`curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\" http:\u002F\u002F127.0.0.1:3080` 应返回 200\n\n整个过程中，编辑配置在本地完成（IDE 体验），安装和重启在远程完成（命令行操作），Mutagen 在中间无缝衔接。\n\n### 典型工作流：修改自定义插件源码\n\n自定义 DSH 插件通常放在 `~\u002F.dsh\u002Fprofiles\u002F` 下（通过 `file:` 依赖挂载进 profile）。开发流程：\n\n1. **本地 IDE 编辑插件源码**（TypeScript \u002F JavaScript），保存即同步\n2. **远程执行构建**：`cd ~\u002F.dsh\u002Fprofiles\u002Fmy-plugin && pnpm build`\n3. **重载 DSH**：`systemctl restart dsh-web`\n\n得益于双向同步，如果我在远程通过 DSH 的 Web UI 做了一些配置变更（比如调整了 LLM 路由参数），这些变更也会同步回本地，下次编辑时看到的就是最新版本。\n\n### 典型工作流：nginx 反代配置\n\nDSH Web 需要经过 nginx 反向代理对外提供服务。nginx 配置文件也可以纳入同步范围：\n\n```bash\nmutagen sync create \\\n  -n nginx-conf \\\n  -m two-way-safe \\\n  --ignore-vcs \\\n  \"D:\\dev\\nginx-confs\" \\\n  \"root@10.0.0.50:\u002Fetc\u002Fnginx\u002Fconf.d\"\n```\n\n改完配置保存，SSH 进去 `nginx -t && systemctl reload nginx` 即可。不用 scp，不用复制粘贴。\n\n> **注意**：nginx 配置中如果包含 SSL 证书路径等敏感信息，要注意同步目录的访问控制。也可以选择只同步非敏感的 server block 配置文件。\n\n### 为什么不用 Git 管理 DSH 配置？\n\n你可能会问：这些配置文件用 Git 管理不也可以吗？\n\n可以，但不适合所有场景：\n\n1. **`cordis.patch.yml` 里有 API Key 和 LLM 路由配置**，不适合提交到 Git 仓库（即使私有仓库也不理想）\n2. **频繁的微调**（改个参数、调个超时）不值得每次都走 commit-push-pull 流程\n3. **有些改动是远程产生的**（比如通过 Web UI 调配置），Git 是单向的，需要手动 pull 回来\n\nMutagen 的双向实时同步解决了这些问题——改完就生效，双向自动对齐，不经过任何中间存储。\n\n---\n\n## 常用运维命令速查\n\n```bash\n# 查看所有会话状态\nmutagen sync list\n\n# 查看某个会话的详细信息（模式、忽略规则、冲突等）\nmutagen sync list \u003C会话名> -l\n\n# 暂停会话（修改文件前建议先暂停！）\nmutagen sync pause \u003C会话名>\n\n# 恢复会话\nmutagen sync resume \u003C会话名>\n\n# 强制同步一轮\nmutagen sync flush \u003C会话名>\n\n# 终止会话（不删文件，只是停止同步）\nmutagen sync terminate \u003C会话名>\n\n# 启动 daemon\nmutagen daemon start\n\n# 停止 daemon\nmutagen daemon stop\n```\n\n---\n\n## 踩坑实录\n\n下面是实际使用中遇到的几个关键问题，希望能帮你避开。\n\n### 坑 1：two-way-resolved 静默覆盖远程文件\n\n**现象**：在远程 VM 上修改了一个配置文件（从 9KB 改到 13KB，新增了大量内容）。关机回家，第二天开机后发现本地还是旧版（9KB），而远程也被覆盖回了旧版。一晚上的工作白费了。\n\n**根因**：\n\n1. 开机自启脚本用了错误的 `start \u002FB` 写法，daemon 根本没活着，远程改动全部积压。\n2. 同步模式是 `two-way-resolved`。当手动启动 daemon 后，它开始首次对账。此时本地文件和远程文件都偏离了基线（双方都改过），按照 `two-way-resolved` 的规则——Alpha（本地）永远赢——本地旧版静默覆盖了远程新版。\n\n**取证过程**：\n\nMutagen 同步会保留源文件的修改时间戳。通过对比两端文件的创建时间（Birth time）可以判断同步方向：\n\n```bash\n# 远程查看文件创建时间\nstat \u003C文件路径>\n# 如果 Birth 时间 = daemon 启动时刻 → 该文件是同步时重建的 → 方向是 本地→远程\n```\n\n远程文件 Birth 时间正好等于手动启动 daemon 的时刻，加上本地文件 mtime 从未改变——确认本地旧版赢了。\n\n**修复**：\n\n1. 先暂停会话：`mutagen sync pause \u003C会话名>`\n2. 从构建产物或备份中找回新版文件\n3. 恢复到远程\n4. 拉回本地覆盖旧版\n5. 恢复同步并验证两端文件一致\n\n**预防**：\n\n- **所有双向工作区一律使用 `two-way-safe`**\n- 确保 daemon 开机自启正确工作\n- 如果发现 daemon 积压了大量改动，启动后**先暂停会话检查差异**，确认没有冲突再恢复\n\n### 坑 2：修改同步模式必须重建会话\n\n`mutagen sync configure` 命令**不支持修改同步模式**。如果你想把模式从 `two-way-resolved` 改成 `two-way-safe`，必须终止重建：\n\n```bash\n# 1. 终止旧会话\nmutagen sync terminate \u003C会话名>\n\n# 2. 用新参数重建\nmutagen sync create -n \u003C会话名> -m two-way-safe --ignore-vcs \\\n  -i node_modules -i target ... \\\n  \"\u003C本地路径>\" \"\u003C远程路径>\"\n```\n\n重建后会触发一次完整的首次对账，等 30-60 秒后确认状态正常。\n\n### 坑 3：Daemon 假活——状态正常但双向不同步\n\n**现象**：所有会话显示 `Watching for changes`，`Connected: Yes`，看起来一切正常。但实际双向写入测试发现根本没有同步。`mutagen sync flush` 报 `session is not currently able to synchronize`，`mutagen sync pause` 也卡住不动。\n\n**根因**：同步范围内存在被运行中进程锁定的文件（如浏览器 profile 的 Cache\u002FCookies\u002FSessions 文件、SQLite 数据库文件、杀毒软件正在扫描的文件等）。Mutagen 的扫描器读不到这些文件就一直重试，陷入死循环，无法处理任何实际的同步任务。\n\n**诊断方法**：\n\n```powershell\n# 查看 mutagen 进程的 CPU 累计时间\npowershell -Command \"Get-Process mutagen | Select-Object Id,CPU,StartTime\"\n```\n\n如果 CPU 秒数**持续上涨**（能烧到几千秒），就是死循环扫描锁文件。正常 Watching 状态下 CPU 应该稳定不动。\n\n**修复**：\n\n1. 把锁文件目录加入**两端**的 `.mutagenignore`\n2. 强杀 daemon（`mutagen daemon stop` 在死循环下会卡住，必须 `Stop-Process -Id \u003Cpid> -Force`）\n3. `mutagen daemon start` 重启\n4. 等对账完成后做双向写入测试确认恢复\n\n**预防**：任何会持续写入并锁定文件的本地状态目录（浏览器 profile、缓存目录、本地数据库）都不要放进同步范围。创建会话时就用 `-i` 排除，或事后补进 `.mutagenignore`。\n\n> **DSH 相关提醒**：DSH 的 sessions 目录里的 `.jsonl.zstd` 文件在 DSH 运行期间是被进程持续写入的——同步这个目录会触发上述假活问题。所以上面的示例中我们把 `sessions` 目录排除掉了。\n\n### 坑 4：mutagen sync list 会自动启动 daemon\n\n当 daemon 没在运行时，执行 `mutagen sync list` 会**自动启动 daemon 并立即开始对账**。\n\n这本身是一个方便的设计，但在排查问题时，如果你还没搞清楚两端差异就执行了这个命令，自动对账可能会导致覆盖（特别是 `two-way-resolved` 模式下）。\n\n**排查时的安全做法**：如果你怀疑两端有差异需要排查，但又不想让自动对账破坏现场，可以在排查前先在远程手动暂停会话（通过 SSH 连到远程执行 `mutagen` 命令），或者确保使用的是 `two-way-safe` 模式（冲突会暂停而不是覆盖）。\n\n### 坑 5：首次对账需要时间\n\n创建会话或重建会话后的首次对账不是瞬间完成的。对于几千个文件、几百 MB 的项目，可能需要 30-60 秒。期间状态可能显示 `Staging files on beta` 或 `Scanning`。\n\n在此期间不要慌张地以为出了问题。等状态变成 `Watching for changes` 再判断。如果你的项目特别大（几万文件、几 GB），首次对账可能需要几分钟。\n\n### 坑 6：DSH 升级后 node_modules 不匹配\n\n这是一个 Mutagen + DSH 结合场景下特有的坑。\n\nDSH 升级流程需要在远程执行 `pnpm install`，这会大规模改动 `node_modules` 目录。如果 `node_modules` 没有被正确排除在同步范围外，会出现两种问题：\n\n1. **本地 → 远程同步覆盖了刚装好的依赖**：如果你之前在本地碰过 node_modules（比如 IDE 自动跑了 `npm install`），Mutagen 会把本地版本推到远程，覆盖掉远程 `pnpm install` 的结果\n2. **远程 → 本地同步把平台特定的二进制拉到 Windows**：`node_modules` 里有些包包含平台特定的二进制文件（如 `.node` 扩展），Linux 编译的在 Windows 上不能用\n\n**解法**：创建同步会话时**必须**把 `node_modules` 和 `.pnpm` 加入忽略规则。依赖管理完全交给远程的包管理器，不让 Mutagen 介入。\n\n---\n\n## 最佳实践总结\n\n### 1. 同步模式\n\n**永远使用 `two-way-safe`**，除非你有非常明确的理由选择其他模式。多出来的\"冲突需要手动解决\"的麻烦，比起数据被静默覆盖的灾难来说根本不算什么。\n\n### 2. 忽略规则\n\n- 构建产物、依赖目录一律不同步（`node_modules`、`.pnpm`、`target`、`build`、`dist`、`.gradle`）\n- 版本控制目录用 `--ignore-vcs` 排除\n- 运行时持续写入的目录（浏览器 profile、数据库、会话数据）用 `.mutagenignore` 或 `-i` 排除\n- 大文件二进制（`*.jar`、模型文件）按需排除\n\n### 3. 开机自启\n\n- 使用 `mutagen daemon start` 自我后台化\n- **绝对不要**用 `start \u002FB` 启动\n- 配好后重启验证 daemon 确实在运行\n\n### 4. 日常运维\n\n- 定期 `mutagen sync list` 检查状态\n- 出现 `Conflicts` 时及时处理（见上文冲突处理流程）\n- 远程大改动前可以先 `pause`，改完再 `resume`\n- 修改模式或核心忽略规则需要重建会话\n\n### 5. 安全网\n\n- 重要文件还是要有版本控制（Git）兜底，Mutagen 是同步工具不是备份工具\n- 定期检查 `.mutagenignore` 是否覆盖了所有需要排除的路径\n- 如果使用 WG 等 VPN，确保网络连通性稳定（MTU 配置也可能影响同步效率）\n- 含敏感信息（API Key、密码）的配置文件，注意同步目录的访问控制\n\n### 6. 与服务运维结合\n\n- 同步只解决文件传输，安装 \u002F 构建 \u002F 重启命令仍需 SSH 进远程执行\n- 远程执行命令前，确认 Mutagen 已经完成同步（`mutagen sync list` 看状态）\n- 如果远程有服务在持续写入某个目录（如 DSH 的 sessions），务必排除该目录\n\n---\n\n## 总结\n\nMutagen 解决的核心问题是：**让你在本地编辑文件享受本地速度，同时远程实时拥有一份完全相同的副本，无需手动操作。**\n\n它的安装和使用都很简单，但有几个关键配置需要特别注意：\n\n1. **同步模式选 `two-way-safe`**——这条最重要，选错了可能丢数据\n2. **开机自启用 `daemon start`**——不要用 `start \u002FB`\n3. **忽略规则要完善**——构建产物、依赖目录、运行时状态文件必须排除\n4. **锁文件目录绝对不能进同步范围**——否则 daemon 会假活\n\n配合 DSH 这类远程 AI Agent 框架使用时，Mutagen 让\"本地编辑配置 + 远程运行服务\"的工作流变得极其自然——你只需要专注于编辑，文件传输完全自动化。\n\n配置正确后，Mutagen 是一个非常稳定可靠的双向同步方案。日常几乎感觉不到它的存在——这正是好的基础设施应该有的样子。\n\n---\n\n> 写于 2026 年 9 月，基于 Mutagen 0.18.1 的实际使用经验。\n>","\u003Ch1 id=\"mutagen-windows-linux\">Mutagen 实现 Windows ↔ Linux 远程双向文件同步：完整教程与踩坑实录\u003C\u002Fh1>\n\u003Cblockquote>\n\u003Cp>本文记录了我在实际项目中使用 Mutagen 搭建 Windows 本地开发机与远程 Linux VM 之间双向文件同步的完整过程，包括方案选型、安装配置、开机自启、踩过的坑、与 AI Agent 框架的协同工作流，以及最终稳定的运维方案。目前该方案已在多个项目目录、超过 5GB 文件的规模下持续稳定运行。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"mutagen\">为什么选择 Mutagen\u003C\u002Fh2>\n\u003Cp>我的工作流是这样的：本地 Windows 上用 IDE 写代码，远程 Linux VM 上跑构建、测试和部署。两边的文件需要实时保持一致——本地改了代码，远程要立刻看到；有时候也在远程改配置，本地也要同步回来。\u003C\u002Fp>\n\u003Cp>传统方案有几个：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>SSHFS \u002F NFS 挂载远程目录\u003C\u002Fstrong>：网络延迟直接变成每次文件读写的延迟，IDE 打开一个项目要等十几秒。在 Windows 上 SSHFS 的稳定性也堪忧。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>rsync 手动推送\u003C\u002Fstrong>：每次改完手动 \u003Ccode>rsync\u003C\u002Fcode> 一下，忘了推就白干。而且不支持反向同步。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Git 做中转\u003C\u002Fstrong>：改一下就 commit + push + pull，太重了，而且不想提交的调试改动也得同步。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>VS Code Remote \u002F JetBrains Gateway\u003C\u002Fstrong>：它们确实能用，但有些构建工具链必须跑在远程，有些编辑器不支持远程模式，而且这种方案把编辑器和远程强绑定了。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Mutagen\u003C\u002Fstrong> 的思路完全不同：它是一个独立的文件同步守护进程，在本机和远程各跑一个 agent，自动监测文件变化并双向同步。不依赖任何编辑器，不改变你的工作习惯，文件系统操作延迟始终是本地级别的。\u003C\u002Fp>\n\u003Cp>核心特点：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>增量同步（基于内容哈希，只传变化的部分）\u003C\u002Fli>\n\u003Cli>支持双向、单向多种同步模式\u003C\u002Fli>\n\u003Cli>内置冲突检测和暂停机制\u003C\u002Fli>\n\u003Cli>SSH 原生传输，无需额外端口\u003C\u002Fli>\n\u003Cli>跨平台：Windows \u002F macOS \u002F Linux 互通\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"_1\">环境概况\u003C\u002Fh2>\n\u003Cp>先看一下示例环境，方便后面的命令对照：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>角色\u003C\u002Fth>\n\u003Cth>系统\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Alpha（本地）\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Windows 11\u003C\u002Ftd>\n\u003Ctd>开发机，IDE 在这里\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Beta（远程）\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Debian 13 (VM)\u003C\u002Ftd>\n\u003Ctd>构建和运行环境，通过 WireGuard VPN 内网访问\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>远程 VM 的网络地址是 WireGuard 内网 IP（示例中用 \u003Ccode>10.0.0.50\u003C\u002Fcode>），SSH 可达即可，Mutagen 不需要额外开端口。\u003C\u002Fp>\n\u003Cp>假设我们同步以下几个项目目录：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>会话名\u003C\u002Fth>\n\u003Cth>本地路径（示例）\u003C\u002Fth>\n\u003Cth>远程路径（示例）\u003C\u002Fth>\n\u003Cth>忽略规则\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>backend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D:\\dev\\my-backend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-backend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>node_modules target build dist .gradle\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>frontend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D:\\dev\\my-frontend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-frontend\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>node_modules\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>docs\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D:\\dev\\docs-site\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>root@10.0.0.50:\u002Froot\u002Fprojects\u002Fdocs-site\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>node_modules\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>workspace\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D:\\dev\\workspace\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>root@10.0.0.50:\u002Froot\u002Fworkspace\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>node_modules target build dist .gradle *.jar\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>所有会话都启用了 \u003Ccode>--ignore-vcs\u003C\u002Fcode>（忽略 \u003Ccode>.git\u003C\u002Fcode> 目录——版本控制的东西不该靠文件同步来传）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"mutagen_1\">安装 Mutagen\u003C\u002Fh2>\n\u003Ch3 id=\"windows\">Windows（本地）\u003C\u002Fh3>\n\u003Cp>从 \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmutagen-io\u002Fmutagen\u002Freleases\">Mutagen 官方 Release 页面\u003C\u002Fa> 下载对应平台的压缩包，解压后把 \u003Ccode>mutagen.exe\u003C\u002Fcode> 放到 PATH 里的某个目录。\u003C\u002Fp>\n\u003Cp>比如放在 \u003Ccode>C:\\Users\\&lt;用户名&gt;\\.local\\bin\\\u003C\u002Fcode>，然后把这个目录加到系统 PATH：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c\"># PowerShell 中执行\u003C\u002Fspan>\n\u003Cspan class=\"nv\">$userPath\u003C\u002Fspan> \u003Cspan class=\"p\">=\u003C\u002Fspan> \u003Cspan class=\"no\">[Environment]\u003C\u002Fspan>\u003Cspan class=\"p\">::\u003C\u002Fspan>\u003Cspan class=\"n\">GetEnvironmentVariable\u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"s2\">\"PATH\"\u003C\u002Fspan>\u003Cspan class=\"p\">,\u003C\u002Fspan> \u003Cspan class=\"s2\">\"User\"\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\n\u003Cspan class=\"k\">if\u003C\u002Fspan> \u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"nv\">$userPath\u003C\u002Fspan> \u003Cspan class=\"o\">-notlike\u003C\u002Fspan> \u003Cspan class=\"s2\">\"*\\.local\\bin*\"\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan> \u003Cspan class=\"p\">{\u003C\u002Fspan>\n    \u003Cspan class=\"no\">[Environment]\u003C\u002Fspan>\u003Cspan class=\"p\">::\u003C\u002Fspan>\u003Cspan class=\"n\">SetEnvironmentVariable\u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"s2\">\"PATH\"\u003C\u002Fspan>\u003Cspan class=\"p\">,\u003C\u002Fspan> \u003Cspan class=\"s2\">\"$userPath;C:\\Users\\$env:USERNAME\\.local\\bin\"\u003C\u002Fspan>\u003Cspan class=\"p\">,\u003C\u002Fspan> \u003Cspan class=\"s2\">\"User\"\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\n\u003Cspan class=\"p\">}\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>验证：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>version\n\u003Cspan class=\"c1\"># 应输出类似：0.18.1\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Ch3 id=\"linux\">Linux（远程）\u003C\u002Fh3>\n\u003Cp>远程 VM 不需要手动安装 Mutagen——当你创建同步会话时，本机的 Mutagen 会自动通过 SSH 把对应版本的 agent 上传到远程并启动。这是 Mutagen 的一个很贴心的设计。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>前提条件\u003C\u002Fstrong>：远程 SSH 可达，且登录用户有读写目标目录的权限。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"_2\">创建同步会话\u003C\u002Fh2>\n\u003Ch3 id=\"_3\">基本命令\u003C\u002Fh3>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>create\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-n\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-m\u003Cspan class=\"w\"> \u003C\u002Fspan>two-way-safe\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>--ignore-vcs\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>node_modules\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>target\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>build\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>dist\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>.gradle\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"&lt;本地路径&gt;\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"&lt;用户名&gt;@&lt;远程地址&gt;:&lt;远程路径&gt;\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>参数说明：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>-n\u003C\u002Fcode>：会话名称，后续管理用这个名字引用\u003C\u002Fli>\n\u003Cli>\u003Ccode>-m\u003C\u002Fcode>：同步模式（\u003Cstrong>关键参数，下面详细讲\u003C\u002Fstrong>）\u003C\u002Fli>\n\u003Cli>\u003Ccode>--ignore-vcs\u003C\u002Fcode>：忽略 \u003Ccode>.git\u003C\u002Fcode> \u002F \u003Ccode>.hg\u003C\u002Fcode> \u002F \u003Ccode>.svn\u003C\u002Fcode> 等版本控制目录\u003C\u002Fli>\n\u003Cli>\u003Ccode>-i\u003C\u002Fcode>：忽略规则，每条一个 \u003Ccode>-i\u003C\u002Fcode> 参数，支持 glob 模式\u003C\u002Fli>\n\u003Cli>最后两个位置参数：Alpha（本地）和 Beta（远程）的路径\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"_4\">示例\u003C\u002Fh3>\n\u003Cp>以一个后端项目为例：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>create\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-n\u003Cspan class=\"w\"> \u003C\u002Fspan>backend\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-m\u003Cspan class=\"w\"> \u003C\u002Fspan>two-way-safe\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>--ignore-vcs\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>node_modules\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>target\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>build\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>dist\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>.gradle\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"D:\\dev\\my-backend\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"root@10.0.0.50:\u002Froot\u002Fprojects\u002Fmy-backend\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>创建后，Mutagen 会立即开始首次对账（reconcile）——把两端文件差异同步一致。根据规模不同，首次对账可能需要几十秒到几分钟。之后进入 \u003Ccode>Watching for changes\u003C\u002Fcode> 状态，只同步增量变化。\u003C\u002Fp>\n\u003Ch3 id=\"_5\">验证会话状态\u003C\u002Fh3>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>list\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>正常状态长这样：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"nl\">Name\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">backend\u003C\u002Fspan>\n\u003Cspan class=\"nl\">Alpha\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"nl\">URL\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">D\u003C\u002Fspan>\u003Cspan class=\"o\">:\u003C\u002Fspan>\u003Cspan class=\"err\">\\\u003C\u002Fspan>\u003Cspan class=\"n\">dev\u003C\u002Fspan>\u003Cspan class=\"err\">\\\u003C\u002Fspan>\u003Cspan class=\"n\">my\u003C\u002Fspan>\u003Cspan class=\"o\">-\u003C\u002Fspan>\u003Cspan class=\"n\">backend\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"nl\">Connected\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">Yes\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"n\">Synchronizable\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">contents\u003C\u002Fspan>\u003Cspan class=\"o\">:\u003C\u002Fspan>\n\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"mi\">1200\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">directories\u003C\u002Fspan>\n\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"mi\">8500\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">files\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"mi\">280\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">MB\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\n\u003Cspan class=\"nl\">Beta\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"nl\">URL\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">root\u003C\u002Fspan>\u003Cspan class=\"mf\">@10.0.0.50\u003C\u002Fspan>\u003Cspan class=\"o\">:\u002F\u003C\u002Fspan>\u003Cspan class=\"n\">root\u003C\u002Fspan>\u003Cspan class=\"o\">\u002F\u003C\u002Fspan>\u003Cspan class=\"n\">projects\u003C\u002Fspan>\u003Cspan class=\"o\">\u002F\u003C\u002Fspan>\u003Cspan class=\"n\">my\u003C\u002Fspan>\u003Cspan class=\"o\">-\u003C\u002Fspan>\u003Cspan class=\"n\">backend\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"nl\">Connected\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">Yes\u003C\u002Fspan>\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"n\">Synchronizable\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">contents\u003C\u002Fspan>\u003Cspan class=\"o\">:\u003C\u002Fspan>\n\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"mi\">1200\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">directories\u003C\u002Fspan>\n\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"mi\">8500\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">files\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"mi\">280\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">MB\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\n\u003Cspan class=\"nl\">Status\u003C\u002Fspan>\u003Cspan class=\"p\">:\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">Watching\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"k\">for\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"n\">changes\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>\u003Ccode>Status: Watching for changes\u003C\u002Fcode> = 正常工作中。Alpha 和 Beta 的 \u003Ccode>Connected\u003C\u002Fcode> 都应该是 \u003Ccode>Yes\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"_6\">同步模式选择：最关键的一步\u003C\u002Fh2>\n\u003Cp>Mutagen 提供三种同步模式。\u003Cstrong>选错模式可能导致数据静默丢失\u003C\u002Fstrong>，这是我在实际使用中踩过的最大的坑。\u003C\u002Fp>\n\u003Ch3 id=\"_7\">三种模式对比\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>模式\u003C\u002Fth>\n\u003Cth>冲突行为\u003C\u002Fth>\n\u003Cth>适用场景\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>two-way-safe\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>双方都改了同一个文件 → \u003Cstrong>暂停同步，列出冲突，等人裁决\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>推荐默认\u003C\u002Fstrong>，双向工作区\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>two-way-resolved\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>双方都改了同一个文件 → \u003Cstrong>Alpha（本地）永远赢\u003C\u002Fstrong>，静默覆盖 Beta\u003C\u002Ftd>\n\u003Ctd>仅当确定本地永远权威\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>one-way-replica\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Beta 变成 Alpha 的精确镜像，Beta 上多余的文件直接删除\u003C\u002Ftd>\n\u003Ctd>单向分发，如部署静态资源\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch3 id=\"two-way-safe\">为什么推荐 two-way-safe\u003C\u002Fh3>\n\u003Cp>\u003Ccode>two-way-resolved\u003C\u002Fcode> 看起来很方便——冲突自动解决，不会有冲突暂停的烦恼。但它有个致命问题：\u003Cstrong>当 daemon 长时间没运行（比如笔记本合盖一晚上），重新启动后会立即开始全量对账。此时如果某个文件两端都改过，Alpha 的版本会静默覆盖 Beta 的版本——没有任何提示\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>这意味着你在远程辛辛苦苦改了一晚上的文件，本地一开机就被旧版覆盖了，而且你完全不知道发生了什么。\u003C\u002Fp>\n\u003Cp>\u003Ccode>two-way-safe\u003C\u002Fcode> 在同样的场景下会\u003Cstrong>暂停同步并列出冲突\u003C\u002Fstrong>，你看到冲突后可以手动决定保留哪个版本。不会丢数据。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>结论：凡是双向都可能修改的工作区，一律用 \u003Ccode>two-way-safe\u003C\u002Fcode>。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>这一条建议是用血泪换来的——下文踩坑部分会讲这个事故的完整经过。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3 id=\"_8\">冲突出现后怎么处理\u003C\u002Fh3>\n\u003Cp>使用 \u003Ccode>two-way-safe\u003C\u002Fcode> 时，如果两端同时修改了同一个文件，Mutagen 会暂停该会话。\u003Ccode>mutagen sync list\u003C\u002Fcode> 会显示类似：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"nv\">Conflicts\u003C\u002Fspan>:\n\u003Cspan class=\"w\">    \u003C\u002Fspan>\u003Cspan class=\"nv\">path\u003C\u002Fspan>\u003Cspan class=\"o\">\u002F\u003C\u002Fspan>\u003Cspan class=\"nv\">to\u003C\u002Fspan>\u003Cspan class=\"o\">\u002F\u003C\u002Fspan>\u003Cspan class=\"nv\">file\u003C\u002Fspan>.\u003Cspan class=\"nv\">yaml\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"ss\">(\u003C\u002Fspan>\u003Cspan class=\"nv\">both\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nv\">modified\u003C\u002Fspan>\u003Cspan class=\"ss\">)\u003C\u002Fspan>\n\u003Cspan class=\"nv\">Status\u003C\u002Fspan>:\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nv\">Watching\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"k\">for\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nv\">changes\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>此时你需要手动决定保留哪个版本：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 1. 暂停会话\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>pause\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 2. 比较两端文件内容，决定保留哪个\u003C\u002Fspan>\n\u003Cspan class=\"c1\">#    可以 SSH 到远程 diff，或在本地用工具对比\u003C\u002Fspan>\n\n\u003Cspan class=\"c1\"># 3. 把\"输\"的版本手动覆盖成\"赢\"的版本\u003C\u002Fspan>\n\u003Cspan class=\"c1\">#    比如决定用远程版本：scp 远程版本到本地\u003C\u002Fspan>\n\u003Cspan class=\"c1\">#    或者决定用本地版本：scp 本地版本到远程\u003C\u002Fspan>\n\n\u003Cspan class=\"c1\"># 4. 恢复会话\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>resume\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 5. 验证冲突已消失\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>list\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>冲突处理是手动的，但这正是 \u003Ccode>two-way-safe\u003C\u002Fcode> 的价值——它给你选择权，而不是自动帮你做一个可能错误的覆盖决定。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"daemon-windows\">Daemon 开机自启（Windows）\u003C\u002Fh2>\n\u003Cp>Mutagen 的同步依赖一个后台 daemon 进程。在 Windows 上，这个 daemon 不会自动启动，需要你手动 \u003Ccode>mutagen daemon start\u003C\u002Fcode> 或者配置开机自启。\u003C\u002Fp>\n\u003Ch3 id=\"_9\">自启脚本\u003C\u002Fh3>\n\u003Cp>在 Windows 启动目录放一个 \u003Ccode>.bat\u003C\u002Fcode> 脚本：\u003C\u002Fp>\n\u003Cp>\u003Cstrong>路径\u003C\u002Fstrong>：\u003Ccode>C:\\Users\\&lt;用户名&gt;\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mutagen_autostart.bat\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>内容\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"p\">@\u003C\u002Fspan>\u003Cspan class=\"k\">echo\u003C\u002Fspan> off\n\u003Cspan class=\"c1\">REM Mutagen daemon 自启脚本\u003C\u002Fspan>\n\u003Cspan class=\"s2\">\"C:\\Users\\&lt;用户名&gt;\\.local\\bin\\mutagen.exe\"\u003C\u002Fspan> daemon start\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>就这么简单。\u003Ccode>daemon start\u003C\u002Fcode> 会让 Mutagen 自我后台化（detach 到后台），不依赖命令行窗口。\u003C\u002Fp>\n\u003Ch3 id=\"start-b\">一个致命的陷阱：start \u002FB\u003C\u002Fh3>\n\u003Cp>如果你在网上搜 Windows 开机自启的写法，很多教程会用 \u003Ccode>start \u002FB\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"p\">@\u003C\u002Fspan>\u003Cspan class=\"k\">echo\u003C\u002Fspan> off\n\u003Cspan class=\"k\">start\u003C\u002Fspan> \u002FB \u003Cspan class=\"s2\">\"\"\u003C\u002Fspan> \u003Cspan class=\"s2\">\"C:\\Users\\&lt;用户名&gt;\\.local\\bin\\mutagen.exe\"\u003C\u002Fspan> daemon\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>\u003Cstrong>这是错的。\u003C\u002Fstrong> \u003Ccode>start \u002FB\u003C\u002Fcode> 启动的进程会挂在当前控制台上。当启动脚本执行完毕、控制台窗口关闭时，Windows 会发送 \u003Ccode>CTRL_CLOSE_EVENT\u003C\u002Fcode>，daemon 在启动几秒后就被杀死了。\u003C\u002Fp>\n\u003Cp>现象：开机后看似\"自启了\"，但 \u003Ccode>mutagen sync list\u003C\u002Fcode> 输出 \u003Ccode>Attempting to start Mutagen daemon...\u003C\u002Fcode>（说明 daemon 根本没在运行）。在你发现并手动拉起 daemon 之前，所有远程的改动都积压着——而当你手动启动 daemon 时，积压的改动会一次性对账，如果模式是 \u003Ccode>two-way-resolved\u003C\u002Fcode>，覆盖事故就发生了。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>正确的写法只有一种：直接调用 \u003Ccode>mutagen.exe daemon start\u003C\u002Fcode>。\u003C\u002Fstrong> Mutagen 内部会处理自我后台化。\u003C\u002Fp>\n\u003Ch3 id=\"_10\">安全软件拦截\u003C\u002Fh3>\n\u003Cp>部分安全软件（如火绒）会对启动目录的文件写入进行拦截。如果你发现脚本写不进去或内容被回滚，可以改用先写到临时目录再复制的方式：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 在 bash \u002F git-bash 中执行\u003C\u002Fspan>\ncat\u003Cspan class=\"w\"> \u003C\u002Fspan>&gt;\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"s2\">\"\u003C\u002Fspan>\u003Cspan class=\"nv\">$LOCALAPPDATA\u003C\u002Fspan>\u003Cspan class=\"s2\">\u002FTemp\u002Fmutagen_autostart.bat\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"s\">&lt;&lt; 'EOF'\u003C\u002Fspan>\n\u003Cspan class=\"s\">@echo off\u003C\u002Fspan>\n\u003Cspan class=\"s\">\"C:\\Users\\&lt;用户名&gt;\\.local\\bin\\mutagen.exe\" daemon start\u003C\u002Fspan>\n\u003Cspan class=\"s\">EOF\u003C\u002Fspan>\ncp\u003Cspan class=\"w\"> \u003C\u002Fspan>-f\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"s2\">\"\u003C\u002Fspan>\u003Cspan class=\"nv\">$LOCALAPPDATA\u003C\u002Fspan>\u003Cspan class=\"s2\">\u002FTemp\u002Fmutagen_autostart.bat\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"\u003C\u002Fspan>\u003Cspan class=\"nv\">$APPDATA\u003C\u002Fspan>\u003Cspan class=\"s2\">\u002FMicrosoft\u002FWindows\u002FStart Menu\u002FPrograms\u002FStartup\u002Fmutagen_autostart.bat\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>写完后读回验证内容没被篡改。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"_11\">忽略规则配置\u003C\u002Fh2>\n\u003Ch3 id=\"vs-mutagenignore\">命令行 vs .mutagenignore\u003C\u002Fh3>\n\u003Cp>忽略规则有两个来源，叠加生效：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>创建会话时的 \u003Ccode>-i\u003C\u002Fcode> 参数\u003C\u002Fstrong>：写入会话配置，之后一直生效\u003C\u002Fli>\n\u003Cli>\u003Cstrong>同步根目录下的 \u003Ccode>.mutagenignore\u003C\u002Fcode> 文件\u003C\u002Fstrong>：动态读取，随时改随时生效，不需要重建会话\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>推荐的做法：\u003Cstrong>通用的、项目类型固定的忽略规则\u003C\u002Fstrong>（如 \u003Ccode>node_modules\u003C\u002Fcode>、\u003Ccode>target\u003C\u002Fcode>、\u003Ccode>build\u003C\u002Fcode>）在创建会话时用 \u003Ccode>-i\u003C\u002Fcode> 写死；\u003Cstrong>特定环境产生的临时目录\u003C\u002Fstrong>（如浏览器调试 profile、缓存目录）用 \u003Ccode>.mutagenignore\u003C\u002Fcode> 补充。\u003C\u002Fp>\n\u003Ch3 id=\"_12\">常见项目的忽略规则\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>项目类型\u003C\u002Fth>\n\u003Cth>推荐忽略\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Java \u002F Maven\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>target\u003C\u002Fcode> \u003Ccode>build\u003C\u002Fcode> \u003Ccode>dist\u003C\u002Fcode> \u003Ccode>.gradle\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Node.js \u002F 前端\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>node_modules\u003C\u002Fcode> \u003Ccode>.nuxt\u003C\u002Fcode> \u003Ccode>.output\u003C\u002Fcode> \u003Ccode>.vitepress\u002Fcache\u003C\u002Fcode> \u003Ccode>.vitepress\u002Fdist\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Python\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>__pycache__\u003C\u002Fcode> \u003Ccode>.venv\u003C\u002Fcode> \u003Ccode>*.pyc\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>通用\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>.git\u003C\u002Fcode>（用 \u003Ccode>--ignore-vcs\u003C\u002Fcode> 更优雅）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch3 id=\"mutagenignore\">.mutagenignore 示例\u003C\u002Fh3>\n\u003Cp>在同步根目录下创建 \u003Ccode>.mutagenignore\u003C\u002Fcode> 文件：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 浏览器调试 profile（会持续写文件 + 锁文件，不能同步）\u003C\u002Fspan>\n\u003Cspan class=\"na\">.chrome-debug\u003C\u002Fspan>\u003Cspan class=\"err\">\u002F\u003C\u002Fspan>\n\n\u003Cspan class=\"c1\"># 本地缓存 \u002F 日志\u003C\u002Fspan>\n\u003Cspan class=\"na\">.cache\u003C\u002Fspan>\u003Cspan class=\"err\">\u002F\u003C\u002Fspan>\n\u003Cspan class=\"nf\">logs\u003C\u002Fspan>\u003Cspan class=\"err\">\u002F\u003C\u002Fspan>\n\n\u003Cspan class=\"c1\"># 大体积二进制\u003C\u002Fspan>\n\u003Cspan class=\"err\">*\u003C\u002Fspan>\u003Cspan class=\"na\">.jar\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>修改后不需要重启 daemon 或重建会话，Mutagen 会自动重新读取。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"ide\">与 IDE 远程开发的配合\u003C\u002Fh2>\n\u003Cp>Mutagen 不排斥 IDE 的远程开发功能，两者可以互补使用：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>VS Code Remote-SSH \u002F JetBrains Gateway\u003C\u002Fstrong>：适合直接在远程编辑单个项目。但如果你的工作流涉及多个工具链交叉（比如同时打开多个项目、使用远程终端构建），Mutagen 的纯文件同步方式更灵活。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Mutagen + 本地 IDE\u003C\u002Fstrong>：文件始终在本地，IDE 索引、搜索、跳转都是本地速度。远程只负责构建和运行。这种方式在 Windows + Linux 混合开发环境中体验最好。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>我个人的选择是后者：全部使用本地 IDE 编辑，Mutagen 负责把文件实时推到远程，远程跑构建和部署。这种方式下编辑体验完全没有远程延迟，而且可以随时切换 IDE（今天用 IntelliJ，明天用 VS Code，不受影响）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"ai-agent\">实战：与 AI Agent 框架的协同工作流\u003C\u002Fh2>\n\u003Cp>文件同步本身只是基础设施。它的真正价值体现在具体的工作流上——下面以我在远程 VM 上运行 DSH（DeepSeek Harness）的实际场景为例，讲讲 Mutagen 是怎么把\"改远程配置\"这件事变得优雅的。\u003C\u002Fp>\n\u003Ch3 id=\"_13\">背景\u003C\u002Fh3>\n\u003Cp>DSH 是一个跑在远程 Linux VM 上的 AI Agent 框架（基于 Cordis 插件体系），通过 systemd 管理服务。它的核心配置文件散落在几个位置：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>~\u002F.dsh\u002Fsettings.yaml\u003C\u002Fcode> — 全局设置\u003C\u002Fli>\n\u003Cli>\u003Ccode>~\u002F.dsh\u002Fprofiles\u002Fweb\u002Fpackage.json\u003C\u002Fcode> — profile 的依赖声明\u003C\u002Fli>\n\u003Cli>\u003Ccode>~\u002F.dsh\u002Fprofiles\u002Fweb\u002Fcordis.patch.yml\u003C\u002Fcode> — 插件加载与 LLM 路由配置\u003C\u002Fli>\n\u003Cli>\u003Ccode>~\u002F.dsh\u002Fprofiles\u002F&lt;插件名&gt;\u002F\u003C\u002Fcode> — 自定义插件的源码\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>日常运维包括：调整 LLM 路由、升级插件版本、修改自定义插件代码、调整 nginx 反代配置等。\u003C\u002Fp>\n\u003Ch3 id=\"_14\">痛点：远程编辑体验极差\u003C\u002Fh3>\n\u003Cp>这些配置文件如果直接在远程用 \u003Ccode>vim\u003C\u002Fcode> 或 \u003Ccode>nano\u003C\u002Fcode> 编辑，体验非常糟糕——尤其 \u003Ccode>cordis.patch.yml\u003C\u002Fcode> 这种嵌套很深的 YAML，在终端里改简直是折磨。你想在本地用 VS Code 打开？以前要么 SSHFS 挂载（卡顿），要么手动 scp 来回传（繁琐）。\u003C\u002Fp>\n\u003Ch3 id=\"mutagen-dsh\">解决方案：Mutagen 同步 DSH 配置目录\u003C\u002Fh3>\n\u003Cp>把 DSH 的配置目录纳入 Mutagen 同步范围，一切就简单了：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>create\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-n\u003Cspan class=\"w\"> \u003C\u002Fspan>dsh-config\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-m\u003Cspan class=\"w\"> \u003C\u002Fspan>two-way-safe\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>--ignore-vcs\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>node_modules\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>.pnpm\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>sessions\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"D:\\dev\\dsh-remote\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"root@10.0.0.50:\u002Froot\u002F.dsh\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>\u003Cstrong>关键忽略规则\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>node_modules\u003C\u002Fcode> \u002F \u003Ccode>.pnpm\u003C\u002Fcode>：这些是 pnpm 管理的依赖目录，体积大且平台相关，必须在远程用 \u003Ccode>pnpm install\u003C\u002Fcode> 安装，不能同步\u003C\u002Fli>\n\u003Cli>\u003Ccode>sessions\u003C\u002Fcode>：DSH 的会话数据（zstd 压缩的 jsonl），属于运行时状态，不需要在本地编辑\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>同步建立后，\u003Ccode>settings.yaml\u003C\u002Fcode>、\u003Ccode>cordis.patch.yml\u003C\u002Fcode>、\u003Ccode>package.json\u003C\u002Fcode>、自定义插件源码全部可以在本地 IDE 中编辑，保存即同步到远程。\u003C\u002Fp>\n\u003Ch3 id=\"dsh\">典型工作流：升级 DSH 插件\u003C\u002Fh3>\n\u003Cp>以升级一个插件版本为例，完整流程：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"err\">┌─────────────────────────────┐\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">┌─────────────────────────────┐\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">       \u003C\u002Fspan>\u003Cspan class=\"nx\">本地\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"nx\">Windows\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">       \u003C\u002Fspan>\u003Cspan class=\"nx\">远程\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">VM\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"p\">(\u003C\u002Fspan>\u003Cspan class=\"nx\">Linux\u003C\u002Fspan>\u003Cspan class=\"p\">)\u003C\u002Fspan>\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">1\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">IDE\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">打开\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"kn\">package\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"nx\">json\u003C\u002Fspan>\u003Cspan class=\"w\">   \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">2\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">改版本号\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"m m-Double\">0.3.5\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"err\">→\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"m m-Double\">0.3.6\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">         \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">3\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">Ctrl\u003C\u002Fspan>\u003Cspan class=\"o\">+\u003C\u002Fspan>\u003Cspan class=\"nx\">S\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">保存\u003C\u002Fspan>\u003Cspan class=\"w\">             \u003C\u002Fspan>\u003Cspan class=\"err\">│──┐\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">8\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">IDE\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">查看远程文件\u003C\u002Fspan>\u003Cspan class=\"w\">        \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">4\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">Mutagen\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">收到新文件\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">     \u003C\u002Fspan>\u003Cspan class=\"nx\">确认同步到位\u003C\u002Fspan>\u003Cspan class=\"w\">            \u003C\u002Fspan>\u003Cspan class=\"err\">│←─┼──────│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">5\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">SSH\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">进去\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">pnpm\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">install\u003C\u002Fspan>\u003Cspan class=\"w\">   \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">6\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">systemctl\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">restart\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">dsh\u003C\u002Fspan>\u003Cspan class=\"w\">   \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">                             \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"mi\">7\u003C\u002Fspan>\u003Cspan class=\"p\">.\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">journalctl\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">查日志确认\u003C\u002Fspan>\u003Cspan class=\"w\">   \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\n\u003Cspan class=\"err\">└─────────────────────────────┘\u003C\u002Fspan>\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"err\">│\u003C\u002Fspan>\u003Cspan class=\"w\">      \u003C\u002Fspan>\u003Cspan class=\"err\">└─────────────────────────────┘\u003C\u002Fspan>\n\u003Cspan class=\"w\">                           \u003C\u002Fspan>\u003Cspan class=\"nx\">Mutagen\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"nx\">自动同步\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>步骤展开：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>本地改 \u003Ccode>package.json\u003C\u002Fcode>\u003C\u002Fstrong>：把插件版本号从 \u003Ccode>0.3.5\u003C\u002Fcode> 改成 \u003Ccode>0.3.6\u003C\u002Fcode>，保存\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Mutagen 自动同步\u003C\u002Fstrong>：几秒内远程的 \u003Ccode>package.json\u003C\u002Fcode> 就更新了\u003C\u002Fli>\n\u003Cli>\u003Cstrong>SSH 进远程执行安装\u003C\u002Fstrong>：\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Ccode>bash\n   cd ~\u002F.dsh\u002Fprofiles\u002Fweb\n   rm -f pnpm-lock.yaml\n   pnpm install --no-frozen-lockfile\u003C\u002Fcode>\u003Cbr>\n4. \u003Cstrong>重启服务\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cp>\u003Ccode>bash\n   systemctl restart dsh-web\n   journalctl -u dsh-web --no-pager -n 50\u003C\u002Fcode>\u003Cbr>\n5. \u003Cstrong>验证\u003C\u002Fstrong>：看 journalctl 输出有没有报错。如果一切正常，\u003Ccode>curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\" http:\u002F\u002F127.0.0.1:3080\u003C\u002Fcode> 应返回 200\u003C\u002Fp>\n\u003Cp>整个过程中，编辑配置在本地完成（IDE 体验），安装和重启在远程完成（命令行操作），Mutagen 在中间无缝衔接。\u003C\u002Fp>\n\u003Ch3 id=\"_15\">典型工作流：修改自定义插件源码\u003C\u002Fh3>\n\u003Cp>自定义 DSH 插件通常放在 \u003Ccode>~\u002F.dsh\u002Fprofiles\u002F\u003C\u002Fcode> 下（通过 \u003Ccode>file:\u003C\u002Fcode> 依赖挂载进 profile）。开发流程：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>本地 IDE 编辑插件源码\u003C\u002Fstrong>（TypeScript \u002F JavaScript），保存即同步\u003C\u002Fli>\n\u003Cli>\u003Cstrong>远程执行构建\u003C\u002Fstrong>：\u003Ccode>cd ~\u002F.dsh\u002Fprofiles\u002Fmy-plugin &amp;&amp; pnpm build\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>重载 DSH\u003C\u002Fstrong>：\u003Ccode>systemctl restart dsh-web\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>得益于双向同步，如果我在远程通过 DSH 的 Web UI 做了一些配置变更（比如调整了 LLM 路由参数），这些变更也会同步回本地，下次编辑时看到的就是最新版本。\u003C\u002Fp>\n\u003Ch3 id=\"nginx\">典型工作流：nginx 反代配置\u003C\u002Fh3>\n\u003Cp>DSH Web 需要经过 nginx 反向代理对外提供服务。nginx 配置文件也可以纳入同步范围：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>mutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>create\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-n\u003Cspan class=\"w\"> \u003C\u002Fspan>nginx-conf\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-m\u003Cspan class=\"w\"> \u003C\u002Fspan>two-way-safe\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>--ignore-vcs\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"D:\\dev\\nginx-confs\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"root@10.0.0.50:\u002Fetc\u002Fnginx\u002Fconf.d\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>改完配置保存，SSH 进去 \u003Ccode>nginx -t &amp;&amp; systemctl reload nginx\u003C\u002Fcode> 即可。不用 scp，不用复制粘贴。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>注意\u003C\u002Fstrong>：nginx 配置中如果包含 SSL 证书路径等敏感信息，要注意同步目录的访问控制。也可以选择只同步非敏感的 server block 配置文件。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3 id=\"git-dsh\">为什么不用 Git 管理 DSH 配置？\u003C\u002Fh3>\n\u003Cp>你可能会问：这些配置文件用 Git 管理不也可以吗？\u003C\u002Fp>\n\u003Cp>可以，但不适合所有场景：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>\u003Ccode>cordis.patch.yml\u003C\u002Fcode> 里有 API Key 和 LLM 路由配置\u003C\u002Fstrong>，不适合提交到 Git 仓库（即使私有仓库也不理想）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>频繁的微调\u003C\u002Fstrong>（改个参数、调个超时）不值得每次都走 commit-push-pull 流程\u003C\u002Fli>\n\u003Cli>\u003Cstrong>有些改动是远程产生的\u003C\u002Fstrong>（比如通过 Web UI 调配置），Git 是单向的，需要手动 pull 回来\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Mutagen 的双向实时同步解决了这些问题——改完就生效，双向自动对齐，不经过任何中间存储。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"_16\">常用运维命令速查\u003C\u002Fh2>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 查看所有会话状态\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>list\n\n\u003Cspan class=\"c1\"># 查看某个会话的详细信息（模式、忽略规则、冲突等）\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>list\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\u003Cspan class=\"w\"> \u003C\u002Fspan>-l\n\n\u003Cspan class=\"c1\"># 暂停会话（修改文件前建议先暂停！）\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>pause\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 恢复会话\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>resume\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 强制同步一轮\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>flush\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 终止会话（不删文件，只是停止同步）\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>terminate\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 启动 daemon\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>daemon\u003Cspan class=\"w\"> \u003C\u002Fspan>start\n\n\u003Cspan class=\"c1\"># 停止 daemon\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>daemon\u003Cspan class=\"w\"> \u003C\u002Fspan>stop\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Chr>\n\u003Ch2 id=\"_17\">踩坑实录\u003C\u002Fh2>\n\u003Cp>下面是实际使用中遇到的几个关键问题，希望能帮你避开。\u003C\u002Fp>\n\u003Ch3 id=\"1two-way-resolved\">坑 1：two-way-resolved 静默覆盖远程文件\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>现象\u003C\u002Fstrong>：在远程 VM 上修改了一个配置文件（从 9KB 改到 13KB，新增了大量内容）。关机回家，第二天开机后发现本地还是旧版（9KB），而远程也被覆盖回了旧版。一晚上的工作白费了。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>根因\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Col>\n\u003Cli>开机自启脚本用了错误的 \u003Ccode>start \u002FB\u003C\u002Fcode> 写法，daemon 根本没活着，远程改动全部积压。\u003C\u002Fli>\n\u003Cli>同步模式是 \u003Ccode>two-way-resolved\u003C\u002Fcode>。当手动启动 daemon 后，它开始首次对账。此时本地文件和远程文件都偏离了基线（双方都改过），按照 \u003Ccode>two-way-resolved\u003C\u002Fcode> 的规则——Alpha（本地）永远赢——本地旧版静默覆盖了远程新版。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>取证过程\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cp>Mutagen 同步会保留源文件的修改时间戳。通过对比两端文件的创建时间（Birth time）可以判断同步方向：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 远程查看文件创建时间\u003C\u002Fspan>\nstat\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;文件路径&gt;\n\u003Cspan class=\"c1\"># 如果 Birth 时间 = daemon 启动时刻 → 该文件是同步时重建的 → 方向是 本地→远程\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>远程文件 Birth 时间正好等于手动启动 daemon 的时刻，加上本地文件 mtime 从未改变——确认本地旧版赢了。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>修复\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Col>\n\u003Cli>先暂停会话：\u003Ccode>mutagen sync pause &lt;会话名&gt;\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>从构建产物或备份中找回新版文件\u003C\u002Fli>\n\u003Cli>恢复到远程\u003C\u002Fli>\n\u003Cli>拉回本地覆盖旧版\u003C\u002Fli>\n\u003Cli>恢复同步并验证两端文件一致\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>预防\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>所有双向工作区一律使用 \u003Ccode>two-way-safe\u003C\u002Fcode>\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>确保 daemon 开机自启正确工作\u003C\u002Fli>\n\u003Cli>如果发现 daemon 积压了大量改动，启动后\u003Cstrong>先暂停会话检查差异\u003C\u002Fstrong>，确认没有冲突再恢复\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"2\">坑 2：修改同步模式必须重建会话\u003C\u002Fh3>\n\u003Cp>\u003Ccode>mutagen sync configure\u003C\u002Fcode> 命令\u003Cstrong>不支持修改同步模式\u003C\u002Fstrong>。如果你想把模式从 \u003Ccode>two-way-resolved\u003C\u002Fcode> 改成 \u003Ccode>two-way-safe\u003C\u002Fcode>，必须终止重建：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c1\"># 1. 终止旧会话\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>terminate\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\n\n\u003Cspan class=\"c1\"># 2. 用新参数重建\u003C\u002Fspan>\nmutagen\u003Cspan class=\"w\"> \u003C\u002Fspan>sync\u003Cspan class=\"w\"> \u003C\u002Fspan>create\u003Cspan class=\"w\"> \u003C\u002Fspan>-n\u003Cspan class=\"w\"> \u003C\u002Fspan>&lt;会话名&gt;\u003Cspan class=\"w\"> \u003C\u002Fspan>-m\u003Cspan class=\"w\"> \u003C\u002Fspan>two-way-safe\u003Cspan class=\"w\"> \u003C\u002Fspan>--ignore-vcs\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>node_modules\u003Cspan class=\"w\"> \u003C\u002Fspan>-i\u003Cspan class=\"w\"> \u003C\u002Fspan>target\u003Cspan class=\"w\"> \u003C\u002Fspan>...\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"se\">\\\u003C\u002Fspan>\n\u003Cspan class=\"w\">  \u003C\u002Fspan>\u003Cspan class=\"s2\">\"&lt;本地路径&gt;\"\u003C\u002Fspan>\u003Cspan class=\"w\"> \u003C\u002Fspan>\u003Cspan class=\"s2\">\"&lt;远程路径&gt;\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>重建后会触发一次完整的首次对账，等 30-60 秒后确认状态正常。\u003C\u002Fp>\n\u003Ch3 id=\"3daemon\">坑 3：Daemon 假活——状态正常但双向不同步\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>现象\u003C\u002Fstrong>：所有会话显示 \u003Ccode>Watching for changes\u003C\u002Fcode>，\u003Ccode>Connected: Yes\u003C\u002Fcode>，看起来一切正常。但实际双向写入测试发现根本没有同步。\u003Ccode>mutagen sync flush\u003C\u002Fcode> 报 \u003Ccode>session is not currently able to synchronize\u003C\u002Fcode>，\u003Ccode>mutagen sync pause\u003C\u002Fcode> 也卡住不动。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>根因\u003C\u002Fstrong>：同步范围内存在被运行中进程锁定的文件（如浏览器 profile 的 Cache\u002FCookies\u002FSessions 文件、SQLite 数据库文件、杀毒软件正在扫描的文件等）。Mutagen 的扫描器读不到这些文件就一直重试，陷入死循环，无法处理任何实际的同步任务。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>诊断方法\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"highlight\">\u003Cpre>\u003Cspan>\u003C\u002Fspan>\u003Ccode>\u003Cspan class=\"c\"># 查看 mutagen 进程的 CPU 累计时间\u003C\u002Fspan>\n\u003Cspan class=\"n\">powershell\u003C\u002Fspan> \u003Cspan class=\"n\">-Command\u003C\u002Fspan> \u003Cspan class=\"s2\">\"Get-Process mutagen | Select-Object Id,CPU,StartTime\"\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\n\u003Cp>如果 CPU 秒数\u003Cstrong>持续上涨\u003C\u002Fstrong>（能烧到几千秒），就是死循环扫描锁文件。正常 Watching 状态下 CPU 应该稳定不动。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>修复\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Col>\n\u003Cli>把锁文件目录加入\u003Cstrong>两端\u003C\u002Fstrong>的 \u003Ccode>.mutagenignore\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>强杀 daemon（\u003Ccode>mutagen daemon stop\u003C\u002Fcode> 在死循环下会卡住，必须 \u003Ccode>Stop-Process -Id &lt;pid&gt; -Force\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>\u003Ccode>mutagen daemon start\u003C\u002Fcode> 重启\u003C\u002Fli>\n\u003Cli>等对账完成后做双向写入测试确认恢复\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>预防\u003C\u002Fstrong>：任何会持续写入并锁定文件的本地状态目录（浏览器 profile、缓存目录、本地数据库）都不要放进同步范围。创建会话时就用 \u003Ccode>-i\u003C\u002Fcode> 排除，或事后补进 \u003Ccode>.mutagenignore\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>DSH 相关提醒\u003C\u002Fstrong>：DSH 的 sessions 目录里的 \u003Ccode>.jsonl.zstd\u003C\u002Fcode> 文件在 DSH 运行期间是被进程持续写入的——同步这个目录会触发上述假活问题。所以上面的示例中我们把 \u003Ccode>sessions\u003C\u002Fcode> 目录排除掉了。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3 id=\"4mutagen-sync-list-daemon\">坑 4：mutagen sync list 会自动启动 daemon\u003C\u002Fh3>\n\u003Cp>当 daemon 没在运行时，执行 \u003Ccode>mutagen sync list\u003C\u002Fcode> 会\u003Cstrong>自动启动 daemon 并立即开始对账\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>这本身是一个方便的设计，但在排查问题时，如果你还没搞清楚两端差异就执行了这个命令，自动对账可能会导致覆盖（特别是 \u003Ccode>two-way-resolved\u003C\u002Fcode> 模式下）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>排查时的安全做法\u003C\u002Fstrong>：如果你怀疑两端有差异需要排查，但又不想让自动对账破坏现场，可以在排查前先在远程手动暂停会话（通过 SSH 连到远程执行 \u003Ccode>mutagen\u003C\u002Fcode> 命令），或者确保使用的是 \u003Ccode>two-way-safe\u003C\u002Fcode> 模式（冲突会暂停而不是覆盖）。\u003C\u002Fp>\n\u003Ch3 id=\"5\">坑 5：首次对账需要时间\u003C\u002Fh3>\n\u003Cp>创建会话或重建会话后的首次对账不是瞬间完成的。对于几千个文件、几百 MB 的项目，可能需要 30-60 秒。期间状态可能显示 \u003Ccode>Staging files on beta\u003C\u002Fcode> 或 \u003Ccode>Scanning\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>在此期间不要慌张地以为出了问题。等状态变成 \u003Ccode>Watching for changes\u003C\u002Fcode> 再判断。如果你的项目特别大（几万文件、几 GB），首次对账可能需要几分钟。\u003C\u002Fp>\n\u003Ch3 id=\"6dsh-node_modules\">坑 6：DSH 升级后 node_modules 不匹配\u003C\u002Fh3>\n\u003Cp>这是一个 Mutagen + DSH 结合场景下特有的坑。\u003C\u002Fp>\n\u003Cp>DSH 升级流程需要在远程执行 \u003Ccode>pnpm install\u003C\u002Fcode>，这会大规模改动 \u003Ccode>node_modules\u003C\u002Fcode> 目录。如果 \u003Ccode>node_modules\u003C\u002Fcode> 没有被正确排除在同步范围外，会出现两种问题：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>本地 → 远程同步覆盖了刚装好的依赖\u003C\u002Fstrong>：如果你之前在本地碰过 node_modules（比如 IDE 自动跑了 \u003Ccode>npm install\u003C\u002Fcode>），Mutagen 会把本地版本推到远程，覆盖掉远程 \u003Ccode>pnpm install\u003C\u002Fcode> 的结果\u003C\u002Fli>\n\u003Cli>\u003Cstrong>远程 → 本地同步把平台特定的二进制拉到 Windows\u003C\u002Fstrong>：\u003Ccode>node_modules\u003C\u002Fcode> 里有些包包含平台特定的二进制文件（如 \u003Ccode>.node\u003C\u002Fcode> 扩展），Linux 编译的在 Windows 上不能用\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>解法\u003C\u002Fstrong>：创建同步会话时\u003Cstrong>必须\u003C\u002Fstrong>把 \u003Ccode>node_modules\u003C\u002Fcode> 和 \u003Ccode>.pnpm\u003C\u002Fcode> 加入忽略规则。依赖管理完全交给远程的包管理器，不让 Mutagen 介入。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"_18\">最佳实践总结\u003C\u002Fh2>\n\u003Ch3 id=\"1\">1. 同步模式\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>永远使用 \u003Ccode>two-way-safe\u003C\u002Fcode>\u003C\u002Fstrong>，除非你有非常明确的理由选择其他模式。多出来的\"冲突需要手动解决\"的麻烦，比起数据被静默覆盖的灾难来说根本不算什么。\u003C\u002Fp>\n\u003Ch3 id=\"2_1\">2. 忽略规则\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>构建产物、依赖目录一律不同步（\u003Ccode>node_modules\u003C\u002Fcode>、\u003Ccode>.pnpm\u003C\u002Fcode>、\u003Ccode>target\u003C\u002Fcode>、\u003Ccode>build\u003C\u002Fcode>、\u003Ccode>dist\u003C\u002Fcode>、\u003Ccode>.gradle\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>版本控制目录用 \u003Ccode>--ignore-vcs\u003C\u002Fcode> 排除\u003C\u002Fli>\n\u003Cli>运行时持续写入的目录（浏览器 profile、数据库、会话数据）用 \u003Ccode>.mutagenignore\u003C\u002Fcode> 或 \u003Ccode>-i\u003C\u002Fcode> 排除\u003C\u002Fli>\n\u003Cli>大文件二进制（\u003Ccode>*.jar\u003C\u002Fcode>、模型文件）按需排除\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"3\">3. 开机自启\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>使用 \u003Ccode>mutagen daemon start\u003C\u002Fcode> 自我后台化\u003C\u002Fli>\n\u003Cli>\u003Cstrong>绝对不要\u003C\u002Fstrong>用 \u003Ccode>start \u002FB\u003C\u002Fcode> 启动\u003C\u002Fli>\n\u003Cli>配好后重启验证 daemon 确实在运行\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"4\">4. 日常运维\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>定期 \u003Ccode>mutagen sync list\u003C\u002Fcode> 检查状态\u003C\u002Fli>\n\u003Cli>出现 \u003Ccode>Conflicts\u003C\u002Fcode> 时及时处理（见上文冲突处理流程）\u003C\u002Fli>\n\u003Cli>远程大改动前可以先 \u003Ccode>pause\u003C\u002Fcode>，改完再 \u003Ccode>resume\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>修改模式或核心忽略规则需要重建会话\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"5_1\">5. 安全网\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>重要文件还是要有版本控制（Git）兜底，Mutagen 是同步工具不是备份工具\u003C\u002Fli>\n\u003Cli>定期检查 \u003Ccode>.mutagenignore\u003C\u002Fcode> 是否覆盖了所有需要排除的路径\u003C\u002Fli>\n\u003Cli>如果使用 WG 等 VPN，确保网络连通性稳定（MTU 配置也可能影响同步效率）\u003C\u002Fli>\n\u003Cli>含敏感信息（API Key、密码）的配置文件，注意同步目录的访问控制\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"6\">6. 与服务运维结合\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>同步只解决文件传输，安装 \u002F 构建 \u002F 重启命令仍需 SSH 进远程执行\u003C\u002Fli>\n\u003Cli>远程执行命令前，确认 Mutagen 已经完成同步（\u003Ccode>mutagen sync list\u003C\u002Fcode> 看状态）\u003C\u002Fli>\n\u003Cli>如果远程有服务在持续写入某个目录（如 DSH 的 sessions），务必排除该目录\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"_19\">总结\u003C\u002Fh2>\n\u003Cp>Mutagen 解决的核心问题是：\u003Cstrong>让你在本地编辑文件享受本地速度，同时远程实时拥有一份完全相同的副本，无需手动操作。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>它的安装和使用都很简单，但有几个关键配置需要特别注意：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>同步模式选 \u003Ccode>two-way-safe\u003C\u002Fcode>\u003C\u002Fstrong>——这条最重要，选错了可能丢数据\u003C\u002Fli>\n\u003Cli>\u003Cstrong>开机自启用 \u003Ccode>daemon start\u003C\u002Fcode>\u003C\u002Fstrong>——不要用 \u003Ccode>start \u002FB\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>忽略规则要完善\u003C\u002Fstrong>——构建产物、依赖目录、运行时状态文件必须排除\u003C\u002Fli>\n\u003Cli>\u003Cstrong>锁文件目录绝对不能进同步范围\u003C\u002Fstrong>——否则 daemon 会假活\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>配合 DSH 这类远程 AI Agent 框架使用时，Mutagen 让\"本地编辑配置 + 远程运行服务\"的工作流变得极其自然——你只需要专注于编辑，文件传输完全自动化。\u003C\u002Fp>\n\u003Cp>配置正确后，Mutagen 是一个非常稳定可靠的双向同步方案。日常几乎感觉不到它的存在——这正是好的基础设施应该有的样子。\u003C\u002Fp>\n\u003Chr>\n\u003Cblockquote>\n\u003Cp>写于 2026 年 9 月，基于 Mutagen 0.18.1 的实际使用经验。\u003Cbr>\n\u003C\u002Fp>\n\u003C\u002Fblockquote>",0,null,{"id":22,"title":30,"slug":31},"锐界幻境 MiragEdge","锐界幻境-miragedge"]