购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第2章
基本的版本控制:记录历史并防止混乱

这一章的内容,是基本的版本控制。如果你接触过一些版本控制工具 ,那么阅读这一章对你来说会很轻松。但是这仍然会是有趣的,值得一读的,因为你会从中了解在远古时代 人类是怎样一步一步发明了软件配置管理的。

2.1 即使只有一个开发人员

即使只有一个开发人员,也需要软件配置管理。只是,这时做软件配置管理的方法,主要是一般性常识,因此常常意识不到做了软件配置管理。让我们来看一看。

开发人员会经常随手按Ctrl+S键 ,这一个动作的意思是告诉计算机,保存当前内容到磁盘。这样心里比较踏实。因为有无数潜在可能,会让尚未保存的修改化为乌有。比如邻桌把电脑电源线踢了,比如编辑器崩溃了,比如操作系统崩溃了,比如一只老鼠迅速跑过键盘,后面还跟着一只猫……

有经验的开发人员,会做得更多。他们会经常备份自己写的源代码。比如,在对某一个文件进行大规模修改之前,他有可能先把这个文件复制一下,并给这个备份文件取个名字,比如protocol.c.bak。因为他知道,万一涂改得产品转不起来了,可以把文件当前版本和上次保存的版本做个比较,看看修改了哪些地方,到底是什么修改导致了问题。还有可能干脆就放弃最近的修改,从上次保存的版本开始,换一种思路写。

有些开发人员就懒一些。备份一个个文件太麻烦了,干脆把整个目录或者整个产品的源代码都备份。需要备份的时候,把整个产品的源代码复制一下,并且取个有意义名字。比如,对于Messenger这个软件,某大侠做过这样一个备份——messenger.0823.to_add_icon,意思是说,Messenger这个软件在8月23号这天的一个备份,打算开始做增加图标的工作。这样做备份,比按文件备份省事儿多了,而且更有意义——备份的是整个产品,恢复的是整个产品。并且,能够知道每次保存版本的原因(见图2-1)。

图2-1 源代码整体备份

顺便提一下,懒是一种值得提倡的精神

也有的开发人员更谨慎。保存在本机,那万一磁盘坏了怎么办?万一磁盘被人卸了拿走了怎么办?应该再备份到另一块磁盘上,或者自己的另一台机器上,或者备份到服务器,备份到位于另一个城市的服务器,甚至备份到位于另一个国家的服务器……

看,这里所做的事情是,及时保存,及时备份,记录历史,以便追溯。这些都是软件配置管理的核心任务。当开发人数增多时,上述所有关心的内容依然存在,只是具体解决的方法可能会有差别。

2.2 建立公共存储区

当一个软件产品由多个开发人员共同开发的时候,面临的挑战又多了一些。为了让事情不要一下子变得太复杂,我们先假定,各开发人员有明确的“责任田”,即每个开发人员负责一个专门的模块,不存在两个开发人员修改同一处源代码的问题。好,基于这样的简化,我们来看看要面对的情况(见图2-2)。

图2-2 开发人员间随意传递源代码

假定每个开发人员有一台电脑,他和他的电脑是上图的一个点。每个开发人员都在编写程序。这涉及到两个基本问题:一是在修改程序之前,从哪儿拿到最新版本;二是在修改之后,把修改的结果提交到哪儿。

如果第一个问题得不到解决,开发人员就可能基于过时的程序开始自己的工作。假定,张大侠负责模块A,王大侠负责模块B,李大侠负责模块C。模块A、B、C共同组成了软件产品。模块A向外提供的一个函数叫Timer,有一个参数,标志着定时的时间。早先的时候,这个参数以毫秒为单位。最近,张大侠把它改成了以秒为单位。王大侠正在编写程序,在模块B里,他打算调用Timer这个函数。而王大侠手里的模块A的版本,是从李大侠那儿复制过来的。这个版本中模块A的Timer函数的参数,还是以毫秒为单位。所以,在王大侠的最新程序里,以参数5000调用Timer函数,让定时器停5000毫秒,也就是5秒。王大侠改完了程序,把模块B的最新版本交给了张大侠。张大侠很高兴,决定运行一下试试。后来……一根筋的张大侠在屏幕前枯坐了一个多小时

