簡體中文 ▾ 主題 ▾ 最新版本 ▾ partial-clone 上次更新於 2.49.0

“部分克隆”功能是 Git 的一種效能最佳化,它允許 Git 在沒有完整倉庫副本的情況下執行。這項工作的目標是讓 Git 更好地處理超大型倉庫。

在克隆和獲取操作期間,Git 會下載倉庫的完整內容和歷史記錄。這包括倉庫生命週期中的所有提交、樹和 blob。對於超大型倉庫,克隆可能需要數小時(或數天)並佔用 100+GiB 的磁碟空間。

在這些倉庫中,使用者常常不需要很多 blob 和樹,例如:

  1. 樹中位於使用者工作區域之外的檔案。例如,在一個每個提交中包含 50 萬個目錄和 350 萬個檔案的倉庫中,如果使用者只需要原始碼樹的一個狹窄“錐形”區域,我們可以避免下載許多物件。

  2. 大型二進位制資產。例如,在一個倉庫中,如果大型構建工件被簽入到樹中,我們可以避免下載這些不可合併二進位制資產的所有先前版本,而只下載實際引用的版本。

部分克隆允許我們在克隆和獲取操作期間 **提前** 避免下載這些不需要的物件,從而減少下載時間和磁碟使用量。缺失的物件可以在稍後按需“按需獲取”。

能夠稍後提供缺失物件的遠端倉庫稱為 promisor 遠端倉庫,因為它承諾在請求時傳送物件。最初 Git 只支援一個 promisor 遠端倉庫,即使用者從中克隆的 origin 遠端倉庫,並且該遠端倉庫配置在“extensions.partialClone”配置選項中。後來實現了對多個 promisor 遠端倉庫的支援。

使用部分克隆要求使用者線上,並且 origin 遠端倉庫或其他 promisor 遠端倉庫可用於按需獲取缺失的物件。這可能對使用者有問題,也可能沒有。例如,如果使用者可以停留在預先選擇的原始碼樹子集中,他們可能不會遇到任何缺失的物件。或者,如果使用者知道他們將離線,他們可以嘗試預先獲取各種物件。

非目標

部分克隆是一種限制 **在給定提交範圍** 內下載的 blob 和樹數量的機制——因此,它獨立於也不旨在與現有的 DAG 級機制衝突,例如限制請求的提交集(即淺克隆、單分支或獲取 引用規範)。

設計概述

部分克隆邏輯上由以下部分組成:

  • 一種客戶端向伺服器描述不需要或不希望的物件的方式。

  • 一種伺服器從傳送給客戶端的 packfile 中省略這些不希望的物件的方式。

  • 一種客戶端優雅地處理缺失物件(以前由伺服器省略)的方式。

  • 一種客戶端按需回填缺失物件的方式。

設計細節

  • 一個名為“filter”的新 pack-protocol 功能被新增到 fetch-pack 和 upload-pack 的協商中。

    這使用了現有的功能發現機制。請參閱 gitprotocol-pack[5] 中的“filter”。

  • 客戶端向 clone 和 fetch 傳遞一個“filter-spec”,該 spec 會傳遞給伺服器,以在 packfile 構建期間請求過濾。

    有各種可用的過濾器來適應不同的情況。請參閱 Documentation/rev-list-options.adoc 中的“--filter=<filter-spec>”。

  • 在伺服器端,pack-objects 在建立“filtered”packfile 時應用所請求的 filter-spec。

    這些 filtered packfile 在傳統意義上是 **不完整的**,因為它們可能包含引用 packfile 中未包含的物件,而客戶端尚沒有這些物件的物件。例如,filtered packfile 可能包含引用缺失 blob 的樹或標籤,或者引用缺失樹的提交。

  • 在客戶端,這些不完整的 packfile 被標記為“promisor packfiles”,並被不同命令以不同方式處理。

  • 在客戶端,將一個倉庫擴充套件新增到本地配置中,以防止舊版本的 git 由於無法處理的缺失物件而 mid-operation 失敗。請參閱 extensions.partialClonegit-config[1] 中。

