簡體   English   中英

git push 對於一個分支來說非常慢

[英]git push is very slow for a branch

我們有一個非常大的 git 存儲庫(ios 應用程序資源)。 我很欣賞 git 在使用它時會很慢,但是如果我創建一個新分支並編輯幾個文件(不是二進制文件)並推送,則需要很長時間。

感覺就像整個 repo 都被推送了。 我的印象是 git 只會發送差異,這是錯誤的嗎? (我知道 git 存儲整個文件的壓縮版本,我的意思是我的分支和我分支的位置之間的差異)。

如果我運行git diff --stat --cached origin/foo然后我會看到一個簡短的文件列表,這些文件看起來像我所期望的那樣,例如34 files changed, 1117 insertions(+), 72 deletions(-) 但是當我推動它到達Writing objects: 21% (2317/10804)並停止時,好像它正在推動所有2.4GB的二進制數據。

我錯過了什么(我已經很難用谷歌搜索了)? 這是預期的行為嗎? 我在 OS X (Mavericks) 和 ssh (git@github.com) 上使用 git 2.2.2。

我在這里發現了一個類似的問題: Git - 為大型項目推送遠程分支確實很慢,但沒有真正的答案。

您正在使用“智能”傳輸(這是一件好事),因此您確實獲得了增量,或者更具體地說,是“增量壓縮”。 但這並不是說 git 會推動差異。

push 和 fetch 在這里的工作方式相同:在智能傳輸上,您的 git 調用遠程,並且兩端進行小型對話以確定誰擁有哪些存儲庫對象,由 SHA-1 標識並附加到特定標簽(通常是分支和標簽名稱,但也允許使用其他標簽)。

例如,在這種情況下,您的 git 會調用他們的 git 並說:“我建議您將分支master設置為 SHA-1 1234567...我看到您的master當前是333333... ,這就是我的想法你需要從那里到7777777... 他們應該回答“好的,我需要其中一些,但我已經有了......”。 一旦您的 git 確定了需要發送的內容以及已經存在的內容,您的 git 就會構建一個“瘦包” 1 ,其中包含所有要發送的對象。 (這是“使用多達 %d 個線程進行增量壓縮”階段。)

然后將生成的薄包裝通過智能運輸發送; 這是您看到“寫入對象”消息的地方。 (整個瘦包必須成功發送,之后接收者再次使用git index-pack --fix-thin將其“增肥”並將其放入存儲庫。)

發送的具體數據取決於精簡包中的對象。 應該只是“他們擁有的東西”和“你正在發送的東西”之間的一組提交,加上這些提交所需的任何對象(樹和 blob),加上你正在發送的任何帶注釋的標簽以及那些需要的任何對象,他們還沒有。

您可以通過使用git fetch他們的最新信息來找到有問題的提交,然后使用git rev-list查看您將發送給他們的提交。 例如,如果您只是要在master上推送內容:

$ git fetch origin   # assuming the remote name is origin
[wait for it to finish]
$ git rev-list origin/master..master

檢查這些提交可能會顯示一個非常大的二進制文件,該文件包含在其中一個中間文件中,然后在以后的提交中再次刪除:

$ git log --name-status origin/master..master

如果一個提交有A giantfile.bin並且隨后的(可能在git log輸出中首先列出)提交有D giantfile.bin ,那么您可能會掛斷為giantfile.bin發送 blob。

如果是這種情況,您可以使用git rebase -i來消除添加巨型二進制文件的提交,這樣git push就不必發送該提交。

(如果你的歷史是線性的——沒有要推送的合並——那么你也可以,或者相反,使用git format-patch來創建一系列包含補丁的電子郵件。這些適合通過電子郵件發送給其他站點的某人——不是有人在 github 等待接收它們,但您可以輕松檢查補丁文件以查看其中是否有巨大的。)


1包是“瘦”的,因為它違反了正常的包文件規則,該規則要求包本身中包含任何增量壓縮“下游”對象。 相反,“下游”對象可以(事實上,必須)在接收瘦包的存儲庫中。

請注意,當您擁有超過 1023 個包時,Git 2.25 修復了包對象的極端減速問題。 請參閱下面的數字。

其他選項:Git 2.38(2022 年第三季度)提出了新設置git -c push.useBitmaps=false push ,以禁用git push打包。

但是對於 Git 2.25 修復:

這可能會對您的案例產生積極影響,因為您有大量的包文件。

請參閱Jeff King ( peff )提交 f66e040 (2019 年 11 月 11 日)。
(由Junio C Hamano -- gitster --提交 8faff38中合並,2019 年 12 月 1 日)