而如果第二个问题得不到解决,开发人员的工作就可能被湮没。假定还是刚才这拨人。上午,客户来找张大侠,说,现在有这么这么个Bug,帮忙解决一下。张大侠三下五除二改好了,交给了客户。下午,客户找到王大侠,说,现在有那么那么个Bug,帮忙解决一下。王大侠三下五除二改好了,交给了客户。过了一会儿,客户发飙了,上午刚改好的Bug,怎么又冒出来了!

爱思考的读者会发现,其实上面这两个问题是相互联系,相互影响的。

那么,我们怎么解决这个问题呢?解决之道是,将源代码流转的渠道从网状结构改成星形结构。也就是说,设立一个公共存储区,作为参照点和枢纽,大家不要再相互之间乱拷贝代码了,一概都从这个公共点取代码。等程序改完了,都把自己改的那部分全部传到公共存储区,别人再从那里取用(见图2-3)。

图2-3 星形结构

来看看刚才的两个例子,用这样的方法是不是解决了问题。在第一个例子里,如果王大侠是从公共存储区里取得的代码,那么他取到的就是张大侠提交的,以秒为参数的Timer函数。王大侠就会在自己的程序里,用5而不是5000作参数来调用Timer。问题解决了。 在第二个例子里,如果张大侠和王大侠改完代码后,不是直接发给客户,而是先上传到公共存储区。“用公共存储区里的代码编译生成”(因为“编译生成”不一定是在公共存储区里完成的,通常是复制到另外一个地方再“编译生成”)。有可运行的程序,再发给客户。这样,王大侠改完代码,发给客户的版本,一定既包含了张大侠的修改,也包含了王大侠自己的修改。问题也解决了。

2.3 防止版本覆盖

在上一节里,我们做了一个假设,假定不存在两个开发人员修改同一处源代码的问题。遗憾的是,在现实世界里,这一假设常常是不成立的。经常发生的情况是,两个开发人员同时修改一处源代码。

假定,开发人员张大侠为修复Bug 1而修改模块A、B,开发人员王大侠为修复Bug 2而修改模块B、C。一开始,两个人都从公共存储区拿到了整套代码,即模块A、B、C,并各自开始在其上工作。张大侠首先完成了Bug 1的修复。他把自己手上模块A、B的最新版本复制到了公共存储区。接着,王大侠完成了Bug 2的修复。他把自己手上模块B、C的最新版本复制到了公共存储区。灾难性的事情发生了。张大侠在模块B上的修改被覆盖了!

怎么避免这个问题?

不同的版本控制工具提供了不同的方法。但总的来讲,不外乎两种方法:通过监控,阻止同时修改这种危险事情的发生,即串行的方法;通过辅助,使同时修改的内容最终合并到一起,即并行的方法。

让我们来看看第一种方法:串行的方法。开发人员在修改前,先告诉版本控制工具,“我要修改它啦,帮我把它锁上,先不要让别人动了!”然后,从公共存储区拿到最新版本,修改,送回去。这时候再告诉版本控制工具,“我改好啦,把锁打开吧,别人可以动了!”(见图2-4)。

图2-4 串行修改

显然,开发人员每次锁的范围小一点比较好。如果能锁一个文件,那就不要锁整个目录,更不要锁整个产品。不然,别的开发人员啥都不能碰了,啥都干不了了,只能干等着。不过,即使只锁定一个或几个文件,有时候还是会影响到别的开发人员,要是人家也要改相同的文件呢?

[Visual SourceSafe] Visual SourceSafe,缩写为VSS,是微软公司出品的一款“古老的”版本控制工具。它默认的方法就是第一种方法,串行的方法。在修改前,锁定相应的文件,以免别人同时修改。在修改后,解锁。通过设置,它也支持下面的第二种方法。它支持基本的版本控制功能,如版本保存、打标签等。另外,它还号称支持分支(见第7章)等高级功能。不过,不太好用。总之,它是一款初级的版本控制工具,并且微软已打算不再维护它。

