Git LFS:版本控制中的大型檔案管理

版本控制系統擅長追蹤文字檔案。開發者提交程式碼、審查差異、無縫合併變更。但當引入大型二進位檔案——機器學習模型權重、影片素材、編譯後的二進位檔——Git 就會陷入停滯。儲存庫膨脹到數 GB。複製需要數小時。簡單的操作就會逾時。 傳統的 Git 會在儲存庫歷史中儲存每個檔案的每個版本。一個 100MB 的檔案修改十次會消耗 1GB 的儲存庫空間。每個複製儲存庫的開發者都會下載所有版本,即使他們只需要最新版本。讓 Git 在程式碼管理上如此強大的分散式特性,在處理大型檔案時反而成為負擔。 Git Large File Storage (LFS) 透過將大型檔案替換為儲存庫中的小型指標檔案來解決這個問題。實際的檔案內容存放在獨立的伺服器上。開發者只下載他們需要的版本。儲存庫保持小巧且快速。 這種方法聽起來很理想,但 Git LFS 引入了複雜性、基礎設施需求和新的故障模式。了解 LFS 何時能增加價值——以及何時更簡單的方法就足夠——決定了它是解決問題還是製造問題。 本文探討 Git 中大型檔案的技術挑戰,檢視 Git LFS 的運作方式,提供何時採用它的指引,並針對不同情境提供替代方案。

大型檔案問題

Git 的架構在處理大型二進位檔案時會產生根本性的問題。

Git 如何儲存檔案

Git 的儲存模型針對文字進行最佳化:

📝Git 儲存架構

物件儲存

  • 每個檔案版本都是一個 blob 物件
  • 儲存在 .git/objects/ 目錄中
  • 壓縮但完整的副本
  • 對相似檔案進行差異壓縮 儲存庫成長
  • 每次提交都會新增新的 blob
  • 歷史包含所有版本
  • 複製會下載整個歷史
  • 無法只擷取部分歷史 文字 vs 二進位
  • 文字:差異壓縮效果良好
  • 二進位:壓縮通常無效
  • 小型文字變更:小型差異
  • 小型二進位變更:完整的新副本 當你提交一個 10KB 的原始檔時,Git 會有效率地儲存它。修改一行,Git 只儲存差異。但二進位檔案很少能有效壓縮。一個稍微修改的 500MB 機器學習模型仍然需要儲存另外 500MB。

實際影響

大型檔案會產生具體的問題:

儲存庫膨脹

情境:機器學習模型訓練 資料科學團隊在每次訓練執行後提交模型權重:

  • 初始模型:500MB
  • 20 次訓練迭代後:20 個版本
  • 儲存庫大小:10GB
  • 在快速連線上的複製時間:45 分鐘 影響:
  • 新團隊成員需要等待數小時才能開始工作
  • CI/CD 管線逾時
  • Git 操作變慢
  • 開發者避免拉取更新 成本:
  • 生產力損失:每位開發者每週 2 小時
  • 基礎設施:更大的儲存空間、更多頻寬
  • 挫折感:「Git 壞掉了」
網路瓶頸

情境:遊戲素材開發 遊戲工作室在 Git 中追蹤 3D 模型和材質:

  • 100 個高解析度材質:每個 50MB
  • 50 個 3D 模型:每個 20MB
  • 6 個月的歷史
  • 儲存庫大小:15GB 影響:
  • 連線速度慢的遠端開發者無法工作
  • 推送/拉取操作需要 30 分鐘以上
  • 二進位檔案的合併衝突無法解決
  • 團隊考慮放棄 Git 成本:
  • 遠端工作變得不可能
  • 協作中斷
  • 失去版本控制的好處
儲存成本

情境:影片製作 影片團隊提交原始素材以進行版本控制:

  • 4K 影片片段:每分鐘 1GB
  • 專案生命週期中有 100 個片段
  • 每個片段有多個版本
  • 儲存庫大小:500GB 影響:
  • 超過 GitHub/GitLab 儲存限制
  • 自架伺服器需要昂貴的儲存空間
  • 備份變得昂貴且緩慢
  • 儲存庫變得無法維護 成本:
  • 儲存:雲端託管每月 $500
  • 備份:每月 $200
  • 開發者時間:每月 10 小時管理問題
  • 總計:一個儲存庫每月 $1,200

