The future of large files in Git is Git

Tyler Cipriani

Git 中大型檔案的未來就是 Git

如果 Git 有宿敵,那肯定就是大型檔案。

大型檔案會膨脹 Git 的儲存空間、拖慢 git clone 的速度,並對 Git 託管平台造成嚴重破壞。

2015 年,GitHub 推出了 Git LFS——一個用來迂迴解決大型檔案問題的 Git 擴充功能。但 Git LFS 也帶來了新的複雜性與儲存成本。

與此同時,Git 專案一直在默默處理大型檔案的問題。雖然 LFS 還沒有被淘汰,但最新的 Git 版本已經指出了通往 LFS 終將過時的未來的道路。

今天就能做的事:以 Git partial clone(部分複製) 取代 Git LFS

Git LFS 的運作方式是將大型檔案儲存在儲存庫之外。

透過 LFS 複製專案時,你會取得儲存庫的歷史紀錄與小型檔案,但會略過大型檔案。相反地,Git LFS 只會下載工作副本中需要的大型檔案。

2017 年,Git 專案推出了能提供與 Git LFS 相同優點的 partial clones

partial clone 讓我們得以在 clone 與 fetch 操作期間避免預先下載 [大型二進位資源],藉此縮短下載時間並減少磁碟使用量。

– Partial Clone Design Notes,git-scm.com

Git 的 partial clone 與 LFS 都能帶來:

  1. 更小的檢出 – 複製時,你只會取得大型檔案的最新版本,而非每一個版本。
  2. 更快的複製 – 因為避免下載大型檔案,每次複製都很快。
  3. 快速設定 – 不同於 shallow clones(淺層複製),你能取得專案的完整歷史紀錄——可以立即開始工作。

什麼是 partial clone?

Git 的 partial clone 就是帶有 --filter 的 clone。

舉例來說,若要避免下載大於 100KB 的檔案,你可以這樣操作:

git clone --filter='blobs:size=100k' <repo>

之後,Git 會在你實際需要檢出超過 100KB 的檔案時,才以延遲載入的方式下載。

預設情況下,如果我對一個包含惱人的 25 MB PNG 檔案多個版本的儲存庫執行 git clone,複製速度會很慢,檢出的檔案也會大得離譜:

$ time git clone https://github.com/thcipriani/noise-over-git
Cloning into '/tmp/noise-over-git'...
...
Receiving objects: 100% (153/153), 1.19 GiB

real    3m49.052s

光是檢出一個 25MB 的檔案就花了將近四分鐘!

$ du --max-depth=0 --human-readable noise-over-git/.
1.3G    noise-over-git/.
$ ^ 🤬

而同一個 25MB 檔案的 50 個版本就占去了 1.3GB 的空間。

但 partial clone 可以避開這些問題:

$ git config --global alias.pclone 'clone --filter=blob:limit=100k'
$ time git pclone https://github.com/thcipriani/noise-over-git
Cloning into '/tmp/noise-over-git'...
...
Receiving objects: 100% (1/1), 24.03 MiB

real    0m6.132s
$ du --max-depth=0 --human-readable noise-over-git/.
49M     noise-over-git/
$ ^ 😻 (the same size as a git lfs checkout)

我的 filter 讓複製速度快了 97%(3m 49s → 6s),並讓檢出大小減少了 96%(1.3GB → 49M)!

不過,這裡仍有一些需要注意的地方。

如果你執行的指令需要用到已被你過濾掉的資料,Git 就必須向伺服器請求才能取得。因此,像 git diffgit blamegit checkout 這類指令,都需要連線到你的 Git 託管平台才能執行。

不過,對於大型檔案來說,這跟 Git LFS 的行為是一樣的。

再說,我也想不起來上次對 PNG 執行 git blame 是什麼時候了 🙃。

何必這麼麻煩?Git LFS 有什麼問題?

Git LFS 把 Git 處理大型檔案的問題轉嫁給使用者。

而這些問題相當嚴重:

  • 🖕 高度廠商鎖定 – 當 GitHub 撰寫 Git LFS 時,其他大型檔案系統——Git Fat、Git Annex 與 Git Media——對伺服器端保持中立。但 GitHub 卻將使用者綁定在其專屬的伺服器實作上,並向使用者收費。1
  • 💸 成本高昂 – GitHub 之所以勝出,是因為它讓使用者可以免費託管儲存庫。但 Git LFS 一開始是付費產品。如今雖然有免費額度,但定價完全取決於 GitHub 的決策。今天,在 GitHub 上存放一個 50GB 的儲存庫,每年需支付 40 美元的儲存費用。相比之下,在 Amazon 的 S3 標準儲存空間存放 50GB,每年僅需 13 美元。
  • 😰 難以復原 – 一旦改用 Git LFS,若不重寫歷史紀錄,就無法復原。
  • 🌀 持續的設定成本 – 所有協作者都必須安裝 Git LFS。若未安裝 Git LFS,協作者拿到的將會是充滿中繼資料、令人困惑的文字檔,而非預期中的大型檔案。

未來:Git large object promisors(大型物件承諾者)

大型檔案也會為 Git 託管平台帶來問題。

GitHub 和 GitLab 對檔案大小設有限制2,因為大型檔案的託管成本更高。Git LFS 透過將大型檔案卸載至 CDN 來壓低伺服器端的成本。

但 Git 專案已經有了新的解決方案。

今年稍早,Git 合併了一項新功能:large object promisors。large object promisors 旨在提供與 LFS 相同的伺服器端優勢,同時免除對使用者造成的麻煩。

這項工作特別旨在改善伺服器端的情況,尤其是針對已經以二進位格式壓縮的大型 blob。

這項工作旨在提供 Git LFS 的替代方案

– Large Object Promisors,git-scm.com

什麼是 large object promisor?

large object promisors 是只存放大型檔案的特殊 Git 遠端。

在那個光明燦爛的未來,large object promisors 的運作方式將如下:

  1. 你將大型檔案推送至 Git 託管平台。
  2. 在背景中,你的 Git 託管平台會將該大型檔案卸載至 large object promisor。
  3. 當你執行 clone 時,Git 託管平台會將 promisor 的資訊告知你的 Git 用戶端。
  4. 你的用戶端會從 Git 託管平台 clone,並自動從 promisor 遠端取得大型檔案。

但我們距離那個光明燦爛的未來還有一段路要走。

Git large object promisors 仍在開發中。large object promisors 的部分功能已於2025 年 3 月合併至 Git。但仍有更多工作待完成,也有待解答的開放性問題

因此,就目前而言,處理超大檔案時你仍得依賴 Git LFS。但一旦 large object promisors 被廣泛採用,或許 GitHub 就會允許你推送大於 100MB 的檔案。

Git 中大型檔案的未來就是 Git。

Git 專案正絞盡腦汁思考大型檔案的問題,這樣你就不用煩惱了。

現今,我們仍受限於 Git LFS。

但不久之後,在 Git 中使用大型檔案的唯一阻礙,將只剩下你那模糊卻不祥的直覺——把 MP3 音樂庫塞進 Git 似乎不是個好主意。


Refactoring English 編輯


  1. 後來,其他 Git 託管平台也打造了自家的 LFS 伺服器。如今,你可以推送至多個 Git 託管平台或使用 LFS 傳輸代理程式,但這些都會讓貢獻者的設定變得更加困難。除非額外費心處理,否則你基本上仍處於被綁定的狀態。↩︎

  2. 檔案大小限制:GitHub 為 100MBGitLab.com 為 100MB↩︎

原文由 Tyler Cipriani 發布

本文章由 muse-spark-1.2-contributor 進行翻譯