第二种方法,并行的方法,可以解决这个问题。第二种方法允许不同的开发人员同时修改同一处地方,比如同一个模块,或同一个文件。在将来的某一时刻,把他们的修改合并 (见图2-5)。

图2-5 并行修改(省略了很多中间步骤和细节)

工具可以判断出何时需要合并,合并哪些内容。并且,在大多数情况下,工具可以自动完成合并。而在工具判定某些修改不能自动合并的时候,会提示人工合并。自动合并可靠么?从统计上看,这种自动合并尽管偶尔会出问题,但在绝大多数情况下都是正确的。很神奇吧!

[Subversion] Subversion,缩写为SVN,默认支持第二种方法,也可以通过对文件加锁而支持第一种方法。SVN是当前最受欢迎的开源的版本控制工具之一。它继承了它的前辈CVS(Concurrent Versions System)几乎所有的优点,去掉了CVS几乎所有的弱点,还增加了很多新功能。对于中小型团队,它很值得考虑使用。而对于正在使用VSS和CVS的广大用户,它可以作为未来版本控制系统升级的目标。

[Git] Git支持第二种方法。Git之父就是Linux之父Linus Torvalds。2005年,他为管理Linux内核的开发而研制了Git。由于功能强、速度快,越来越多的项目特别是大型开源项目开始采用Git,包括Perl、Gnome、Android等。Git是分布式版本控制系统的典型代表。第3.5节将讲解分布式版本控制。

[ClearCase] ClearCase,有时候缩写为CC,同时支持两种方法。ClearCase是IBM Rational出品的大型商用软件配置管理工具,其核心是版本控制。尽管能够听到对ClearCase的很多抱怨,比如昂贵、复杂、不好用,但它仍然是收费的版本控制软件中,市场份额最大的。它有两个版本:Base ClearCase和ClearCase UCM。

Base ClearCase向你提供的是文件、目录、版本、标签、分支、触发器和链接等“裸露”的环境。在此基础上,你可以进行比较自由地设置和二次开发,以满足你实际项目的需要。它的优点是灵活。

ClearCase UCM是开箱即用(Out of the Box)的,它提供了基于Base ClearCase的一套不错的封装。你只需要做一些简单的设置,就可以在实际项目中直接使用了。 [其他版本控制工具] 市面上还有至少几十种版本控制工具,或者包含了版本控制功能的ALM工具 。除上文已给出的外,比较出名的还有Perforce、StarTeam、AccuRev、TFS(Team Foundation Server)、RTC(Rational Team Concert)、Telelogic Synergy等收费产品和Mercurial等开源免费工具。它们至少支持上文中两种避免版本覆盖的方法中的一种。工具很多,在本书中将重点介绍其中三个:Subversion、Git和ClearCase(特别是ClearCase UCM)。注意,本书是不能代替相关工具的培训教程的。本书中关于相关工具的段落,主要是帮你把一般概念和原理,与具体某个工具联系上。

2.4 行话

直到现在,我们一直在说土话,什么公共存储区啊,下载源代码啊之类的。如果你用这些词汇跟人交流,那听起来有点不够专业,有损你的形象。其实更重要的是,“车同辙,书同文”10,我们得用一种公共语言说话。坦率地说,当前,在软件配置管理领域,术语名称并不统一,涵义也并不完全一致。不同的理论、标准和指南中,不同厂商的工具中,有不同的叫法和不同的内涵。这里我们尽力而为,介绍一些相对通用的。

我们上文所谈到的公共存储区,正式的名称是版本库(Repository),也叫存储库、存储池、配置库等。在版本库里,存储源代码的各个版本11。当然,版本控制工具通常不会傻乎乎地把文件的每个版本的全部内容都存上,而是只存储不同版本间有差异的部分,这叫增量存储(Incremental Storage)。

