記一次刪除Git記錄中的大文件的過程

最近在開發一個新應用,有一天在gitlab上clone代碼的時候發現個人應用居然有170+M,明明是一個全新的應用,代碼都沒有幾行呢,爲何會有這麼大呢?html

後來通過了解Git的原理,解決了這個問題,把相關內容記錄下來。分享一下。git

Why

個人一個新應用居然要170+M,這是打死我我也不會信的,因而就開始分析爲何會這麼大。github

step 1. 把代碼拉到本地

git clone git@github.com:hollischuang/Architecture-Evolution.git

只是用這個地址舉例,實際並非這個項目。shell

step 2. 查看哪一個文件佔用的空間比較大

$cd Architecture-Evolution
$du -d 1 -h
 174M   ./.git
 264K   ./test
 96K    .

因而,發現是.git目錄本身就佔用了174M,瞭解Git的人都知道,.git目錄是git本身生成的,記錄了git倉庫的相關信息的。看到這裏其實並不難知道緣由。gitlab

Git 維護着一個微型的文件系統,其中的文件也被稱做數據對象。全部的數據對象均存儲於項目下面的 .git/objects中。

通過個人驗證,確實是.git/objects這個文件夾中的文件佔了磁盤上174M的空間。3d

也就是說,只要我有一次將一個大文件誤提交了,那麼即便我後面把它刪除了,可是,實際上在.git中,這個文件仍是存在的,雖然咱們可能不再須要他了,可是他還在那裏默默的存在着。。。版本控制

Git與大部分版本控制系統的差異是很大的,好比Subversion、CVS、Perforce、Mercurial 等等,使用的是「增量文件系統」 (Delta Storage systems), 就是說它們存儲每次提交(commit)之間的差別。Git正好與之相反,它會把你的每次提交的文件的所有內容(snapshot)都會記錄下來。這會是在使用Git時的一個很重要的理念。

也就是說,若是我又一次把一個大文件務提交到git倉庫中了,那麼,下次提交時,即便你只改動了某個文件的一行內容,Git 也會生成一個全新的對象來存儲新的文件內容。日誌


由於以上兩個特性,我回想起個人一次手殘行爲:
剛剛建立一個應用以後,我快速的寫完代碼,編譯,運行,發現沒啥問題以後,我準備先把他發佈掉,因而我開始建立git倉庫,並嘗試把代碼提交上去,這時我並無建立.gitignore文件,我直接git add . git commit -m 'init' git push一鼓作氣的執行了熟悉的操做。code

相信聰明的人已經發現了,逗比啊,我在編譯代碼以後,會有不少jar被我down到target目錄下。我直接git add.把target下面的jar包,war包等這些也直接提交了。。。雖而後面我意識到,而且刪除了這些文件,而後再次提交,可是因爲剛咱們說過的緣由,這些文件依然佔用着個人空間。。。htm

更多關於git的原理內容參見:Git 內部原理

How

問題已經定位到了,接下來就是要解決問題了。若是對git的原理及命令瞭解的比較多的話,這個問題仍是比較好解決的,因爲當時博主並不十分了解git的原理,因此作了一些知識儲備以後纔開始動手的。(Git 之術與道 — 對象、爲何你的 Git 倉庫變得如此臃腫

Step 1 查看哪些歷史提交過文件佔用空間較大

使用如下命令能夠查看佔用空間最多的五個文件:

git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"
rev-list命令用來列出Git倉庫中的提交,咱們用它來列出全部提交中涉及的文件名及其ID。 該命令能夠指定只顯示某個引用(或分支)的上下游的提交。

--objects:列出該提交涉及的全部文件ID。

--all:全部分支的提交,至關於指定了位於/refs下的全部引用。

verify-pack命令用於顯示已打包的內容。

step 2. 重寫commit,刪除大文件

使用如下命令,刪除歷史提交過的大文件:

git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch big-file.jar' --prune-empty --tag-name-filter cat -- --all

上面腳本中的big-file.jar請換成你第一步查出的大文件名,或者這裏直接寫一個目錄。

filter-branch命令能夠用來重寫Git倉庫中的提交

--index-filter參數用來指定一條Bash命令,而後Git會檢出(checkout)全部的提交, 執行該命令,而後從新提交。

–all參數表示咱們須要重寫全部分支(或引用)。

在重寫提交的過程當中,會有如下日誌輸出:

Rewrite 6cdbb293d453ced07e6a07e0aa6e580e6a5538f4 (266/266)
# Ref 'refs/heads/master' was rewritten

若是顯示 xxxxx unchanged, 說明repo裏沒有找到該文件, 請檢查路徑和文件名是否正確,重複上面的腳本,把全部你想刪除的文件都刪掉。

step 3. 推送修改後的repo

以強制覆蓋的方式推送你的repo, 命令以下:

git push origin master --force

step 4. 清理和回收空間

雖然上面咱們已經刪除了文件, 可是咱們的repo裏面仍然保留了這些objects, 等待垃圾回收(GC), 因此咱們要用命令完全清除它, 並收回空間,命令以下:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now

至此,咱們已經完全的刪除了咱們不想要的文件。

原文連接

相關文章
相關標籤/搜索