Subversion Re-education
当我们公司的程序员决定将版本控制工具从SVN转为水银时,我很迷惑。
我首先想到的是所有不能换工具的理由。“我们必须要有一个中心仓库,那样才安全,”我说。然而我错了。在水银中,每个开发人员的硬盘中都存有仓库的一份拷贝,那样才真的是安全。况且几乎每个用水银管理的团队都用了中心仓库。
“分布式版本控制的麻烦在于它制造了太多的分支,”我说,“而分支往往带来麻烦。”结果,这个想法又错了。连续挫败。分支在SVN上造成问题是因为SVN没有存储足够的信息来进行合并操作。在水银中,合并不会带来任何损失且很容易,因此分支是常用而无害的东西。
最后我说:“好吧,我会用它的,但是别指望我弄懂它。”然后我让Jacob帮我弄一张小抄,列出所有SVN常用操作在水银上的替代。
现在,我可以给你展示一下这张小抄,但我想还是算了。因为它把我的脑子搞坏了几个月。
如果你一直是用SVN的,那说好听点,你会脑残。不好意思,这似乎并不好听。你需要来点再教育。我脑残了6个月,在这6个月中,我觉得水银比SVN复杂多了。而现在想起来,都是因为我没有理解其精髓。而一旦我了解到了,水银就变得异常简单。
所以我为你写下这份教程。我小心翼翼地绕开SVN来解释这个系统,因为我不想你也像我一样脑子灌水。因为这个世界已经够二了。对于那些也是从SVN转来的朋友。我写这一章的目的就是为了试着替你清洗脑伤以使得你可以如一张白纸般学习水银。
如果你完全没有使用过SVN,那么你可以直接跳到下一章而不会错过任何事。
准备好了吗?OK,让我们以一个简短的测试开始。
问题1. 你会一次就写出完美的代码吗?
如果你的回答是“是的”,那么你是一个骗子一个作弊生。你的测试没有通过。重新测试吧。
新的代码总是会有问题。需要花些时间花些努力才能让它较好地运行。而在这之前,你的代码可能会给同一团队的其他开发者造成困扰。
下面SVN的方式:
- 当你把你的新代码check in,所有其他人都得到了它。
由于你写的新代码会有问题,因此你有两个选择。
- 你可以check in有问题的代码并让所有人都抓狂,或者
- 你可以知道它完全没有问题了再check in。
SVN往往给你这样的可怕困境。要么仓库中由于新代码的提交充满了各种bug,要么新代码没办法放到仓库里面去。
作为SVN的使用者,我们是如此习惯于这样的困境以致于基本不会去想象没有这个困境的样子。
SVN的团队经常数日或数周都不check in任何东西。在SVN的团队中,新手更是不敢check in任何东西,因为害怕破坏构建,或者害怕被上司骂。Mike有一次非常生气,因为有一次checkin破坏了整体的构建。他冲到实习生的隔间,把他桌子上的东西推落一地,然后咆哮道:“这是你的最后一天了!”(当然没有成为实习生的最后一天,但是那个可怜的实习生显然哭了鼻子。)
这所有的恐惧导致人们几周几周地不敢使用版本控制工具来控制自己的版本,然后再找个高手帮助他们把代码check in。如果你没办法用版本控制工具,你又为何使用它?
下图是SVN生活下的简单示例。
在水银中,每个开发者在他们的电脑中都有它们自己的仓库:
这样,你就可以把代码提交到你的私人仓库,并且享受版本控制带来的好处。每当你到达了一个代码好了一点点的逻辑点,你就可以提交你的代码。
一旦你的版本变的稳固了,并且希望别人也能使用你的新代码时,你就可以把你的变更从你的仓库推送到中央仓库。这样,别人就都可以通过拉取来看到你的代码了。
水银将提交新代码的操作与影响其他人的操作分离了。
这就意味着你可以在其他人不知情的前提下提交(hg com)。当你已经有了一堆变更,并且你觉得已经稳定了且希望给别人看时,你可以推送(hg push)它们到主仓库中。
一个更大的概念上的差别
你知道如何给每条街命名吗?
在日本,他们往往只是在街与街之间用数字标明街区。只有非常非常重要的街才会有名字。
SVN与水银的差别有些类似。
SVN喜欢考虑版次。一个版次是说在某个特定的时间点,整个文件系统看起来是什么样的。
在水银中,考虑的是变更集。一个变更集是两个版次间的差异列表。
半斤与八两:区别在哪里?
区别在这里。想象一下你和我都在一段代码上进行工作,并且我们对那段代码进行了分支。我们分别在这段代码上做了很多工作,也改变了很多,所以它们越走越远了。
当我们想要合并时,SVN试着去看两个版本——我改的代码,和你改的代码——它尝试着去猜测如何把他们打碎拼装成一个大块。它往往会失败,产生整页整页的“合并冲突”。而它们往往不是真的冲突,而是SVN无法认出我们干了什么。
另一方面,当我们各自在水银工作,水银做的是保持一系列的变更集。因此,当我们想要合并我们的代码时,水银有更多的信息:它知道我们各自改了什么,并且可以重新应用这些变更,而非只是看看最终的结果,并看看如何把它俩放到一起。
例如,如果我对某个函数更改了一点,然后把它移到别的地方去了。SVN并不记住这些步骤,所以当要做合并时,它可能会认为一个新的函数产生了。而水银记住了这些变更:函数改变了,函数移动了。这以为着,如果你也改变了函数一点点。那么水银很可能会成功地合并我们的变更。
既然水银考虑的是变更集,那么你就可以对那些变更集做些有趣的事情。你可以把变更集推送给你的队友进行尝试,而不是放到中心仓库去影响所有的人。
如果这些看着有些晕,不要着急——当你完成了所有教程,你会完全明白。现在你要知道的最重要的事情就是:由于水银是考虑的“变更集”而不是“版次”,因此可以比SVN合并地更好。
这就以为着你可以随意分支。因为合并不再是一个噩梦。
想知道些有趣的事吗?几乎所有的SVN团队都会给我讲同一个故事的不同变种。这个故事是那么地普遍以致于我想叫它“SVN故事#1”。故事是这样说的:在某个时间点,他们试图对代码进行分支,通常是为了把交付给客户的版本与开发版本相剥离。所有的团队都说,当他们这么做时,没有问题。直到他们希望合并时,这成了噩梦。如果合并操作只需要五分钟,而不是整个团队围绕一台电脑奋战2周,会是怎么样的一种体验?
几乎每个SVN的团队告诉我他们发誓不要再发生这样的事情,并且不再使用分支。现在他们这么做:每个新特征都在一个大的#ifdef
的代码块中。这样,他们就可以工作在一个树干中。客户不会看到新代码,除非是在调试过程中。坦白地说,这很荒唐。
分开保存稳定版代码和开发版代码应当是源代码控制帮助你做的事情。
当你转而使用水银,你可能还没有意识到,但分支变的可用,而且你不需要害怕。
这意味着你可以有一个团队仓库,小团队的成员协作完成一个新特性。当他们完成时,他们把工作合并到开发仓库中。然后事情就成了。
这意味着你可以在独立的仓库进行实验,如果它们成功了,你可以合并到主仓库中,如果失败了,就可以直接删除!
最后一个概念上的区别
最后一个主要的SVN和水银的差别并不是什么大事,但是如果你不注意的话会让你出差错,这就是:
SVN是对文件的版本控制。但是在水银中,版本控制总是应用于整个文件夹——包括所有的子目录。
你注意到这个的方式是,在SVN中,如果你进入一个子目录并且提交你的变更,它仅仅提交那个子目录以下的变更,这潜在地意味着你忘了把其它子目录里面的变更checkin了。然而,在水银中,所有的命令都应用在整个目录结构中。如果你的代码在c:\code
,则当你执行hg commit
命令,你的工作目录可以在c:\code
或者在其中的任何一个子文件夹中。效果是一样的。
这不是一个大问题,但是如果你习惯于让全公司拥有一个巨大的仓库——人们只check out出他们关心的子目录,并且在上面工作,则水银不是一种好的方式——你最好是构建为不同的项目构建很多小的仓库。
最后……
这一部分,我希望你直接相信我。
水银比SVN好。
它是一个团队共同在源代码上工作的更好的方式。它是你自己在源代码上工作的更好方式。
它就是更好。
记住我的话,如果你理解水银的工作方式,如果你用水银的方式工作,如果你不去斗争,不去试着用SVN的方式使用水银而是学习水银希望你工作的那种方式。你会变得快乐,成功,满足,而且总能找到电视的遥控器。
早期,你会被诱惑,离开水银重投SVN的怀抱。因为水银对你来说很陌生,你就像生活在异国他乡。你会想家,并提出很多回家的理由,譬如,你会抱怨水银的工作目录太占空间了,然而实际上,它们比SVN占的空间要小。(这是真的!)
然后你想回到SVN的怀抱因为你尝试着用SVN的方式去分支,然而你迷惑了,它并不能工作。因为你真的需要用水银的方式分支——通过拷贝仓库的方式——而不是SVN的方式。相信我,这会工作地很好。
之后你会遇到Jacob,或者在你的办公室相当于Jacob的那个人,给你一张“SVN到水银的转换小抄”,然后你会在三个月的时间内认为hg fetch
就像svn up
,而并不真正了解hg fetch
在干吗。当有一天,事情变糟了。你会怪罪水银。但其实你应该怪罪的是你自己没有理解水银工作的方式。
我知道你会做那些因为我自己曾经做过。
不要犯同样的错误。学习水银,相信水银,用水银的方式来干活,之后你会在版本控制的思维上有大的进步。当供应商更新了一个库,你的竞争者在花一周时间解决所有的合并问题;而你只是输入了hg merge
,然后告诉你自己,“天呐,太酷了,成了!”
FAQ
Q:分布式版本控制不安全,因为svn有一个中央仓库,那样更安全。
A:分布式版本控制使得每个人那里都有一个备份,而且一般都会配置一个中心仓库。你说哪个更安全?
Q:分布式版本控制太容易造成分支了,而分支往往会带来问题。
A:那是因为SVN没有存储足够的信息来进行分支间的合并。而水银的分支间合并已经做的很好了,根本无害。不要因为SVN做的不好,就说分支本身不好!