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 的一个 特性, 那就是对操作系统来说工作副本就是一个普通的目录, 与未被版本化的 目录相比并没有本质上的区别. 不过工作副本里的未被版本化的文件和目录会给 用户产生一定的困扰. 比如说, 命令 svn addsvn import 默认会递归地执行, 命令并不知道目录中的哪些 文件是用户想要的, 哪些是不想要的. 命令 svn status 默认报告工作副本里的每一个项目的状态—包括未被版本控制的文件与目 录—如果未被版本控制的项目很多, 命令的输出就比较扰人.

于是, Subversion 提供了几种方式告诉 Subversion 哪些文件是可以忽略的. 其中一种要用到 Subversion 的运行时配置系统 (见 “运行时配置区域”一节), 会受到配置影响的通常是在特定 计算机上执行的 Subversion 操作, 或计算机上的某些特定用户. 另外两种方式 用到了 Subversion 的目录属性, 与版本化目录的联系更为紧密, 因此它会影响 到版本化目录的所有工作副本. 上面说的两种机制都会用到 文件模式 (file patterns) (用于匹配文件名的字符串, 包含了字面字符与通配符) 来决定应该忽略哪些 文件.

Subversion 运行时配置系统提供了一个选项— global-ignores —选项的值是空白符分隔的文件名模式集. 如果文件的名字 与集合中的某个模式匹配, 那这个文件对 Subversion 来说相当于是不存在的, 命令 svn add, svn importsvn status 就会忽略它. 如果工作副本里有永远不会 被版本控制的文件 (比如 Emacs 的备份文件 *~.*~), 这个特性就会非常有用.

如果被版本控制的目录上设置了属性 svn:ignore, 属性值应该是一个文件名模式列表, 各项之间用换行符分开, Subversion 根据 文件名模式列表判断 相同 目录内的哪些文件是可以忽略 的. 属性 svn:ignore 不会覆盖运行时配置选项 global-ignores 的值, 而是作为一种补充. 与 global-ignores 不同的是, 属性 svn:ignore 里的模式只能作用在该属性所在的目录上, 不会递归作用到子目录上. 属性 svn:ignore 的一个常用目的是告诉 Subversion 去忽略每个 用户的工作副本中可能都会有的文件, 例如编译器的输出文件—对于本书而 言, 就是 HTML, PDF, PostScript 文件, 或其他 DocBook XML 转换过程中产生 的临时文件和输出文件.

Subversion 1.8 提供了一个比 svn:ignore 更强大的 属性—svn:global-ignores. 和 svn:ignore 相同的是, svn:global-ignores 只能设置到目录上, 属性值是文件名模式集合.[20] svn:global-ignores 定义的文件名模式会添加到运行时配置选项 global-ignores 与 属性 svn:ignore 定义的模式上. 与 svn:ignore 不同的是, svn:global-ignores 是可继承的 [21], 它会递归地作用到目录内的 所有 路径上, 而不仅仅是目录的直接子文件.

[注意] 注意

Subversion 的忽略文件名模式只对未被版本控制的文件和目录起作用, 一旦 文件和目录被版本控制了, 忽略文件名模式就不会对它们产生影响. 也就是说, 不要以为某个被版本控制的文件名符合忽略模式, 在你提交时, Subversion 就 会忽略它的修改—Subversion 总是 会注意到所有 被版本控制的对象.

运行时配置选项 global-ignores 里的忽略模式更 倾向于个人化 [22], 并且和工作副本相比, 更贴近用户的个人需求. 所以, 本节的余 下部分主要关注 svn:ignore, svn:global-ignores 及如何使用它们.

假设某个工作副本的 svn status 输出是:

$ svn status calc
 M      calc/button.c
?       calc/calculator
?       calc/data.c
?       calc/debug_log
?       calc/debug_log.1
?       calc/debug_log.2.gz
?       calc/debug_log.3.gz

在上面的例子里, 用户已经修改了 button.c, 但是 工作副本里还有一些未被版本控制的项目: 刚从源代码编译出的 calculator 程序, 一个叫做 data.c 的 源代码文件, 还有几个用于调试的日志文件. 假设用户已经知道编译系统总是会 输出一个目标文件 calculator [23], 而且测试程序总是会留下一些 调试日志文件, 除了用户自己的工作副本, 该项目所有的工作副本都有可能出现 这些文件. 用户非常清楚地知道, 当他执行 svn status 时, 并不想看到这些他不感兴趣的文件, 而且他也相信其他人也对它们不感兴趣. 于是, 用户决定为目录 calc 设置属性 svn:ignore :

$ svn propget svn:ignore calc
calculator
debug_log*
$

属性设置完毕后, 目录 calc 包含了未被提交的本地 修改. 注意看 svn status 的输出发生了什么变化:

$ svn status
 M      calc
 M      calc/button.c
?       calc/data.c

现在, 命令的输出变得干净多了! 编辑器产生的目标文件 calculator 和日志文件仍然留在工作副本里, Subversion 只是不 再提醒用户这些文件的存在. 输出变干净后, 用户就能更容易地关注到更重要的 事情上—例如用户可能忘记把源代码文件 data.c 添加到仓库里.

当然, 减少垃圾信息只是一个选择, 如果用户确实想看到所有的文件, 包括 正常情况下会被忽略的文件, 可以给 svn status 加上选项 --no-ignore:

$ svn status --no-ignore
 M      calc
 M      calc/button.c
I       calc/calculator
?       calc/data.c
I       calc/debug_log
I       calc/debug_log.1
I       calc/debug_log.2.gz
I       calc/debug_log.3.gz
I       calc/wip.1.diff

被隐藏的未被版本控制的项目再度显示出来, 但是在项目的左边加上了字母 I (Ignored). 请等一下, 为什么 wip.1.diff 也有 I? calc 的属性 svn:ignore 里并没有匹配 wip.1.diff 的模式, 那么它为什么会被忽略?[24] 答案是继承的属性 svn:global-ignores. 执行带上选项 --show-inherited-props 的命令 svn propget , 就可以看到属性 svn:global-ignores 被设置 在了工作副本的根目录上, 果然在这个属性里找到了匹配 wip.1.diff 的模式:

$ svn pg svn:global-ignores calc -v --show-inherited-props
Inherited properties on 'calc',
from '.':
  svn:global-ignores
    *.diff
    *.patch

之前提过, svn addsvn import 也会用到忽略模式列表, 这两个操作都会要求 Subversion 开始管理文件与目录. 在递归的添加操作或导入操作中, Subversion 不会要求用户去选择目录 中的哪些文件应该被版本控制, 而是使用忽略模式—包括全局的, 每个目录 与继承的—来决定哪些文件应该被忽略. 同样, 用户也可以用选项 --no-ignore 告诉 Subversion 不会忽略任意一个文件.

[提示] 提示

即使已经设置了属性 svn:ignore svn:global-ignores, 使用 shell 的通配符可能还是会产生一些 问题. 在 Subversion 接收到参数之前, shell 会把通配符扩展成显式的文件 名列表, 所以执行 svn SUBCOMMAND * 相当于执行 svn SUBCOMMAND file1 file2 file3 …. 如果是 svn add, 效果就像是给命令带上选项 --no-ignore, 所以最好使用 svn add --force . 完成文件的批量添加. 显式地指定目标文件确保了当前目录不 会因为已经处于版本控制之下而被忽略, 选项 --force 使得 Subversion 在遍历目录, 添加未被版本控制的文件时, 仍然遵循属性 svn:ignore, svn:global-ignores 和运行时配置选项 global-ignores 的值. 如果你不想 递归地添加所有项目, 别忘了给命令 svn add 加上选项 --depth files.



[20] svn:global-ignores 的各个文件名模式之间可能用空白符 分隔 (就像运行时配置选项 global-ignores), 而不仅 仅是换行符 (属性 svn:ignore 的各个文件名模式之间 只能用换行符分隔).

[21] 当然, 只 有 1.8 或更新版本的 Subversion 客户端才能认识 svn:global-ignores 的意义与可继承性

[22] 抛开个人化不说, 如果用户没有显式地设置 global-ignores, Subversion 就会使用默认值, 见 “通用配置选项”一节

[23] 这不就是构建系统存在的意义吗?

[24] 假设用户的运行时配置选项 global-ignores 里也没有匹配的模式