• 2004-12-28

    再谈Mock Object

    发现静寂的夜能让我的思维加快。

    Mock Object进行Unit Test已经一周多了,发现以前对Mock Object还是很肤浅,即使是现在我也不敢说我对Mock Object的理解就一定正确。

     

    这篇blog假设你已经熟悉JUnit、了解MockTDD

    如果你是直接开始使用JMock Easy Mock或者是MockMakerMock Object框架的,我建议你简单了解一下Mock Object的演化历史,这样你在使用Mock Object时才会更有的放矢。

     

    Mock object有关键的两个概念

    Ø         建立起环境的概念。在www.mockobjects.comfaq中有一句是这样叙述的“I think the fundamental thing to remember about Mock objects is that they are just that - simple shells or placeholders.Mock object只是替代了被测Object环境的代码

    Ø         test assertions被隐藏在Mock object内部实现中了,在你的test case中用来verify被测代码与Mock object的交互。

     

    在单元测试中,人们发现有一些问题(这些问题在我的“认识Mock Object”中已列出)常见单元测试工具(如JUnit)并不能很好、很便捷的解决,这样Mock object被引入来解决这些问题。

     

    最初人们手工编写Mock Object。随着测试问题越来越复杂,人们自己手工编写的Mock object越来越多,人们开始将编写Mock Object过程中一些通用的东西抽象出来,形成了一些Mock Object Lib,以帮助开发人员快速得到自己需要的Mock Objects

     

    当前的Mock Object Lib有多种,大致可分为两类:

    Ø         Static Mock Objects Lib

    Ø         Dynamic Mock Objects Lib

     

    这里简单举例说明一下不同类型Mock object lib的使用方法,并与手工编写进行对比。

    问题:我们在测试某个class时,我们需要与MyInterface这个接口进行交互,而该接口尚未实现,这时我们使用Mock object来替代。

    接口MyInterface:

    public interface MyInterface {

    public SomeClass getSomething();

    public void setSomething(SomeClass aSomething)

    // Other methods omitted...

    }

     

    Ø         手工编写mock object:

    public class HandcraftMockMyInterface implements MyInterface{

    public SomeClass getSomething() {

    return something;

    }

    public void setSomething(SomeClass aSomething){

             this.someting = aSomething;

    }

    private SomeClass something;

    //others

    }

     

    Ø         使用Static Mock Object Lib编写Mock Interface

    public class StaticMockMyInterface extends MockObject implements MyInterface{

        private final ExpectationValue something = new ExpectationValue("something"); 

        public void setSomething(SomeClass something){

            this.something.setActual(something);

    }

    public void setExpectedSomething(SomeClass something){

            this.something.setExpected(something);

    }

    public void SomeClass getSomething(){

       return this. something;

    }  

    }

     

    Ø         使用Dynamic Mock Object Lib编写Mock Interface(JMock为例)

    Mock dynamicMockMyInterface = new Mock(MyInterface.class);

    MyInterface mi = (MyInterface) dynamicMockMyInterface.proxy();

     

    SomeClass someThing = new SomeClass();

    dynamicMockMyInterface.expects(once()).method(“setSomething”).with(eq(someThing));

    dynamicMockMyInterface.expects(once()).method(“getSomething”).will(returnValue(someThing));

     

    //执行你的测试代码,比如你的tested objectMyInterface mi交互的代码。

     

    dynamicMockMyInterface.verify();

     

    Mock Object的使用流程

    Ø         Setup any state --- setup the fixture for your test

    Ø         Set expectations for the test

    Ø         Run the target code

    Ø         Verify that your expectations have been met

     

    Mock Object Practical Experience

    Ø         好的设计是容易测试的设计

    Ø         尽量让测试在内存中完成,不要在硬盘上留下“垃圾”

    Ø         面向接口使用Mock Objectbetter to implement interface , not inherit class

  • 2004-12-27

    写在工作时

    不知怎么的,我总是喜欢在工作的时候来完成我的blog,这也就是我这篇blog的题目来由。享受完美妙的平安夜和快乐的圣诞节,又开始我新一天的工作。

     

    北国寒冬,从寝室出来,道路上几乎不见行人,身上的热气不断逃离,寒气扑来,打了几个寒颤。心里想着今天的工作,脚下步伐却在不知不觉中加快。

     

    来到温暖的办公区,打开outlook,看到一份来自远方好朋友的邮件,邮件中如是说“你的blog也不更新。看到梦想风暴的圣诞前夜写的blog,感慨挺多的。”看完后的第一感觉是很兴奋,因为第一次有人提醒我该更新我的blog了;但是心里也不免有些惭愧,毕竟看到我的最后一篇blog1215号的,掐指算来已有近半个月没有动笔了,想了想这段时间我的“所作所为”,除了公司项目的更迭,再就是Dominootdd(test driven development)。从大学接触计算机程序以来,一直是直接完成功能代码然后测试的,突然让我先写test code然后完成business code,我的思维很难做出这个“急转弯”,心情也是很郁闷的,时间在不停的流逝,而代码却被反复的推翻。dreamhead看出了这点,决定和我结对编程,一步步带我形成测试驱动开发的思维。经过那次结对后发现自己正向着tdd靠拢,加上昨天一天自己的亲自试验,发现效果还是不错的。这周是2004年的最后一周,对我们的dominoo来说也是很重要的一周,因为我们的计划是这周拿出一个“初具规模”的内部版本,我们现在都在为此而努力着。

     

    自从dreamhead向我推荐blog以来,我逐渐的开始喜欢阅读其他人的blog了。打开dreamhead的“剧本集”(blog),也欣赏一下那篇well-known的“写在平安夜”,看看dreamhead发的“牢骚”^_^,居然看到他的“剧本”中还有我的“角色”^_^

     

    由于一个好朋友的内部推荐,昨天接到了国内某著名大公司的面试邀请,不过被我宛然谢绝了,我刚刚入司不到1年,虽说这里并不像我想象中的那么有激情而且待遇又不是令我满意,但是能遇到像dreamheaddarwin_yuanmodeltoidi_xu这样的对软件如此痴迷的而又满怀激情的人,我还是蛮欣慰的。我们成为朋友是因为我们志同道合,我们有自己的共同的梦想,我们有着同样的激情。我们还要在一起共同的奋斗,我暂时还离不开这些朋友。

     

    在写这篇blog时又看到了远方那位好友回复的邮件,说他正在准备一篇要投到一个国际会议并且EI检索的paper,我知道这是他的第一篇正式要发表的paper,真心祝愿他能成功。

  • JDK5.0源代码初览

     

    早就听说Sun开放了JDK的源代码,不过一直认为那么多源代码,根本没时间看,所以一直也没去下载。随着对Java了解的深入,览一览神秘的Java源代码的渴望是“与日俱增”,今天的工作不忙就去down了一份jdk5.0的源代码。

     

    我是使用SCSL许可证下载的,解压后大约有190MB。我最初的打算就是想看看java.lang包下一些类的实现。我吭哧吭哧的找了半天才在\j2se\src\share\classes\java下找到lang包的目录。在\j2se\src\目录下有4subdir,分别是linuxsharesolariswindows。其中share是最最重要的包,所有jdk核心的源代码都在这里,其他三个subdir存放的都是与平台相关的一些代码。

     

    了解了jdk源码的目录结构后,我们就方便多了,以后在java编码中遇到什么问题需要参考相关class的源码的话,我们就可以轻松查到了。

     

    jdk源代码的确很庞大,只能以后慢慢看慢慢体会其中的精髓吧^_^

  • 2004-12-10

    认识Mock Object

     

    上周六我们Dominoo group讨论(以下称讨论)TDDJUnit的时候,提到过Mock Object,那次可能是我第一次听到Mock Object这个概念,程序员对新鲜的的东西都是敏感的,所以今天晚上花了一些时间了解了一下Mock Object的概念,做了一些简单实践。

     

    术语

    Tested Object – 被测对象

    Mock – 假的 or 仿制的对象

     

    What is Mock Object?

    在讨论中我大致了解到Mock Object一般是用来做辅助单元测试,它负责隔离Tested Object与真实环境中模块或实体(Real world object)的交互,并“替代”or “冒充这些真实模块或实体与Tested Object进行交互。

    在“JUnit in action”这本书中关于Mock Object的描述如下:

    A mock object (or mock for short) is an object created to stand in for an object that your code will be collaborating with. Your code can call methods on the mock object, which will deliver results as set up by your tests.

     

    Mock Object给我带来什么好处?

     

    看看下面的图:

    |-----------------------------------------------------------------|

    |                                                                                         |                   

    |      |---------------------|                                                  |             |-----------------------------|

    |      |   Tested             | <------------------------------------------à | External Mock Object |

    |      |      Object            |                                                    |            |----------------------------|

    |      |---------------------|                                                 |

    |                   /|\               |--------------------|                       |

    |                    |-------|  Internal Mock    |                       |

    |                                     |     Object           |                       |

    |                                     |--------------------|                    |

    |      [Your system scope]                                                  |

    |--------------------------------------------------------- --------|

     

    在测试你的Tested Object时,你可能会与你系统内的某个模块或系统外某个实体交互,而这些模块或实体在你做单元测试的时候可能并不存在,这时:

    Ø         Internal Mock Object可能是一个你的系统尚未完成的模块的“替身”(replacement)

    Ø         External Mock Object可能是测试你的Tested Object时需要的外部的环境实体的“替身”(replacement)。

    不知道这样给Mock Object分类是否正确J

     

    我们来看看与Real world object交互有什么不足之处:

    Ø         Real world object的行为具有不确定性,我们难于控制它们的输出or返回结果。

    Ø         Real world object有些时候是难于被建立的或者说是无法获得的。

    Ø         Real world object的有些行为难于被触发,如磁盘已满,网络error等。

    Ø         Real world object可能不存在,比如你的Tested Object需要与你的系统的另一个module交互,而另一个module尚未开发完毕。

     

    当然还不止这些,我们仅仅是列出一部分。

     

    使用Mock Object替代Real world object后我们就会解决上述问题,换句话说当上面的情况出现后,我们就可以使用Mock Object。这也是什么时候该使用Mock Objectanswer

    Mock Object是我们自己编写的,我们拥有控制它的绝对的权力,我们可以定制它的行为和输出。

     

    Use Mock Object

    使用Mock Object解决上述问题可分三步走:

     

    1. Use an interface to describe the object

    2. Implement the interface for production code

    3. Implement the interface in a mock object for testing [3]

    还有一点就是对于Internal Mock Object早晚你要实现出其Real world object的,因为那是你系统的一部分。

     

    一个改自资料[3]的例子

    public interface Environmental {

    public long getTime();

    // Other methods omitted...

    }

    对于这样一个接口,我们提供两种实现,

    //real world object

    public class SystemEnvironment implements Environmental {

    public long getTime() {

    return System.currentTimeMillis();

    }

    // other methods ...

    }

     

    //mock object

    public class MockSystemEnvironment implements Environmental {

    public long getTime() {

    return currentTime;

    }

    public void setTime(Time aTime){

             this.currentTime = aTime;

    }

    private Time currentTime;

    //others

    }

    我们可以看到在MockSystemEnvironment中我们提供“setTime”函数是为了提供控制Mock Object的接口。

     

    我们要测试的类

    //TestedObject

    public class TestedObject{

             private Environmental env;

             TestedObject(Environmental aEnv){

                       this.env = aEnv;

    }

    public boolean isAm(){

             Calendar cal = Calendar.getInstance();

    cal.setTimeInMillis(env.getTime());

    int hour = cal.get(Calendar.HOUR_OF_DAY);

             if (hour <=12) return true;

             return false;

    }

    }

     

    将要测试的类放入单元测试框架

    public class TestTestedObject extends TestCase {

            public void testIsAm(){

                      MockSystemEnvironment env = new MockSystemEnvironment();

                       // Set up a target test time

    Calendar cal = Calendar.getInstance();

    cal.set(Calendar.YEAR, 2004);

    cal.set(Calendar.MONTH, 10);

    cal.set(Calendar.DAY_OF_MONTH, 1);

    cal.set(Calendar.HOUR_OF_DAY, 16);

    cal.set(Calendar.MINUTE, 55);

    long t1 = cal.getTimeInMillis();

    env.setTime(t1);

     

                       TestedObject to = new TestedObject(env);

                       assertFalse(to.isAm());

    }

    }

    在该单元测试中我们使用了Mock Object,并且在使用前我们利用setTime接口,输入了我们需要的值。结果我们会通过测试。如果我们使用Real Object,我们得到的测试结果将是不固定的,后者可不是所期望的。从这个例子中你也应该体会到Mock object的一些好处了。

     

    如果我们总是手动写我们需要的Mock Object,那将是一个很大的工作量。现在业界有了Mock Objectseasy mock等开源框架的支持,是我们编写Mock object变得越来越容易。

     

    参考资料:

    1、《Test-Driven Development – A practical guide

    2、《JUnit in action

    3、《Pragmatic Unit Testing

  • 2004-12-09

    学习重构

     

    DMC采用驱动开发的方式,这就意味着重构“Refactoring”是我要学习的对象。早在大三的时候就已经把那本经典的“Refactoring Improving the Design of Existing Code”英文版买到手了,但就是在买回来后的第n天,它就被“打入冷宫”了。

    What Is Refactoring?

    Refactoring is the art of safely improving the design of existing code. [1]

    什么时候进行Refactoring?

    TDD开发中,当我们的应用代码顺利通过测试集后,在保持应用代码对外行为(behavior)保持不变的前提下,对应用代码进行Refactoring

    另外一种情况就是对测试集代码进行Refactoring

     

    Refactoring vs Pattern

    [2]这本书中作者提到“我们应该把模式作为重构的目标,我们要渐进的引入模式”。原因是“有些人滥用模式,他们把任何一个需求都看成是模式的组合,他们一开始就用模式来进行设计,他们已经陷入到模式的滥用中”。

     

    Refactoring流程

    [1]中给出了一个程式化的refactoring流程:

    While smells remain:

    Ø         Choose the worst smell.

    Ø         Select a refactoring that will address the smell.

    Ø         Apply the refactoring.

    注意:这里refactoring是个持续的过程,直到the code without smell

     

    Refactoring实践

    Refactoring的例子很多,这里举一个常见的例子。

    Considering the following code:

    class Shape {

             //0 – Circle 1- Rectangle 2- Triangle

             private int type;

             public String getType(){

                       switch(type){

    case 0:

             return “Circle”;

    case1:

             return “Rectangle”;

    case2:

             return “Triangle”;

    default:

             return “Unknown”;

    }

    }

    }

    我们按照Refactoring的流程,首先要发现代码中的bad smell

    Ø         使用switch结构限制了Shape类的扩展,一旦要加入新的Shape类型,getType函数就得修改。

    Ø         还有一点是Shape使用private member variable type来区分不同的Shape类型,这样是不合理的,我们完全可以用”Polymorphism”来代替。

     

    下面是重构后的代码:

    public abstract class Shape{

             public abstract String getType();

    }

    public Circle extends Shape{

             public String getType(){

                       return “Circle”;

    }

    }

    public Rectangle extends Shape{

             public String getType(){

                       return “Rectangle”;

    }

    }

    public Circle extends Shape{

             public String getType(){

                       return “Circle”;

    }

    }

    Ok, now the code above feels nice.^_^
    书还没看完,就先说到这了。

     

    参考资料:

    1、《Refactoring workbook

    2、《Test-Driven Development – A practical guide

  •  

    effective java中有一item保护性拷贝,今天又看了许多部门里的代码,发现很多代码都与该item“相违,晚上和toidi_xu讨论这个问题有些收获。

    Considering the following code:

    public class Box {       

             private int length;

             public void setLength(int length){

                       this.length = length;

             }                

             public int getLength(){

                       return this.length;

             }

    }

    public class TestJava {

             private String id;

             private int idx;

             private Box box;

             public void setId(String id){

                       this.id = id;

             }

             public String getId(){

                       return this.id;

             }

             public void setIdx(int idx){

                       this.idx = idx;

             }

             public int getIdx(){

                       return this.idx;

             }

             public void setBox(Box box){

                       this.box = box;

             }

             public Box getBox(){

                       return this.box;

             }

             public static void main(String[] args) {

                       TestJava tj = new TestJava();

                       String id = "tony";

                       int idx = 5;

                       Box aBox = new Box();

                       aBox.setLength(7);

     

                       tj.setId(id);

                       tj.setIdx(idx);

                       tj.setBox(aBox);

                      

                       System.out.println(tj.getId());

                       System.out.println(tj.getIdx());

                       System.out.println(tj.getBox().getLength());

     

                       id = "bai";

                       idx = 6;

                       aBox.setLength(8);

                       System.out.println(tj.getId());

                       System.out.println(tj.getIdx());

                       System.out.println(tj.getBox().getLength());

             }

    }

    //output:

    tony

    5

    7

    tony

    5

    8

     

    对于TestJava类中的3个类型(String ,int ,Box)的成员变量我们编写了相同的settergetter,但结果是Box类型的成员变量居然不通过setBox就被修改了,而Stringint类型在外部不能被修改。这是为什么呢。在effective java中曾经说过String类和Number类都是immutable的。任何对String or Number类对象的操作都会copy出一个不同于原objectobject,而原来的object的状态并未被修改。在上面的例子中Box不是immutable class所以被外部修改了。

    为了使client只能通过Box提供的setLength来修改,我们必须作保护性的copy。修改如下:

    public void setBox(Box box){

                       this.box = new Box();

                       this.box.setLength(box.getLength());

             }

             public Box getBox(){

                       Box aBox = new Box();

                       aBox.setLength(this.box.getLength());

                      return aBox;

             }

    修改后的output:

    tony

    5

    7

    tony

    5

    7

     

    这样我们在TestJava中就出现两种settergetter的样式,我们在写代码的时候该使用哪种呢?在实践中“为类中immutable class类型(如String和数值Number类)的field membersetter\getter时,我们不需要提供defensive copy;在为其它非immutable class类型(如上例中的Box)field membersetter\getter时,建议考虑defensive copy,以防止client对你的代码的恶意破坏”。

    所以上面的代码还有另一种改法就是将Box写成immutable class

    这篇blog就当作是对effective java中“defensive copy”一节的细化和补充吧J

  • Dominoo项目日记(三)

     

    上周的主要工作是和toidi_xu共同完成“把xml文件解析到java内存对象”的工作,但是一周下来发现我们的工作完成的并不好。

     

    这周的前3天我一直在学习effective java,由于有java基础所以看起来也不是很费劲,自己也写了些小例子,之后由于周末要讨论tddjunit所以我又花了近一天的时间来熟悉相关的资料。真正开始进入开发阶段是在周五的时候,我研究了一下dreamhead已经写的dominoo代码,初步制定了我的工作计划:

    ²        制定xml描述文件

    ²        编写相关entity class

    ²        编写digester parse rules

     

    突然想起昨天看到江西台的一个人物节目,主人公是刚刚获得“中国十大杰出青年”奖章的602所(中国直升机研究所)的总设计师,当记者问到“你觉得你这些年付出这么多值么”,令我惊讶的是这位总师的回答竟然是“我没有付出亚,谁说我付出很多了,他进一步解释道“我的工作已经是我工作的一部分了,工作给我带来无穷无尽的生活乐趣,所以我不觉得我付出,我是在实实在在的享受着生活的乐趣”。想想我今天的工作,这位总师的心声也恰恰是我们程序员的心声,编码会给我们带来无穷无尽的乐趣,编码已经成为我们生活的一部分。跑题了^_^

     

    Toidi_xu上周并未介入到代码实现中,这也是我们进度缓慢的一个原因。

    说一说我在开发过程中遇到的问题吧。

     

    Ø         我们软件的内部处理流程是“model---[1]----à”xml文件”---[2]----〉“java内存模型”,在modeljava内存模型中都存在着对entity(如class , package , interface等)的描述,我们需不需要作得通用一些,这样复用一个entity包即可。

     

    Ø         nested classnested interface的描述一直困扰着我

     

    Ø         还有如何描述和处理enum的问题

     

    Ø         如何描述field在类内部的initializer问题

     

    Ø         普通methodconstructor如何区分描述的问题

     

    Ø         最大的困惑就是如何对泛型entity进行描述,如模板类,不光是类本身,该模板类的method也涉及到参数类型为类的参数的问题,如下面代码:

    public class Box<T> {

      protected List<T> contents;

      public Box( ) {

        contents = new ArrayList<T>( );

      }

      public void add(T o) {

        contents.add(o);

      }

    public T grab( ) {

        if (!isEmpty( )) {

          return contents.remove(0);

        } else

          return null;

        }

    }

    如何保持对class描述和method描述的一致性问题和处理的一致性问题?

    这些问题都要这周我们共同讨论来解决,我打算先拿出个解决方案。

  • Effective Java阅读()-item异常

     

    Dreamhead把他用大把银子买来的“Effective Java”借给我阅读,我真是很感动亚,我只能用行动来感谢Dreamhead了。^_^

     

    39、只针对不正常的条件才使用异常

    异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流。

     

    40、对于可恢复的(recoverable conditions)使用被检查异常(checked exceptions,对于程序错误(error)使用执行期异常(runtime exceptions

    Java中大致分为两种不正常状态:checked exception(被检查异常) and error(错误,一般抛出的runtime-exception也都是error的表现,所以我把runtime exceptions也划归为error)。

     

    ²        如果期望调用者能够恢复,那么对于这样的条件应该使用被检查的异常。

    ²        用运行时异常来指明程序错误。

    大多数运行时异常都是表明API的客户没有遵守API规范建立的约定。建议你所实现的所有的unchecked exceptions都应该是RuntimeException的子类(直接或间接的)。

     

    42、尽量使用标准的异常

    Java平台库提供了一组基本的unchecked exceptions,它们覆盖了绝大多数的API抛出异常的需要。

     

    常用的异常复习:

    IllegalArgumentException      在参数的值不合适时抛出。

    IllegalStateException             对于这个方法而言,对象状态不合适。

    NullPointerException            参数值为null

    IndexOutOfBoundsException        下标越界

    UnsupportedOperationException   对象不支持用户要求的方法。

    CocurrentModificationException   在禁止并发修改时,对象检测到并发修改

     

    46、努力保持 failure atomicity (失败之原子性)

    解释一下失败原子性的概念:即一个失败的方法调用应该使对象保持“它在被调用之前的状态”。这样的方法被称为具有“失败原子性”的方法。

    使用“不可变”对象是获取“失败原子性”的最常见的办法。

     

    47、不要忽略异常

    忽略一个异常方法很简单:

    try{

           //....

    }catch(SomeException e){

           //empty block

    }

    空的catch块儿会使异常达不到应有的目的。至少在catch block中应该包含一条说明,用来解释为什么忽略掉这个异常是合适的。

  • 2004-12-02

    Advanced CVS

    Advanced CVS

     

    做了几个月的实际项目,感觉还是只用到CVS的皮毛,CVS中的高级功能比如create tagcreate branchmerge等都未使用过。Dreamhead发过来一本”pragmatic version control-using CVS”,顺便do some practice and research on the advanced functions of CVS

     

    1TagsBranches and Tagging的概念理解

    Tags分为Regular tagBranch tag两种。

    Regular tag其实就是我们一般理解中的tag,Branch tag即是我们理解中的branch

    HEAD也是一个特殊的分支,the main branch,只是我们把它默认当作main trunk

     

    建立分支的必要性,举个简单的例子当我们的项目对外release1.0版本后,比如我们对整个project建立version为“Release_1_0”,但是这个版本肯定有bug存在,我们就这样提交给用户使用,相信不会等多久bug report就会feedback回来。所以我们得一边做下一个版本Release_2_0的开发一边fix bug,而我们fix bug时基于的版本只能使release1.0,这时我们就要考虑难道新版本的开发和fix bug的工作都要在main trunk 上么,这样可行么?回答是“也许可行,但是不用过多久你就会发现这样会造成太多的冲突,开发人员的抱怨声也越来越多”。Branch可以帮助我们解决这个难题。我们在建立version Release_1_0的同时建立一个branch,比如叫做“Release_1_0_Branch,并同时建立一个regular tag ”Root_of_Releas_1_0_Branch”,这个regular tag的用途是为了以后branch 合并到main trunk时提供一个参考点。之后开发新版本的人员就基于main trunk工作,而fix bug的人员就基于Release_1_0_Branch工作。一旦在Release_1_0_Branch上将Release_1_0bug修复了,我们就可以将Release_1_0_Branch合并到main trunk中来一次性remove the bugs。上面举的这个例子只是branch较简单的一个用法。有效的利用branch会给你的项目的开发带来很多便捷的。

     

    下面的英文段落是从某书中摘录的关于versionbranch的说明。

    You can use CVS to maintain different branches of a project. Doing so is often

    necessary if you release a version of your project to the public, such as version 1.0.

    As you begin to work on adding new features for version 2.0, the code is not stable

    enough for release; so, if any serious bugs are discovered in version 1.0, they must

    be made to the original 1.0 code. CVS allows you to create a separate branch,

    starting with the original 1.0 code, so you can maintain this code separately from

    the new development continuing with the main branch, HEAD.

    //...

    Versions differ from branches. A version is a snapshot of a branch at a given

    point in time—in other words, it’s a particular set of file revisions.

    //....

    Adding a version label to a project associates the revision number of each particular

    file with a single project-level label.You should consider tagging a project with a version label at all significant development milestones, such as a beta or an official release, or before any drastic

    change is undertaken.

     

    2Merge

    理解了branch的概念后,那我们如何将一个Branch合并(Merge)到main trunk or another branch?下面我们使用Eclipse作为工具来说明如何将branch合并到其他branch中(main trunk is also a branch , a special branch)。

     

    在你在CVS中创建branch后一段时间,你可能要将你在branch中的changes合并到其他branch中。要想将你的branch中的changes合并到其他branch中,你首先要知道“the name of your branchand “the version from which your branch was created”

     

    知道上面的两样后,我们就可以实施我们的merge了。我们以branch mergeHEAD为例。具体步骤如下:

    Ø         目标version加载

    首先将目标version 加载到你的工作区,在这个例子里我们将HEAD version加载进来,具体方法是在你工作区(可能是“Navigator view”)中,Right click your project name, choose Replace With > Another Branch or Version from the context menu. Then select the HEAD to replace with your current version in your workplace

     

    Ø         选择branch

    Select the project and choose Team > Merge.

    在随后出现的对话框中,你首先选择“the version from which the branch was created.”,然后在下一步中选择你的Branch

     

    Ø         Synchronize view中的操作

    在第二步结束后,Synchronize view中将显示“all the differences between the branch and your workspace version(that is the HEAD version),你必须在Synchronize view中通过菜单中提供的“Update, Override and Update, or Mark as Merged”手工决定合并到你工作区的change

     

    Ø         Commit the changes

    在所有期望的changes都被merge到你的工作区后,你就可以“committhe changes to the repository了。

     

    注:Merge actions

    Update

    Running this action will bring the changes into the file in the workspace. Any conflicts that are not auto-mergable will be skipped.

    Override and Update

    This action is enabled on files with conflicting changes. Running this action will discard any local changes you have and replace the file with the remote contents.

    Mark as Merged

    This action will remove the selected changes from the view. The changes will only reappear if the remote state of the resource changes and the CVS Merge Synchronization is refreshed.