没有使用版本控制的黑暗时代——版本控制心得

翟二喜

来自:morningspace.51.net 作者:晨光(Morning)


  在没有使用版本控制的开发团体中,我所熟悉的一种常用开发方式是:多个开发人员共同负责一个软件的开发,每个人在各自的机器上有整个软件的拷贝,并对之实施编码,分别完成各自任务之后,再通过文本比对工具将各自机器上的不同版本的软件整合到一台机器上。

  本文就这样的开发方式,提出在软件开发中出现的几个和版本控制密切相关的典型问题(但未必全面),同样也是需要通过版本控制来解决的问题,它们来源于实际开发过程中的切身体会,并经过总结和整理。

1 软件代码的一致性

  软件的开发、维护和升级,往往是多个人共同协作的过程。不同人对同一个软件的不同部分同时做着修改,这种行为有时会出现彼此交叉的情况。由于同一软件在各自开发人员的机器上都有拷贝,软件的全部代码都暴露在每个开发人员面前,原则上他有权限可以不加限制地更改软件的任何部分。而当他们修改的内容属于公共部分,或者需要被其他人员所负责的部分调用时(软件各模块间的彼此依赖关系决定了这种情况是经常发生的),这种修改就属于交叉情况。此时,就有可能出现代码的不一致现象。比如:修改者在改动了某个公共函数的同时也修改了其调用接口,若其他人员没有得知此事,而在各自机器上仍调用原来版本的函数,则当整合时,就会出现错误。另一种更为严重的情况是,修改者决定废弃原有函数而另外编写一个新的函数,但他并未删除原有函数,这种情况即使最后的整合也可能不会被察觉,如果将这种一致性错误的纠正延迟到测试阶段,则会增加调试的难度,从而降低开发效率。为了始终保证代码的一致性,一种解决办法是,要求修改者每次修改后都通过某种方式告知同组其他人员,或者随时对软件做整合。但是这样,一方面会增加开发人员的负担,另一方面也降低了软件的开发效率。

2 软件内容的冗余问题

  软件在各自开发人员的机器上都有拷贝,并且同一个开发人员在不同时期也会在本机保留当时的软件版本,也就是说,一台机器上还可能不止一个版本。这类似于一种信息的冗余。对于不同版本而言,其差别有时可能并不很大,如果说不必要的占用存储空间是一个次要问题的话,那么另一个问题可能更重要。随着时间的推移,开发人员可能对自己机器上的不同版本间具体差异的了解变得模糊不情,甚至忘记了当时为什么区分这些版本的原因,这会给整合带来麻烦。而且,如果需要同时维护多个版本,则对某个版本的改动可能需要反映到其余版本的对应处,很难保证这一过程不会出差错。还有一点,作为开发人员,有时即使知道自己机器上软件的某个版本可能不会再使用了,他也不会去删除(生怕万一需要从那里获取点什么),但是通常也不会再去维护或查看它,因此久而久之,这种“僵死之物”会在多台机器上“蔓延”。

3 软件过程的“事务性”

  对于软件的某个版本,如果开发人员想要为其增添新的功能,或改善原有功能,而又担心会搅乱原来运行良好的软件。一种常用的办法,就是保留现有版本,另复制一个新的拷贝,并在新的副本上进行修改。这类似于一种事务处理,当将一系列操作做为一个事务时,如果中间某个操作出现偏差,则希望恢复到执行事务之前的状况。而当完成修改之后,开发人员该如何处理这两个副本呢?是在新的副本上继续新的开发,将之作为最新版本;还是将新的改动加入原有版本,删除新的副本。无论怎样,都可能会出现如下情况:如果软件运行正常,则出现2中提到的冗余情况;当删除修改前的版本后,又发现改动中有问题,但已无法恢复了;改动无误,将改动加入原有版本时,可能出现人为错误,导致软件运行出错;即使没有人为错误,这种做法也会给开发人员增加额外负担,一定程度的降低开发效率。重复上述过程则会出现多个类似的副本,此时,如何对待这些不同版本,将是开发人员需要面对的问题。而如果调试的过程中,发现确实有必要恢复到上一个版本,甚至上上个版本……,此时就不得不保留所有版本了。

4 软件开发的“并发性”

  由于是多人共同开发一个软件,期间出现多人修改软件的同一部分,尤其是同时修改,有时是不可避免的。对于前者,具有良好编程习惯的人员的一种通常做法是,对他人的源程序进行修改时添加必要的注释,写明修改人,修改原因,修改日期等。但实际情况是,当修改内容很零散或修改过程很复杂时,注释很难写,或者代码被注释分割得支离破碎影响正常阅读,或者注释无法详细说明实际情况。而且,这种做法也增加了开发人员的负担:他需要在考虑代码逻辑的同时,兼顾如何写注释;而对于注释和代码的一致性也是他需要随时留意的问题,不能疏忽。因此,我认为这种措施在实际开发中,并未取得实质效果,是不必要的,事实上代码本身(而非注释)是最能说明问题的,而修改的记录应该置于代码之外的某处。而且,不论是否是同时修改,都需要考虑1所提到的一致性问题,需要及时进行人工的差异比较和整合以便形成一个统一的新版本。