Git LFS 架構

Git LFS 用指標替換大型檔案,同時將實際內容分開儲存。

LFS 如何運作

核心機制是指標替換:

📝LFS 指標系統

指標檔案 version https://git-lfs.github.com/spec/v1 oid sha256

size 133742 發生的事情

  1. 開發者提交大型檔案
  2. LFS 將檔案上傳到 LFS 伺服器
  3. Git 儲存小型指標檔案(130 位元組)
  4. 儲存庫保持小巧 檢出時
  5. Git 檢出指標檔案
  6. LFS 偵測到指標
  7. LFS 從伺服器下載實際檔案
  8. 用真實檔案替換指標 好處
  • 儲存庫只包含指標
  • 複製只下載目前版本
  • 歷史保持輕量
  • 大型檔案有效率地儲存 指標檔案非常小——無論實際檔案大小如何,大約 130 位元組。一個 5GB 的模型權重在 Git 歷史中變成 130 位元組的指標。儲存庫保持快速。

LFS 伺服器架構

LFS 需要額外的基礎設施:

graph TB Dev[開發者] Git[Git 儲存庫] LFS[LFS 伺服器] Storage[物件儲存] Dev -->|git push| Git Dev -->|lfs push| LFS LFS -->|儲存| Storage Dev2[另一位開發者] Dev2 -->|git clone| Git Dev2 -->|lfs pull| LFS Storage -->|擷取| LFS style Git fill:#f9f,stroke:#333,stroke-width:2px style LFS fill:#bbf,stroke:#333,stroke-width:2px style Storage fill:#bfb,stroke:#333,stroke-width:2px
📝🏗️ LFS 基礎設施

元件

  • Git 儲存庫:儲存指標
  • LFS 伺服器:管理大型檔案
  • 物件儲存:儲存實際內容
  • 身份驗證:控制存取 託管選項
  • GitHub:免費 1GB,提供付費方案
  • GitLab:每個儲存庫免費 10GB
  • Bitbucket:免費 1GB,提供付費方案
  • 自架:完全控制,更複雜 需求
  • 與 Git 分開的儲存空間
  • 上傳/下載的網路頻寬
  • 身份驗證整合
  • 備份策略 與完全分散式的一般 Git 不同,LFS 引入了集中式元件。LFS 伺服器成為關鍵依賴項。如果它停機,開發者就無法存取大型檔案。

何時使用 Git LFS

LFS 解決特定問題,但並非總是正確的選擇。

適合 LFS 的候選檔案

LFS 適用於某些檔案類型:

積極開發中的二進位素材

遊戲工作室和設計團隊處理在開發過程中頻繁變更的二進位素材。一個 3D 角色模型可能會經歷數十次迭代,因為藝術家會調整比例、材質和動畫。行銷材料的設計檔案會隨著利害關係人提供回饋而演變。音訊片段會針對時間和混音進行調整。 這些檔案對於一般 Git 來說太大了——高解析度材質可能是 50MB,角色模型 30MB,Photoshop 合成檔 100MB。沒有 LFS,儲存庫在幾週的開發後就會膨脹到數 GB。但這些素材需要版本控制。藝術家需要回復變更、比較版本,並在不覆蓋彼此工作的情況下協作。 LFS 完美地解決了這個問題。儲存庫保持小巧——即使有數百個素材也低於 100MB。藝術家可以直接提交而不用擔心儲存庫大小。版本歷史得以保留。當發生衝突時,它們在 Git 工作流程中是可見的。團隊獲得版本控制的所有好處,而沒有效能損失。 範例:遊戲開發

  • 角色模型:每個 50MB
  • 材質檔案:每個 20MB
  • 音訊片段:每個 10MB
  • 總計:500 個檔案,15GB 的素材
  • 使用 LFS 的儲存庫大小:80MB
機器學習模型檢查點

