2006 年 11 月 06 日
版本控制系统或资源管理系统是现代软件开发的一个重要方面。不使用版本控制系统就如同超速驾驶一辆汽车:很刺激,也可能会更快抵达目的地,但事故却在所难免。本文概述了软件配置管理(SCM)系统及其优势,内容涵盖 CVS、Subversion、Arch 和 Git。本文还讨论了最常见的 SCM 体系结构。本文在最后给出了新出现的一些方法及这些方法与先前方法的区别。
什么是软件配置管理?
在学校学习的重要工具中,通常不会 包含 SCM。软件(或资源)控制,正如其名字所暗示的那样,是一种工具和一种相关的过程,而这种过程被用于维护源代码及其演进。SCM 提供的主要功能如下:
- 在存储库(repository)中维护一个文件。
- 在存储库中维护文件的修订。
- 检测源更改冲突并为多个开发人员的环境提供合并。
- 跟踪变更的发起方。
- 为了实现一致、可重复的构建提供文件(相关修订)的配置管理。
|
SCM 的适用性
资源控制 主要是指源代码和相关文件的控制,而资源管理 则可应用于任何类型的资源。包含超文本标记语言(HTML)和二进制图像文件、一般文本文档或其他任何文件的 Web 站点是 SCM 系统进行修订控制的对象。 |
|
所以,SCM 允许您控制存储库中的一组文件并跟踪这些文件的修订。其他开发人员更改了存储库中的这些文件后,SCM 会标识出这些变更与您所做的变更之间存在的冲突,然后自动将其合并或通知您存在冲突。这个功能非常重要,因为它允许多个开发人员修改同一组文件。SCM 还能跟踪出谁做了何种变更。SCM 还允许您将文件按逻辑划分为相关的文件组,比如组成软件图像或可执行文件的源文件。
SCM 的术语
在深入研究 SCM 体系结构的类型和细节之前,先要熟悉一下术语的含义。存储库 是文件存储和管理的中心位置(有时又被称为树)。从存储库中取出文件放到本地系统的工作文件夹的过程被称作签出。如果变更了本地文件而又想同步变更存储库,就需要执行更新。如果要将变更后的文件签入到存储库,就需执行提交。如果所变更的文件先前已经被变更并已由别人提交,则会发生合并,表明两组变更集将会被合并在一起。若由于变更冲突的原因不能合并时,则必已发生了冲突。在此情况下,提交被拒绝,并要求开发人员手工合并变更。当提交了变更之后,就会创建此文件的一个新的修订。
一个或多个开发人员可以对主树(存储库的当前头)或位于主树旁的单个的分支进行操作。这就让开发人员可以操作分支而不会影响到主树。当分支稳定后,再将分支与主树进行合并。
要标记源树演进过程中的重要阶段,可以对一组文件修订应用标签。这会将这组文件组合成一个有用的集合(有时会用作针对于惟一构建的文件的一个发布)。
体系结构
各种 SCM 间有许多的差异,其中有两种最基本的区别很值得研究:
集中式存储库与分布式存储库
现代 SCM 体系结构的最为重要的差异之一是存储库是集中式的还是分布式(分散的)。当前最为常见的体系结构是集中式存储库。这个星型 体系结构由中心的源存储库和工作于这个存储库的多个开发人员组成(参见图 1)。开发人员从中心存储库签出源代码到本地的沙箱内,进行变更后,将它提交回中心存储库。其他开发人员就可以再访问这些变更。
图 1. 在集中式体系结构中,所有开发人员都通过同一个中心存储库进行工作
中心存储库也可以创建分支,允许多个开发人员协作于针对位于存储库(但必须在 mainline 或 tip 之外)的资源的一组变更。
分布式体系结构则允许开发人员为其变更创建其自己本地的存储库。本地开发人员的存储库与原始的存储库(已被分布)很相似。主要的区别是:在集中化的方式中,变更是在沙箱内完成的,而在分布式方法中则允许开发人员工作于分离的存储库。开发人员进行变更、提交变更到其本地的存储库,之后,在不影响主干的前提下,与其他开发人员的变更合并。这样,本地开发人员就使得变更集对上线(upline)开发人员可用(参见图 2)。
图 2. 在分散体系结构中,开发人员通过其各自的存储库员同步工作
分散体系结构很有意思,因为它允许不同的开发人员在对等网络内同步工作。当工作准备好(最好是稳定)后,开发人员可以分发变更集(或修补)以使他人也可以使用这些特性。这是当今许多开源系统的模型,包括 Linux® 内核。
变更集模型与快照模型
早先的和现代的 SCM 的体系结构的另一个有趣的区别是 delta 变更的存储方式。它们理论上是相同的,且产生的结果也相同,但在修订的存储方式上二者却大不相同。
在快照模型中,整个存储库的每个修订(带优化以减少树的大小)的全部文件都被存储。而在变更集模型中,只有 delta 被存储于修订之间,这样就创建了一个精简的存储库(参见图 3)。
图 3. 快照模型和变更集模型各有所长
从图 3 可以看出,模型不同但结果却相同。在快照模型中,可以快速获得修订,但需要更多的空间来存储它们。而变更集模型需要的空间少,但需要更多时间来获得某个修订,原因是 delta 必须应用于基本修订。在本文后面的部分将介绍如何建立优化来最小化必须要应用的 delta 的数量。
示例 SCM
让我们来看一下按体系结构(集中或分布)划分的一些 SCM。您很快就会看到,其中一些 SCM 支持两种模型。
CVS
并发版本系统(Concurrent Versions System,CVS)是当今最为常用的 SCM。它是一种集中式的解决方案,采用了快照模型,开发人员使用集中式的存储库来进行软件的协作开发。CVS 十分常见而且是 Linux 发布版的一部分。其简单和方便的语法(对大多数人而言)使其迅速成为了多个或单个开发人员 SCM 的一种理想选择。
清单 1 给出了一组 CVS 命令示例和简短的相关描述。要获得关于 CVS 的更多信息,请参阅 参考资料 部分。
清单 1. CVS 命令示例
# Create a new repository
cvs -d /home/user/new_repository init
# Connect to the central repository
export CVSROOT=:pserver:user@example.com:/cvs_root
# Check out a sandbox for module project from the central repository
cvs checkout project
# Update a local sandbox from the central repository
cvs update
# Check in changes from the local sandbox to the central repository
cvs commit
# Add new files to the local sandbox (need to be committed)
cvs add <file/subdirectory>
# Show changes made in the local sandbox
cvs diff
|
对于喜欢用指向-单击方式操作的用户,CVS 提供了许多开源的图形化前端以供使用,包括 WinCVS 和 TortoiseCVS(集成了 Microsoft® Windows Explorer)。
尽管 CVS 被广泛采用,但它仍然有一些不足之处。CVS 不允许重命名文件,而且对一些特殊文件(如 symlink)的支持不是很好。变更可以按文件跟踪,而非按变更本身,这点十分讨厌。合并有时也会出现问题(CVS 内部会为此使用 diff3)。
然而,CVS 总体而言还是很有用的,能够实现其需要实现的任务,而且所有主流的平台都支持 CVS。如果喜欢用 CVS 而又想回避其缺点,则可以考虑使用 Subversion。
Subversion
Subversion(SVN)是为了替代 CVS 而设计的,消除了 CVS 原有的问题和缺点。与 CVS 一样,Subversion 也是一种集中化的解决方案,采用的是快照模型。它的命令也模仿了 CVS 的,但增加了一些对诸如删除文件、重命名文件或恢复为原始文件等的处理功能。
Subversion 还允许远程访问,可以采用很多协议,比如超文本传输协议 (HTTP)、安全 HTTP或定制的 SVN 协议,此协议还支持通过 Secure Shell (SSH)的隧道技术。
清单 2 给出了 Subversion 所支持的一些命令及 CVS 中所没有的一些扩展。要获得关于 Subversion 的更多信息,请参阅 参考资料 部分。很明显,Subversion 的命令集和 CVS 的很相似,这使它极受 CVS 用户的欢迎。
清单 2. Subversion 的命令示例
# Create a new repository
svnadmin create /home/user/new_repository
# Check out a sandbox from the central repository
svn checkout file:///server/svn/existing_repository new_repository
# Update a local sandbox from the central repository
svn update
# Check in changes from the local sandbox to the central repository
svn commit
# Add new files to the local sandbox (need to be committed)
svn add <file/subdirectory>
# Show changes made in the local sandbox
svn diff
# Rename a file in the local sandbox (requires commit to the repository)
svn rename <old_file> <new_file>
# Remove files (also removed from repository, requires commit)
svn delete <file/subdirectory>
|
与 CVS 一样,Subversion 集成了一些图形化的前端如 ViewCVS 和 TortoiseSVN。另外也有一些工具可以把 CVS 存储库转化为 Subversion(例如 cvs2svn.py),但这些工具并不能处理复杂版本的分支和标记。和许多开源项目一样,随着时间的推移,这种情况会有所改变。Subversion 还集成了 TortoiseMerge 作为一个单独的查看器和修补程序。
Subversion 解决了许多让 CVS 用户困扰的问题,比如特殊文件的版本化和自动的提交和签出。如果您喜欢用 CVS 和集中式存储库的方式,那么 Subversion 就是您所需的 SCM。
现在让我们先放下对集中方式的讨论,看看被人们视为 SCM 未来的协作式分散存储库。
Arch
Arch 是分散 SCM 的规范,它提供许多不同的实现,包括 ArX、Bazaar、GNU arch 和 Larch。Arch 不仅可以作为一种分散 SCM 运作,如 图 2 所示,还可使用变更集模型(参见 图 3)。Arch SCM 是开源开发的一种很流行的方法,因为开发人员可在单独的存储库上进行开发,同时又能够控制全部资源。这是因为分布式存储库就是具有修订控制的实际存储库。可以在本地存储库利用变更创建一个修补来供上游的开发人员使用。这就是分散模型的真正的强势所在。
与 Subversion 相似,Arch 解决了 CVS 中所存在的许多问题,包括元数据的变更,例如修订文件许可、处理文件删除和重命名以及自动的签入(将签入组在一起而非作为单独的文件)。
清单 3 中显示了 Arch SCM 中的一些常见命令,还给出了 GNU arch,因为它是由 Arch 的设计者 Tom Lord 开发的。GNU arch 提供了 SCM 应有的一些基本功能,包括 Subversion 中的一些新特性。
清单 3. GNU arch (tla) 的命令示例
# Register a public archive
tla register-archive http://www.mtjones.com/arch
# Check out a local repository from the upstream repository
tla get project@mtjones.com--dev/project--stable myproject
# Update from the local repository
tla update
# Check in changes to the local repository
tla commit
# Add new files to the local repository (need to be committed)
tla add <file>
# Show changes made in the local repository (patch format)
tla what-changed
# Rename a file in the local repository (requires commit to the repository)
tla mv <old_file> <new_file>
# Remove files (also removed from repository, requires commit)
tla rm <file>
|
Arch 还允许用 star-merge
合并来自上游存储库的变更。为了最小化必须应用到基本修订的修补的数量(根据变更集模型), cacherev
命令将会在存储库中创建基本修订的一个新的快照。
创建基本修订的一个新快照
Arch 的优势之一是虽然它是为分散操作所设计的,但也可以用于集中存储库范例。
tla
用户对其不满之处是它太过复杂。其他的 Arch 实现,例如 baz
则非常简单。如果 tla
不能满足要求,可以尝试使用后者。
现在让我们来看一下最后一个分散式的 SCM,它由 Linux 内核的维护者 Linus Torvalds 本人编写。
Git
Git SCM 由 Linus Torvalds 开发,用来替代 Bitkeeper SCM(参阅 参考资料 部分)。 它非常简单,是一种分散式的基于变更集的 SCM 且用作 Linux 内核的 SCM。它使用文件组模型而非跟踪单个的文件。使用 SHA1 压缩并混编变更集以验证其完整性(参见清单 4)。
清单 4. Git 的命令示例# Get a Git repository (first time)
git clone \
rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
linux-2.6
rsync -a \
--progress rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git/ \
.git/
# Update a Git repository from the defined upstream Git repository
git pull rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
# Checkout from the Git repository into the local working repository
git checkout
# Update from the local Git repositor3
git-update-cache
# Commit changes to the local Git repository
git commit
# Add new files to the local repository
git-update-cache --add <file>
# Show changes made to the local working directory
git diff
# Remove files (requires commit)
git-update-cache --remove <file> |
Git SCM 在其自己的 Git 存储库内是自托管的,这意味着您必须引导 Git 来将其安装到本机。Git 的命令集与您到目前为止看到的那些命令十分类似,而且相对简单。
您可能会问:“为什么不使用现有的 SCM 中的一种呢?” 问得很好。 Git 非常有趣,吸引着许多 Linux 内核黑客,所以它很可能成为下一个主流的 SCM。Linus 将 Git 描述为一个高速的目录内容管理器,虽然它所做的工作不多但完成得十分高效。
收益
无论您使用何种 SCM,都会获得一些普遍的收益。使用 SCM,可以跟踪文件中的变更来判断变更对软件的影响程度。当发生不正确的变更时,可以发现这些变更并将它们恢复回原始资源中。可以组合文件修订集并标记它们来制作发布,而发布又可以在任何时间签出以供重复构建代码的特定发布(SCM 的要求)。
不管使用集中式存储库还是分布式存储库,使用快照模型还是变更集模型,所带来的收益都是相同的。因为任何现代软件开发项目都必须要有 SCM,所以越早越多地使用它们就会受益越多。
前景展望
本文只讨论了现在最为常用的 SCM 的一些最基础的知识。还有许多其他的开源 SCM 可以使用,例如 Aegis、Bazaar-NG、DARCS 和 Monotone。当然,SCM 也引起了一些争论,而这些争论通常很难有正确的答案。如果使用一种工具后效率极高,那就使用这种工具吧!SCM 大多时候都用在团队开发的情况下,而很少用于孤立的个人开发的环境中(除非您是个独断专行的老板,喜欢自己做所有决定)。所以,您尽可以尝试所有的可能性来熟悉一些不同的风格。SCM 是软件开发中的必需工具,也是您的开发工具箱中的必备部分。
参考资料
学习获得产品和技术讨论