This text is a work in progress—highly subject to change—and may not accurately describe any released version of the Apache™ Subversion® software. Bookmarking or otherwise referring others to this page is probably not such a smart idea. Please visit http://www.svnbook.com/ for stable versions of this book.

创建与配置仓库

在本章的开头部分 (“仓库部署策略”一节), 我们 看到了几个在创建与配置仓库之前需要确定的几个问题. 现在, 我们终于要真枪 实弹地开始干了! 本节读者将看到如何创建一个 Subversion 仓库, 并对它进行 配置, 使得当特定的事件发生时, 仓库将执行预定的操作.

创建仓库

创建 Subversion 仓库是一件非常简单的工作, 用到的命令是 svnadmin create.

$ # Create a repository
$ svnadmin create /var/svn/repos
$

假设父目录 /var/svn 已存在, 并且管理员对父 目录拥有写权限, 上面的命令在 /var/svn/repos 创建了一个新的仓库, 使用的是默认的后端存储类型 (FSFS). 你还可以利用 选项 --fs-type 显式地指定后端存储类型, 该选项接受 的参数是 fsfsbdb.

$ # Create an FSFS-backed repository
$ svnadmin create --fs-type fsfs /var/svn/repos
$
# Create a legacy Berkeley-DB-backed repository
$ svnadmin create --fs-type bdb /var/svn/repos
$

执行完这个简单的命令, 你就拥有了一个新的 Subversion 仓库. 取决于 用户的访问方式, 管理员可能还需要调整仓库目录的文件系统权限. 与系统 管理有关的基础知识不在本书的讨论范围之内, 所以这方面的内容就当作训练 留给读者.

[提示] 提示

