版本
大版分支
在发布一个稳定的版本后,我们会创建一个分支,这是因为我们的人力还需要马不停蹄的继续开发大量的新版本功能(修改代码),而客户使用的是稳定版本,但很难说不会有BUG,这个时候我们就可以在这个分支修改BUG,立即交付给客户。创建一个分支是TFS和很多源代码管理工具都自带的功能,可惜很多人不知道,我就啰嗦一下。
在TFS的“源代码管理资源管理器”中,找到你的产品单元目录,例如MyProduct,右键,选择“Branch 分支”,出现下面的画面:
在“Target 目标”的地方键入MyProduct-1.0,这样,就在与MyProduct平级的目录,建立了一个新目录:MyProduct-1.0。由于之前已经在1.0发版时打了标签,所以我这里选择了这个标签。点击OK后,你还需要实际的签入才能生效。
建立一个平级的目录的好处是,你可以像以前的工作方式一样,把这个1.0看成一个新的产品单元,这样之前定义的流程在分支情况下仍然适用。
当真的需要修改BUG时,你需要到MyProduct-1.0下修订这个BUG,测试并签入代码,与往常没有什么不一样,唯一的另外就是,通常这个BUG在当前开发版本中(在这里例子就是MyProduct)也有,TFS提供了简便的方法帮助你快速完成MyProduct上此BUG的修订,可以在MyProduct-1.0/src目录上点击右键,选择“Merge 合并”,出现下图:
在“Source branch 源分支”中选择稳定版本(已经修订BUG的版本),在“Target branch 目标分支”上选择开发版本(未修订Bug的版本),注意,一般我们选择“Selected changesets 选择变更集”,而不是“All changes up to a specific version 所有变更”,这样就仅针对你这个bug修改进行合并,点击“Next 下一步”。
这里你能看见所有未合并的变更集,选择你的变更集,然后下一步,最后完成。重新回到MyProduct对应的MyCode所在目录,你会发现TFS自动签出了这个文件:
如果你打开这个文件,可以看见你修订BUG的代码也合并进去,你需要的是继续在当前MyProduct上完成编译和测试,最后签入即可。
这里仅仅是一个演示,分支与合并其实是一门蛮高深的学问,我这里就不细说,仅作为一个引子。
补丁
当我们已经修订BUG并测试通过后,必然已经更新了TFS上的bin目录,我们可以根据比对历史版本,来知道到底修改了哪些组件。
在bin目录上点击右键,选择“View History 查看历史”,在出现的历史列表中,选择两个版本,然后点击“Compare Folders 比较目录”。最后出现这个清单:
这是手工操作的方法,我是不会这样干的,我还是使用“项目工具”,首先看看目录结构:
你看见多了一个hotfixs目录,下面的目录名,其01是产品单元的编号,而119就是在TFS上的“Changeset 变更集”编号,“项目工具”总是检测最后一个补丁的编号,并与最新的编号比较,就跟你上图中手工处理一样,获取到清单后,工具就下载这些文件到一个临时目录,并使用安装包制作工具创建一个补丁安装程序。
可能你会说,并不是把最新的dll复制到客户就能修正某个bug的,可能还需要修复数据库的表结构,甚至修复数据。是的,你说的没有错,但这个流程我们不会把他设计到补丁工具上,而是
这些最新的组件其包含这些功能,这样补丁的流程就简单很多。
如果你的某个版本时间跨度很长,很可能积累了很多的补丁,要知道这些补丁是向前依赖的,不能说单独安装一个补丁。我建议你的产品设计一个在线更新的功能,这样就不会那么痛苦了。另外一个常见的实践是制作SP1这样的较大补丁包,他实际上就是把一堆的补丁合并成一个大补丁,方便新客户的安装。
有几点要切记,切记:
- 补丁尽量只包含BUG的修订,不要包含新功能;
- 给新客户的安装包,是原版+SP的形式,而不是直接一个最新版本。
这些忠告直接关系你的管理成本,如果你要问我为什么?怎么说呢,好复杂,你看Windows都是这么干的,O(∩_∩)O哈哈~算是解释吗。
中版分支
我知道,你在看到上面的第一点忠告时已经暴跳起来,
天啊,客户怎么可能等上一两年没有一个新版本?
做,当然做,不过我们换一种方法来做。
让我们设想一下,如果你将新特性放在MyProduct_1.0源代码中,就会出现一个问题,新特性的开发毕竟需要一个较长的编码和测试周期,当客户想你反映一个BUG时,你虽然可以很快修订这个BUG,但是你无法发布给客户,因为你的组件包含了很多尚未测试完毕的新特性,这个非常糟糕。
我的方法是,创建一个特性包分支,参加下图
在完成1.0的发版后,会产生分支,这个我们上面已经讲过,并且在修正1.0的BUG后合并到开发版中,形成1.0.1版本。
在完成1.0分支后我们就开展了2.0的大规模开发,但我们会优先开发R1的功能,在完成R1的开发和测试后,即1.1.1后,我们再次产生MyProduct-1.0 R1的分支,因为这个版本还包含了R1不需要的一些代码(开发版不可能完全遵照R1的要求开发,可能更进一步),所以我们需要花一些时间调整和测试。
在此期间,如果在正式版发现的BUG仍然需要合并到开发版,但是无需合并到R1版本,因为我们很快就已经将就绪的R1合并到正式版,然后封存R1这个分支,这样我们就仅维护两个版本。在日后的开发中,如果再次修订BUG,你应该选择合并对应的变更集,这是因为正式版已经包含很多的R1代码,如果选择全部合并,会把R1的代码也合并过来,而此功能在开发版已经包括了。
特性包
事实上,中版的分支方式我是不喜欢的,因为不管TFS的分支与合并功能再好,我们也还是需要维护多套源码,一个BUG在多处修订容易,每处都要测试就不轻松了,这都是“成本”。
我更喜欢“特性包”这样的方式。特性包的案例你可以想象是Word这样的软件,当Word开发完毕后,二次开发商利用提供的VSTO开发新的特性,二次开发商并没有修改Word的源代码,他们的代码也没有重叠,什么地方出现BUG就在什么产品单元上修改即可。回过来看我们的中版开发,你可以认为这个二次开发商就是自己。
特性包的做法前提是你的产品具有较强的二次开发能力,当开发完毕1.0后,并不立即产生分支,所有的中版开发放在一个独立的产品单元,注意这个产品单元并不是一个分支,更不是一份拷贝的副本,而是基于1.0 SDK的二次开发。
从上图可以看出,所谓特性版本就是一个独立的产品单元,各自维护自己的BUG。但是这样做对基础产品的要求就比较高,要能在不改动基础产品的情况下,完成很多的增强特性。当然,人无完人,产品也是,通常遇到不能支撑的时候,我们还是适当的修改底层,但要保持兼容。
开发2.0产品的方法是,首先从1.0产生分支出MyProduct_2.0,将各个特性版的二次开发代码整合到这个分支,最终形成2.0版本。但这个时候显然还是要维护2套版本,当发布3.0时,需要维护3套版本。
另外一种比较极端的做法是,开发2.0仍然看成已有产品单元的再次二次开发,3.0也是。这种方法对底层平台的要求更高,即特性版也能支撑二次开发。为提高性能,可以在特性版或各个补丁包安装完毕后,执行自动整合任务,以便将分散的二次开发整合成一个产品,有点像编译的过程。