5 软件代码的安全性

  由于代码完全暴露于所有开发人员面前,任何人都可以增、删、改之。除了会造成1所提到的不一致问题外,从安全的角度来看,也是存在隐患的,这一点对于一个自主产权的长期开发的产品而言更是如此。即使是一般的项目开发,不同的人员其分工不同,允许别人可以不加限制地任意修改自己负责模块的代码(相当于所有人员都具有管理员身份),总是容易产生问题。对于共有模块更是如此,所有人都可以修改,一旦修改,则波及全体。

6 软件的整合

   在软件的整合过程中,一般比较可靠的做法是使用文本比对工具辅助完成的。这种措施,有以下缺点:
可靠性,整合中的人为错误会影响软件的可靠性,有时这种错误很难察觉,可能编译没有问题,而在测试时却发现了问题。这种潜在的错误发现的时间越晚就越难以纠正。
效率,对于纯手工的整合即使熟练操作的人也是需要时间来完成的,而有些整合只是将两段代码拼接在一起,这一过程完全可以借助于某些自动机制。这可以大大节省时间,使开发人员可以专注于后续的开发任务。
  对于一个采用版本控制进行软件开发的多人开发团队而言,其一般的开发方式是:采用服务器/客户端的形式,在上面分别安装版本控制工具的服务器和客户端版本,软件放在服务器上为大家所共享,开发人员在客户端从服务器上将软件的相关部分下载到本地,进行修改,改动结果最终提交到服务器上。

1 软件版本控制的主要功能和主要特点

  版本控制的功能:跟踪记录整个软件的开发过程,包括软件本身和相关文档(所带来的结果是:可标识不同阶段的软件及相关文档,进行差别分析;对软件进行可撤消的修改;便于汇总不同人员所做的修改),辅助协调和管理软件开发团队。


  我认为,对于软件的版本控制而言,其主要特点包括如下:

1.1 空间上集中统一管理

  由于采用服务器/客户端方式,尽管开发人员可以在自己的本地留有备份,但最终唯一有效的只有服务器端的那个原始拷贝。一定程度可以解决一致性问题、冗余问题。[1]

1.2 时间上全程跟踪记录

  工具将会自动记录每个更改细节,和不同时期的不同版本。一定程度可以解决冗余问题、事务性问题、并发性问题。[1]

1.3 操作权限控制

  对于不同开发人员,对软件的不同部分可以定义不同的访问权限。一定程度可以解决安全性问题。[1]

1.4 自动或半自动

  由于有工具辅助控制,可以减轻开发人员的负担,节省时间,同时降低人为错误。像软件整合这样的工作,其工作量可以相对减轻。[1]

2 软件版本控制评价标准

  我认为,对软件进行版本控制,衡量其效果的标准,归根结底有两点:效率和质量。如果版本控制最终使软件开发效率得到提高、使软件质量得到提升,那就是成功的,反之则是失败的。效率的提高比较容易理解,质量的提升则体现在:软件的一致性、冗余程度等。需要指出的是,单就版本控制工具本身并不能保证这两点。对工具不熟悉或错误的使用,以及开发人员的不良习惯等都将导致失败。有时可能反而降低效率。

3 几个重要观点

  

3.1 版本控制包括代码和文档

  我认为,广义的版本控制也应该包括和代码相关的其他内容,主要指文档。虽然文档的一致性问题并不像代码那么突出,多人同时修改一个文档的情况一般较为少见,但将文档只留一个服务器拷贝,会便于集中管理,减少冗余,加上工具的全程跟踪记录,可以随时查看不同时期文档的内容,相互比对。

3.2 版本控制管理应该包括工具软件的使用和人为规范的遵循

  在版本控制中,人的因素更为重要,规范的行为可以避免很多意想不到的后果,和错误使用工具所引起的问题。单纯依赖工具,并不能取得良好效果。没有版本控制的意识、对工具的使用不熟悉(一些功能不知道怎么用,一些功能使用错误)、人为的不良习惯,都可能导致错误。因此需要使用版本控制工具的开发人员具有自觉良好的意识和习惯,也需要一些相关的规范和制度的保证。

3.3 不能忽略版本控制管理员这一角色的重要性

  可以不必单独划定一人来担任这个角色,但如果没有这一角色的存在,任何人都可以任意操作服务器上已纳入版本控制的软件以及版本控制工具的配置信息(比如用户权限信息),或者说任何人都可以做管理员,则又会出现安全性问题。从某种程度上讲,这和没有版本控制下每台机器上都有若干软件版本的情况是等效的。