處理缺失物件

  • 由於部分克隆或獲取,或者由於倉庫損壞,物件可能缺失。為了區分這些情況,本地倉庫將從 promisor 遠端倉庫獲得的此類 filtered packfile 特別標記為“promisor packfiles”。

    這些 promisor packfile 由一個具有任意內容的“<name>.promisor”檔案(類似於“<name>.keep”檔案)組成,除了它們的“<name>.pack”和“<name>.idx”檔案。

  • 本地倉庫將“promisor 物件”視為一個物件,它(盡其所能)知道 promisor 遠端倉庫承諾擁有它,要麼是因為本地倉庫在其 promisor packfile 中擁有該物件,要麼是因為另一個 promisor 物件引用了它。

    當 Git 遇到一個缺失的物件時,Git 可以判斷它是否是一個 promisor 物件並適當地處理它。如果不是,Git 可以報告損壞。

    這意味著客戶端無需顯式維護一個昂貴的、需要修改的缺失物件列表。[a]

  • 由於幾乎所有 Git 程式碼當前都期望任何引用的物件都存在於本地,並且我們不想迫使每個命令首先執行一次 dry-run,因此添加了一個回退機制,允許 Git 嘗試從 promisor 遠端倉庫動態獲取缺失的物件。

    當正常的物件查詢無法找到物件時,Git 會呼叫 promisor_remote_get_direct() 來嘗試從 promisor 遠端倉庫獲取物件,然後重試物件查詢。這允許物件在沒有複雜預測演算法的情況下被“容錯載入”。

    出於效率原因,不檢查缺失的物件是否確實是 promisor 物件。

    動態物件獲取通常很慢,因為物件是逐個獲取的。

  • checkout(以及任何使用 unpack-trees 的其他命令)已被教導一次性預取所有必需的缺失 blob。

  • rev-list 已被教導列印缺失的物件。

    這可以被其他命令用於批次預取物件。例如,“git log -p A..B”可能希望首先執行類似“git rev-list --objects --quiet --missing=print A..B”的操作,然後批次預取這些物件。

  • fsck 已更新為完全瞭解 promisor 物件。

  • GC 中的 repack 已更新為完全不觸碰 promisor packfile,並且只 repack 其他物件。

  • 全域性變數“fetch_if_missing”用於控制物件查詢是否會嘗試動態獲取缺失的物件或報告錯誤。

    我們對這個全域性變數不滿意,並希望刪除它,但這需要對物件程式碼進行大量重構,以傳遞一個額外的標誌。

獲取缺失物件

  • 物件的獲取是透過呼叫“git fetch”子程序來完成的。

  • 本地倉庫傳送一個包含所有請求物件雜湊的請求,並且不執行任何 packfile 協商。然後它會收到一個 packfile。

  • 由於我們正在重用現有的獲取機制,獲取當前會獲取請求物件所引用的所有物件,即使它們不是必需的。

  • 使用 --refetch 進行獲取將從遠端倉庫請求一個完整的新的 filtered packfile,該 packfile 可用於更改過濾器而無需動態獲取缺失的物件。

使用多個 promisor 遠端倉庫

可以配置和使用多個 promisor 遠端倉庫。

這允許使用者例如擁有多個地理位置靠近的快取伺服器來獲取缺失的 blob,同時繼續從中央伺服器進行過濾的 git-fetch 命令。

在獲取物件時,promisor 遠端倉庫會一個接一個地嘗試,直到所有物件都被獲取。

被視為“promisor”遠端倉庫的遠端倉庫是由以下配置變數指定的:

  • extensions.partialClone = <name>

  • remote.<name>.promisor = true

  • remote.<name>.partialCloneFilter = ...

只能使用 extensions.partialClone 配置變數配置一個 promisor 遠端倉庫。此 promisor 遠端倉庫將在獲取物件時作為最後一個被嘗試的。

我們決定將其作為我們嘗試的最後一個,因為使用多個 promisor 遠端倉庫的使用者很可能是因為其他 promisor 遠端倉庫在某些方面(例如,它們更近或對某些物件更快)比 origin 更好,而 origin 可能是 extensions.partialClone 指定的遠端倉庫。

這個理由不是很充分,但必須做出一個選擇,而且無論如何,長期計劃應該是使順序某種程度上完全可配置。

但目前,其他 promisor 遠端倉庫將按照它們在配置檔案中出現的順序進行嘗試。

當前限制

  • 除了它們在配置檔案中出現的順序之外,無法指定嘗試 promisor 遠端倉庫的順序。

    也無法指定在從一個遠端倉庫獲取時使用的順序,以及在從另一個遠端倉庫獲取時使用的不同順序。

  • 無法僅將特定物件推送到 promisor 遠端倉庫。

    無法按照特定順序同時推送到多個 promisor 遠端倉庫。

  • 動態物件獲取只會向 promisor 遠端倉庫請求缺失的物件。我們假設 promisor 遠端倉庫對倉庫有完整的檢視,並且可以滿足所有此類請求。

  • Repack 本質上將 promisor 和非 promisor packfile 視為 2 個不同的分割槽,並且不混合它們。

  • 動態物件獲取會 **為每個專案** 呼叫一次 fetch-pack,因為大多數演算法會遇到一個缺失的物件,並在繼續工作之前需要解決它。如果需要許多物件,這可能會產生顯著的開銷 — 以及多次身份驗證請求。

  • 動態物件獲取目前使用現有的 pack protocol V0,這意味著每個物件都透過 fetch-pack 請求。伺服器將在建立連線時傳送完整的 info/refs 集。如果存在大量引用,這可能會產生顯著的開銷。

