一、前言
前幾天同事在拉取一個項目的Git倉庫時,發(fā)現(xiàn)項目拉取速度非常慢幔欧,半個鐘都無法拉取下來,并且發(fā)現(xiàn)一直卡在了99%的進度上丽声。
開始時以為是Git網(wǎng)絡(luò)出問題了礁蔗,檢查了其它倉庫卻都可以正常的推送和拉取,后面發(fā)現(xiàn)經(jīng)過很長時間后恒序,這個倉庫竟然拉下來了瘦麸,但是拉取的文件大小竟然有700M多,整個.git文件也隨即增大到1G多歧胁。
于是在Gerrit上查看了近幾次提交記錄發(fā)現(xiàn)兩個非常大的臨時文件被上傳了滋饲,并且審核通過被推送到Git倉庫中,沒錯就是這倆貨:
真相大白喊巍,原來是推送了超大文件導致了問題出現(xiàn)屠缭,那么接下來就好辦了,通過Git命令應(yīng)該就可以了愉快的解決這個問題崭参。
但是呵曹,凡事總有個但是,解決的過程遠不是想象中那么順利何暮。下面就來看看我們經(jīng)歷了什么奄喂。
二、問題分析與解決
-
刪除文件海洼,再次提交
首先想到的就是將文件刪除跨新,然后推送到遠程倉庫,發(fā)現(xiàn)拉取速度一樣龜速坏逢。
分析了一下域帐,發(fā)現(xiàn)這樣根本是行不通的赘被。
因為遠程倉庫中,大文件的提交記錄依然存在肖揣,這樣刪除只是將產(chǎn)生了一個新的提交記錄民假,將當前commit中大文件去掉而已,隨時可以回滾回來龙优,pull的時候依然會將大文件的歷史記錄拉取下來羊异。
-
git reset 命令
我們知道git reset可以將當前的內(nèi)容回滾到指定的某次提交,分為兩個模式:
#將內(nèi)容回滾到commitid這次提交陋率,并刪除所有‘commitid’之后的提交歷史內(nèi)容
git reset --hard commitid
#將內(nèi)容回滾到commitid這次提交球化,并保留所有‘commitid’之后的內(nèi)容
git reset --soft commitid
由于提交大文件之后秽晚,有更新了幾次提交瓦糟,所以只能用soft參數(shù),否則后面的幾次提交內(nèi)容就沒有了赴蝇。于是想到的解決方案如下菩浙。
通過git reset --soft命令,將當前提交的內(nèi)容恢復到這個兩個大文件提交之前句伶,然后再次commit劲蜻,再次push到遠程倉庫,結(jié)局可以想而知考余,這樣就想刪除文件先嬉?no way!
git reset --soft命令一樣是無法將提交記錄從倉庫中抹掉的楚堤,雖然通過reset之后疫蔓,大文件的提交記錄在git log中已經(jīng)查找不到,但實際上身冬,這個記錄并不會真正的從倉庫中刪除衅胀,只要能找到commit id,依然可以從倉庫中恢復該提交歷史酥筝。所以滚躯,刪除不了的原因與第一種方案是一樣的。
-
git filter-branch
1)前面兩種修改的方式都是我們平時所熟悉的嘿歌,使用頻率比較高的刪除某些文件或者提交記錄的方式掸掏,但這些方式實際上都是生成了新的提交記錄,并不會修改或者刪除我們的提交歷史宙帝,也就是說丧凤,想要永久刪除倉庫中的某個文件,這樣是行不通的茄唐。
Git這么強大息裸,肯定是存在可以永久刪除歷史記錄的命令姐刁,找了一圈,發(fā)現(xiàn)確實有“后悔藥”命令帖池,那就是git filter-branch盾剩,通過以下命令,就可以永久刪除你想要刪除的任何文件:
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch path-to-your-remove-file' --prune-empty --tag-name-filter cat -- --all
將path-to-your-remove-file替換為刪除文件的相對路徑访圃,并執(zhí)行厨幻,如果有以下執(zhí)行反饋,說明刪除成功了腿时。
如果所有分支都是unchanged說明要么是該分支沒有要刪除的文件况脆,要么是刪除文件的路徑不對。
執(zhí)行以后命令以后批糟,你會發(fā)現(xiàn)本地目錄中的.git文件并不會馬上就變小格了,而是與原來是一樣的!
不是說好了徽鼎,可以永久刪除記錄的嗎盛末?摔!不是說好了否淤,不能再通過commit id找回原來的大文件了嗎悄但?摔!別急石抡,接下來就告訴你為什么檐嚣。
2)原來Git倉庫歷史有個緩存期,如果不主動回收啰扛、清理倉庫歷史嚎京,一般的這些記錄還會保存一段時間,以備你突然后悔了侠讯,沒辦法找回刪掉的文件挖藏。那么怎么樣才能主動回收資源能?就是通過以下命令:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
執(zhí)行以上命令厢漩,就會發(fā)現(xiàn).git目錄變小了膜眠。那么接下來只要把本地的記錄,強制更新到遠程倉庫就行了溜嗜。
強制更新是一個非常危險的動作宵膨,一定要確保你的本地內(nèi)容是最新的,已經(jīng)沒有人在你之后提交了代碼炸宵,否則會將其它的人提交的代碼也一并刪除了辟躏。
強制推送命令如下:
git push origin master --force
#其中master為你要推送的分支
3)由于我們采用的是gerrit進行代碼審核,想當然地就認為土全,應(yīng)該把這次修改強制推送到gerrit上捎琐,然后再由gerrit上審核通過会涎,并推送到遠程倉庫。天知道瑞凑,這竟然又沒有效果啊末秃,再摔!pull的時候籽御,依然龜速练慕。摔摔摔!
這是使用的錯誤推送命令:
git push origin HEAD:refs/for/dev --force
推送到gerrit沒效果技掏,那么直接推送到git遠程倉庫呢铃将?
推送不上去,由于配置了gerrit哑梳,普通權(quán)限的開發(fā)人員是無法直接推送到遠程倉庫的劲阎,否則gerrit就形同虛設(shè)了呀。那么就來看看gerrit可以配置那些權(quán)限涧衙。
4)修改Gerrit推送權(quán)限
i. 首先進入Gerrit首頁哪工,然后依次選擇Projects-->List-->選擇項目-->Access奥此,進入到項目的權(quán)限管理頁面弧哎。
如果要修改項目的權(quán)限,那么你要有管理員權(quán)限才行稚虎。
ii. 點擊Access頁面上的Eidt按鈕修改權(quán)限撤嫩,然后點擊Add Permission,可以看到有許多的權(quán)限蠢终,如代碼審核權(quán)限序攘,代碼核實/推送權(quán)限等等。
其中有一項Push寻拂,這權(quán)限就是可以直接推送到Git程奠,而不需要經(jīng)過gerrit審核。如果需要強制推送祭钉,那么還需要勾選右邊的Force Push瞄沙。
iii. 通過以上配置以后,再次強制推送:
成功了慌核。
4)最后距境,我們再來clone一下遠程倉庫
終于可以輕松的拉取倉庫,并且只有41.42M垮卓,至此垫桂,終于將錯誤推送到遠程倉庫的超大文件刪除,可以輕松愉快的拉取倉庫了粟按。
三诬滩、總結(jié)
通過這次事件霹粥,可以看到:
- 代碼審核是非常重要的,而且要認真的進行審核才行疼鸟,否則很容易導致錯誤的推送蒙挑,不僅會浪費倉庫容量,導致拉取變慢愚臀,甚至可能會泄漏私密文件忆蚀,如密鑰文件等。
- 解決問題時姑裂,在嘗試一些方案時馋袜,最好先分析一下方案的可行性,已經(jīng)結(jié)果評估舶斧,否則會浪費了許多時間欣鳖,還有可能導致一些不可逆轉(zhuǎn)的錯誤。
- 解決問題的過程需要耐心茴厉,在這過程中遇到的問題往往不止一個泽台,我們只要耐心的一個個去解決,總會有解決的辦法矾缓,而解決問題的關(guān)鍵往往就在下一秒怀酷,所以耐心,堅持不懈非常重要嗜闻。
以上蜕依。