源碼管理系統(tǒng)不是永遠(yuǎn)不出問題的黑盒子。如果你沒有合理地維護(hù)好你的倉庫,對(duì)于要使用它們的開發(fā)人員來說,這些倉庫會(huì)變成一個(gè)重大的負(fù)擔(dān)。有些工具能幫你維護(hù)它們, Tyley的BFG Repo- for Git就是這樣一個(gè)工具。這個(gè)工具可以用來刪除那些你不小心提交到git分支上的二進(jìn)制大文件。
InfoQ:給那些不熟悉git的讀者解釋一下,為什么二進(jìn)制大文件是個(gè)問題?
:在git中,所有和你共用一個(gè)庫的人,當(dāng)他們把庫下載下來時(shí),他們會(huì)得到你這個(gè)項(xiàng)目的完整歷史記錄。一般情況下這沒什么問題——真正的代碼只占很少體積,但擁有完整的歷史記錄使你能夠?qū)嵤┓植际降暮献鳌⒖焖俚玫絛iff結(jié)果以及更多的便利——但項(xiàng)目的歷史記錄會(huì)慢慢膨脹。如果你提交了大文件——比如100MB以上——你倉庫的體積會(huì)飛速增長(zhǎng),并且當(dāng)其他人clone這個(gè)倉庫的時(shí)候,他們要下載的數(shù)據(jù)就更多了。最終你會(huì)意識(shí)到你犯了一個(gè)錯(cuò)誤,但是就算你決定要通過一個(gè)來刪除這些文件,它們其實(shí)還在那兒svn服務(wù)器空間不足,在你的歷史數(shù)據(jù)中,下載整個(gè)倉庫所花的時(shí)間不會(huì)有任何變化。
除了大文件以外,不小心進(jìn)去的密碼或其他隱私數(shù)據(jù)也有這個(gè)問題——就算你刪掉它們svn服務(wù)器空間不足,它們實(shí)際上還在歷史數(shù)據(jù)中。你只是犯了個(gè)錯(cuò),但是git卻不讓你糾正。現(xiàn)在我們?cè)撛趺崔k?
InfoQ:要從git庫中移除二進(jìn)制或其他不合適的文件,最基本的方法是使用git--命令。你能不能解釋一下這個(gè)命令到底做了什么,以及為什么你認(rèn)為它還不夠好。
:沒錯(cuò),git--命令是一個(gè)強(qiáng)大的工具——就像一把萬能的電鋸一樣——它調(diào)用腳本來重寫git的歷史記錄,從頭開始寫。你給它一個(gè)bash腳本,它會(huì)從頭到尾針對(duì)每個(gè)運(yùn)行這個(gè)腳本,一個(gè)接一個(gè),逐步重建歷史記錄。你的腳本可以做你想做的任何事——?jiǎng)h除文件、移除不想要的文本(比如密碼),或者把每個(gè)jpg文件都替換為一張烤土豆的照片。一旦git--結(jié)束后,你的git歷史記錄可能從表面上看起來完全一樣——一樣的日期,一樣的注解等等——但是這個(gè)新的歷史記錄實(shí)際上和以前完全不一樣了——你已經(jīng)動(dòng)過它了。
從2007年開始,git--就成為標(biāo)準(zhǔn)git的一部分,當(dāng)時(shí)git發(fā)布才兩年。所以很久以來git--一直是解決git歷史記錄問題的權(quán)威方法。但它有兩個(gè)問題:
1,很難用
2,太慢了
難用的原因有幾個(gè)——git--不是要達(dá)到一個(gè)固定的目的,它本身并不給你解決方案。它很靈活,理論上可以做任何事,但是你必須清楚自己在干什么。你想做的一件最簡(jiǎn)單的事情可能是——從每個(gè)中刪除一個(gè)文件——以下是你的做法:
這命令行太不友好了。而且如果你想做點(diǎn)更復(fù)雜事——比如刪掉某個(gè)密碼所有出現(xiàn)過的地方,會(huì)怎么樣?或者刪掉所有你不小心放進(jìn)去的大文件?git--本身不支持找出這些東西,你必須寫一個(gè)復(fù)雜的腳本,才能告訴git--去刪除什么。網(wǎng)上有無數(shù)為-編寫的腳本——我在寫B(tài)FG之前就寫過一些。幾乎同一時(shí)間,我的一些在的同事想要從我們最大的代碼庫中刪除密碼,幾天后他們搞出一個(gè)-腳本,這腳本看起來要跑數(shù)個(gè)小時(shí)——他們只能小心翼翼地把它放到計(jì)劃任務(wù)中,讓它在晚上跑,以免影響到團(tuán)隊(duì)中的其他開發(fā)人員,而且他們費(fèi)了好大勁,預(yù)先測(cè)試了這個(gè)腳本好多次,確保排除所有的bug。
整件事情干下來太痛苦了。雖然git--是一個(gè)高級(jí)工具,但git已經(jīng)越來越普及(這種趨勢(shì)持續(xù)至今),大量的人會(huì)遇到這個(gè)問題,這會(huì)給他們帶來無盡的困惑和痛苦。所以這看起來是一個(gè)值得去解決的問題。
我已經(jīng)比較精通git了,寫過Agit,一個(gè)下的git客戶端,并且對(duì)git數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)的基本原理有很好的理解。當(dāng)我思考git--為什么這么慢的時(shí)候,我意識(shí)到只要你愿意對(duì)此問題的需求稍作調(diào)整,你就能得到一個(gè)快很多的解決方案。
InfoQ:你的項(xiàng)目網(wǎng)站說,BFG Repo-比git--“快10到720倍”。這是如何做到的?
:準(zhǔn)確地說,如果跟git--能處理的最簡(jiǎn)單任務(wù)(比如刪除一個(gè)已知的文件)做比較,這個(gè)數(shù)據(jù)是10到1000倍。再舉個(gè)獨(dú)立人士的評(píng)測(cè)結(jié)果, (開發(fā)的軟件工程師)在把代碼庫從svn遷移到git的時(shí)候,測(cè)試了BFG的性能——BFG做這件事情只要十分鐘,而git--需要三天——大約快了430倍。
BFG之所以能在性能上有如此大的改進(jìn),原因在于它本來就和git--不一樣。
BFG做到了,因?yàn)槲覀兂浞挚紤]了最常見的場(chǎng)景,以及git本身存儲(chǔ)數(shù)據(jù)的方式。這個(gè)場(chǎng)景就是刪除不想要的文件——至少我想說這是最常見的使用git--的場(chǎng)景,并且也是我感興趣的場(chǎng)景。針對(duì)這個(gè)特定的問題,git的實(shí)現(xiàn)方式?jīng)Q定了我們可以采取完全不同的方案:在git中,所有文件和文件夾的數(shù)據(jù)都只存儲(chǔ)一次,并且每個(gè)都會(huì)有一個(gè)獨(dú)一無二的id——git-id。如果大量的都沒有修改某個(gè)文件,那么這個(gè)文件只會(huì)被保存一次。如果這個(gè)文件有兩個(gè)版本,并且需要在兩個(gè)版本間來回切換,這個(gè)文件只會(huì)存儲(chǔ)兩次,每個(gè)版本各一次。所以,如果一個(gè)文件只存儲(chǔ)一次,因?yàn)樗霈F(xiàn)在100個(gè)中,就要對(duì)它清理100次,這肯定是不現(xiàn)實(shí)的。BFG只對(duì)git庫中的每個(gè)對(duì)象清理一次,然后記住它的git-id,以后遇到它,不把它計(jì)算在內(nèi)就行了。我們對(duì)功能做了限制——git--能讓你做任何事,而BFG不行——以此換來性能上的巨大改進(jìn)。
速度的提升還受益于沒有進(jìn)程間的切換(JVM會(huì)搞定一切,不需要在C代碼和bash代碼間來回切),以及能充分利用你的多核電腦。我很高興看到BFG會(huì)使你所有的處理器核發(fā)燙,每個(gè)核都在盡其所能地刪除文件和目錄——但是很不幸git--只能串行處理工作,一個(gè)接一個(gè)地處理——前一個(gè)處理完之前,無法開始下一個(gè)。
BFG的速度——明顯是——一大優(yōu)勢(shì)。它可以讓人們更高效地對(duì)改寫代碼庫歷史記錄進(jìn)行安全的試驗(yàn),確定無誤后再開始真正動(dòng)手清理代碼庫,把結(jié)果push到服務(wù)器,通知所以開發(fā)人員刪掉舊庫,拉一份全新的——這種溝通的事情對(duì)于一個(gè)大型團(tuán)隊(duì)來說,工作量很大。
InfoQ:你為什么選擇用Scala寫B(tài)FG Repo-而不是直接用Java?
:公司熱衷于使用Scala,并且對(duì)于絕大多數(shù)工作讓我在Java和Scala之間選,我每次都會(huì)選Scala。事實(shí)上這個(gè)項(xiàng)目用Scala非常合適:基于JVM,我就可以利用高性能的JGit庫。作為函數(shù)式編程語言,Scala很適合去處理git的不可變數(shù)據(jù)結(jié)構(gòu),并且能利用并行開發(fā)的優(yōu)勢(shì)。用Scala編程是一種樂趣。在我為準(zhǔn)備的演講《Scala幫助git運(yùn)行得更快》中,我對(duì)Scala的優(yōu)勢(shì)做了更深入的剖析。
InfoQ:你有沒有想過提供一個(gè)GUI版本的BFG Repo-,作為獨(dú)立的工具也好,作為其他git工具的插件也好?
:雖然我覺得作為命令行工具BFG工作得很好,我也做了一些有意思的可視化方面的嘗試。六月份我在泰特現(xiàn)代藝術(shù)館(Tate )舉行的“玩轉(zhuǎn)空間(Hack The Space)”活動(dòng)上,把git--和BFG操作git庫歷史記錄的過程用實(shí)驗(yàn)性的3D過程展示了出來。除了視覺效果非常漂亮以外,它也非常清晰地顯示了兩者在速度方面差別這么大!此舉的另一個(gè)目的是更清楚地解釋BFG對(duì)你的倉庫到底做了些什么——我可以理解人們很擔(dān)心把刪除舊文件的任務(wù)交給BFG(哪怕它不會(huì)碰你當(dāng)前的文件,因?yàn)槲覀兗僭O(shè)用戶過去犯過的錯(cuò)誤不會(huì)再犯,當(dāng)前庫中的文件都是他們要的)。有時(shí)候在需求方面,用戶想要的和他們真正需要的東西有一定的差距,圖形化的展示對(duì)于這兩者的統(tǒng)一很有幫助。
關(guān)于插件,我真的很想簡(jiǎn)化一下在BFG中用腳本自定義處理動(dòng)作的方式——接近git--的靈活性,但依然保持快速。 最近把BFG集成進(jìn)了一個(gè)自定義工具(git-),但離達(dá)到我滿意的程度還有差距。
按照現(xiàn)在的情況,命令行版本的BFG被廣泛采用——上有很多很棒的帖子在討論如何使用它,BFG文檔網(wǎng)站的引用者統(tǒng)計(jì)記錄顯示,BFG在被形形色色的用戶使用,從主流的投資銀行,到研究型實(shí)驗(yàn)室、手機(jī)生產(chǎn)商,甚至那些為軍用飛機(jī)編寫航天軟件的神秘公司。基于下載量來做個(gè)估算,據(jù)此我猜測(cè),BFG自發(fā)布以來,已經(jīng)為大家節(jié)省了大約30人年的工作量。
BFG是開源、完全免費(fèi)的,并且我希望你們的讀者下次需要清理git歷史記錄的時(shí)候,BFG能給他們帶來良好的體驗(yàn)。
關(guān)于受訪者
Tyley是公司的開發(fā)人員、會(huì)員項(xiàng)目的技術(shù)經(jīng)理,BFG、gu-who、Agit的作者,也為其他很多開源項(xiàng)目做貢獻(xiàn)。他以前在工作過,創(chuàng)建了" diff"項(xiàng)目,他熱衷于解釋事物的原理。