3.4 向版本控制过渡是一个循序渐进的、持久的过程

  对于一个团队而言,向版本控制过渡,需要有一个逐步转变的过程。这包括:制订一系列合理的循序渐进的措施,使版本控制的意识逐步得到大家的认可,使人员逐渐养成良好习惯(习惯于这种开发方式)。这是一个持久的过程,需要坚持。

  这里列出了若干在使用版本控制的过程中容易出现的常见问题,这些问题来自实际工作中的切身体会。但是,这个问题列表未必全面,并且对于具体个人而言,其情形也不尽相同。每个使用版本控制的开发人员的心里可能都有一个类似这样的列表,并且在实际开发中,或许这个列表还会得到扩充,不断完善。

Item 1. 项目的逻辑结构混乱(这里的“项目”是版本控制中的术语,见A.1)

  这是在实施版本控制过程中一个容易出现的问题,尤其是对于项目开发(此处非术语)。其原因有很多,比如:开始时对需求不明确,导致软件本身结构混乱,使在定义软件的逻辑结构时,时常变化。又如:一个团队中,大家各自都之关心自己负责的模块,每个人各自制定适合自己的逻辑结构,导致最终的项目结构是一个大杂烩(多个结构组合而成)。久而久之,就会导致软件管理混乱,增加维护负担,反而降低效率。结构中,有的目录可能是“死角”,永远都没有使用到;有的目录可能是重复的,造成冗余;有的目录可能大家同时在用,各自对代码的修改彼此影响。自始至终合理安排和规划项目的逻辑结构,这是一定需要坚持的。

Item 2. 多人修改同一个文件

  一旦出现这样的情况,很有可能某人辛勤劳动的成果,会被别人毁于一旦。其解决办法是:在一般情况下,确保在任何时刻都只有一个成员对某个特定的文件进行修改,这样可以防止文件被其他成员的修改意外更新。为了适应多人同时修改同一个文件的情况,版本控制管理员也可以改变此缺省设置以允许对单个文件同时有多个签出(checkout),并且仍禁止对他人的修改进行覆盖。

Item 3. 本地版本和服务器版本不一致

  有时会碰到这样的情形,开发人员在从服务器那里更新本地版本时,只更新了部分内容,导致本地编译不通过。应该时刻注意保持本地版本和服务器版本的一致性,这是一个认识的问题,因为服务器版本才是真正唯一有效的。多个程序员还必须注意不要为了解决同一个问题而浪费时间。对某项功能的实现,由于本地和服务器的不一致,导致大家重复实现。应该对服务器端数据的全部内容,包括所有子文件夹,定期进行备份,这是绝对重要的一项工作。

Item 4. 用户权限混乱

  对于所有开发人员和各自负责的模块,根据实际情况,制定合理的用户权限,哪些人对哪些目录只有可读权限,哪些人对哪些目录有读写权限。不应该出现所有人都是管理员这样的极端情况。

Item 5. 手工修改文件的只读标记

  为了防止你对没有签出的文件进行修改,版本控制管理工具会将这些文件指定并标明为只读文件。当你签出一个文件时,只读标记便被删去。一种经常出现的不良习惯是,为了图省事,在没有签出文件时便试图修改文件,当发现文件不能保存时,便手工修改其只读标记。这是一切混乱的“源头”,它将导致不一致、有效内容被覆盖等问题。

Item 6. 没有指定工作目录或存在多个工作目录

  每个开发人员必须拥有一个独一无二的工作目录,它不能与任何其他开发人员共享(这里的“工作目录”是版本控制中的术语,见A.2)。

Item 7. 频繁的签入或很少签入

  掌握好签入的时间,比如一天,或者在其他人需要的时候。并非每次微小的改动都需要马上签入,也并非每改完一个文件都将其签入,但也不要忘记签入。

Item 8. 从服务器上获取最近版本时的疏忽

  如果选择获取当前已经签出并且已经修改的文件最新版本,操作时必须非常小心。如果你选择取代文件,你将用最近一次签入的文件版本改写你做的修改,这可能会使你所做的工作白费。大多数情况下,最保险的做法是选定Apply To All Items,并选择Leave。

A 软件版本控制中出现的几个主要概念

  参考Visual SourceSafe,这里列出几个主要的基本概念。

A.1 项目(Project)

  版本控制的一个单位,包含若干不同类型的文件。其下所属代码及相关文档,以目录结构分别存放。一个软件可以对应一个或多个项目,视情况而定。

A.2 工作目录(Working Folder)

  开发人员对项目文件进行调试修改的地方,一般位于本地机器上。开发人员签出(checkout)项目中的文件时,将被拷贝到工作目录下,当修改完文件后,开发人员再将文件从工作目录签入(checkin)服务器。

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: