為什么你的 Git 倉(cāng)庫(kù)變得如此臃腫

你已經(jīng)掌握了 Git 的基本用法柑土,只消熟練使用幾個(gè)常用命令,足以應(yīng)付開發(fā)過(guò)程中的絕大多數(shù)場(chǎng)景酌予。在 Git 的幫助下磺箕,你過(guò)上了快樂(lè)的生活。然而抛虫,某天早上你一覺(jué)醒來(lái)之后松靡,發(fā)現(xiàn)了一件令人納悶的事情:“為什么我的 Git 倉(cāng)庫(kù)變得如此臃腫?”

情形一:倉(cāng)庫(kù)自身的增長(zhǎng)

大多數(shù)版本控制系統(tǒng)存儲(chǔ)的是一組初始文件建椰,以及每個(gè)文件隨著時(shí)間的演進(jìn)而逐步積累起來(lái)的差異雕欺;而 Git 則會(huì)把文件的每一個(gè)差異化版本都記錄在案(關(guān)于 Git 是如何存儲(chǔ)數(shù)據(jù)的,請(qǐng)參閱這篇文章)棉姐。這意味著屠列,即使你只改動(dòng)了某個(gè)文件的一行內(nèi)容,Git 也會(huì)生成一個(gè)全新的對(duì)象來(lái)存儲(chǔ)新的文件內(nèi)容伞矩。

如果你改動(dòng)了一個(gè)很大的文件笛洛,問(wèn)題就來(lái)了。

對(duì)象碎片

現(xiàn)在乃坤,讓我們生成一個(gè)包含 1000000 行隨機(jī)字符串的大文本文件苛让,并把它添加到版本庫(kù)中:

$ perl -le 'for (1..1000000) { print map { (0..9, "a".."z")[rand 36] } 1..80 }' > bigfile
$ git add bigfile

我們看到沟蔑,Git 已經(jīng)為這個(gè)文件生成了一個(gè) Blob 對(duì)象,大小是 54M狱杰。

$ find .git/objects -type f
.git/objects/7f/a055b2d22855b67287e4e30d9a91584c8b27c1
$
$ du -ah    # 此處略去了無(wú)關(guān)輸出
 54M    ./.git/objects/7f/a055b2d22855b67287e4e30d9a91584c8b27c1
 77M    ./bigfile
132M    .

如果往文件末尾添加一行瘦材,會(huì)怎么樣呢?

$ perl -le 'print map { (0..9, "a".."z")[rand 36] } 1..80' >> bigfile
$ git add bigfile

可以看到仿畸,Git 生成了一個(gè)全新的 Blog 對(duì)象來(lái)存儲(chǔ)新的文件內(nèi)容宇色,這個(gè)對(duì)象的大小同樣是 54M。倉(cāng)庫(kù)瞬間患上了肥胖癥颁湖。

$ find .git/objects -type f
.git/objects/7f/a055b2d22855b67287e4e30d9a91584c8b27c1
.git/objects/f3/79c10af3f3e497d37558ac2497fe3c69d2de89
$
$ du -ah    # 此處略去了無(wú)關(guān)輸出
 54M    ./.git/objects/7f/a055b2d22855b67287e4e30d9a91584c8b27c1
 54M    ./.git/objects/f3/79c10af3f3e497d37558ac2497fe3c69d2de89
 77M    ./bigfile
186M    .

你的倉(cāng)庫(kù)里面現(xiàn)在有兩個(gè)內(nèi)容幾乎完全相同,大小均為 54M 的龐大對(duì)象例隆。如果 Git 可以只保存其中一個(gè)對(duì)象甥捺,再保存另一個(gè)對(duì)象與這個(gè)對(duì)象的差異內(nèi)容,豈不妙哉镀层?

gc 命令

“垃圾回收”是一個(gè)很親切的功能镰禾。讓我們開始吧:

$ git gc --prune=now

現(xiàn)在,重新檢視一下倉(cāng)庫(kù)的大小唱逢,發(fā)現(xiàn)確實(shí)有效拔庹臁:

$ find .git/objects -type f
.git/objects/info/packs
.git/objects/pack/pack-9d75315485cb7bfbf51ce5c94a4535da99b58dbb.idx
.git/objects/pack/pack-9d75315485cb7bfbf51ce5c94a4535da99b58dbb.pack
$
$ du -ah    # 此處略去了無(wú)關(guān)輸出
4.0K    ./.git/objects/pack/pack-9d75315485cb7bfbf51ce5c94a4535da99b58dbb.idx
 52M    ./.git/objects/pack/pack-9d75315485cb7bfbf51ce5c94a4535da99b58dbb.pack
 77M    ./bigfile
130M    .

運(yùn)行 gc 命令之后,兩個(gè) Blob 對(duì)象不見(jiàn)了坞古。Git 創(chuàng)建了一個(gè)包文件和一個(gè)索引文件备韧。包文件中包含了之前的兩個(gè) Blob 對(duì)象,索引文件中包含了每個(gè)對(duì)象在包文件中的偏移信息痪枫。Git 在打包的過(guò)程中使用了增量編碼方案(delta encoding)织堂,只保存對(duì)象的不同版本之間的差異,這使得倉(cāng)庫(kù)瘦身成功奶陈。

不過(guò)...

實(shí)際上易阳,你并不需要手動(dòng)調(diào)用 gc 命令。每當(dāng)碎片對(duì)象過(guò)多吃粒,或者你向遠(yuǎn)端服務(wù)器發(fā)起推送的時(shí)候潦俺,Git 就會(huì)自動(dòng)執(zhí)行一次打包過(guò)程。

情形二:錯(cuò)誤的大文件

讓我們添加一個(gè)名為 new.txt 的新文件徐勃,并且執(zhí)行兩次提交:

$ git commit -m "first commit"
$ echo 'new file' > new.txt
$ git commit -a "second commit"

當(dāng)執(zhí)行第二次提交的時(shí)候事示,你突然發(fā)現(xiàn),其實(shí) bigfile 這個(gè)文件在項(xiàng)目中并沒(méi)什么卵用僻肖。然而它很大很魂。即使你把這個(gè)文件從項(xiàng)目中移除了,它還是會(huì)頑固地永遠(yuǎn)存在于你的提交歷史中檐涝。有沒(méi)有辦法把這個(gè)文件從歷次提交中徹底地移除呢遏匆?

辦法是有的法挨,不過(guò)務(wù)必要謹(jǐn)慎哦。

能夠勝任這個(gè)任務(wù)的命令叫做 filter-branch

$ git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch bigfile' \
  --prune-empty --tag-name-filter cat -- --all
Rewrite 1d92bc51b15c80582cef9cfb27ee056f000590bc (1/2)rm 'bigfile'
Rewrite 4ef010df40a1e81b1f9a11391d63879b649e9690 (2/2)rm 'bigfile'

Ref 'refs/heads/master' was rewritten

然后幅聘,刪除緩存的對(duì)象凡纳。這一步可以暫時(shí)跳過(guò),等到確認(rèn)完全不會(huì)出現(xiàn)問(wèn)題之后再執(zhí)行(可以說(shuō)帝蒿,這些緩存對(duì)象給你提供了撤銷操作的最后一次機(jī)會(huì))荐糜。

$ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
$ git reflog expire --expire=now --all
$ git gc --prune=now

現(xiàn)在倉(cāng)庫(kù)的總大小只有 88K 了,是不是很棒:

$ du -ah    # 此處略去了無(wú)關(guān)輸出
4.0K    ./.git/objects/pack/pack-846da79290b3ef2f6617aa8aab03e4f54439a40a.idx
4.0K    ./.git/objects/pack/pack-846da79290b3ef2f6617aa8aab03e4f54439a40a.pack
4.0K    ./new.txt
 88K    .

當(dāng)然葛超,你可能還需要把這一次的改動(dòng)提交到遠(yuǎn)端倉(cāng)庫(kù):

$ git push --force --verbose --dry-run
$ git push --force
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暴氏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绣张,更是在濱河造成了極大的恐慌答渔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥涵,死亡現(xiàn)場(chǎng)離奇詭異沼撕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芜飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門务豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人嗦明,你說(shuō)我怎么就攤上這事笼沥。” “怎么了娶牌?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵敬拓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我裙戏,道長(zhǎng)乘凸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任累榜,我火速辦了婚禮营勤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壹罚。我一直安慰自己葛作,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布猖凛。 她就那樣靜靜地躺著赂蠢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辨泳。 梳的紋絲不亂的頭發(fā)上虱岂,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天玖院,我揣著相機(jī)與錄音,去河邊找鬼第岖。 笑死难菌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔑滓。 我是一名探鬼主播郊酒,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼键袱!你這毒婦竟也來(lái)了燎窘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蹄咖,失蹤者是張志新(化名)和其女友劉穎褐健,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體比藻,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年倘屹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了银亲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纽匙,死狀恐怖务蝠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烛缔,我是刑警寧澤馏段,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站践瓷,受9級(jí)特大地震影響院喜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晕翠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一喷舀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淋肾,春花似錦硫麻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碌尔,卻和暖如春浇辜,著一層夾襖步出監(jiān)牢的瞬間券敌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工奢赂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陪白,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓膳灶,卻偏偏與公主長(zhǎng)得像咱士,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轧钓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容