Subversion Re-education
當(dāng)我們公司的程序員決定將版本控制工具從SVN轉(zhuǎn)為水銀時(shí)粱锐,我很迷惑芽世。
我首先想到的是所有不能換工具的理由。“我們必須要有一個(gè)中心倉庫吼蚁,那樣才安全,”我說罚斗。然而我錯(cuò)了跺株。在水銀中,每個(gè)開發(fā)人員的硬盤中都存有倉庫的一份拷貝府喳,那樣才真的是安全蒲肋。況且?guī)缀趺總€(gè)用水銀管理的團(tuán)隊(duì)都用了中心倉庫。
“分布式版本控制的麻煩在于它制造了太多的分支钝满,”我說兜粘,“而分支往往帶來麻煩⊥溲粒”結(jié)果孔轴,這個(gè)想法又錯(cuò)了。連續(xù)挫敗碎捺。分支在SVN上造成問題是因?yàn)镾VN沒有存儲(chǔ)足夠的信息來進(jìn)行合并操作路鹰。在水銀中,合并不會(huì)帶來任何損失且很容易收厨,因此分支是常用而無害的東西悍引。
最后我說:“好吧,我會(huì)用它的帽氓,但是別指望我弄懂它趣斤。”然后我讓Jacob幫我弄一張小抄黎休,列出所有SVN常用操作在水銀上的替代浓领。
現(xiàn)在玉凯,我可以給你展示一下這張小抄,但我想還是算了联贩。因?yàn)樗盐业哪X子搞壞了幾個(gè)月漫仆。
如果你一直是用SVN的,那說好聽點(diǎn)泪幌,你會(huì)腦殘盲厌。不好意思,這似乎并不好聽祸泪。你需要來點(diǎn)再教育吗浩。我腦殘了6個(gè)月,在這6個(gè)月中没隘,我覺得水銀比SVN復(fù)雜多了懂扼。而現(xiàn)在想起來,都是因?yàn)槲覜]有理解其精髓右蒲。而一旦我了解到了阀湿,水銀就變得異常簡單。
所以我為你寫下這份教程瑰妄。我小心翼翼地繞開SVN來解釋這個(gè)系統(tǒng)陷嘴,因?yàn)槲也幌肽阋蚕裎乙粯幽X子灌水。因?yàn)檫@個(gè)世界已經(jīng)夠二了间坐。對于那些也是從SVN轉(zhuǎn)來的朋友灾挨。我寫這一章的目的就是為了試著替你清洗腦傷以使得你可以如一張白紙般學(xué)習(xí)水銀。
如果你完全沒有使用過SVN眶诈,那么你可以直接跳到下一章而不會(huì)錯(cuò)過任何事涨醋。
準(zhǔn)備好了嗎?OK逝撬,讓我們以一個(gè)簡短的測試開始浴骂。
問題1. 你會(huì)一次就寫出完美的代碼嗎?
如果你的回答是“是的”宪潮,那么你是一個(gè)騙子一個(gè)作弊生溯警。你的測試沒有通過。重新測試吧狡相。
新的代碼總是會(huì)有問題梯轻。需要花些時(shí)間花些努力才能讓它較好地運(yùn)行。而在這之前尽棕,你的代碼可能會(huì)給同一團(tuán)隊(duì)的其他開發(fā)者造成困擾喳挑。
下面SVN的方式:
- 當(dāng)你把你的新代碼check in,所有其他人都得到了它。
由于你寫的新代碼會(huì)有問題伊诵,因此你有兩個(gè)選擇单绑。
- 你可以check in有問題的代碼并讓所有人都抓狂,或者
- 你可以知道它完全沒有問題了再check in曹宴。
SVN往往給你這樣的可怕困境搂橙。要么倉庫中由于新代碼的提交充滿了各種bug,要么新代碼沒辦法放到倉庫里面去笛坦。
作為SVN的使用者区转,我們是如此習(xí)慣于這樣的困境以致于基本不會(huì)去想象沒有這個(gè)困境的樣子。
SVN的團(tuán)隊(duì)經(jīng)常數(shù)日或數(shù)周都不check in任何東西版扩。在SVN的團(tuán)隊(duì)中废离,新手更是不敢check in任何東西,因?yàn)楹ε缕茐臉?gòu)建资厉,或者害怕被上司罵厅缺。Mike有一次非常生氣蔬顾,因?yàn)橛幸淮蝐heckin破壞了整體的構(gòu)建宴偿。他沖到實(shí)習(xí)生的隔間,把他桌子上的東西推落一地诀豁,然后咆哮道:“這是你的最后一天了窄刘!”(當(dāng)然沒有成為實(shí)習(xí)生的最后一天,但是那個(gè)可憐的實(shí)習(xí)生顯然哭了鼻子舷胜。)
這所有的恐懼導(dǎo)致人們幾周幾周地不敢使用版本控制工具來控制自己的版本娩践,然后再找個(gè)高手幫助他們把代碼check in。如果你沒辦法用版本控制工具烹骨,你又為何使用它翻伺?
下圖是SVN生活下的簡單示例。
在水銀中沮焕,每個(gè)開發(fā)者在他們的電腦中都有它們自己的倉庫:
這樣吨岭,你就可以把代碼提交到你的私人倉庫,并且享受版本控制帶來的好處峦树。每當(dāng)你到達(dá)了一個(gè)代碼好了一點(diǎn)點(diǎn)的邏輯點(diǎn)辣辫,你就可以提交你的代碼。
一旦你的版本變的穩(wěn)固了魁巩,并且希望別人也能使用你的新代碼時(shí)急灭,你就可以把你的變更從你的倉庫推送到中央倉庫。這樣谷遂,別人就都可以通過拉取來看到你的代碼了葬馋。
水銀將提交新代碼的操作與影響其他人的操作分離了。
這就意味著你可以在其他人不知情的前提下提交(hg com)。當(dāng)你已經(jīng)有了一堆變更畴嘶,并且你覺得已經(jīng)穩(wěn)定了且希望給別人看時(shí)扫尖,你可以推送(hg push)它們到主倉庫中。
一個(gè)更大的概念上的差別
你知道如何給每條街命名嗎掠廓?
在日本换怖,他們往往只是在街與街之間用數(shù)字標(biāo)明街區(qū)。只有非常非常重要的街才會(huì)有名字蟀瞧。
SVN與水銀的差別有些類似沉颂。
SVN喜歡考慮版次。一個(gè)版次是說在某個(gè)特定的時(shí)間點(diǎn)悦污,整個(gè)文件系統(tǒng)看起來是什么樣的铸屉。
在水銀中,考慮的是變更集切端。一個(gè)變更集是兩個(gè)版次間的差異列表彻坛。
半斤與八兩:區(qū)別在哪里?
區(qū)別在這里踏枣。想象一下你和我都在一段代碼上進(jìn)行工作昌屉,并且我們對那段代碼進(jìn)行了分支。我們分別在這段代碼上做了很多工作茵瀑,也改變了很多间驮,所以它們越走越遠(yuǎn)了。
當(dāng)我們想要合并時(shí)马昨,SVN試著去看兩個(gè)版本——我改的代碼竞帽,和你改的代碼——它嘗試著去猜測如何把他們打碎拼裝成一個(gè)大塊。它往往會(huì)失敗鸿捧,產(chǎn)生整頁整頁的“合并沖突”屹篓。而它們往往不是真的沖突,而是SVN無法認(rèn)出我們干了什么匙奴。
另一方面堆巧,當(dāng)我們各自在水銀工作,水銀做的是保持一系列的變更集饥脑。因此恳邀,當(dāng)我們想要合并我們的代碼時(shí),水銀有更多的信息:它知道我們各自改了什么灶轰,并且可以重新應(yīng)用這些變更谣沸,而非只是看看最終的結(jié)果,并看看如何把它倆放到一起笋颤。
例如乳附,如果我對某個(gè)函數(shù)更改了一點(diǎn)内地,然后把它移到別的地方去了。SVN并不記住這些步驟赋除,所以當(dāng)要做合并時(shí)阱缓,它可能會(huì)認(rèn)為一個(gè)新的函數(shù)產(chǎn)生了。而水銀記住了這些變更:函數(shù)改變了举农,函數(shù)移動(dòng)了荆针。這以為著,如果你也改變了函數(shù)一點(diǎn)點(diǎn)颁糟。那么水銀很可能會(huì)成功地合并我們的變更航背。
既然水銀考慮的是變更集,那么你就可以對那些變更集做些有趣的事情棱貌。你可以把變更集推送給你的隊(duì)友進(jìn)行嘗試玖媚,而不是放到中心倉庫去影響所有的人。
如果這些看著有些暈婚脱,不要著急——當(dāng)你完成了所有教程今魔,你會(huì)完全明白。現(xiàn)在你要知道的最重要的事情就是:由于水銀是考慮的“變更集”而不是“版次”障贸,因此可以比SVN合并地更好错森。
這就以為著你可以隨意分支。因?yàn)楹喜⒉辉偈且粋€(gè)噩夢惹想。
想知道些有趣的事嗎问词?幾乎所有的SVN團(tuán)隊(duì)都會(huì)給我講同一個(gè)故事的不同變種督函。這個(gè)故事是那么地普遍以致于我想叫它“SVN故事#1”嘀粱。故事是這樣說的:在某個(gè)時(shí)間點(diǎn),他們試圖對代碼進(jìn)行分支辰狡,通常是為了把交付給客戶的版本與開發(fā)版本相剝離锋叨。所有的團(tuán)隊(duì)都說,當(dāng)他們這么做時(shí)宛篇,沒有問題娃磺。直到他們希望合并時(shí),這成了噩夢叫倍。如果合并操作只需要五分鐘偷卧,而不是整個(gè)團(tuán)隊(duì)圍繞一臺(tái)電腦奮戰(zhàn)2周,會(huì)是怎么樣的一種體驗(yàn)吆倦?
幾乎每個(gè)SVN的團(tuán)隊(duì)告訴我他們發(fā)誓不要再發(fā)生這樣的事情听诸,并且不再使用分支。現(xiàn)在他們這么做:每個(gè)新特征都在一個(gè)大的#ifdef
的代碼塊中蚕泽。這樣晌梨,他們就可以工作在一個(gè)樹干中。客戶不會(huì)看到新代碼仔蝌,除非是在調(diào)試過程中泛领。坦白地說,這很荒唐敛惊。
分開保存穩(wěn)定版代碼和開發(fā)版代碼應(yīng)當(dāng)是源代碼控制幫助你做的事情渊鞋。
當(dāng)你轉(zhuǎn)而使用水銀,你可能還沒有意識(shí)到瞧挤,但分支變的可用篓像,而且你不需要害怕。
這意味著你可以有一個(gè)團(tuán)隊(duì)倉庫皿伺,小團(tuán)隊(duì)的成員協(xié)作完成一個(gè)新特性员辩。當(dāng)他們完成時(shí),他們把工作合并到開發(fā)倉庫中鸵鸥。然后事情就成了奠滑。
這意味著你可以在獨(dú)立的倉庫進(jìn)行實(shí)驗(yàn),如果它們成功了妒穴,你可以合并到主倉庫中宋税,如果失敗了,就可以直接刪除讼油!
最后一個(gè)概念上的區(qū)別
最后一個(gè)主要的SVN和水銀的差別并不是什么大事杰赛,但是如果你不注意的話會(huì)讓你出差錯(cuò),這就是:
SVN是對文件的版本控制矮台。但是在水銀中乏屯,版本控制總是應(yīng)用于整個(gè)文件夾——包括所有的子目錄。
你注意到這個(gè)的方式是瘦赫,在SVN中辰晕,如果你進(jìn)入一個(gè)子目錄并且提交你的變更,它僅僅提交那個(gè)子目錄以下的變更确虱,這潛在地意味著你忘了把其它子目錄里面的變更c(diǎn)heckin了含友。然而,在水銀中校辩,所有的命令都應(yīng)用在整個(gè)目錄結(jié)構(gòu)中窘问。如果你的代碼在c:\code
,則當(dāng)你執(zhí)行hg commit
命令宜咒,你的工作目錄可以在c:\code
或者在其中的任何一個(gè)子文件夾中惠赫。效果是一樣的。
這不是一個(gè)大問題荧呐,但是如果你習(xí)慣于讓全公司擁有一個(gè)巨大的倉庫——人們只check out出他們關(guān)心的子目錄汉形,并且在上面工作纸镊,則水銀不是一種好的方式——你最好是構(gòu)建為不同的項(xiàng)目構(gòu)建很多小的倉庫。
最后……
這一部分概疆,我希望你直接相信我逗威。
水銀比SVN好。
它是一個(gè)團(tuán)隊(duì)共同在源代碼上工作的更好的方式岔冀。它是你自己在源代碼上工作的更好方式凯旭。
它就是更好。
記住我的話使套,如果你理解水銀的工作方式罐呼,如果你用水銀的方式工作,如果你不去斗爭侦高,不去試著用SVN的方式使用水銀而是學(xué)習(xí)水銀希望你工作的那種方式嫉柴。你會(huì)變得快樂,成功奉呛,滿足计螺,而且總能找到電視的遙控器。
早期瞧壮,你會(huì)被誘惑登馒,離開水銀重投SVN的懷抱。因?yàn)樗y對你來說很陌生咆槽,你就像生活在異國他鄉(xiāng)陈轿。你會(huì)想家,并提出很多回家的理由秦忿,譬如麦射,你會(huì)抱怨水銀的工作目錄太占空間了,然而實(shí)際上小渊,它們比SVN占的空間要小法褥。(這是真的!)
然后你想回到SVN的懷抱因?yàn)槟銍L試著用SVN的方式去分支酬屉,然而你迷惑了,它并不能工作揍愁。因?yàn)槟阏娴男枰盟y的方式分支——通過拷貝倉庫的方式——而不是SVN的方式呐萨。相信我,這會(huì)工作地很好莽囤。
之后你會(huì)遇到Jacob谬擦,或者在你的辦公室相當(dāng)于Jacob的那個(gè)人,給你一張“SVN到水銀的轉(zhuǎn)換小抄”朽缎,然后你會(huì)在三個(gè)月的時(shí)間內(nèi)認(rèn)為hg fetch
就像svn up
惨远,而并不真正了解hg fetch
在干嗎谜悟。當(dāng)有一天,事情變糟了北秽。你會(huì)怪罪水銀葡幸。但其實(shí)你應(yīng)該怪罪的是你自己沒有理解水銀工作的方式。
我知道你會(huì)做那些因?yàn)槲易约涸?jīng)做過贺氓。
不要犯同樣的錯(cuò)誤蔚叨。學(xué)習(xí)水銀,相信水銀辙培,用水銀的方式來干活蔑水,之后你會(huì)在版本控制的思維上有大的進(jìn)步。當(dāng)供應(yīng)商更新了一個(gè)庫扬蕊,你的競爭者在花一周時(shí)間解決所有的合并問題搀别;而你只是輸入了hg merge
,然后告訴你自己尾抑,“天吶领曼,太酷了,成了蛮穿!”
FAQ
Q:分布式版本控制不安全庶骄,因?yàn)閟vn有一個(gè)中央倉庫,那樣更安全践磅。
A:分布式版本控制使得每個(gè)人那里都有一個(gè)備份单刁,而且一般都會(huì)配置一個(gè)中心倉庫。你說哪個(gè)更安全府适?
Q:分布式版本控制太容易造成分支了羔飞,而分支往往會(huì)帶來問題。
A:那是因?yàn)镾VN沒有存儲(chǔ)足夠的信息來進(jìn)行分支間的合并檐春。而水銀的分支間合并已經(jīng)做的很好了逻淌,根本無害。不要因?yàn)镾VN做的不好疟暖,就說分支本身不好卡儒!