pack-objects : 避免無意義的oe_map_new_pack()調用

簽字人:傑夫·金
審核人:Derrick Stolee

43fa44fa3b (pack-objects: move in_pack out of struct object_entry, )開始,我們使用復雜的系統來節省一些每個對象的內存。

每個object_entry結構都有一個 10 位字段來存儲它所在包的索引。我們使用packing_data->in_pack_by_idx,我們在程序開始時對其進行初始化。
如果我們有 2^10 個或更多包,那么我們將創建一個包指針數組,每個對象一個。 這是packing_data->in_pack

到目前為止,一切都很好。 但是還有另一個棘手的情況:如果在我們初始化in_pack_by_idx,它還沒有索引。 我們通過調用oe_map_new_pack()來解決這個問題,它只是即時切換到不太理想的in_pack機制,為已經看到的對象分配數組並回填它。

但是即使我們已經切換到它,這種邏輯也會起作用(無論是因為我們真的看到了一個新包,還是因為我們一開始就有太多的包)。 結果不會產生錯誤的結果,但速度很慢。 會發生什么:

  • 假設您有一個包含 500k 個對象和 2000 個要重新打包的包的倉庫。

  • 在查看任何對象之前,我們調用prepare_in_pack_by_idx()
    它開始為每個包分配一個索引。
    在第 1024 個包中,它發現太多了,所以它放棄了,將in_pack_by_idxNULL

  • 在實際將對象添加到裝箱單時,我們調用oe_set_in_pack() ,它檢查包是否已經有索引。
    如果它是第一個 1023 之后的包之一,那么它沒有一個,我們將調用oe_map_new_pack()

但是該功能沒有任何有用的工作要做。
我們已經在使用in_pack ,所以它只是無用地遍歷對象的完整列表,試圖回填in_pack

我們最終為近 1000 個包執行此操作(每個包都可能由多個對象觸發)。 每次觸發時,我們可能會迭代多達 500k 個對象。 所以在絕對最壞的情況下,這是對象數量的二次方。

解決方案很簡單:如果我們已經轉換為使用in_pack,因為根據定義,我們不會使用它。 所以我們可以將“包是否有有效的索引”檢查推到條件的那一半,我們知道我們將要使用它。

遺憾的是,p5303 中的當前測試沒有注意到這個問題,因為它的最大值為 1000 包。 如果我們在 2000 包時添加一個新測試,它確實顯示出改進:

 Test HEAD^ HEAD
>     ----------------------------------------------------------------------
 5303.12: repack (2000) 26.72(39.68+0.67) 15.70(28.70+0.66) -41.2%

但是,這些多包測試用例運行起來相當昂貴,因此添加越來越大的數字並沒有吸引力。 相反,我們可以通過使用GIT_TEST_FULL_IN_PACK_ARRAY,這迫使我們進入絕對最壞的情況:沒有包有索引,所以我們將毫無意義地為每個對象觸發oe_map_new_pack() ,使其真正成為二次方。

以下是包含對 p5303 的更改的數字(在git.git上):

 Test HEAD^ HEAD
>     ----------------------------------------------------------------------
 5303.3: rev-list (1) 2.05(1.98+0.06) 2.06(1.99+0.06) +0.5% 5303.4: repack (1) 33.45(33.46+0.19) 2.75(2.73+0.22) -91.8% 5303.6: rev-list (50) 2.07(2.01+0.06) 2.06(2.01+0.05) -0.5% 5303.7: repack (50) 34.21(35.18+0.16) 3.49(4.50+0.12) -89.8% 5303.9: rev-list (1000) 2.87(2.78+0.08) 2.88(2.80+0.07) +0.3% 5303.10: repack (1000) 41.26(51.30+0.47) 10.75(20.75+0.44) -73.9%

同樣,這些改進對於 1-pack 案例是不現實的(因為在現實世界中,全陣列解決方案不會起作用),但測試更復雜的代碼路徑更有用。

在我們研究這個問題時,我們將再調整一件事:在oe_map_new_pack()中,我們調用REALLOC_ARRAY(pack->in_pack) 但是除非我們第一次回填它,否則我們永遠不會期望到達這里,在這種情況下它將是NULL
因此,為了清楚起見,讓我們將其切換為ALLOC_ARRAY() ,並添加一個 BUG() 來記錄期望。 不幸的是,這段代碼在測試套件中沒有得到很好的覆蓋,因為它本質上是活潑的(只有在我們重新打包時其他人添加了新包時它才會生效)。

就我而言,我只是無意中在我的提交中添加了一個非常大的文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM