深入理解 Rational ClearCase UCM 依赖
 
2008-11-21 作者:沈雪芳 来源:IBM
 
本文内容包括:

统一变更管理(Unified Change Management,UCM)是 IBM Rational 提出的用于管理软件开发过程(包括从需求到版本发布)中所有变更的“最佳实践”流程。在使用 UCM 的过程中,当一个或多个元素的版本依赖其他一个或多个元素的版本时,用户在执行递交(即 deliver)操作会被提示发现依赖。UCM 依赖在 Rational ClearCase 的正常操作中经常被用户认为是 ClearCase 的缺陷,但实际上它并不是缺陷。Rational ClearCase 出于保护数据的目的而设计了依赖,目的是为了防止由于误操作导致数据被破坏。

本文详细介绍了 Rational ClearCase UCM 依赖的产生情况,以及如何解决和应对 UCM 依赖带来的困扰。

统一变更管理(Unified Change Management,UCM)是 IBM Rational 提出的用于管理软件开发过程(包括从需求到版本发布)中所有变更的“最佳实践”流程。在使用 UCM 的过程中,当一个或多个元素的版本依赖其他一个或多个元素的版本时,用户在执行递交(即 deliver)操作会被提示发现依赖。UCM 依赖在 ClearCase 的正常操作中经常被用户认为是 ClearCase 的缺陷,但实际上它并不是缺陷。ClearCase 出于保护数据的目的而设计了依赖,目的是为了防止由于误操作导致数据被破坏。

活动依赖

当执行选择活动的递交操作时,活动依赖会作为一致性检查的结果以问题方式出现(见图一)。

图一:发现依赖
发现依赖

用户在使用 ClearCase 时碰到的 UCM 依赖其实都属于活动依赖,按照依赖产生的原因可以将依赖分为两种:变更集依赖和基线依赖。

变更集依赖

变更集依赖是最常见也是最容易理解的活动依赖。这主要发生在以下两种场景:

  • 用户在同一流上使用不同的活动对同一个文件进行操作时很容易产生活动依赖的问题;
  • 当用户在源流上的工作还未结束时,如果执行变基(即 rebase)操作,也可能会出现活动依赖的情况。

在进行并行开发时,当项目组采用 UCM 模式时,由于开发人员同时面对变更、缺陷、任务等各种类型的活动,开发人员不可避免的要面临活动依赖的情况。

图二:变更集依赖
变更集依赖

在图二中,由于 Hello.c 的版本 1 在上一次递交操作已经递交了,而 Act2 活动和 Act3 活动间包含了 Hello.c 文件的不同版本,所以你可以执行的操作具体如下:

  • 你可以在不递交 Act3 活动的情况下只递交 Act2 活动;
  • 如果要递交 Act3 活动,你必须将 Act2 活动一起递交。因为 Act2 活动包含了 Act3 活动所包含的 Hello.c 版本 4 和上一次递交版本之间的版本(即版本 2 和 3)。
  • 因为 Act2 活动包含了 Prog.c 的版本 6,你可能需要递交其他和 Prog.c 有依赖的活动。例如,如果 Act4 活动(图二中没有显示)包含 Prog.c 的一个未递交版本 5,那么如果要递交 Act2 活动的话,你必须一起递交 Act4 活动。

在很多时候,变更集依赖是由自己造成的,而且这种情况非常普遍。如图二所示, Act3 活动依赖于 Act1 活动和 Act2 活动。因为 Act3 活动的变更集中有一个元素 Hello.c 的 V4 版本是 Act2 活动和 Act1 活动中 Hello.c 文件版本的继承者。

还有一种比较罕见的变更集依赖是由于变基操作引起的。如果在源流上的工作未结束前进行了变基操作的话,也可能会出现变更集重叠导致的活动依赖(见图三)。

图三:变基操作导致变更集依赖
变基操作导致变更集依赖

coreTeam 流上的 Unfinished 活动还没有准备递交,变基操作产生的 rebaseA 活动在后续的递交操作中必须被包含,而用户在 coreTeam 流做的变基操作使 rebaseA 活动与 Unfinished 活动产生了变更集依赖,因此 Unfinished 活动将阻止后续其他活动的递交操作。在没有包括 Unfinished 活动的情况下,用户将无法执行递交操作。

