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 支持把 关键字 (keywords )—跟文件有关的一段有用的动态信息—替换成文件 的内容. 关键字提供了与文件最后一次修改有关的信息, 但是每次文件被修改时, 这个信息都会发生变化, 更重要的是, 文件刚被修改后, 除了版本控制系统, 对 任何一个企图保持数据最新的过程都是一场混乱, 如果把工作交给用户, 就很容易 造成信息过时.

比如说用户有一个文档, 他想显示文档最后一次被修改的日期. 他可以要求 文档的每一个作者在他们提交修改之前, 在文档中记录一下本次修改的日期. 但 是很快就会出现, 总有人会忘记记录修改日期. 更好的做法是让 Subversion 去 完成记录时间的操作, 比如说在每次提交时, 把文档中的关键字 LastChangedDate 替换成当时的日期. 通过在文档中放置一个 关键字锚点 (keyword anchor), 用户可以控制关键字的插入位置. 锚点就是一段简单的文本, 格式是 $KeywordName$ .

如果只想单纯地往文件中添加关键字锚点并不会产生什么特别的效果, 除非 用户显式要求 Subversion, 否则的话它决不会执行文本替换操作, 毕竟用户有可 能只是想写一篇介绍如何使用关键字的文档[25], 此时用户当然不希望 Subversion 把 示例中的关键字锚点都替换掉.

为了告诉 Subversion 是否要替换某个文件中的关键字, 我们要再次使用 与属性有关的子命令. 设置在文件上的属性 svn:keywords 决定了文件中的哪些关键词将会被替换, 属性值是空格分隔的关键字名或别名列表.

举个例子, 假设用户一个叫作 weather.txt 的文件, 文件的内容是:

Here is the latest report from the front lines.
$LastChangedDate$
$Rev$
Cumulus clouds are appearing more frequently as summer approaches.

如果文件上没有设置属性 svn:keywords, Subversion 就不会对文件做什么特别的操作. 现在开启关键字 LastChangedDate 的替换.

$ svn propset svn:keywords "Date Author" weather.txt
property 'svn:keywords' set on 'weather.txt'
$

文件 weather.txt 此时含有未被提交的属性修改, 但文件的内容并没有发生变化 (除非用户在设置属性之前又修改了文件). 注意 文件还包含了关键字 Rev 的锚点, 但 svn:keywords 的属性值并没有包含关键字 Rev . 如果文件中没有要被替换的关键字, 或者关键字没有出现在 svn:keywords 的属性值里, Subversion 就不会真正地替换 关键字.

属性修改提交后, Subversion 会立刻更新工作副本里的文件, 将其中的关键 字替换成对应的文本. 关键字锚点将会出现替换后的文本, 替换的结果仍然包含 关键字的名字以及两边的美元符 ($). 因为 svn:keywords 的属性值里没有包含对应的关键字, 所以 Rev 没有被替换.

注意我们把属性 svn:keywords 设置成 Date Author, 而关键字锚点则写成了 $LastChangedDate$, 但仍然得到了正确的结果, 这是因为 LastChangedDateDate 的别名.

Here is the latest report from the front lines.
$LastChangedDate: 2006-07-22 21:42:37 -0700 (Sat, 22 Jul 2006) $
$Rev$
Cumulus clouds are appearing more frequently as summer approaches.

如果其他人向 weather.txt 提交了新的修改, 自己 工作副本里的文件不会自动更新—直到用户显式地更新了工作副本, 此时, weather.txt 的关键字会被重新替换, 以反应最新的修改 时间.

作为锚点出现在文件里的关键字都区分大小写: 用户必须使用大小写正确的 关键字. 同样也要注意属性 svn:keywords 的值也区分大 小写. 为了保持向后兼容, 某几个关键词是不区分大小写的, 但不建议用户使用 这个特性.

Subversion 定义了几个支持替换的关键字, 下面列出这些关键字, 其中一些 关键字拥有别名:

Date

这个关键字描述了仓库中的文件已知的最后一次被修改的时间, 格式类似 于 $Date: 2006-07-22 21:42:37 -0700 (Sat, 22 Jul 2006) $ . 它的别名是 LastChangedDate. 和关 键字 Id 不同 (Id 使用 UTC 时间), Date 会按照本地时区显示日期.

Revision

这个关键字描述了仓库中的文件已知的最后一次被修改的版本号, 显 示格式 类似于 $Revision: 144 $, 它的别名有 LastChangedRevisionRev.

Author

这个关键字描述了仓库中的文件已知的最后一次是被谁修改的, 显示 格式类似 于 $Author: harry $, 它的别名是 LastChangedBy.

HeadURL

这个关键字描述了仓库中的文件的最新版本的完整 URL 路径, 显示格式 类似于 $HeadURL: http://svn.example.com/repos/trunk/calc.c $, 它的别名是 URL.

Id

这个关键字是几个关键字的组合, 它显示的内容类似于 $Id: calc.c 148 2006-07-28 21:30:43Z sally $, 例子的意思是文件 calc.c 最后一次修改是在 2006 年 7 月 28 日, 版本号 148, 作者是 sally. Id 使用 UTC 时间, 而 Date 使用本地时区.

Header

这个关键字和 Id 类似, 但是增加了 HeadURL 的内容, 看起来就像 $Header: http://svn.example.com/repos/trunk/calc.c 148 2006-07-28 21:30:43Z sally $.