与位于中央服务器上的公共版本库相对应的,是每个人在自己的计算机上的私有的工作区,又称工作空间,英文原名是Workspace。也有的时候叫工作目录树(Working Tree)、视图(View)、沙箱(Sandbox),等等。它是每个开发人员在版本控制之下工作的地方。开发人员从版本库拿到源代码,放在这里,然后在这里查看、修改、编译、运行和调试。等改好了,再把新版本的代码从这里送回到版本库。

[Subversion] 将工作区叫做工作复制(Working Copy)。而Repository常被翻译为项目仓库。

[Git] 将工作区叫做工作目录树(Working Tree)。版本库就是Git中的Repository。

[ClearCase] 将版本库叫做版本对象库(Versioned Object Base,VOB)。工作区大致对应于视图(View)这个概念。视图分为两种,静态视图(Snapshot View)和动态视图(Dynamic View)。静态视图是位于本机的一个目录树,这和其他工具很像。而动态视图有点像虚拟盘符,看着是在本机上,其实是连到了服务器上。事实上,它比虚拟盘符更玄妙,详情请参考ClearCase相关文档。

开发人员首先创建或者说初始化工作区,从版本库里把代码抓下来,填到工作区里。随后,开发人员在工作区里修改代码。修改完毕,把结果送回版本库,这被称做提交或交付(Deliver/Commit)。大家不断提交修改,导致版本库中的内容不断变化。如果某个开发人员想把版本库中新的内容同步到他已有的工作区里,那么他就要进行一个更新(Update)操作。

提交与更新。还有一套相似的词汇也常能听到:签入与签出。签入或检入(Check In,CI12)与提交的含义大体相同,是从工作区到版本库的同步。相应的,签出或检出(Check Out,CO)一般来说指的是从版本库到工作区的同步,大体对应于上一段所说的初始化工作区或更新操作。注意在不同的工具中,这些概念的含义不尽相同。

[Subversion] 使用签出(Check Out)、提交(Commit)、更新(Update)三个术语。在Subversion的世界里,签出意味着初始化工作区。

[Git] 要更复杂一些。使用pull命令或fetch、merge等命令,代码从服务器上的版本库流到本地版本库,再流到工作区。在相反方向,综合使用add、commit、push等命令,工作区里的改动先流到本地版本库,再流到服务器上的版本库。这里提到的本地版本库的概念会在第3.5节详细讲。

[ClearCase] 在ClearCase的世界里,签出(Check Out)和签入(Check In)都是针对某个文件的。尽管工作区里已经塞满了从版本库下载的文件,但在着手修改某个文件前,先要以该文件名为参数调用签出命令,不得偷懒省略这一步。修改好了,再以该文件名为参数调用签入命令。而在ClearCase UCM里,还有Delivery、Rebase这一对儿词,我们随后再讲。

回顾一下前面讲过的星形结构(重画于图2-6)。

图2-6 星形结构

中间那个大圆点是版本库,周围的小圆点是工作区。从大圆点到小圆点的代码流动是初始化和更新工作区,从小圆点到大圆点的代码流动是提交到版本库。

我们使用 版本控制工具 或称版本控制系统(Version Control System),来建立版本库、工作区,来进行提交、更新等操作。这比起手工的方式来,既便捷又安全。既然我们使用版本控制工具,那么就需要有人来负责工具的选择、安装、设置、二次开发、培训、疑难解答、制定相关规定和流程等一系列工作。这些是 软件配置管理工程师 (Software Configuration Management Engineer,SCM Engineer)的职责的一部分13。有时候,他们也被称为配置管理员,简称为配管(CM),也可能被尊称为配置经理(Configuration Manager)。嘿嘿,其实都差不多啦。 Be0qsOLrIPDI/n6aasnoIoefim3IsAWyRc62+nQMPpxtbrT35eTFV82nXxwUz1dj

点击中间区域
呼出菜单
上一章
目录
下一章
×