說到代碼版本管理,現在首屈一指的就是Git。感謝Linus教主於15年的創舉,從此碼農世界就改變了。Git千好萬好,就有一點不好,那就是對大型二進位文件不友好。對於一個有許多大型二進位文件的項目,比如視頻遊戲倉庫,對倉庫的管理和處理就非常頭疼。由於Git底層設計邏輯,每一次版本變化時候都是做全文件快照,如果如果一個大的二進位文件(比如視頻)有多次變化,那麼Git倉庫的大小將會是倍數級的增長,這樣在倉庫同步、團隊協作時候,尤其是成員初始Clone時候將一個噩夢。
當然針對這個問題Git開源社區也一直在想辦法解決,比如一個解決問題(不是根本)的辦法Git LFS。
還有一個比較優雅的辦法就是使用部分克隆(Partial Clone)
部分克隆Git的一項新功能,它用來取代了Git LFS,並通過控制Git下載(或者跳過)部分Git倉庫對象(快照)的方法從根本來是實現對大文件的管理。
部分克隆的發展受到業界的關注和參與包括Git翹楚GitHub和GitLab,以及IT巨頭微軟和谷歌。GitLab從12.4開始beta版本支持。
部分克隆可以大大加快獲取和克隆的速度,由於減少了Git對象的下載,可以節省網絡傳輸,也可以大大節約本地用戶的磁碟空間。
和部分克隆相伴隨著的還有一種選擇性下載方法,那就是稀疏籤出(Sparse Checkout),稀疏籤適合具有大量文件和版本的倉庫 。
Git大文件倉庫的探索史
git-annex
Git大文件倉庫的歷史要追溯到2010年的git-annex。git-annex 採用 Haskell Script 編寫,允許映射Git資料庫到文件,幫助用戶管理Git倉庫的文件,git-annex的設計中大文件是獨立於Git倉庫中保存的,在git倉庫中只保存文件名和文件元數據等信息用來實現大文件追蹤。
git-media
git-media 是使用 Ruby 語言開發的,和git-annex把大文件(media)存儲在本地定期同步的方法不同。 git-meida一個中心伺服器來存儲大文件的方案。用於通過git media sync命令進行同步
Git LFS
2015年,Git社區發布了自帶的解決方案Git LFS(Large File Storage),該方案類似於git-media。
Git LFS方案中可以把音樂、圖片、視頻等指定的大文件存在 Git 倉庫之外存儲,在 Git 倉庫中用一個元數據文件來代替(.gitattributes)。通過把大文件存儲在 Git 倉庫之外,可以減小 Git 倉庫本身的體積,使克隆 Git倉庫的速度加快,也使得 Git 不會因為倉庫中充滿大文件而損失性能。
目前Git LFS 已經被主流的Git服務商支持,GitHub 和 GitLab 都對其提供內置支持。
但是這些解決方案中,都是通過把大文件置於Git倉庫之外存儲的針對問題的臨時解決方案而不是根本的解決方法。方案中要單獨維護大文件管理,對其同步和下載管理都比較繁瑣,而且其對應存儲在git倉庫中的額外的元屬性文件也會干擾Git倉庫管理。
針對這些問題,部分克隆可以避免維護兩種存儲和元屬性文件的管理的問題,從而從根本上優雅的解決大文件的問題。
部分克隆入門
我們以Gitlab gitlab about文檔站點為例子介紹。該項目是gitlab在線文檔倉庫,倉庫中有很多圖像,3.5G的文件。使用部分克隆(--filter=blob:none)進行部分克隆至少可以加快要50%,下載的數據可以減少70%。
對於更倉庫庫,比如具有詳細紋理和模型的視頻遊戲倉庫,部分克隆的性能提高將更加明顯。
和傳統的git clone相比較,部分克隆提供一個過濾器規範,該規範可控制下載Git對象時要排除的內容。比如,本例中,我們想要排除大型二進位文件。我們使用--no-checkout對比顯示效果。
上面我們通過--no-checkout選項,制定Git不籤出默認分支。通常情況下checkout不需要從伺服器獲取任何數據,因為在clone本地已經下載了所有對象。上面使用部分克隆時,由於故意設置沒有下載所有內容,因此Git在後續checkout操作時候需要獲取所有本地不存在的對象。
我們繼續籤出其他分支或提交時,則需要下載更多不存在的快照對象。
Git可以記得在克隆倉庫時提供的過濾器規範,以便獲取更新還可以排除大型文件,只會在需要這些文件,才會下載對應的快照對象。
git config remote.origin.promisor
# true
git config remote.origin.partialclonefilter
# blob:none
提交更改時,只需和其文件一樣提交二進位文件。無需安裝或配置額外的工具,無需將大文件與小文件區別對待。
部分克隆深入
易用性
Git LFS的缺點之一是它需要安裝其他工具。而部分克隆不需要任何其他工具。只需更新git版本,並學習git已有命令的一些新的選項。比如git clone的--filter選項。需要自己確定過濾器最佳的Blob大小或其他過濾器的參數。
網絡和存儲
如果使用過LFS的同學可能知道大文件的存儲和傳輸方式與常規Git對象是不一樣的,他們都是額外存儲的。由於要兼顧大存儲,所以最終的存儲媒介和一般文件不一樣(SSD),所以下載要慢一點。而部分存儲則是完全存在Git對象中,可以完全享受Git快速存儲的優勢。以Gitlab的部分存儲為例,他們可以支持多區域伺服器,可以根據自己的位置選擇離自己較近位置的區域服務,這樣可以實現更好的下載性能。
性能優化
當使用過濾器規範從Git伺服器排除部分對象的下載時,Git會檢查每個對象並排除任何與過濾規範匹配的對象。Git最新版本2.25中,尚未針對性能優化過濾性能優勢不明顯。但是Git開源社區也在促進這部分優化完善,Jeff King提交提交了一個部分克隆Blob大小過濾的性能改進補丁,該補丁可以極大提高性能,預計會在Git 2.26中發布。伺服器側GitLab預計在4月22的12.10版本中會引入。
對--filter:sparse基於文件路徑進行過濾的稀疏過濾器的優化則更複雜,由於包含文件內容的Blob不包含文件路徑信息,存儲庫的目錄結構存儲在樹對象中。
文件鎖定和工具集成
對大倉庫的管理,還有一個需要考慮的問題就是對客戶端用戶文件鎖定和工具集成的。和純文本原始碼不同,解決二進位文件不同版本之間的衝突通常是不可能的。為了防止二進位文件編輯中的衝突,一般會使用排他文件鎖定,這意味著一次只能由一個人編輯文件,而與分支無關。如果無法解決衝突,則允許在不同分支上並行創建文件的多個版本會報錯。Git伺服器端已經具有基本的文件鎖定支持,但只對對純文本有用,也僅適用於默認分支,並且還不能與任何本地工具集成。
本地工具集成對於二進位文件工作流,自動將文件鎖傳播到本地開發環境以及允許設計人員無需在命令行上使用Git的需求非常重要。將文件鎖快速傳播到本地開發環境也很重要,因為它可以防止在工作發生之前就浪費工作。
結論
大文件對於許多項目來說是必需的。儘管部分克隆還是一種實驗性功能,但是其基於git根本解決方案,具有天然的優勢,開源社區也對該功能進行不斷地優化和完善。一個功能成熟、高性能的Git大文件優雅管理的解決方案預計很快就有了。