資料科學家訓練模型需要追蹤實驗。使用不同超參數訓練的模型會產生不同的權重。比較這些版本需要保留多個檢查點。沒有版本控制,團隊會採用手動命名方案——model_v1.bin、model_v2_final.bin、model_v2_final_actually_final.bin——這很快就會變得無法維護。 模型權重通常範圍從 100MB 到 4GB。這些檔案對於一般 Git 來說太大,但對 LFS 來說很完美。關鍵好處是將程式碼連結到模型。當你檢出特定提交時,你會同時獲得訓練程式碼和它產生的模型權重。這實現了真正的可重現性——你可以驗證特定模型來自特定的程式碼和超參數。 LFS 適用於最大約 4GB 的模型——選擇這個大小限制是為了適應大多數檔案系統。超過這個大小,像 DVC 或 Weights & Biases 這樣的專門工具提供更好的工作流程。但對於小型到中型模型,LFS 提供了最簡單的版本控制路徑。 範例:深度學習專案

  • 模型檢查點:每個 200MB - 4GB
  • 10 個實驗,每個 5 個檢查點
  • 總計:50 個檔案,10GB
  • 使用 LFS 的儲存庫大小:120MB
  • 好處:程式碼和模型保持同步
文件素材

技術文件通常包含二進位素材——影片教學、專有格式的架構圖、PDF 匯出。這些素材應該與它們記錄的程式碼一起版本化。當程式碼變更時,文件會更新。保持它們同步可以防止文件過時的常見問題。 文件素材的變更頻率低於程式碼,使它們成為 LFS 的理想選擇。影片教學可能錄製一次,每季更新一次。架構圖隨著主要版本演變。適中的檔案大小——通常 10MB 到 200MB——和不頻繁的更新意味著 LFS 儲存成本保持低廉。 替代方案是分開儲存文件,但這會破壞程式碼和文件之間的連結。使用 LFS,檢出發布標籤會同時給你程式碼和描述它的文件。撰寫者可以直接提交到儲存庫。團隊維護單一真實來源。 範例:產品文件

  • 影片教學:每個 100MB
  • 圖表來源(Visio、Sketch):每個 10MB
  • PDF 匯出:每個 5MB
  • 總計:50 個檔案,2GB
  • 使用 LFS 的儲存庫大小:90MB

LFS 不適用的情況

許多情境不適合使用 LFS:

⚠️真正的大型檔案(> 4GB)

LFS 不支援部分檔案下載。當你檢出檔案時,你會下載整個檔案。這使得 LFS 對於大於約 4GB 的檔案不切實際——選擇這個大小限制是為了適應包括 FAT32 在內的大多數檔案系統。 一個 50GB 的原始影片檔案在典型連線上需要數小時才能下載。一個 100GB 的資料集對於 LFS 工作流程來說太大了。即使你的網路可以處理,LFS 伺服器儲存成本也會變得過高。一個 50GB 檔案的十個版本會消耗 500GB 的 LFS 儲存空間。 對於這些檔案,帶有參考的外部儲存效果更好。將檔案儲存在 S3 或類似的物件儲存中。將帶有儲存位置和校驗和的小型中繼資料檔案提交到 Git。只在需要時下載大型檔案。這種方法支援任何檔案大小,啟用部分下載,並且在規模上成本更低。 範例:影片製作

  • 4K 原始素材:每個檔案 50GB
  • 專案生命週期中有 20 個片段
  • 總計:1TB
  • LFS 成本:過高
  • 更好的方案:S3 搭配 Git 中的清單
⚠️建置產物

編譯後的二進位檔、打包的應用程式和其他建置輸出根本不應該在版本控制中。這些是生成的檔案——建置過程的輸出,而不是來源輸入。版本控制是用於來源的。 提交建置產物會產生問題。儲存庫隨著每次建置而成長。開發者下載他們不需要的產物。歷史充滿雜訊。當你需要特定建置時,你無法分辨是哪個原始碼產生的。 像 Artifactory 或 Nexus 這樣的產物儲存庫可以正確地解決這個問題。它們儲存建置輸出,並帶有連結到來源提交的中繼資料。你可以擷取任何建置並追溯到確切的原始碼。儲存針對二進位檔進行最佳化。舊產物可以自動清理。這是適合這項工作的正確工具。 範例:應用程式發布

  • 編譯後的二進位檔:200MB
  • 每日建置:每年 365 次
  • 總計:每年 73GB
  • 錯誤:LFS 或 Git
  • 正確:Artifactory 搭配 Git 標籤