在介绍关键字时, (隐式的或显式的) 用到了形容词 已知的, 这是因为关键字替换是一个客户端操作, 客户端只能知道最近一次更新工作副本 时从仓库中获取的信息. 如果工作副本一直得不到更新, 即使仓库中的文件已经 修改了, 工作副本里的关键字也不会被替换成更新的信息.

除了前面几个预定义的关键字, Subversion 1.8 允许用户定义新的关键字. 为了定义一个关键字, 给属性 svn:keywords 的值添加 新的记号, 记号的格式是 MyKeyword= FORMAT, 其中 MyKeyword 是关键字的名字 (关键字锚点需要), FORMAT 是一个格式化的字符串, 替换文件中的关 键字时会根据格式字符进行替换.

格式化字符串支持的格式控制符有以下这些:

%a

%r 指定的版本号的作者.

%b

文件的 URL 的基本名 (basename).

%d

%r 指定的版本号的日期的短格式.

%D

%r 指定的版本号的日期的长格式.

%P

文件相对于仓库根目录的路径.

%r

已知的文件最后一次被修改时的版本号 (和用来替换 Revision 的版本号相同).

%R

仓库根目录的 URL.

%u

文件的 URL.

%_

一个空格符 (定义关键字的字符串中不能包含字面空格).

%%

一个百分号 (%).

%H

等价于 %P%_%r%_%d%_%a.

%I

等价于 %b%_%r%_%d%_%a.

可以看到, 很多单独的格式控制字符所表示的信息与预定义的关键字所表示 的信息相同, 但是自定义关键字允许用户得到更灵活和更丰富的信息. 比如说, 用户希望有一个关键字能被替换成文件在仓库里的相对路径, 以及最后一次修改 文件的版本号, 此时就需要自定义一个关键字:

$ svn pset svn:keywords "PathRev=%P,%_r%r" calc/button.c
property 'svn:keywords' set on 'button.c'
$

接下来用户要把关键字锚点插入到文档的适当位置, 在这个例子里, 关键字 锚点要写成 $PathRev$. 提交修改后, 文件中原来显示 $PathRev$ 的文本, 变成了 $PathRev: trunk/calc/button.c, r23 $.

[注意] 注意

如果关键字替换后的内容超过了 255 个字符, Subversion 会自动截断过 长的部分. 自定义关键字的名字如果超过了 255 个字符, Subversion 会自动 忽略超出的部分.

用户还可以为替换后的字符串指定一个固定的长度. 在关键字名字后面加 两个冒号 (::), 然后是一定个数的空格, 这样就指定了 一个固定长度. 当 Subversion 准备替换关键字时, 如果发现锚点指定了一个固 定长度, Subversion 就只会替换空格部分. 如果替换后的字符串不够长, 不足 的部分就会用空格填充; 如果替换后的字符串不过长, 字符串就会被截断, 并在 截断的地方放置一个 # 字符.

比如说, 你有一个文档, 文档把 Subversion 的关键字按照表格的样式进行 排版, 如果使用原来形式的关键字替换语法, 替换前的文件内容看起来就像:

$Rev$:     Revision of last commit
$Author$:  Author of last commit
$Date$:    Date of last commit

现在看起来表格的格式还挺工整的, 但是提交后 (开启了关键字替换功能), 文件的内容就变成了:

$Rev: 12 $:     Revision of last commit
$Author: harry $:  Author of last commit
$Date: 2006-03-15 02:33:03 -0500 (Wed, 15 Mar 2006) $:    Date of last commit

替换后的效果令人感到失望, 用户可能会忍不住手工地调整每一行没对齐的 文本, 但是实际上只要关键字的值占用相同的宽度, 格式就不会被打乱. 如果版本 号增长到比较长的位数 (例如从 99 增长到 100), 或者有一个名字很长的用户提 交了修改, 文件的版式就得重新调整. 如果用户使用的 Subversion 版本大于等于 1.2, 就可以使用具有固定长度的关键字语法, 为了使用这种关键字语法, 把文件 的内容改成:

$Rev::               $:  Revision of last commit
$Author::            $:  Author of last commit
$Date::              $:  Date of last commit

提交修改, 这次 Subversion 会注意到文件中使用了具有固定长度的关键字语 法, 替换后, 字段的长度保持不变—较短的 RevAuthor 使用空格填充不足的部分, 较长的 Date 被井字符截断:

$Rev:: 13            $:  Revision of last commit
$Author:: harry      $:  Author of last commit
$Date:: 2006-03-15 0#$:  Date of last commit

固定长度的关键字替换在以下场景非常方便: (1) 文件把数据放在长度固定 的字段里; (2) 除了格式的本地应用程序外, 其他程序难以修改某些数据字段 的存放大小. 当然, 如果涉及到二进制文件格式, 用户必须非常小心, 关键字替换 (无论是长度是否固定) 不能破坏格式的完整性. 虽然这听起来很容易, 但是对于 现在流行的大多数二进制格式而言, 实际做起来可能会非常困难, 绝不是稍微用 点心就能对付过去的.

[警告] 警告

注意关键字字段的长度以字节为单位, 在处理多字节字符时可能会出问题. 比如说, 用户名如果包含 UTF-8 字符, 截断可能会发生在组成一个字符的多个 字节之间. 从字节来看这可能只是一个非常普通的截断, 但是在解释成 UTF-8 字符就会产生错误, 很可能会产生乱码. 有些应用程序在打开这种含有错误编 码的文件时会认为整个文件已经损坏, 拒绝对文件进行进一步操作. 所以在限 制关键字的长度时, 注意避免在字符当中发生截断.



[25] …或者是书中 的一节…