基线依赖

源流上已存在的基线将会影响这条流上可以选择递交的活动。简单的说,UCM 只能递交没有包含在源流中任何基线中的活动。这表现在以下两种情况:

  • 如果要递交某基线包含的活动,那么必须将该基线包含的所有活动都一起递交;
  • 一旦一个活动被包含在源流的一条基线中,那么在该流后续的递交活动中(包含跨项目的递交操作),他都必须被包含。

在一些实际使用场景中,有时用户或者项目管理人员会有意的在开发流上创建基线,并使得这些基线产生依赖关系。这种情况在扁平的流结构中不常见,但是在嵌套的流结构中却非常普遍(见图四)。当活动依赖在这种场景中出现时,用户一般是可以理解的。

图四:基线依赖
基线依赖

基线除了作为检查点外,还可以作为开发流执行变基操作的变基点,在图四的场景中,笔者故意在 coreTeam 流上创建了基线 Core BL1 和 Core BL2。在这个场景中,只有 deliverC 和 Intg 活动符合条件可以执行递交操作(因为这两个活动没有包含在 coreTeam 流现有的基线中)。即使这样,如果你想只递交 deliverC 活动或 Intg 活动到 Integration 流的话,你依然会被告知他们依赖 deliverA 和 deliverB 活动(因为 Core BL2 基线还未递交到 Integration 流)。如果你打算只递交 deliverB 活动到 Integration 流,那么 UCM 会告诉你必须包括 deliver A 活动(因为他们在源流上被包含在同一个基线上)。一旦 deliverA 和 deliverB 这两个活动被递交到 Integration 流,那么你就可以选择递交 deliverC 或 Intg 活动到 Integration 流(假设他们没有变更集依赖)。

另一种基线依赖是“递交基线”依赖。递交基线是一种特殊的基线,它是在不被人知的情况下,通过 UCM 递交活动在源流上被秘密创建。因为递交基线是不可视的,在基线的图形浏览界面默认不显示递交基线(可以通过命令行方式查看),所以许多时候用户并不知道这些递交基线的创建与存在。这些基线影响了用户的正常操作,同时也不容易被用户理解。通过递交操作而创建基线的这个功能是 UCM 众所周知的功能之一,但是用户经常碰到因为这些基线的存在而无法执行需要的递交操作。

递交基线产生基线依赖的一种场景如图五所示。如果你想只递交 deliverB 活动,UCM 会提示 deliverB 活动依赖于 deliverA 活动。从表面现象上看,也就表现为用户无法只递交后产生的 deliver 活动,而需要按照 deliver 活动产生的时间顺序依次递交。这是由于从开发流向中间流执行递交操作时,UCM 会在开发流自动生成一条隐含基线,两次递交形成了两条基线。如果想要再从中间流向最高层流递交时,你不可以只递交最后一个 deliver 活动,因为基线的原因,他们会产生依赖的活动关系。如果你要将递交操作产生的 deliver 活动往上一级流递交的话,必须将所有依赖的基线都一起递交。

图五:递交基线依赖场景一
递交基线依赖场景一

当项目允许跨项目或跨流执行递交操作时,也可能存在递交基线依赖的情况(见图六)。

图六:递交基线依赖场景二
递交基线依赖场景二

在这个场景中,UCM 项目中存在两条平行开发流。开发人员在 Dev1 流上有几个正在工作的活动。开发人员选择了 Act1 活动跨流递交到 Dev2 流。开发人员现在要以默认递交方式将另一个活动 Act2 递交到项目的集成流。

UCM 提示 Act2 活动现在依赖 Act1 活动,开发人员从 Dev1 流递交 Act2 活动到 Integration 流时必须包括 Act1 活动(因为 Act1 活动现在被包含在一个基线里,且该基线还未递交)。即使 Act1 和 Act2 活动没有任何变更集依赖,这种基线依赖的情况是可预料并且是一定会出现的。

出现这种情况是由于从 Dev1 流向 Dev2 流执行递交操作时,UCM 会在 Dev1 流自动生成一条隐含的递交基线。这个存在的递交基线强迫后续从 Dev1 流进行的递交操作必须包括 Act1 活动。

