雲端 Mac 檔案同步實戰:用 Mutagen 打通本地與遠端的 iOS 專案

遠端 Mac ·約 8 分鐘閱讀

雲端 Mac 檔案同步實戰:用 Mutagen 打通本地與遠端的 iOS 專案

專案倉庫將近 40GB,Pods、node_modules、DerivedData 加起來比原始碼本身還大。你在本機打開 Xcode,專案掛載在遠端雲端 Mac 的一個 SMB 共享磁碟區上,結果索引轉了一分多鐘,切個分支卡到讓你懷疑網路是不是斷了——這是很多人把開發環境搬到雲端 Mac 之後第一個撞上的牆。掛載共享磁碟區看起來省事,實際用起來才發現,大型 iOS 專案的檔案存取模式跟網路檔案系統天生不合拍。這篇文章記錄我們在 HireVPS 節點上用 Mutagen 做本機與遠端雙向同步,把這套使用體驗拉回接近本機的過程。

掛載共享磁碟區為什麼不適合大型 iOS 專案

NFS、SMB 這類網路檔案系統的設計前提是「偶爾存取的大檔案」,而 Xcode 索引、SourceKit、Pods 的 CocoaPods 快取掃描,做的恰恰是相反的事:短時間內對數萬個小檔案進行 stat、open、close。每一次操作都要走一趟網路往返,哪怕延遲只有 20ms,乘以幾萬次也是分鐘級的卡頓。更麻煩的是斷線處理——掛載點在連線不穩時會直接假死,終端機和編輯器一起卡死,只能強制卸載重新掛載。

三種常見方案對比一下會更直觀:

方案 即時性 大量小檔案表現 斷線復原 適用場景
SMB/NFS 掛載 強(所見即最新) 差,逐檔案網路往返 差,容易假死 偶爾讀寫大檔案
rsync 排程任務 弱,有同步時間差 一般,批量傳輸效率高 好,任務制重跑即可 定期備份、單向推送
Mutagen 雙向同步 接近即時 好,本機快取+增量傳輸 好,斷線自動重連續傳 持續開發的雙向協作

我們踩過的一個教訓是:一開始圖省事直接掛載,結果 Xcode 的「Building workspace」這一步平均要多等 15~25 秒,團隊每天光是等待索引就浪費不少時間,換成 Mutagen 之後這個等待基本上消失了。

用 Mutagen 建立雙向同步工作階段

安裝與首次連線

Mutagen 本身是單一二進位檔,本機和雲端 Mac 都不需要額外常駐服務,連線建立時會自動把 agent 分發到遠端。本機先安裝:

brew install mutagen-io/mutagen/mutagen
mutagen version

假設你的雲端 Mac 已經開好 SSH,憑證在開機通知信裡。建立一個同步工作階段:

mutagen sync create \
  --name=ios-app \
  --ignore-vcs \
  --symlink-mode=posix-raw \
  /Users/me/Projects/ios-app \
  ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app

--ignore-vcs 會自動跳過 .git 內部物件(git 本身用自己的協定同步更合適,不需要 Mutagen 重複搬運),--symlink-mode=posix-raw 讓 CocoaPods 裡常見的符號連結原樣保留,不做轉換。

忽略規則:哪些目錄絕對不能同步

專案目錄下一定要建一份 .mutagenignore,否則第一次同步就會把幾個 GB 的快取目錄也傳一遍:

DerivedData/
Pods/
.build/
node_modules/
*.xcuserstate
xcuserdata/
.DS_Store

這些目錄的共同點是「可以在任一端本機重新產生」,同步過去不但浪費流量,還可能帶來下面這節說的坑。

Xcode 相關的幾個坑

實測表現與衝突處理

日常使用下,一次儲存觸發的增量同步(改動幾個 Swift 檔案)基本上一兩秒內就完成,肉眼感覺跟本機儲存差不多;第一次全量同步(數萬個原始檔案)因為要掃描目錄樹,會明顯慢一些,建議開工前先跑一次讓它穩定,而不是邊寫程式碼邊等首次同步。

衝突處理上,預設的雙向模式是 two-way-safe——遇到衝突不會自動覆蓋,而是停下來等你處理,這一點比強制覆蓋的方案安全,但如果兩邊都在改同一個檔案就會頻繁暫停。團隊協作場景更推薦:

mutagen sync create \
  --name=ios-app \
  --sync-mode=two-way-resolved \
  --default-file-mode-alpha=0644 \
  /Users/me/Projects/ios-app \
  ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app

two-way-resolved 會在衝突時預設讓本機(alpha 端)勝出,配合「一個人同一時間只在一端改程式碼」的約定,基本上不會丟失變更。真遇到二進位資源(圖片、字型)衝突,mutagen sync list 會列出衝突路徑,手動比對時間戳與檔案大小決定留哪一份。

一份可以直接抄的設定

Mutagen 支援專案層級設定檔,放在專案根目錄的 mutagen.yml 裡,團隊成員複製倉庫後一行命令就能啟動同步,不用每個人手動敲一遍參數:

sync:
  ios-app:
    alpha: "."
    beta: "ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app"
    mode: "two-way-resolved"
    ignore:
      vcs: true
      paths:
        - "DerivedData"
        - "Pods"
        - "node_modules"
        - "xcuserdata"
        - ".build"
    symlink:
      mode: "posix-raw"

常用的日常命令記這幾個就夠:

mutagen sync list
mutagen sync monitor ios-app
mutagen sync pause ios-app
mutagen sync resume ios-app
mutagen sync terminate ios-app

monitor 在排查「怎麼改動沒同步過去」時特別好用,能即時看到掃描、暫存、傳輸三個階段各卡在哪一步。

上線前自查清單

把這幾項過一遍,雲端 Mac 的編輯體驗基本上就能貼近本機,團隊協作時也不會再因為「誰的變更沒同步過去」互相甩鍋。

常見問題

為什麼不直接用 SMB/NFS 把雲端 Mac 目錄掛載到本地?

網路掛載對大量小檔案(如 Xcode 索引、Pods 目錄)的每次存取都要走一次網路往返,大型 iOS 專案開啟或索引常常要卡上幾十秒;Mutagen 走本地檔案系統快取加增量同步,互動延遲更低,斷線也不會讓檔案系統假死。

DerivedData 要不要同步?

不要。DerivedData 裡的模組快取、索引檔案跟機器架構與本機路徑綁定,同步過去很容易觸發重新索引甚至編譯錯誤,應該把它加進 .mutagenignore,讓本地與雲端各自獨立產生。

雙向同步出現衝突怎麼處理?

先用 mutagen sync list 定位衝突檔案,大多數情況選 two-way-resolved 模式並讓 alpha 端(本地)優先;少數二進位資源衝突再手動 diff 決定保留哪一份,同步前先跑一次 git status 清理工作區能避免大部分衝突。

HireVPS

今天就試試獨享雲端 Mac mini

按日起租,2 分鐘取得 SSH/VNC 憑證,配置隨時可升級。

立即租用 Mac mini