版本管理危机

    起始阶段:
    项目的开始,项目组只有从第三方获取的类库、具备编程知识的程序员和PM(项目经理)。由于成员数量不少,使用简单共享方式的版本管理往往难以胜任,某些人往往会因为新功能的需要或者无意将一些代码改得面目全非,无从追踪。我们需要一个简单的版本管理工具,比如Visual Source Safe,每个人在修改代码之前要求先将代码文件标记为“检出”状态,每一次“检入”代码都在服务器上生成一个新的版本。好了,所有的代码都有了版本记录,我们可以查看代码的演进过程,对任何两个版本进行比较,也可以轻松的获取到早先的版本。

    开始迭代:
    由于客户的要求,项目开始进行简单的迭代。PM要求所有人员检入可以工作的代码。然后开始执行构建。第一次全部构建的过程可能并不顺利,因为有人修改了A组件导致了依赖A组件的B组件不能正常工作了。当然这个不难解决,我们需要对成员进行培训,要求每个人在检入代码前保证所有的构建都是成功的。这很凑效,虽然每次构建要耗费不少时间。编译的错误很容易发现,但是逻辑的错误却没有那么简单了。不过,到现在为止,这个并不太重要,毕竟项目刚刚开始迭代,在给客户演示的时候偶尔崩溃也是可以忍受的。

    版本建立:
    随着项目第一次交付期的临近,PM决定停止新特性的开发,确定1.0版本。并且将这个版本发布SIT(系统集成测试)。刚刚SIT测试的时候,大家都忙于修改自己的代码中的BUG,忙得不亦乐乎。慢慢的,BUG数量曲线开始趋于平滑。很多人开始觉的可以将当前版本发布,从而可以投入精力进行新特性的开发了。PM也这么认为,因为从需求跟踪矩阵的情况看,还有许多的工作量,客户会要求在第二次交付(2.0.x版本)的时候看到剩下的需求都已经实现。为了不影响1.0.x版本的构建,PM下令所有人可以在本地编辑代码以添加新特性,但是不得检入版本机,允许被检入版本机的是修改BUG的代码。这在一段时间里看起来工作得不错,知道有,小王发现他在 a.java 文件中添加和编辑了许多的新特性相关的代码,但是 现在要命的是发现了一个跟 a.java 相关的BUG。冥思苦想,小王决定将a.java先备份起来,然后撤销检出,ok,回到1.0.x的代码了,在a.java中修改了一通,检入了,很幸运,居然没有引起问题。小王开始将原先备份的 a.java和修改过BUG的a.java中的更改进行合并,合并的结果将产生一个新的a.java文件,这个文件没有了已经发现的BUG,而且包含了新特性的代码。由于小王是高手,所编写的代码遵从了SRP(单一职责原则),所以小王的合并并没有耗费缩少时间。但是接下来的时间里,小王又发现b.java,c.java,d.java…x.java需要进行这种手工的合并,每一次合并,他都要将文件预先备份起来。而且,因为在1.0.x稳定运行之前,小王不得检入自己的代码,因此小王担心,如果自己的硬盘崩溃,小王也为不能够使用其它人编写的新特性代码而感到无比郁闷。PM也意识到这种情况,在一个晚上的权衡之后,PM决定在版本服务器上建立了2.0.x的目录,将1.0.x的代码拷贝到这里来。Ok,所有的新特性的开发在2.0.x中进行,所有的BUG修改在1.0.x和2.0.x中同时进行。这真是一个不错的主意。但是,过不了多久,项目组被频繁的拷贝粘贴折腾的死去活来,代码的修改没有办法被有效跟踪则更是让人伤透了脑筋。

典型的版本管理难题

    看完了上篇,我们对于多分支开发容易产生的问题应该有了一些基本的了解吧。事实上,通常,并行开发的版本管理面临以下几个典型的难题:
        如何保证新版本开发与BugFix同时进行?也是要求修改过的BUG不能存在于新版本中。
        如何保证两个新版本并行开发?可能的情况是两个完全不同的版本,或者一个是另外一个基础。
        如何保证版本的发布不受开发人员无意的代码检入影响?
    不再拐弯抹角了,解决这三个难题的答案是使用分支(这里涉及到一个的版本管理工具ClearCase,分支正是其中的重要工具和概念)。要理解分支必须同时理解其他的术语,比如标签、视图。本文不打算详细地描述基础的概念,相关的概念可以参考ClearCase的文档。

    上面是一棵版本树,形象地记载了一个文件的版本变化情况。
    其中,1、2、3是不同的版本;Main、Ver2.0是分支;Release1023和Ver2.0Begin则是标签,标签像是打在代码版本上的标记;视图是由分支类型、标签名称、获取规则动态的决定的代码横截面。可以建立Main分支的视图,在这个视图中我们看不到Ver2.0分支中的任何代码修改;也可以建立Ver2.0分支的视图,在这个视图中我们可以看到Ver2.0分支的新代码和未在Ver2.0分支中产生修改的Main分支中位于Ver2.0Begin标签处的代码。
    开发人员总是习惯工作于一个视图上。那看看解决第一个问题的办法。

    1. 建立用于修改Bug的分支视图,在此视图上进行修改。
    2. 将在BugFix上修改的代码合并到主分支中,合并产生新的版本3,移动Ver2.0Begin标签到版本3,Ver2.0分支自动获取到修复Bug以后的代码,同时,主分支上的Bug也得到了修正。
    3. 如果此时代码已经在Ver2.0上发生了变化,则需要执行另外一个合并,将更改合并到Ver2.0中。但幸运的是,大多数时候不会在BugFix之前修改Ver2.0的代码。
    这样做我们至少收获了几个附加的好处:
        我们获得了从Main分支发布稳定版本的能力;
        我们获得了从Ver2.0分支发布新预览版的能力;
        开发人员的检入检出不影响版本发布;
        版本管理员可以对Main分支进行锁定等控制,防止其他人员越权或者意外的修改Main分支的代码。

 版本的强制控制和版本合并

    版本需要强制控制的几种常见场景:
    1. 要转产或者上市了,不希望开发者随意的代码检入影响到产品的质量和稳定性。
    2. 已经转产了,希望控制Bug的修改,不希望开发者随意的代码检入影响到补丁(包)的发布。
    版本强制控制的手段包括:
    1. 将需要保护的分支锁定(仅允许版本管理员修改),打上Release标签。
    2. 让开发者在以Release标签为基线的分支上进行开发。
    3. 登记开发者在以Release标签为基线的分支上的代码修改动作。
    4. 在以Release标签为基线的分支上发布版本进行集成测试。
    5. 对于集成测试通过的代码修改,通过版本合并手段合并到被保护的分支上。
    上面提到了版本合并。事实上,版本合并也有如下的几种常见情景:
    1. 修改了Bug ,需要合并到基线版本中,以便可以发布稳定版本。