每个开发人员应该知道的Clearcase版本控制系统的核心概念是什么?
核心概念?
集中式(复制)VCS:ClearCase位于集中式VCS世界(一个或多个"集中式"存储库或VOBS - 版本对象库 - 每个开发人员必须访问提交)和分布式VCS世界之间.
但它也支持"复制"模式,允许您复制远程站点(MultiSite ClearCase)中的仓库,发送增量和管理所有权.(附带的许可证费用非常陡峭)
这不是真正的"分散"模式,因为它不允许并行并发演进:分支机构被掌握在一个VOB或另一个VOB中; 虽然您只能访问任何副本上的任何分支,但您只能在主VOB中为已掌握的分支机构签入.
线性版本存储:每个文件和目录都有一个线性历史记录; 它们之间没有直接关系,如DAG VCS(有向无环图),其中文件的历史链接到链接到提交的目录.
这意味着
当你比较两个提交时,你必须比较所有文件和目录的列表以找到增量,因为提交在文件或目录中不是原子的,这意味着对于构成一个文件的所有文件的所有更改都没有单一的名称.逻辑增量.
这也意味着合并必须通过历史探索找到共同的基础贡献者(并不总是与共同的祖先相同)(参见下一点).
(Git处于该频谱的另一端,既是分散的,也是面向DAG的:
如果其图形的节点具有与不同提交的节点相同的"id",则不必进一步探索:所有子图保证相同
两个分支的合并实际上是DAG中两个节点内容的合并:递归并且非常快速地找到共同的祖先)
alt text http://publib.boulder.ibm.com/infocenter/cchelp/v7r0m0/topic/com.ibm.rational.clearcase.hlp.doc/cc_main/images/merg_base_contrib.gif
3向合并:要合并两个版本,ClearCase必须在其线性历史中找到一个基于公共的贡献者,对于复杂的版本树(分支/子分支/子子/分支,...),这可能相当长.基本的ClearCase merge命令合并文件或目录,但它不是递归的.它只会影响单个文件,或者只影响没有文件的单个目录(ct findmerge
递归)
以文件为中心(而不是其他最近的VCS更多以存储库为中心):这意味着提交是逐个文件,而不是"修改过的文件集":事务处于文件级别.几个文件的提交不是原子的.
(几乎所有其他现代工具都是"以存储库为中心",具有原子提交事务,但第一代系统如RCS,SCCS,CVS和大多数其他旧系统都没有该功能.)
id-managed:每个文件和目录都有一个唯一的id,这意味着它们可以随意重命名:它们的历史记录不会改变,因为id仍然是"element".另外,目录将在其历史记录中检测文件的任何添加/抑制.当文件被"删除"(rmname
)时,它不知道它:只通知目录并在其历史记录中创建新版本,其中包含不包括文件删除的子元素列表.
(创建两个具有相同大小和内容的文件,它们将在Git中获得相同的ID - 一个SHA1密钥 - 并且只会在Git仓库中存储一次!在ClearCase中不是这样.
另外,如果两个文件具有相同的路径和名称是在两个不同的分支中创建的,它们的id不同意味着这两个文件永远不会被合并:它们被称为" 邪恶双胞胎 "
分支是一等公民:大多数VCS认为分支和标记是相同的:历史中可以从中增长(分支)或从附加描述(标记)的单个点.
对于ClearCase不是这样,其中分支是引用版本号的一种方式.任何版本号从0开始(仅在ClearCase中引用)从1,2,3开始,依此类推.每个分支可以包含一个新的版本号列表(再次为0,1,2,3).
这与版本号唯一并且总是在增长的其他系统(如SVN中的修订版)不同,或者只是唯一的(如Git中的SHA1键).
path-accessible:要访问某个版本的文件/目录,需要知道它的扩展路径(由分支和版本组成).它被称为"扩展路径名称":myFile@@/main/subBranch/Version
.
(Git确实通过id引用所有内容 - 基于SHA1 - :版本[或提交],树[或目录版本]和blob [或文件版本,或者更确切地说是文件内容 ].所以它是"id-accessible"或"id-referenced".
对于ClearCase,id指的是"元素":目录或文件,无论其版本是什么.)
悲观锁定和乐观锁定 :( ClearCase中保留或未保留的结账):即使是悲观锁定(保留结账)也不是真正的悲观锁定,因为其他用户仍然可以检出该文件(尽管处于"非保留模式"):他们可以更改它但必须等待第一个用户提交他的文件(签入)或取消请求.然后他们将合并他们的同一文件的结帐版本.
(注意:"保留"结帐可以释放其锁定,并由所有者或管理员保留)
廉价分支:分支不会触发所有文件的副本.它实际上没有触发任何内容:任何不结帐的文件将保留在其原始分支中.只有修改过的文件才会将新版本存储在声明的分支中.
平面文件存储:VOB以简单文件的专有格式存储.这不是具有简单查询语言的数据库.
本地或网络工作区访问:
本地工作空间是通过结帐到硬盘驱动器(快照视图的"更新").
网络工作区是通过动态视图,结合版本化文件和目录通过网络访问(无本地拷贝,即时访问)和本地文件(签出的文件或私人文件).远程(版本化)和本地(私有)文件的组合允许动态视图看起来像经典硬盘驱动器(而实际上"写入"的任何文件存储在关联的视图存储中).
集中驱逐存储:[查看]存储是为了保留一些数据并避免与中央参考的某些或任何通信.
工作区可以有:
分散的存储:就像.svn
遍布整个地方的子目录一样
集中存储:与ClearCase中的视图存储一样,它们包含有关视图显示的文件的信息,并且该存储对于视图是唯一的.
被驱逐的存储:存储不是视图/工作区本身的一部分,但可以位于计算机的其他位置,甚至可以位于LAN/WAN外部
(Git本身没有"存储".它.git
实际上是所有的存储库!)
面向元数据:任何"键值"都可以附加到文件或目录中,但这些数据本身不会被历史记录化:如果值发生更改,它将覆盖旧值.
(意味着该机制实际上比SVN的"属性"系统弱,其中属性可以有历史;
另一端的Git对元数据不太热衷)
基于系统的保护:所有者和与文件/目录或存储库关联的权限基于底层系统的权限管理.ClearCase中没有应用程序帐户,用户组直接基于Windows或Unix现有组(在异构环境中,Windows客户端和Unix VOB服务器非常具有挑战性!)
(SVN更像是"基于服务器"的保护,Apache服务器可以获得第一级保护,但必须使用钩子才能获得更好的权限
.Git没有直接的权限管理,必须由钩子控制在存储库之间推送或拉动期间)
钩子可用:任何ClearCase动作都可以作为钩子的目标,称为触发器.它可以是前期或后期操作.
CLI托管:cleartool是命令行界面,可以从中执行所有操作.
ClearCase是一个可以使用的野兽.慢,马车,昂贵.我为应对CC所做的一些事情是:
办理登机手续时务必留下好评.
使用通用配置规范,不要经常更改它.
切勿尝试通过VPN或慢速网络连接使用CC.
在启动时关闭CC医生的装载.
不要将文件移动到不同的目录.
每个文件至少安排2分钟办理登机手续.
快照视图很慢,但动态视图速度较慢.
养成早期检查的开发习惯,因为保留的文件和合并很痛苦.
默认情况下让所有开发人员检出未保留的文件.
我们已经使用CC已经超过十五年了.它有很多很好的功能.
我们所有的发展都在分支机构完成; 我今天创建了一对,进行了几组不同的更改.当我进入分支机构时,我找到了一位同事来审核这些变化,然后合并回/ main/LATEST - 这恰好是我的工作需要去的地方.如果它是在一个分支上的旧版本,它将不会更难.
我临时分支机构的合并是全自动的; 没有人在我检查过的时候改变了我工作的文件.虽然默认情况下会保留(锁定)结帐,但您可以随时取消保存结帐,或创建未保留的结帐.当更改需要多天时,我的临时分支与主分支的重新同步很容易并且通常是自动的.合并工具还可以; 对我来说最大的问题是我的服务器机器离我的办公室(或家里)大约1800英里,所以那个遥远的X有点慢(但不是无法容忍的).我没有使用更好的合并工具,但由于我没有使用任何其他图形合并工具,因此可能没有多说.
视图(动态视图)在我们的设置上很快.我没有使用快照视图,但是当我可以帮助它时,我不在Windows上工作(我们的团队在Windows上使用快照视图;我不清楚为什么).我们有复杂的分支系统,但主要的开发是在/ main/LATEST上完成的,并且发布工作是在分支上完成的.在GA之后,维护工作在特定于发布的分支上完成,并且合并到/ main/LATEST(通过任何中间版本).
CC确实需要优秀的管理员 - 我们拥有他们并且很幸运.
使用CC并不是一件容易的事,尽管目前我发现'git'对于那些没有使用CC的人来说是令人生畏的.但基础知识大致相同 - 结帐,更改,签入,合并,分支等.目录可以分支 - 谨慎 - 当然是版本控制.这是非常宝贵的.
我没有看到办公室随时从CC切换.
我写:
我对CC的最大问题是它没有将版本号嵌入到源文件中 - 这也是git也存在的问题,AFAICT.我可以看到为什么; 不过,我不确定我是否愿意放弃这种可跟踪性.所以,在我的大部分个人工作中,我仍然使用RCS(甚至不是CVS).有一天,我可能会改用git - 但这将是一个颠簸,重新配置围绕(SCCS和)RCS配置的发布系统将需要做大量的工作.
作为回应,@ VonC注意到:
我们一直认为这种做法是邪恶的(将元数据信息混合到数据中),引入"合并地狱".另请参见如何在Java文件中获取Clearcase文件版本.当然,如果您使用适当的合并管理器,则可以使用触发器进行RCS关键字替换(Clearcase Manual:Checkin Trigger Example).
这个讨论暴露了几个问题,它们都混合在一起.我的观点接近古老,但有理由背后,我会花时间写下来(生活搞砸了 - 可能需要多次编辑才能完成).
我在1984年学习了SCCS,关于RCS发布的时间(1983年,我相信),但SCCS在我的机器上,互联网充其量只是新生儿.我在90年代中期不情愿地从SCCS转移到RCS,因为SCCS日期格式多年使用两位数,并且不清楚SCCS是否会在时间上普遍固定(它是).在某些方面,我不像SCCS那样喜欢RCS,但它有一些好处.在商业上,我的雇主使用SCCS到1995年中期,但他们从1994年初开始转向Atria ClearCase,一次处理一个单独的产品集.
当我们已经有一些CC经验时,我们的项目后来迁移了.部分是因为我坚持,我们通过签入触发器在源文件中嵌入版本控制信息.这持续了一段时间 - 但只有一段时间 - 因为,正如VonC所说,它导致合并地狱.麻烦的是,如果带有标签/ main/branch1/N的版本与/ main/M从公共基本版本/ main/B合并,则提取的文件版本包含一行,每行都有编辑 - 冲突 并且必须手动解决冲突,而不是自动处理.
现在,SCCS有ID关键字.ID关键字有两种格式,一种用于正在编辑的文件,另一种用于未编辑的文件:
Edit Non-Edit %I% 9.13 %E% 06/03/09 %Z% @(#) %M% s.stderr.c
如果您尝试对可编辑版本的SCCS文件进行3向合并(使用%x%表示法),则包含元数据的行上不会发生冲突,除非您更改了这些行上的元数据(例如,通过更改来自US- style%D%日期为英国式%E%日期 - SCCS不支持ISO样式的2009-03-15日期作为标准.)
RCS还有一个关键字机制,关键字也有两种格式,一种是用于尚未插入RCS的文件,另一种是用于具有以下内容的文件:
Original After insertion $Revision$ $Revision: 9.13 $ $Date$ $Date: 2009/03/06 06:52:26 $ $RCSfile$ $RCSfile: stderr.c,v $
不同之处在于关键字后面的'$'和':',空格,文本,空格以及最后的'$'.我没有做足够的与RCS合并以确定它对关键字信息的作用,但我注意到,如果它将扩展和"缩小"的符号视为等效(无论扩展材料的内容如何),那么合并可以在没有冲突的情况下进行,将合同符号留在合并的输出中,当在签入后检索结果文件时,这将合适地扩展.
正如我在讨论SCCS和RCS时所指出的那样,如果以正确(约定或可编辑)格式处理关键字进行三向合并,则不存在合并冲突.
CC的问题(从这个角度来看 - 很明显,CC的实现者不同意)是它缺乏处理关键字的系统,因此也缺乏合适的合并管理器.
如果有一个处理关键字的系统和一个合适的合并管理器,那么:
系统会自动将元数据嵌入到适当标记的文件中.
在合并时,系统会识别出具有元数据标记的行不会发生冲突,除非标记的更改方式不同 - 它会忽略元数据内容.
这样做的缺点是它需要一个识别元数据标记的特殊差异工具并专门处理它们,或者它要求提供给差异工具的文件是规范化的(元数据标记被简化为中性形式 - $ Keyword $或RCS和SCCS术语中%K%).我确信这一点额外的工作是它不被支持的原因,我一直认为在这样一个强大的系统中是短视的.我对RCS或SCCS符号没有特别的附件 - SCCS符号在某些方面更容易处理,但它们基本上是等价的 - 并且可以使用任何等效的符号.
我喜欢在源代码中使用元数据,因为我的源代码(而不是我雇主的源代码)是在源代码控制系统的支持下分发的.也就是说,它主要是开源的 - 我让所有人都可以使用它.如果有人在文件中报告问题,特别是在他们修改过的文件中,我认为知道它们从哪里开始是有帮助的,并且由源文件中的原始元数据表示.
在这里,SCCS优于RCS:SCCS关键字的扩展形式与常规文本无法区分,而RCS关键字看起来像关键字,因此如果其他人将材料导入到他们自己的RCS存储库中,则他们的元数据将替换为我的元数据,SCCS不会以相同的方式发生的问题(另一个人必须做的工作来覆盖元数据).
因此,即使有人拿走了我的源代码块并对其进行了修改,通常也会有足够的标签来标识它的来源,而不是让我猜测它所基于的版本.反过来,这样可以更容易地看到问题的哪些部分,以及它们的制作部分.
现在,在实践中,开源的工作方式,人们不会像你想象的那样迁移代码.他们倾向于坚持使用已发布的版本,因为在下一次正式发布时,偏差太昂贵了.
我不确定你应该如何确定源自你的工作的源代码的基本版本,并且从那时起就进行了修订.但是,找到正确的版本似乎是这样做的关键,如果代码中有指纹,那么它可以更容易.
所以,这是对我为什么要在源文件中嵌入版本信息的适度总结.它在很大程度上是历史性的 - SCCS和RCS都做到了,我喜欢他们这样做的事实.它可能是古老的遗物,在DVCS时代要告别.但我还不完全相信.然而,我可能还需要更多的文章来解释我的发布管理机制的细节,看看为什么我像我一样做事.
推理的一个方面是,基本上所有程序都使用密钥文件,例如'stderr.c'和'stderr.h'.当我发布一个使用它的程序时,我只是确保我有最新的版本 - 除非有一个需要反向版本的界面更改.我暂时没有遇到这个问题(我在2003年进行了系统的重命名;这引起了一些过渡性的麻烦,但是Perl脚本让我很容易实现重命名).我不知道有多少程序使用该代码 - 介于100到200之间是一个公平的猜测.今年的一系列变化(版本9.x系列)仍然有些推测; 我还没有决定是否保留它们.它们也是实现的内部,不会影响外部接口,所以我不知道 我还得下定决心.我不知道如何使用git处理它.在构建我的软件之前,我不想将库代码构建到必须安装的库中 - 这对我的客户来说太麻烦了.因此,每个程序将继续使用库代码的副本(不同类型的繁琐)进行分发,但只包含程序所需的库代码,而不是整个库.我为每个使用库函数的程序选择并选择.所以,我不会导出整个子树; 实际上,覆盖库代码中最后更改的提交通常与涵盖程序中最后更改的提交完全无关.我甚至不确定git是否应该为库使用一个存储库而另一个用于使用它的程序,或者是一个常见的更大的存储库.
好的 - 足够的wittering.我的作品对我有用; 它不一定适合所有人.它对VCS没有特别的要求 - 但它确实需要嵌入在文件中的版本元数据,CC和Git以及(我认为)SVN存在问题.这可能意味着我是有问题的人 - 为失去的过去做好准备.但我重视过去所提供的.(我可以逃脱它,因为我的大多数代码都没有分支.我不确定分支会产生多大的差异.)
Eric的源代码控制HOWTO是一个独立于工具的优秀指南.