[英]Reduce size of git repository on Bitbucket
經過幾個月(提交和推送)我的項目,存儲庫的大小在Bitbucket上逐漸增加! 它大約是1 GB,我試圖刪除一些不重要的數據庫文件夾。 搜索后我發現大多數建議都是建議:
git filter-branch -f --tree-filter 'rm -rf folder/subfolder' HEAD
刪除幾個文件夾后,我將更改推送到存儲庫 - 強制,如
git push origin master --force
我終於發現每次使用這些命令時存儲庫都會變大!! 可見,存儲庫變大了2.5 GB!
有什么建議嗎?
編輯根據下面的建議,我嘗試了以下命令
(適用於所有大文件)
git filter-branch --index-filter“git rm -rf --cached --ignore-unmatch $ files”--tag-name-filter cat - --all
(刪除臨時歷史git-filter-branch,否則長時間留下)
rm -rf .git / refs / original /
git reflog expire --all
git gc --aggressive --prune
但文件夾.git / objects仍然很大!!!!
好的,鑒於您對評論的回答,我們現在可以說出發生了什么。
git filter-branch
作用是將您的提交(部分或全部) 復制到新的提交,然后更新引用。 這意味着您的存儲庫變得更大 (不小),至少在最初階段。
復制的提交是通過給定的引用可以訪問的提交。 在這種情況下,你給出的引用是HEAD
(git變成“你當前的分支”,可能是master
,但無論你當前的分支是在filter-branch
命令時)。 如果(並且僅當)新副本精確地,與原始副本一點一點地相同,那么它實際上是原始副本並且沒有實際副本(原件被重新使用)。 但是,只要您進行任何更改 - 例如刪除folder/subfolder
,從這一點開始就是這些副本。
在這種情況下,復制的東西更小,因為你刪除了一些項目。 (它通常不是很小,因為git很好地壓縮項目。)但是你仍然在向存儲庫添加更多東西:新提交,它引用新樹,幸運的是 - 引用相同的舊blob(文件對象)和以前一樣,這次只是少了一些( folder/subfolder
文件folder/subfolder
文件的對象仍在存儲庫中,但復制的提交和樹對象不再引用它們)。
從圖形上看,在filter-branch
過程的這一點上,我們現在都有舊的提交:
R--o--o---o--o <-- master
\ /
o--o <-- feature
和新的(我假設folder/subfolder
出現在原始的根提交R
所以我們在這里有一個副本R'
):
R'-o'-o'--o'-o'
\ /
o'-o'
在復制過程結束時, filter-branch
現在做了什么,重新指向一些引用(主要是分支和標記名稱)。 它重新指出的那些是你告訴它的那些,通過提及它們作為文檔所謂的“積極參考”。 在這種情況下,如果你在master
(即HEAD
是master
另一個名字),你給出的單個正參考是master
...所以這是所有 filter-branch
重新點。 它還會生成名稱以refs/original/
開頭的備份引用。 這意味着您現在擁有以下提交:
R--o--o---o--o <-- refs/original/refs/heads/master
\ /
o--o <-- feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o'
請注意,該feature
仍然指向所有舊的 (未復制的)提交,因此即使在您刪除任何refs/original/
references之后,git也將保留所有仍然引用的任何垃圾收集活動的提交, :
R--o
\
o--o <-- feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o'
要使filter-branch
更新所有引用,您需要將它們全部命名。 一個簡單的方法是使用--all
,它完全命名所有引用。 在這種情況下,最初的“之后”圖片看起來像這樣:
R--o--o---o--o <-- refs/original/refs/heads/master
\ /
o--o <-- refs/original/refs/heads/feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o' <-- feature
現在,如果刪除所有refs/original/
引用,則所有舊提交都將被取消引用,並且可以進行垃圾回收。 嗯,就是說, 除非有標簽指向它們, 否則它們會這樣做。
對於標記引用, filter-branch
僅在您提供--tag-name-filter
以任何方式更新它們。 通常你需要--tag-name-filter cat
,它保持標簽名稱不變,但是使filter-branch
將它們指向新復制的提交。 這樣你就不會掛起舊的提交了:練習的重點是讓一切都使用新的副本,然后丟棄舊的副本,這樣大文件對象就可以被垃圾收集了。
把這一切放在一起,而不是:
git filter-branch -f --tree-filter 'rm -rf folder/subfolder'
您可以使用:
git filter-branch -f --tree-filter 'rm -rf folder/subfolder' \
--tag-name-filter cat -- --all
(你不需要反斜杠 - 換行符序列;我把它放在只是為了使行更符合stackoverflow。請注意--tree-filter
非常慢:對於這種特殊情況,它使用--index-filter
要快得多--index-filter
。這里的索引過濾器命令是git rm --cached --ignore-unmatch -r folder/subfolder
。)
另請注意,您需要在原始存儲庫(副本)上執行所有這些操作(您確實保留了備份,對吧?)。 (如果你沒有備份, refs/originals/
可能是你的救贖。)
編輯:好的,所以你做了一些filter-branch
-ing,你做了一些刪除任何refs/originals/
。 (在我對temp repo的實驗中,在HEAD
上運行git filter-branch
使用我所在的任何分支作為重新指向的分支,並創建了前一個值的“原始”副本。)沒有備份庫。 怎么辦?
那么,作為第一步,立即進行備份 。 這樣,如果事情變得更糟,你至少可以回到“只是稍微糟糕”。 要備份repo,您可以簡單地克隆它(或者:克隆它,然后將原始文件稱為“backup”,然后開始處理克隆)。 為了將來參考,由於git filter-branch
可能具有很強的破壞性,因此通常可以從這個備份過程開始。 (另外,我會注意到bitbucket上的一個克隆,當還沒有push
ed-to時,會服務。不幸的是你做了push
。也許bitbucket可以從他們自己的一些備份或快照中檢索存儲庫的早期版本。)
接下來,讓我們注意一下提交的特性及其SHA-1“真實姓名”,我之前提到過。 提交的SHA-1名稱是其內容的加密校驗和。 讓我們看看git自己的源代碼樹中的一個示例提交(只是為了長度而修剪了一下,並且電子郵件地址被打到了收割機):
$ git cat-file -p 5de7f500c13c8158696a68d86da1030313ddaf69
tree 73eee5d136d2b00c623c3fceceffab85c9e9b47e
parent c4ad00f8ccb59a0ae0735e8e32b203d4bd835616
author Jeff King <peff peff.net> 1405233728 -0400
committer Junio C Hamano <gitster pobox.com> 1406567673 -0700
alloc: factor out commit index
We keep a static counter to set the commit index on newly
allocated objects. However, since we also need to set the
[snip]
在這里,我們可以看到此提交的內容(其“真實名稱”為5de7f50...
)以tree
和另一個SHA-1, parent
和另一個SHA-1, author
和committer
,然后是空行然后是提交消息文本。
如果你看一tree
你會看到它包含子樹(子目錄)的“真實名字”(SHA-1值)和文件對象(git術語中的“blob”)及其模式 -實際上,只是blob是否應該具有執行權限集,以及它們在目錄中的名稱。 例如,上面tree
的第一行是:
100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f .gitattributes
這意味着應該提取存儲庫對象5e98806...
,放入名為.gitattributes
的文件中,並設置不可執行文件。
如果我要求git進行新的提交,並設置,作為其內容:
73eee5d...
) c4ad00f...
) 然后,當我得到git將該提交寫入存儲庫時,它將生成相同的“真實名稱” 5de7f50...
換句話說,它實際上是相同的提交:它已經在存儲庫中, git commit-tree
將只返回現有的ID。 雖然設置這一切有點棘手,但這正是git filter-branch
最終要做的事情:它提取原始提交,應用過濾器,設置所有內容,然后執行git commit-tree
。
在您的原始倉庫中,您運行了一個git filter-branch
命令,該命令將提交復制到新的,已修改的提交(使用不同的tree
,因此,在某些時候,不同的真實名稱會導致后續提交中的不同父ID,依此類推) 。 但是,如果通過應用此次不執行任何操作的過濾器來復制這些復制的提交,則新的tree
對象將與舊的tree
對象相同 。 如果新父級是相同的,並且作者,提交者和消息也保持不變,則副本的新提交ID將與舊ID 相同 。
也就是說,這些新副本畢竟不是副本,它們只是原件!
任何其他提交 - 未在第一次傳遞中復制的提交都會被復制,因此具有不同的ID。
事情變得棘手。
如果您當前的存儲庫看起來像這樣 (從圖形上講):
R--o--o---o--o <-- xxx [needs a name so that filter-branch will process it]
\ /
o--o <-- feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o'
我們將這個新的filter-branch
到所有引用(甚至是“除了master
”之外的所有引用),這次它會生成相同的樹,它會再次復制R
並且新樹將與R'
匹配,所以副本實際上是 R'
。 然后它將復制第一個后R
節點,進行相同的更改,副本實際上將是第一個后R'
, o'
節點。 這將重復所有節點,甚至可能包括R'
和所有o'
。 如果filter-branch
副本R'
,則生成的副本將再次成為R'
,因為“刪除不存在的目錄”不做任何更改:我們的過濾器對這些特定提交不執行任何操作。
最后,filter-branch將移動標簽,留下refs/originals/
versions:
R--o--o---o--o <-- refs/originals/refs/xxx
\ /
o--o <-- refs/originals/refs/feature
R'-o'-o'--o'-o' <-- master, xxx
\ /
o'-o' <-- feature
事實上,這是理想的結果。
如果存儲庫看起來更像這樣怎么辦? 也就是說,如果沒有xxx
或類似的標簽指向原始(預過濾) master
,那么你有這個:
R--o
\
o--o <-- feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o'
filter-branch
腳本仍然會復制R
,結果仍然是R'
。 然后,它會復制第一o
節點,結果依然會是第一個o'
點,依此類推。 它不會復制現在刪除的節點,但它不必:我們已經擁有那些,可通過branch-name master
。 和以前一樣, filter-branch
可以復制R'
和各種o'
節點,但這沒關系,因為過濾器什么也不做,所以副本實際上只是原件。
最后, filter-branch
將像往常一樣更新引用:
R--o
\
o--o <-- refs/originals/refs/feature
R'-o'-o'--o'-o' <-- master
\ /
o'-o' <-- feature
使這一切工作的關鍵是過濾器保留已修改的提交不變,因此它們的第二個“副本”再次只是第一個副本。 1
完成所有操作后,您可以執行git filter-branch
文檔中描述的相同收縮,以拋棄refs/originals/
names並垃圾收集now-unreferenced對象。
1如果你一直在使用一個不那么容易重復的過濾器(例如,以“當前時間”作為時間戳進行新提交的過濾器),你真的需要一個未經修改的原始存儲庫,或那些refs/originals/
引用(任何一個都足以保持“原始副本”)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.