未來工作

  • 改進指定 promisor 遠端倉庫嘗試順序的方法。

    例如,這可以允許明確指定類似:“當從這個遠端倉庫獲取時,我希望按此順序使用這些 promisor 遠端倉庫,但是,當推送到或從那個遠端倉庫獲取時,我希望按那個順序使用那些 promisor 遠端倉庫。”

  • 允許推送到 promisor 遠端倉庫。

    使用者可能希望使用多個 promisor 遠端倉庫進行三角形工作流,每個倉庫對倉庫都有不完整的檢視。

  • 允許基於非路徑名的過濾器使用 packfile 點陣圖(如果存在)。這只是在初始實現期間的疏忽。

  • 研究使用一個長期執行的程序來動態獲取一系列物件,如 [5,6] 中所述,以減少程序啟動和開銷成本。

    如果 pack protocol V2 允許該長期執行的程序透過一個長期執行的連線進行一系列請求,那將是很好的。

  • 研究 pack protocol V2 以避免每次與伺服器連線時廣播 info/refs 來動態獲取缺失的物件。

  • 研究處理 loose promisor 物件的需求。

    promisor packfile 中的物件允許引用可以從伺服器動態獲取的缺失物件。我們曾假設 loose 物件僅在本地建立,因此不應引用缺失的物件。如果,例如,我們動態獲取一個缺失的樹並將其儲存為 loose 物件而不是單個物件 packfile,我們可能需要重新審視這個假設。

    這並不一定意味著我們需要將 loose 物件標記為 promisor;放寬物件查詢或 is-promisor 函式可能就足夠了。

非任務

  • 每次出現“按需載入 blob”的主題時,似乎總有人建議允許伺服器“猜測”併發送可能與請求物件相關的附加物件。

    實際上還沒有進行任何這方面的工作;我們只是記錄這是一個常見的建議。我們不確定它將如何工作,也沒有計劃進行這項工作。

    伺服器傳送比請求更多的物件是有效的(即使是對於動態物件獲取),但我們不基於此進行構建。

腳註

[a] 昂貴的、需要修改的缺失物件列表:在部分克隆設計的早期,我們討論了對單個缺失物件列表的需求。這本質上是一個 OID 的排序線性列表,這些 OID 在克隆或後續獲取期間被伺服器省略。

此檔案需要在每次物件查詢時載入到記憶體中。在每次顯式的“git fetch”命令 **以及** 任何動態物件獲取時,它都需要被讀取、更新和重寫(類似於 .git/index)。

如果存在許多缺失的物件,讀取、更新和寫入此檔案的成本可能會為每個命令增加顯著的開銷。例如,如果存在 1 億個缺失的 blob,此檔案在磁碟上至少會是 2GiB。

使用“promisor”概念,我們根據引用它的 packfile 的型別 **推斷** 一個缺失的物件。

[0] https://crbug.com/git/2 Bug#2:部分克隆

[1] https://lore.kernel.org/git/20170113155253.1644-1-benpeart@microsoft.com/
主題:[RFC] 新增對按需下載 blob 的支援
日期:2017 年 1 月 13 日星期五 10:52:53 -0500

[2] https://lore.kernel.org/git/cover.1506714999.git.jonathantanmy@google.com/
主題:[PATCH 00/18] 部分克隆(從克隆到懶載入獲取,共 18 個補丁)
日期:2017 年 9 月 29 日星期五 13:11:36 -0700

[3] https://lore.kernel.org/git/20170426221346.25337-1-jonathantanmy@google.com/
主題:Git 倉庫缺失 blob 支援提案
日期:2017 年 4 月 26 日星期三 15:13:46 -0700

[4] https://lore.kernel.org/git/1488999039-37631-1-git-send-email-git@jeffhostetler.com/
主題:[PATCH 00/10] RFC 部分克隆和獲取
日期:2017 年 3 月 8 日星期三 18:50:29 +0000

[5] https://lore.kernel.org/git/20170505152802.6724-1-benpeart@microsoft.com/
主題:[PATCH v7 00/10] 將 filter 過程程式碼重構為可重用模組
日期:2017 年 5 月 5 日星期五 11:27:52 -0400

[6] https://lore.kernel.org/git/20170714132651.170708-1-benpeart@microsoft.com/
主題:[RFC/PATCH v2 0/1] 新增對按需下載 blob 的支援
日期:2017 年 7 月 14 日星期五 09:26:50 -0400