⚠️頻繁變更的大型檔案

LFS 儲存隨著每個版本而成長。每天修改的 1GB 檔案每年會產生 365GB 的 LFS 儲存空間。資料庫傾印、日誌檔案和頻繁變更的快取檔案儲存成本高昂,且提供的價值很少。 這些檔案不受益於版本控制。你很少需要比較昨天的資料庫傾印和今天的。日誌檔案用日誌管理工具分析更好。快取檔案本質上是暫時的。追蹤它們的歷史浪費儲存空間且沒有好處。 解決方案很簡單:不要版本化這些檔案。將它們加入 .gitignore。在本地或適當的系統中儲存它們——資料庫用於資料,日誌聚合器用於日誌,暫存儲存用於快取。版本控制是用於歷史重要的檔案。 範例:開發資料庫

  • 資料庫傾印:2GB
  • 開發期間每天更新
  • 30 天:60GB 的 LFS 儲存空間
  • 價值:最小(只需要最新的)
  • 更好的方案:本地檔案,根據需要重新生成
⚠️沒有可用的 LFS 伺服器

LFS 需要 Git 之外的基礎設施。某些企業網路會封鎖 LFS 端點。某些 Git 託管提供者不支援 LFS。自架需要維護 LFS 伺服器和物件儲存。 沒有 LFS 基礎設施,你無法推送或拉取大型檔案。對於需要這些檔案的團隊成員來說,儲存庫變得無法使用。這種基礎設施依賴性是一個真正的限制——與完全分散式的一般 Git 不同,LFS 引入了必須可用的集中式元件。 如果 LFS 基礎設施不可用或不可靠,請使用替代方法。帶有參考的外部儲存不需要特殊基礎設施。像 DVC 這樣的專門工具可以使用任何 S3 相容的儲存。有時最簡單的解決方案是完全不將大型檔案納入版本控制。

實際實作

有效使用 LFS 需要了解其工作流程和限制。

設定 Git LFS

基本設定很簡單:

📝LFS 設定步驟

