由於 Git 分散式的特性,所以每個開發者都可以在自己的電腦上建立一個完整的 Git 儲存庫,這樣就可以在沒有網路的情況下進行開發,而且每個開發者都可以在自己的電腦上建立多個分支,這樣就可以在開發新功能時,不會影響到其他開發者的工作。不過,缺點就是在 git clone
的時候會大量的下載資料,尤其是專案很大的時候,這個問題就會特別明顯了!有沒有辦法可以解決呢?可以的,這篇文章我將分享 Git 的 Partial Clone (部分複製) 功能,讓你更有效率的下載專案原始碼!
-
不想下載完整的 Repo 回來,尤其是超大的 Blob
物件能不下載就不下載
這個技巧又稱 blobless clone
,以下我就一個簡單的例子來比較一下差異。
假設我們先下載 ASP.NET MVC 5 原始碼回來:
git clone https://github.com/aspnet/AspNetWebStack.git
下載時的訊息如下:
Cloning into 'AspNetWebStack'...
remote: Enumerating objects: 50283, done.
remote: Counting objects: 100% (14945/14945), done.
remote: Compressing objects: 100% (1870/1870), done.
remote: Total 50283 (delta 13240), reused 13297 (delta 13065), pack-reused 35338
Receiving objects: 100% (50283/50283), 14.86 MiB | 2.08 MiB/s, done.
Resolving deltas: 100% (42518/42518), done.
你可以從 Enumerating objects: 50283, done.
看的出來,目前在 Git 版本庫中總共有 50,283
個 Git 物件,而且總共佔用了 14.86 MiB
的空間。
我們改用 --filter=blob:none
參數(部分複製)來下載專案:
git clone --filter=blob:none https://github.com/aspnet/AspNetWebStack.git AspNetWebStack_partial
下載時的訊息如下:
Cloning into 'AspNetWebStack_partial'...
remote: Enumerating objects: 21077, done.
remote: Counting objects: 100% (614/614), done.
remote: Compressing objects: 100% (206/206), done.
remote: Total 21077 (delta 454), reused 517 (delta 398), pack-reused 20463
Receiving objects: 100% (21077/21077), 2.91 MiB | 2.93 MiB/s, done.
Resolving deltas: 100% (16355/16355), done.
remote: Enumerating objects: 3367, done.
remote: Counting objects: 100% (3076/3076), done.
remote: Compressing objects: 100% (2338/2338), done.
remote: Total 3367 (delta 1254), reused 812 (delta 738), pack-reused 291
Receiving objects: 100% (3367/3367), 3.09 MiB | 3.11 MiB/s, done.
Resolving deltas: 100% (1352/1352), done.
Updating files: 100% (3402/3402), done.
你可以看的出來,我們只需要下載 21,077
個 Git 物件而已,而且總共佔用了 2.91 MiB
的空間而已,相當有效率!👍
另外,上面你也會看到 remote: Enumerating objects: 3367, done.
這段訊息,這段是因為我們 git clone
時,也會順便做一次 git checkout
將檔案取出到工作目錄的關係,所以會再次下載所需的相關物件。
Git 有四種物件類型,分別是 blob
, tree
, commit
, tag
這四種,詳細介紹可以參見我的 第 06 天:解析 Git 資料結構 - 物件結構 文章。透過 --filter=blob:none
參數來複製專案時,就會排除 Git 的 Blob 類型物件,大幅降低下載的物件數量。
blob
物件:就是工作目錄中某個檔案的 "內容",且只有內容而已,當你執行 git add
指令的同時,這些新增檔案的內容就會立刻被寫入成為 blob
物件,檔名則是物件內容的雜湊運算結果,沒有任何其他其他資訊,像是檔案時間、原本的檔名或檔案的其他資訊,都會儲存在其他類型的物件裡 (也就是 tree
物件)。
下載的物件中因為包含了 commit
物件,因此你可以透過 git log
指令來查看歷史記錄,這些物件內容都已經下載回來了。
你可能會想問,那如果用 git diff
查詢歷史版本的檔案內容,或使用 git switch
切換分支時那怎麼辦?你放心,Git 會在需要 blob
物件時自動下載這些檔案回來,只是下載的時候就需要網路了。
-
不想下載完整的 Repo 回來,尤其是超大的 Blob
物件與檔案目錄資訊 Tree
物件也要盡可能的能不下載就不下載
這個技巧又稱 treeless clone
,他會自動排除 tree
與 blob
物件,只下載需要的物件回來。
命令如下:
git clone --filter=tree:0 https://github.com/aspnet/AspNetWebStack.git AspNetWebStack_partial
以下是執行時的訊息:
Cloning into 'AspNetWebStack_partial'...
remote: Enumerating objects: 2223, done.
remote: Counting objects: 100% (33/33), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 2223 (delta 2), reused 18 (delta 2), pack-reused 2190
Receiving objects: 100% (2223/2223), 590.81 KiB | 1.83 MiB/s, done.
Resolving deltas: 100% (21/21), done.
remote: Enumerating objects: 352, done.
remote: Counting objects: 100% (171/171), done.
remote: Compressing objects: 100% (164/164), done.
remote: Total 352 (delta 0), reused 48 (delta 0), pack-reused 181
Receiving objects: 100% (352/352), 129.83 KiB | 916.00 KiB/s, done.
remote: Enumerating objects: 3367, done.
remote: Counting objects: 100% (3076/3076), done.
remote: Compressing objects: 100% (2338/2338), done.
remote: Total 3367 (delta 1254), reused 812 (delta 738), pack-reused 291
Receiving objects: 100% (3367/3367), 3.09 MiB | 4.19 MiB/s, done.
Resolving deltas: 100% (1352/1352), done.
Updating files: 100% (3402/3402), done.
你可以看的出來,我們只需要下載 2,223
個 Git 物件而已,而且總共下載了 590.81 KiB
的空間而已,太讓人驚艷了!👍
何時會想這樣用了?我們最終不是還是要 Checkout 嗎?最後還是要下載 Blob 與 Tree 物件回來啊?是的,沒錯,但如果你只是想看 Git Log 就好,就可以連工作目錄都不要取出,不用取出檔案就可以用 git log
查看歷史紀錄。例如:
git clone --filter=tree:0 --no-checkout https://github.com/aspnet/AspNetWebStack.git AspNetWebStack_partial
cd AspNetWebStack_partial
git log
是不是很棒!😎
-
在執行 CI 時僅下載特定分支的最新版,我連完整的 Commit 物件都不要
這個技巧又稱 shallow clone
(淺層複製),你可以排除 tree
與 blob
物件,而且連大部分的 commit
物件都排除,只下載需要的物件回來。
我們在做 CI 的時候,通常都會取出某個分支的程式碼,如果你連 git log
都不需要,就可以不用取回 commit
物件,那麼你的下載量就可以更小,速度也更快。
命令如下:
git clone --filter=tree:0 --depth=1 -b main https://github.com/aspnet/AspNetWebStack.git AspNetWebStack_partial
這裡的 --depth=1
是取出的 commit 深度,而 -b main
是預設取出 main
分支到工作目錄。
-
最極端的 Partial Clone 方法
例如我想取回 10 筆 commit 記錄、沒有 tree、沒有 blob、沒有 tags,只取得工作目錄中特定一個資料夾的檔案就好,那就需要這個超級極端的 git clone
方法:
# Treeless clone + Shallow clone + No tags + No checkout + Sparse checkouts
git clone --filter=tree:0 --sparse --depth=10 --no-checkout --no-tags https://github.com/aspnet/AspNetWebStack.git AspNetWebStack_partial
以下是執行時的訊息:
Cloning into 'AspNetWebStack_partial'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 11 (delta 0), reused 1 (delta 0), pack-reused 0
Receiving objects: 100% (11/11), 8.11 KiB | 8.11 MiB/s, done.
是不是很扯?才下載 11 個物件而已! XD
然後你可以進入一個「空白」的工作目錄,透過 Sparse checkouts 功能只取出自己要的檔案就好,例如:
git sparse-checkout init --cone
git sparse-checkout set "src"
git checkout main
以上就是我的終極 Partial Clone 方法,你可以參考看看!😎
這個技巧其實我幾年前就知道了,為什麼今天才寫這篇文章呢?因為 Azure DevOps Services 上面的 Azure Repos 一直都沒有提供 partial clones 能力,我從 Jan 28, 2020 11:00 PM
就在 Developer Community 網站提出了這個需求,但直到 2023/10/31 的「今天」才部署到正式環境,所以大家現在就都可以開始用這個技巧了!👍