The future of large files in Git is Git

Tyler Cipriani

Git 大型檔案的未來,就是 Git 本身

原文由 Tyler Cipriani 發布,訂閱此部落格

如果 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 專案推出了 partial clone,能帶來和 Git LFS 相同的好處:

透過部分複製,我們可以在 clone 與 fetch 操作時避免預先下載 [大型二進位資源],藉此減少下載時間與磁碟使用量。

– Partial Clone 設計筆記,git-scm.com

Git 的 partial clone 和 LFS 都能帶來:

  1. 更小的檢出 – 複製時,你只會拿到大型檔案的最新一份副本,而不是每一份副本。
  2. 更快的複製 – 因為不用下載大型檔案,每次複製都很快。
  3. 快速上手 – 不像 shallow clone,你會取得專案的完整歷史紀錄——可以馬上開始工作。

什麼是 partial clone?

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

舉例來說,如果想避免下載大於 100KB 的檔案,你可以這樣下指令:

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

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

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

$ 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 large object promisor

大型檔案也會為 Git 託管平台帶來麻煩。

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

但 Git 專案現在有了新的解法。

今年稍早,Git 合併了一項新功能:large object promisor。large object promisor 的目標是提供和 LFS 相同的伺服器端好處,同時省去對使用者的麻煩。

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

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

– Large Object Promisors,git-scm.com

什麼是 large object promisor?

large object promisor 是一種只存放大型檔案的特殊 Git remote。

在那個光明燦爛的未來,large object promisor 的運作方式會是這樣:

  1. 你把大型檔案推送到 Git 託管平台。
  2. 在背景中,你的 Git 託管平台會把那個大型檔案卸載到 large object promisor 上。
  3. 當你複製時,Git 託管平台會告訴你的 Git 客戶端關於 promisor 的資訊。
  4. 你的客戶端會從 Git 託管平台複製,並自動從 promisor remote 取得大型檔案。

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

Git large object promisor 仍在開發中。部分 large object promisor 的程式碼已在2025 年 3 月合併進 Git。但還有更多工作要做,也有尚未解決的開放問題

所以,就目前而言,處理超大檔案你還是得靠 Git LFS。但一旦 large object promisor 被廣泛採用,或許 GitHub 就會讓你推送超過 100MB 的檔案了。

Git 大型檔案的未來,就是 Git 本身。

Git 專案正在為大型檔案絞盡腦汁,這樣你就不用煩惱了。

今天,我們還離不開 Git LFS。

但不久之後,在 Git 中使用大型檔案的唯一阻礙,就只剩下你隱約記得的那種不祥預感——把整個 MP3 音樂庫塞進 Git 似乎不是個好主意。


Refactoring English 編輯


  1. 後來,其他 Git 託管平台也打造了自己的 LFS 伺服器。如今,你可以推送到多個 Git 託管平台或使用 LFS 傳輸代理,但這些都會讓貢獻者的設定變得更複雜。除非你額外花功夫去解鎖,否則基本上還是被綁住了。↩︎

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

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

留言