安裝 安裝 LFS: git lfs install 追蹤檔案類型: git lfs track “.psd” git lfs track “.bin” git lfs track “models/*.h5” 提交追蹤設定: git add .gitattributes git commit -m “Configure LFS tracking” 建立的內容 .gitattributes 檔案: *.psd filter=lfs diff=lfs merge=lfs -text .bin filter=lfs diff=lfs merge=lfs -text models/.h5 filter=lfs diff=lfs merge=lfs -text 使用 LFS 正常新增和提交: git add model.bin git commit -m “Add trained model” 推送會同時傳送到 Git 和 LFS: git push origin main 複製會自動擷取 LFS 檔案: git clone https://github.com/user/repo.git .gitattributes 檔案告訴 Git 哪些檔案要用 LFS 處理。一旦設定完成,LFS 對大多數操作來說都是透明的。

常見工作流程

不同的情境需要不同的方法:

📝LFS 工作流程

選擇性檢出 複製時不下載 LFS 檔案: GIT_LFS_SKIP_SMUDGE=1 git clone repo.git 稍後下載特定檔案: git lfs pull —include=“models/production/清理舊版本 從本地快取移除舊的 LFS 檔案: git lfs prune 只保留最近的版本: git lfs prune —verify-remote —recent 遷移現有檔案 將現有檔案轉換為 LFS: git lfs migrate import —include=“.psd” 重寫歷史(小心!): git lfs migrate import —include=“*.bin” —everything 檢查 LFS 狀態 查看哪些檔案被追蹤: git lfs ls-files 檢查 LFS 儲存使用量: git lfs env

疑難排解常見問題

LFS 引入了新的故障模式:

⚠️常見 LFS 問題

「This exceeds GitHub’s file size limit」

  • 原因:檔案在沒有 LFS 追蹤的情況下提交
  • 解決方案:在提交前設定 .gitattributes
  • 預防:及早設定 LFS 追蹤 「Error downloading object」
  • 原因:LFS 伺服器無法連線或檔案遺失
  • 解決方案:檢查網路,驗證 LFS 伺服器狀態
  • 因應措施:暫時跳過 LFS 檔案 「Encountered X file(s) that should have been pointers」
  • 原因:檔案在設定 LFS 之前提交
  • 解決方案:使用 git lfs migrate 修復歷史
  • 預防:在第一次提交前設定 LFS 複製/拉取緩慢
  • 原因:下載許多大型 LFS 檔案
  • 解決方案:使用 GIT_LFS_SKIP_SMUDGE=1 進行選擇性下載
  • 替代方案:只擷取需要的檔案

Git LFS 的替代方案

許多情境有比 LFS 更好的解決方案。

帶有參考的外部儲存

對於真正的大型檔案,改為儲存參考:

💡基於參考的方法

架構

  • 將檔案儲存在 S3、GCS 或類似服務中
  • 在 Git 中提交中繼資料和參考
  • 根據需要下載檔案
  • 透過物件儲存進行版本控制 範例結構 repo/ ├── models/ │ ├── config.yaml # 在 Git 中 │ └── download.sh # 在 Git 中 └── data/ ├── manifest.json # 在 Git 中 └── fetch_data.py # 在 Git 中 好處
  • 不需要 LFS 基礎設施
  • 支援任何檔案大小
  • 靈活的儲存選項
  • 規模化時成本更低
  • 可能進行部分下載 這種方法適用於資料集、大型模型和影片檔案。儲存庫保持小巧且快速。儲存成本更低。團隊有更多靈活性。

專門工具

不同領域有專門建置的解決方案:

📝🛠️ 領域特定工具

機器學習

  • DVC (Data Version Control):類似 Git 的資料/模型版本控制
  • Weights & Biases:實驗追蹤
  • MLflow:模型註冊表
  • Hugging Face:模型託管 遊戲開發
  • Perforce:專為大型二進位檔案設計
  • Plastic SCM:處理大型素材效果良好
  • Unity Collaborate:為 Unity 專案建置 媒體製作
  • Frame.io:影片協作
  • Dropbox:簡單的檔案同步
  • Resilio Sync:P2P 檔案同步 建置產物
  • Artifactory:通用產物儲存庫
  • Nexus:Maven/npm/Docker 註冊表
  • Docker Hub:容器映像 這些工具比通用版本控制更好地解決特定問題。它們了解領域需求並相應地進行最佳化。

結論

Git LFS 為需要版本控制的二進位素材團隊解決了實際問題。它保持儲存庫快速,同時為否則會使 Git 膨脹的檔案保留歷史。 但 LFS 不是通用解決方案。它需要基礎設施、增加複雜性,並有大小限制。對於大於 4GB 的檔案,帶有中繼資料參考的專門儲存效果更好。對於建置產物,專用的產物儲存庫更合適。對於大量資料集,像 DVC 這樣的工具提供更好的工作流程。 關鍵是將工具與問題匹配。對積極開發中的二進位素材使用 LFS——3D 模型、設計檔案、小型機器學習模型。對大型靜態檔案使用外部儲存。對領域特定需求使用專門工具。對不需要版本控制的檔案什麼都不用。 Git LFS 在正確應用時很強大。了解其優勢和限制可確保它解決問題而不是製造問題。

💡決策框架

使用 Git LFS 當:

  • 二進位檔案需要版本歷史
  • 檔案大小為 10MB - 2GB
  • 團隊在素材上協作
  • LFS 基礎設施可用 使用外部儲存當:
  • 檔案超過 4GB
  • 不需要詳細歷史
  • 需要部分下載
  • 非常頻繁的更新 使用專門工具當:
  • 領域特定需求
  • 需要進階功能
  • 團隊已經在使用它們
  • 更適合的工作流程 什麼都不用當:
  • 檔案是生成的產物
  • 暫時或快取檔案
  • 可以輕鬆重新建立
  • 不需要協作