我們現在有越來越多的專案都是前後端分離的架構,所以在一個 Git Repo 裡面同時放前後端程式碼是很常見的狀況。不過,問題來了,如果前後端分離的很乾淨,若是前端人員不想要看到「後端」的原始碼,那 Git 有辦法做到「部分取出」的功能嗎?是的,還真的有!這篇文章我就來說說這個好用的功能。
簡介 Sparse checkouts 功能
Sparse checkouts 是一個從 Git 2.25 才開始支援的功能,主要用途就是幫助你取得一個 Repo 的部分內容,大幅減少本機磁碟空間的佔用,也可以幫助你更加專注在當前的開發項目。這樣的機制尤其對目前 Monorepo 正夯的時候,提供一個絕佳的解決方案。
英文的 Sparse
是一個形容詞,表示「稀疏的」、「零落的」的意思,代表你僅會從 Repo 取出「零星的」檔案!我們從 Git 官網的 git-sparse-checkout 文件可以看到他的定義:
Reduce your working tree to a subset of tracked files
基本使用方式
而 Sparse checkouts 的使用方式,其實也就是兩個步驟而已:
-
先透過 git clone
取得一個 Repo 並啟用 Sparse checkouts 功能
這個動作是在複製遠端儲存庫內容時,直接啟用 Sparse checkouts 功能並套用預設樣式清單,預設只會取出根目錄下的檔案:
git clone --filter=blob:none --sparse https://github.com/doggy8088/MonorepoAspNetCoreWithAngular.git
cd MonorepoAspNetCoreWithAngular
注意: 這個命令依然會下載完整的 Git Repo 內容,只是預設不會取出所有檔案到工作目錄而已!
-
啟用圓錐模式 (Cone mode)
目前 Git 的 Sparse checkouts 支援兩種模式,預設為 非圓錐模式 (non-cone mode),但建議改用 圓錐模式 (cone mode) 效能比較好!
git sparse-checkout init --cone
-
接著你可以透過 git sparse-checkout
明確指定你想要取得的資料夾名稱
這個命令會指定取得指定資料夾下的所有檔案,但在圓錐模式下,預設會包含根目錄下的所有檔案喔!
git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"
TIPS: 快速下載超大 Repo 的小技巧
你可以在 git clone
的時候套用 --depth 1
命令,僅取得最近 1 個版本的相關 Blob 物件,這樣就可以大幅縮短 git clone
的時間。我以 ASP.NET Core 原始碼專案為例,正常的 git clone
在高速網路下也要十幾秒才能下載完畢,但透過以下指令就只要 2 秒就可以複製完畢:
git clone --depth 1 --filter=blob:none --sparse https://github.com/dotnet/aspnetcore.git
cd aspnetcore
注意: 使用 --sparse
預設就只會 取出 (Checkout) 根目錄下的所有檔案而已,但沒有任何「子資料夾」喔。
然而,你若只想取回 /docs
資料夾的話,就只要用以下命令即可即時下載這個資料夾下的用到的 Blob 物件,速度極快:
git sparse-checkout set "/docs"
簡介圓錐模式
Git 的 Sparse checkouts 支援兩種模式, 非圓錐模式 (non-cone mode) 與 圓錐模式 (cone mode),主要差別在於解析 路徑樣式清單 (Pattern Set) 的格式不同!
事實上,你使用 git sparse-checkout
所做的任何操作,都會記錄在一個 .git/info/sparse-checkout
設定檔中,這是一個文字檔 (Text file),內容跟 .gitignore
的格式有點相似,但你要特別注意在不同的模式下,其內容所代表的意義是不同的!
-
非圓錐模式 (non-cone mode)
先說,雖然這是 Git 的預設值,不過不建議使用,主要是效能考量!🔥
在 非圓錐模式 下,這個 .git/info/sparse-checkout
設定檔的內容會採用 FULL PATTERN SET
語法,其內容格式與 .gitignore
完全一樣。
那什麼是 FULL PATTERN SET
呢?簡單來說,就是這些 Patterns 是對整個 Repo 的檔案路徑進行比對,所以你可以用「正向比對」,也可以用 !
進行「反向比對」,設定上比較靈活。不過,這層靈活性的唯一的缺點就是效能較差,因為當 Git 在做 Checkout 動作時,每一個檔案在取出時,每一條規則都要判斷,所以複雜度是 O(M*N)
這麼多,這意味著檔案越多或規則集越多,取出(Checkout)的速度就越慢!
我舉個簡單的例子,以下列命令為例,該專案預設使用了非圓錐模式,然後指定了 MonorepoAspNetCoreWithAngular/ClientApp
路徑。事實上,這個 MonorepoAspNetCoreWithAngular/ClientApp
所代表的意義,是指「所有目錄與子目錄下有包含 MonorepoAspNetCoreWithAngular/ClientApp
的路徑,都在取出的範圍內」。
git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"
你如果設定 /MonorepoAspNetCoreWithAngular/ClientApp
路徑,就代表著只有「位於根目錄下的 /MonorepoAspNetCoreWithAngular/ClientApp
路徑,才包含在取出範圍內」。
git sparse-checkout set "/MonorepoAspNetCoreWithAngular/ClientApp"
-
圓錐模式 (cone mode)
在 圓錐模式 下,這個 .git/info/sparse-checkout
設定檔的內容會採用 CONE PATTERN SET
語法,這個語法只能說近似於 .gitignore
的格式,但比對上稍微再嚴格一些,而且有些特殊的規則。
基本上圓錐模式下的 CONE PATTERN SET
規則集有兩大特性:
Recursive
: 你所指定的路徑,預設包含所有的子目錄下的所有檔案路徑。
Parent
: 你所指定的路徑所在的目錄下的所有檔案也都包含在內!(有點繞口,需要思考一下)
我舉個簡單的例子,以下列命令為例,我將該專案啟用了圓錐模式,然後指定了 MonorepoAspNetCoreWithAngular/ClientApp
路徑:
git clone --filter=blob:none --sparse https://github.com/doggy8088/MonorepoAspNetCoreWithAngular.git
cd MonorepoAspNetCoreWithAngular
git sparse-checkout init --cone
git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"
這個 Pattern 將會包含:
Recursive
: 包含所有子目錄所有路徑 (MonorepoAspNetCoreWithAngular/ClientApp/**
)
Parent
: 包含上層的 /MonorepoAspNetCoreWithAngular/*
與 /*
目錄下的所有檔案
所以我們可以說,以下圓錐模式的樣式集(CONE PATTERN SET):
MonorepoAspNetCoreWithAngular/ClientApp
會等同於以下非圓錐模式的樣式集(FULL PATTERN SET):
/* 根目錄所有檔案
!/*/ 排除根目錄下的所子目錄 (沒有排除檔案)
/MonorepoAspNetCoreWithAngular/ 包含這個子目錄以及底下所有路徑(目錄+檔案)
!/MonorepoAspNetCoreWithAngular/*/ 排除這個子目錄下的所有子目錄 (沒有排除檔案)
/MonorepoAspNetCoreWithAngular/ClientApp/ 包含這個子目錄以及底下所有路徑(目錄+檔案)
在圓錐模式下,你無法排除已經包含在這些 Pattern Set 在內的所有檔案。
如果你真的想要精準的包含與排除檔案,只能使用非圓錐模式 (non-cone mode)!
為什麼要取名叫圓錐模式?
我從 Bring your monorepo down to size with sparse-checkout 文章中擷取兩張圖片來說明。
假設你的 Monorepo 有以下目錄結構:
若你只想取出 /client/android
目錄下的原始碼,那麼你若用圓錐模式的話,就只要用一條規則就可以取出你要的檔案:
git sparse-checkout set "client/android"
你從這個檔案取出的模式來看,整個資料夾結構就好像一個「圓錐體」,而取得檔案的方式是從上到下以「圓錐狀」的方式取出,圓錐的頂端就是專案的「根目錄」,而之後每一層的檔案也都需要取出,一直取到某一層之後,就會取出以下的所有檔案。
我只能說取名是一種藝術,大家要多發揮想像力! 😄
常用 Sparse checkouts 命令與參數
-
啟用 Sparse checkouts 功能
# 啟用非圓錐模式 (non-cone mode)
git sparse-checkout init
# 啟用圓錐模式 (cone mode)
git sparse-checkout init --cone
-
取得 Sparse checkouts 的路徑樣式清單
git sparse-checkout list
注意: 如果你的 Repo 並沒有啟用過 Sparse checkouts,第一次使用會出現 fatal: this worktree is not sparse
錯誤訊息。
-
設定 Sparse checkouts 的路徑樣式清單 (.git/info/sparse-checkout
)
使用 git sparse-checkout set
命令會直接覆蓋現有的路徑清單:
git sparse-checkout set "/MonorepoAspNetCoreWithAngular/ClientApp"
git sparse-checkout list
執行完 git sparse-checkout set
命令之後,你的工作目錄下所有檔案就會立即反應出結果,執行前請先確認擁有一個乾淨的工作目錄!
你可以使用以下命令調整 Sparse checkouts 回覆到預設值:
git sparse-checkout set ""
git sparse-checkout list
無論你使用哪種 Sparse 模式,執行完之後預設都只會剩根目錄下的檔案!
-
一次設定多組 Sparse checkouts 的路徑樣式清單
設定多組 Sparse checkouts 路徑樣式時,套用順序很重要!
git sparse-checkout set "!/*" "/MonorepoAspNetCoreWithAngular/ClientApp" ".git*"
git sparse-checkout list
此範例僅適用於非圓錐模式!(FULL PATTERN SET
)
-
加入新路徑到 Sparse checkouts 的路徑樣式清單
git sparse-checkout set "!/*" "/MonorepoAspNetCoreWithAngular/ClientApp"
git sparse-checkout add ".git*"
git sparse-checkout list
此範例僅適用於非圓錐模式!(FULL PATTERN SET
)
-
重新套用 Sparse checkouts 的路徑樣式清單
這個命令僅用於你手動調整 .git/info/sparse-checkout
設定檔內容之後執行。
git sparse-checkout reapply
-
停用 Sparse checkouts 功能
這個命令會將所有工作目錄中的檔案完整取出:
git sparse-checkout disable
注意: 此命令不會更動 .git/info/sparse-checkout
設定檔中的路徑樣式清單。
-
將 Sparse checkouts 功能切換至圓錐模式 (cone mode)
git sparse-checkout disable
git sparse-checkout init --cone
#git sparse-checkout set
git sparse-checkout list
由於圓錐模式與非圓錐模式的 Patterns 並不相容,切換過程記得要重新檢視過!
-
將 Sparse checkouts 功能切換至非圓錐模式 (non-cone mode)
git sparse-checkout disable
git sparse-checkout init
#git sparse-checkout set
git sparse-checkout list
由於圓錐模式與非圓錐模式的 Patterns 並不相容,切換過程記得要重新檢視過!
相關連結