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 都能帶來:
- 更小的檢出 – 複製時,你只會拿到大型檔案的最新一份副本,而不是每一份副本。
- 更快的複製 – 因為不用下載大型檔案,每次複製都很快。
- 快速上手 – 不像 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 diff、git blame 和 git 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 的運作方式會是這樣:
- 你把大型檔案推送到 Git 託管平台。
- 在背景中,你的 Git 託管平台會把那個大型檔案卸載到 large object promisor 上。
- 當你複製時,Git 託管平台會告訴你的 Git 客戶端關於 promisor 的資訊。
- 你的客戶端會從 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 編輯
後來,其他 Git 託管平台也打造了自己的 LFS 伺服器。如今,你可以推送到多個 Git 託管平台或使用 LFS 傳輸代理,但這些都會讓貢獻者的設定變得更複雜。除非你額外花功夫去解鎖,否則基本上還是被綁住了。↩︎
隨機一篇部落格
留言
登入後參與討論