传递给 svnadmin 的路径参数只是一个普通的文件 系统路径, 而不是一个 URL (就像 svn 访问仓库时用 到的 URL 参数). svnadminsvnlook 都是服务器端的工具—它们只在存放着 仓库的主机上使用, 用于查看或修改仓库的某些部分, 实际上它们也无法 跨网络执行任务. Subversion 新手的一个常见错误是试图为这两个工具传 递 URL (即使是 file:// 也不行).

存放在仓库子目录 db/ 内的就是版本化文件系统的 实现. 新仓库的版本化文件系统的生命开始于版本号 0, 根据定义版本号 0 不 包含任何数据, 只有最顶层的根目录. 初始时, 版本号 0 也有一个版本号属性, svn:date, 属性的值是创建仓库的日期.

仓库既然创建好了, 接下来就可以对它进行改造.

[警告] 警告

虽然仓库的一部分—例如配置文件和钩子脚本—需要手工 查看和修改, 但管理员不应该 (也不需要) 手工修改仓库的其他部分. svnadmin 工具应该足以应付仓库的任意修改, 或者管理 员也可以用第三方工具对仓库进行调整. 不要 为了篡改 版本控制历史而手工修改仓库里的数据文件!

实现仓库钩子

钩子 (hook) 是 一个由某些仓库事件触发的程序, 仓库事件包括但不限于新版本号的产生或 是某些非版本化属性被修改了. 有些钩子 (称为 前置钩子) 在仓库的操作执行之前运行, 从而实现提前报告即将发生的事情, 或阻止即将 发生的事情. 还有些钩子 (称为 后置钩子) 在仓库事件完成 之后运行, 可以用来执行一些检查—但不修改—仓库的任务. 每一 个钩子都能得到足够的信息, 包括发生了什么事件, 被修改的仓库, 以及触发 事件的用户.

默认情况下, 子目录 hooks 包含了各种 钩子的模板:

$ ls repos/hooks/
post-commit.tmpl          post-unlock.tmpl  pre-revprop-change.tmpl
post-lock.tmpl            pre-commit.tmpl   pre-unlock.tmpl
post-revprop-change.tmpl  pre-lock.tmpl     start-commit.tmpl
$

Subversion 仓库支持的每个钩子都有一个模板, 通过查看这些模板脚本的 内容, 用户可以看到每个脚本所执行的操作, 以及传递给脚本的参数. 模 板展示了用户可能会用钩子完成哪些操作, 借助 Subversion 工具的配合, 钩子可以完成一些常见的操作. 为了安装一个能实际工作的钩子, 用户只 需要把可执行程序或脚本放到仓库目录的 hooks 子目录里, 按照钩子的要求对文件进行命名后, Subversion 就可以根据文件名 (例如 start-commitpost-commit) 决定在什么时候执行它.

如果存放仓库的主机是 Unix 操作系统, 这就意味着用户必须提供一个脚 本或程序 (可以是 shell 脚本, Python 程序, 已编译的二进制程序, 或其他 形式的可执行文件), 文件的名字必须严格按照钩子的规定进行命名. 当然, 子目录 hooks 里的模板不仅仅是为了向用户展示钩子的 典型内容—在 Unix 系统中, 安装钩子最简单的方法是复制一个适当的 模板文件, 复制出的新文件要删掉扩展名 .tmpl, 然后 对脚本的内容进行必要的修改, 并确保脚本具有可执行权限. 然而, Windows 操作系统是根据文件的扩展名判断文件是否是可执行的, 因此管理员所提供的 钩子, 其文件名 (不包括扩展名部分) 是钩子的名字, 而扩展名是 Windows 规定的可作为可执行程序的几种扩展名之一, 例如二进制可执行文件用 .exe, 批处理文件用 .bat.

Subversion 执行钩子时的用户身份, 和访问仓库的进程的所有者的身份 是相同的, 在大多数情况下, 仓库经由一个 Subversion 服务器进程进行访问, 因此执行钩子的用户身份和 Subversion 服务器进程的所有者是一致的. 为 了能让这个用户执行钩子, 操作系统必须允许该用户执行钩子脚本, 这还意味 着将被钩子直接或间接访问到的程序或文件 (包括 Subversion 仓库里的文件), 都是以该用户的身份进行访问. 总之, 要注意与权限有关的问题可能会导致 钩子无法正常工作.

Subversion 目前支持多种钩子, 详细的信息见 Subversion 仓库钩子参考手册. 作为一个仓库管理员, 你需要决定仓库 应该实现哪些钩子 (通过提供具有适当名字与权限的钩子文件), 以及如何实现. 在做这个决定时, 始终在心里面记着仓库的部署方式, 比如说如果你是通过服务 器配置来决定哪些用户可以向仓库提交修改, 那就没必要再在钩子里实现相同的 访问权限控制.

钩子脚本环境配置

默认情况下, Subversion 在空环境中执行钩子脚本, 空环境指的是没有 设置任何环境变量的运行环境, 甚至连 $PATH (在 Windows 系统中则是 %PATH%) 都没有设置. 正是因为 这个原因, 很多管理员都曾遇到过这种问题: 自己手工执行钩子程序是没问题 的, 但由 Subversion 执行时却无法正常工作. 传统的解决办法是在钩子脚本 中手工设置钩子所需的所有环境变量.

Subversion 1.8 为钩子脚本的运行环境管理引入了一种新方法— 钩子脚本环境配置文件. 如果 Subversion 服务器进程在仓库的子目录 conf/ 内找到了一个名为 hooks-env 的文件, 它就把该文件当成 INI 格式的 配置文件进行解析, 将解析到的选项名和值作为环境变量, 添加到钩子脚本 的运行环境中.

hooks-env 的语法非常简单直观: 每一节的名字 就是钩子脚本的名字 (例如 [pre-commit][post-revprop-change]), 节内的配置项被看成是环境 变量的名字到值的映射. 另外还有一个特殊的 [default] 节, 它所配置的环境变量对所有的钩子脚本都起作用 (除非又被各节自己的 设置显式地覆盖了). hooks-env 配置文件的例子 如 例 5.1 “hooks-env (配置钩子脚本环境)” 所示.

例 5.1. hooks-env (配置钩子脚本环境)

# All scripts should use a UTF-8 locale and have our hook script
# utilities directory on the search path.

[default]
LANG = en_US.UTF-8
PATH = /usr/local/svn/tools:/usr/bin


# The post-commit and post-revprop-change scripts want to run
# programs from our custom synctools replication software suite, too.

[post-commit]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s

[post-revprop-change]
PATH = /usr/local/synctools-1.1/bin:%(PATH)s

[注意] 注意

例 5.1 “hooks-env (配置钩子脚本环境)” 还展示了 Subversion 配置文件解析器灵活的字符串替换语法. 在这个 例子里, 选项 PATH 的值—拉取自文件的 [default] 部分—会替换掉其他地方的占位 符文本 %(PATH)s. 关于这种语法的更多信息, 见 Subversion 运行时配置目录内的 README.txt 文件. (关于运行时配置目录的更多信息, 见 “运行时配置区域”一节.)

当然, 在每一个仓库的 conf/ 目录内都放置 一份完全一样的钩子环境配置文件, 做起来可能会有点笨拙, 特别是当它 们都需要修改时. 为此, Subversion 允许管理员为配置信息指定一个可 选的位置 (可能是共享的).

钩子脚本的常见用法

仓库的钩子脚本可以提供非常多的用处, 但大多数都可以简单地归类成 以下几种: 通知, 验证和重做.

通知脚本用于通知用户发生了某些事情. 最常见的用法是把每次提交的 修改以邮件的形式发送给项目的每一位成员, 用到的钩子是 post-commit 和 (或) post-revprop-change. 除此之外还有很多其他形式的 通知, 例如整合了问题跟踪的脚本, 或者是充当 IRC 机器人的脚本, 当仓库 发生变化时向大家通报.

站在验证的角度, 人们经常使用钩子 start-commit 和 pre-commit, 根据不同的标准去禁止或允许提交, 常用的判断标准有提交的作者, 描述 提交的日志消息的格式和 (或) 内容, 甚至是所提交的修改的底层细节. 同样地, 钩子 pre-revprop-change 充当的是版本号属性修改的看门狗, 考虑到版本号属性不属于版本控制的范畴, pre-revprop-change 在保护 版本号属性免受破坏性修改这一点上非常重要.

Subversion 1.5 发布后, 有一类验证用得非常广泛, 那就是验证提交 修改的客户端软件. Subversion 从 1.5 开始支持合并跟踪 (关于合并跟踪 的详细介绍, 见 第 4 章 分支与合并), 管理员需要提供 一种方法, 以便确保一旦仓库的用户开始使用新的合并跟踪特性, 那么他们 所有的 合并都要被跟踪. 为了避免用户向仓库提交 未被跟踪的合并, 管理员使用钩子 start-commit 检查客户端公示的特性字 符串, 如果客户端没有宣称支持合并跟踪, 那么提交就会被拒绝, 从而迫使 用户升级他们的客户端软件! 例 5.2 “要求客户端必须支持合并跟踪的钩子 start-commit” 展示了如何用钩子 start-commit 实现这个功能.

例 5.2. 要求客户端必须支持合并跟踪的钩子 start-commit

#!/usr/bin/env python
import sys

# sys.argv[3] is a colon-delimited capabilities list
if 'mergeinfo' not in sys.argv[3].split(':'):
  sys.stderr.write("""\
ERROR: Commits to this repository must be made using Subversion
clients which support the merge tracking feature.  Please upgrade
your client to at least Subversion 1.5.0.
""")
  sys.exit(1)

从 Subversion 1.8 开始, 向 Subversion 1.8 服务器提交修改的客户端, 除了提供它自己的特性字符串外, 还会通过 短暂事务属性 (ephemeral transaction properties) 提供关于 它自己的额外信息. 短暂事务属性本质上是版本号属性, 在提交时由客户 端将短暂事务属性设置到提交事务上, 服务器端在事务成为最终的版本号 之前, 删除该属性. 查看短暂事务属性的方法和查看设置在提交事务上的其 他非版本化属性的方法相同, 需要用到的钩子是 start-commit 和 (或) pre-commit.

下面是 Subversion 当前提供并已实现的短暂事务属性:

svn:txn-client-compat-version

携带了客户端支持的 Subversion 函数库版本字符串, 这对于判 断客户端是否支持仓库数据处理所要求的最小特性集非常有用.

svn:txn-user-agent

携带了 用户代理 (user agent) 字符串, 它描述 了发起提交的客户端程序. Subversion 库函数定义了该字符串的起始 部分的内容, 但是使用了 Subversion API 的第三方程序 (例如 GUI 客户端) 可以向字符串附加自定义的信息.

[注意] 注意

大多数客户端是在提交过程的早期阶段传送短暂事务属性, 从而可以 被钩子 start-commit 检查, 但 Subversion 的某些配置会使得这些属性 直到提交过程的后期才会被设置上. 管理员应该考虑同时在钩子 start-commit 和 pre-commit 上执行基于短暂事务属性的检查工作. 利用钩子 start-commit 过滤掉无效的客户端, 如果无法在 start-commit 完成检查工作, 就在 pre-commit 完成.

前面已经说过, 就在事务变成最终的版本号之前, 短暂事务属性将会被 删除, 因此有些管理员希望这些属性上的信息能够永久保留. 我们的建议是 在钩子 pre-commit 里, 把属性上的值复制到新的属性上. 实际上, Subversion 发布的源代码所提供的脚本 persist-ephemeral-txnprops.py (在 tools/hook-scripts/) 做的正是这件事.

钩子的第三种常见用途是重做. 如果管理员只是在做一个简单的备份, 又或者是远程仓库镜像备份, 钩子脚本都能起到非常重要的作用. 关于仓库 备份的更多内容, 见 “仓库备份”一节“仓库复制”一节.

搜索或自己编写钩子脚本

读者应该可以想到, 从 Subversion 社区或其他地方都能找到大量可以 随意使用的钩子程序和脚本. 实际上, Subversion 发布的源代码就提供了 几个适用性很广泛的钩子脚本, 脚本文件放在 tools/hook-scripts/. 然而, 如果读者无法找到 满意的钩子脚本, 就可能需要自己编写. 关于如何使用 Subversion 的公共 API 进行软件开发, 见 第 8 章 嵌入 Subversion.

[警告] 警告

钩子脚本几乎可以做任何事情, 但作者应该对脚本的功能有所控制. 虽然说使用钩子脚本去自动纠正被提交的文件中的错误, 缺陷或违反策略 的做法—是一件很有诱惑力的事情, 但这样做会导致问题. Subversion 在客户端缓存了仓库的部分数据, 如果钩子脚本按照这种方式 修改了提交事务, 那么客户端缓存的数据就成了过时了的, 且难以察觉, 过时的缓存会产生不可预料的后果. 虽然通过钩子脚本添加新的提交事务属 性通常没什么问题, 但除此之外, 一个提交事务的其它信息都应该看成是只 读的. 管理员不应该为了优化事务的载荷而对事务进行修改, 更好的做法是 在钩子 pre-commit 里 验证 事务, 拒绝不满足要 求的事务. 作为回报, 仓库的用户将逐渐养成良好的提交习惯.

FSFS 配置

从 Subversion 1.6 开始, FSFS 提供了几个配置参数, 管理员可利用它 们对仓库的性能和磁盘使用进行调整. 管理员可以在仓库目录的 db/fsfs.conf 里找到所有的配置参数及其说明.