在这个特殊的案例中,从 Dev1 流到任何目标流的递交操作都至少必须包括 Act1 活动。换句话说,只有活动没有被包含在源流的任何基线中才可以作为选择的活动被单独递交,一旦源流上有未递交的基线,那么就无法只选择需要的活动进行递交操作,而必须将未递交的基线一起递交。后续从 Dev1 流发出的递交操作都必须包含 Act1 活动,这是由于 Dev1 流发起了一笔到 Dev2 流的递交操作,导致 Act1 活动包含在 Dev1 流的一条递交基线中。同样的,如果从 Dev1 流完成了第二次递交操作(将 Act1 和 Act2 活动都递交到 Ingegration 流),假设再从 Dev1 流发出到其他任何目标流的递交操作,都必须包含 Act1 和 Act2 活动(因为现在 Act1 和 Act2 活动都包含在 Dev1 流的基线中)。

解决神秘的活动依赖

解决活动依赖的一个显而易见的解决方案是将所有工作都递交,或者是最小化的选择需要的活动和他的依赖,但显然这都不是好办法。

第二种方法是采用每个开发人员一条开发流,而不使用所有开发人员共享一条开发流的方式。这种方式,可以大大减少不同开发人员之间活动依赖的情况,但是仍不可避免同一个开发人员所承担开发活动之间依赖的可能。另外,由于每个开发人员使用一条开发流,每个开发人员之间处于彼此隔离状态,集成工作量较大,集成时间可能加长,对开发人员使用 ClearCase 技能的要求也较高。

第三个可以选择的方法是使用每个活动都采用一条流的模式进行工作。这种模型通常可以避免因为采用在各自的流上工作的方式来隔离各个活动带来的特殊的问题。但是这种模型引入的管理成本较大,并且项目的复杂性将大大增大。

考虑到方法二和方法三在大部分公司实施所需的成本较大,以及需要对项目现有开发模式做较大的改动,实施起来会有较大的困难,在这里笔者介绍一种相对简单的方法。我们可以充分利用 ClearCase 强大的触发器功能来减少活动发生依赖的可能性。例如,为了不发生因使用不同活动修改同一个文件而发生的变更集依赖,我们可以在执行检出操作时,判断这个元素当前版本关联的活动是否已经处于 closed 状态。如果还没有处于 closed 状态,那么就不允许检出。通过类似触发器的创建,我们对开发流的活动依赖进行了限制。但是这种方法仅适用于变更集产生的依赖,对于基线依赖(如递交基线依赖)也不能很好的解决,这种情况可以参考下一节。

另外,我们还可以通过制定一定的管理规则,例如,我们可以要求开发人员在开始新 build 的开发时应该先执行变基操作,将公共开发流的稳定版本更新到个人工作空间后,然后才可以在个人工作空间上进行新的开发任务。类似这样的管理规则,也可以从一定程度上减少和解决活动依赖。

但是,由于 ClearCase 数据安全的设计机制,要想完全避免活动依赖,可能需要牺牲一定的开发效率(例如上例提到的触发器设置)。既然我们不能完全避免活动依赖,那么一旦碰到因为活动依赖导致我们无法完成需要的操作而影响工作时(如版本发布),那么我们可以按照下一节的应对方法来应对活动依赖。

递交基线依赖问题的应对办法

当你因为之前从源流执行过递交操作而不能递交选择的活动,你可以按照下面的方法来解决这个问题:

  1. 在开发流创建一个新的活动;
  2. 将初始的 deliver 活动的变更集移动到步骤 1 新建的活动中(见图七);
图七:移动变更集
移动变更集
  1. 从源流移走初始的活动(见图八);
图八:删除活动
删除活动
  1. 尝试重新递交选择的活动。这次递交操作在没有 deliver 初始活动变更集的情况下将会成功。即使包含了相同的变更,由于新的活动不属于任何基线,所以也不再有依赖关系。

除了上述办法之外,对于活动依赖的困扰,我们可以使用 David E. Bellagio 在《Software Configuration Management Strategies and IBM® Rational® ClearCase® Second Edition A Practical Introduction》中提供的 cset.pl 脚本来快速解决这个问题。cset.pl 脚本下载地址:IBM Rational IC Package: CSET.PL

参考资料

学习 获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织