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 对版本控制的支持就到此为止, 从版本控制的角度来看它的功能已经很完整了.

但 Subversion 并没有停下脚步.

作为目录和文件版本控制的补充, Subversion 提供了为每一个文件和目录添加, 修 改和删除版本化元数据的接口. 我们把这些元数据称为 属性 (properties), 属性可看作是一张两列的表格, 附加到 工作副本的每个项目上, 表格把属性的名字映射到任意值. 一般来说, 属性的名字 和值可以是任意的, 唯一的要求是属性名只能使用 ASCII 字符. 属性最好的地方 是它们也是被版本控制的对象, 就像文件的内容那样, 用户可以修改, 提交和撤销 属性的修改. 用户执行提交和更新操作时, 属性的修改也会被发送和接收— 用户的工作流程不会因为属性的加入而发生变化.

[注意] 注意

Subversion 保留了一组名字以 svn: 开始的属性, 所以你在创建自己的属性时应该避免使用以 svn: 开始 的名字, 否则的话, 未来 Subversion 的新版本可能会采用同名的属性来 支持新特性, 而属性的含义可能与你创建时的完全不同.

除了文件和目录, 属性还可以出现在其他地方, 每一个版本号都是一个实体, 可以在它上面附加任意的属性, 唯一的要求是属性名只能使用 ASCII 字符. 同文件和目录的属性相比, 最大的不同是版本号的属性不会被版本控制, 也就是 说如果版本号的属性被删除或修改了, Subversion 没有能力把它们恢复到以前 的值.

关于属性的使用, Subversion 并没有很特别的策略, 唯一的要求是用户不 要使用以 svn: 开始的属性名, 这是保留给 Subversion 使用的名字空间, Subversion 使用的属性包括版本化的和未版本化的. 文件和 目录上特定的版本化属性具有特殊的意义或效果, 或提供了版本号的一些信息. 在提交时, 特定的版本号属性被自动地附加到版本号上, 属性包含了与版本号 有关的信息. 大多数属性会在谈到相关的主题时再介绍, Subversion 的预定义 属性的完整列表见 “Subversion 的保留属性”一节.

[注意] 注意

虽然 Subversion 自动地把属性 (svn:date, svn:author, svn:log 等) 附加到 版本号, 但它并 假设在这之后这些属性仍然存在, 用户及其使用的工具也应如此. 版本号的属性可以通过 API 或客户端工具删除 (如果仓库的钩子允许), 删除后并不影响 Subversion 的正常功能. 所以在 编写与 Subversion 仓库交互的脚本时, 不能假定任意版本号的属性是存在的.

本节将介绍 svn—不仅是对 Subversion 用户, 也对 Subversion 自身—对属性的支持. 读者将会学到与属性相关的 svn 子命令, 以及属性如何影响用户的工作检验.

为什么需要属性?

Subversion 使用属性存放和文件, 目录, 版本号相关的额外信息, 读者 可能也会发现属性的类似用法. 你会发现, 如果在数据附近能有个地方保存 自定义元数据将会是一项非常有用的特性.

假设你想要设计一个网站, 其中存放了很多数字照片, 在显示时会给照片 加上标题和日期. 因为你的照片经常发生变化, 所以你希望网站能够尽量地自动 处理由于照片变动而产生的影响. 照片可以很大, 你希望在网站上可以显示照片 的缩略图.

你可以用传统的文件实现缩略图, 也就是说你可以把照片 image123.jpg 及其缩略图 image123-thumbnail.jpg 放在同一个目录里. 如果你 希望照片及其缩略图能使用相同的文件名, 也可以把缩略图放在不同的目录里, 例如 thumbnails/image123.jpg. 你可以按照类似的方 法存放标题和日期. 这里最大的问题是每增加一个新图片, 网站的文件数量都 会成倍地增加.

现在考虑如果利用 Subversion 的文件属性来部署网站. 设想有一个图片 文件 image123.jpg, 带有属性 caption, datestampthumbnail. 使用属性后的工作副本看起来更容量管理 —实际上, 普通的浏览器只能看到图片文件, 但是你的自动化管理脚本 可以知道得更多. 脚本可以使用 svn (更好的做法是用 Subversion 的语言绑定—见 “使用 API”一节) 获取图片的属性信息, 而不必读取索引文件或处理路径.

[注意] 注意

Subversion 对属性的名字和值有一些限制, 如果属性的值很大, 或者在 单个的文件或目录上设置了很多的属性, 对于这两种情况 Subversion 处理 起来非常笨拙. Subversion 通常会把单个项目的所有属性及其值同时加载 到内存中, 如果属性过多, 性能就会受到影响, 甚至导致命令失败.

自定义版本号属性也经常用到, 一种常见的用法是为版本号添加一个包含 问题跟踪 ID 的属性, 表示该版本号修复了这个问题. 其他一些用法还可以 是为版本号附加一个更友好的名字—人们很难记住版本号 1935 是一个 经过充分测试的版本, 但是如果给版本号 1935 添加一个属性 test-results, 属性值是 all passing, 这样一来就方便多了. 用户可以通过 svn commit 的选项 --with-revprop 为新提交的版本号附加属性 test-results:

$ svn commit -m "Fix up the last remaining known regression bug." \
             --with-revprop "test-results=all passing"
Sending        lib/crit_bits.c
Transmitting file data .
Committed revision 912.
$

操作属性

命令 svn 提供了几种用于添加或修改文件和目录 属性的方法. 如果属性的值比较短, 而且是人类可读的, 那么添加新属性的 最简单的方法是在子命令 svn propset 的命令行参 数上指定属性名和值:

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/button.c
property 'copyright' set on 'calc/button.c'
$

Subversion 对于属性值给予了很大的灵活性, 如果属性值包含多行文本, 甚至是二进制格式, 此时用户就不太可能把值写在命令行参数上, 为了解决 这个问题, svn propset 支持选项 --file (-F), 该选项指定了一个包含 属性值的文件的名字.

$ svn propset license -F /path/to/LICENSE calc/button.c
property 'license' set on 'calc/button.c'
$

对属性名有一些限制条件, 属性名必须以字母, 冒号 (:) 或下划线 (_) 开始, 接下来的字符, 除了前面介绍的, 还可 以用数字, 连字符 (-), 句点 (.). [12]

除了 propset, svn 还提供了 子命令 propedit. propedit 使用 预先配置的外部编辑器 (见 “通用配置选项”一节) 来添加或修改属性. 执行 svn propedit 时, 命令在一个临时文件上打开 编辑器, 临时文件的内容是属性的当前值 (如果是添加新属性, 内容就是空的), 然后用户就可以按照自己的需要在编辑器里修改属性值, 修改完成后保存临时 文件, 最后退出编辑器. 退出编辑器后, 如果 Subversion 检测到属性原来的 值被修改了, 它就把修改后的值当作属性的新值. 如果用户没有修改便退出 编辑器, 属性值就保持不变:

$ svn propedit copyright calc/button.c  ### exit the editor without changes
No changes to property 'copyright' on 'calc/button.c'
$

应该注意到, 和 svn 的其他子命令一样, 属性操作 可以同时施加到多个路径上, 这就允许用户用一个命令修改整个文件集合的属性, 例如我们可以这样做:

$ svn propset copyright '(c) 2006 Red-Bean Software' calc/*
property 'copyright' set on 'calc/Makefile'
property 'copyright' set on 'calc/button.c'
property 'copyright' set on 'calc/integer.c'
…
$

如果用户不能方便地获取属性值, 那么属性的添加和删除就没什么大用处, 所以 svn 提供了两个子命令用于显示文件和目录上的 属性名和值. 命令 svn proplist 列出指定路径上的属性 的名字, 一旦知道了属性名, 就可以用命令 svn propget 分别地获取各个属性的值, 它根据指定的属性名和一个路径 (或多个路径) 打印 出属性的值.

$ svn proplist calc/button.c
Properties on 'calc/button.c':
  copyright
  license
$ svn propget copyright calc/button.c
(c) 2006 Red-Bean Software

执行命令 svn proplist 时如果加上选项 --verbose (-v), 命令就会同时列出 所有属性的名字和值.

$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright
    (c) 2006 Red-Bean Software
  license
    ================================================================
    Copyright (c) 2006 Red-Bean Software.  All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions 
    are met:

    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the recipe for Fitz's famous
    red-beans-and-rice.
    …

最后一个与属性相关的子命令是 propdel. 因为 Subversion 允许为属性设置空值, 所以用户不能想当然地认为用 svn propeditsvn propset 把属性值设置成空值, 就能实现完全删除属性的效果, 比如说下面的命令不会产 生用户想要的效果 (用户想要的效果是删除属性 license) :

$ svn propset license "" calc/button.c
property 'license' set on 'calc/button.c'
$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright
    (c) 2006 Red-Bean Software
  license
    
$

为了完全删除属性, 需要使用子命令 propdel, 它的使用语法和其他属性命令类似:

$ svn propdel license calc/button.c
property 'license' deleted from 'calc/button.c'.
$ svn proplist -v calc/button.c
Properties on 'calc/button.c':
  copyright
    (c) 2006 Red-Bean Software
$

还记得那些非版本化的版本号属性吗? 用户也可以用我们刚刚介绍过的 svn 的子命令去修改它们, 只要加上选项 --revprop 和欲修改的版本号. 因为版本号是全局的, 所以 只要用户已经位于欲修改的版本号的工作副本中, 就不需要为命令指定目标路径, 否则的话, 可以在命令行上提供目标路径的 URL 参数. 例如, 用户可能想修改 一个已存在的版本号的提交日志,[13] 如果你的当前工作目录是工作副本的一 部分, 可以不带目标路径地执行命令 svn propset:

$ svn propset svn:log "* button.c: Fix a compiler warning." -r11 --revprop
property 'svn:log' set on repository revision '11'
$

即使用户没有检出仓库的工作副本, 仍然可以通过提供仓库的根 URL 来 修改属性:

$ svn propset svn:log "* button.c: Fix a compiler warning." -r11 --revprop \
              http://svn.example.com/repos/project
property 'svn:log' set on repository revision '11'
$

需要注意的是只有在仓库管理员配置后用户才能修改非版本化属性 (见 “修正提交日志消息”一节). 这是因为如果属性是非 版本化的, 用户一不小心就有可能弄丢信息. 仓库管理员可以采取一定的措施 防止信息丢失, 在默认情况下, 修改非版本化属性是被禁止的.

[提示] 提示

如果可以的话, 用户应该尽量使用 svn propedit, 而不是 svn propset. 虽然这两个命令的执行结果是 一样的, 但是 svn propedit 允许用户看到将要被修改 的属性的当前值, 这可以帮助他们确认自己是否正在按照自己想要的那样 操作. 另外, 在编辑器中修改具有多行文本的属性, 要比在命令行上修改方便 得多.

属性和 Subversion 工作流程

既然读者已经熟悉了所有与属性相关的 svn 子命令, 现在来看属性修改将会如何影响 Subversion 的工作流程. 我们已经说过, 文件和目录的属性是被版本控制的, 就像文件内容那样, 因此 Subversion 也 支持属性的合并—或干净利落地, 或带有冲突.

和文件内容一样, 属性修改一开始只是本地的, 只有用 svn commit 提交后, 属性的修改才会持久化. 属性的修改 也能轻易地撤消—命令 svn revert 可以撤消所有 文件和目录的本地修改, 包括属性修改, 内容修改, 以及其他所有的本地修改. 你也可以用 svn statussvn diff 获取文件和目录的属性状态.

$ svn status calc/button.c
 M      calc/button.c
$ svn diff calc/button.c
Property changes on: calc/button.c
___________________________________________________________________
Added: copyright
## -0,0 +1 ##
+(c) 2006 Red-Bean Software
$

注意, 子命令 statusM 显示 在了第二列, 而不是第一列, 这是因为我们修改的是 calc/button.c 的属性, 而不是内容. 如果我们同时修改 了内容和属性, 我们就会同时在第一列和第二列看到 M (我们在 “查看修改的整体概述”一节 介绍了 svn status).

读者可能已经注意到了 Subversion 的属性差异输出并不是一种标准的 格式, 用户仍然可以用 svn diff 并把它的输出重定向 到补丁文件里, 但 patch 会忽略属性的补丁— patch 的一条规则是忽略所有不能理解的内容, 这就意味 着如果用户用的是 patch, 为了完整地打上 svn diff 生成的补丁, 用户必须手工地打上和属性相关的 修改.

Subversion 1.7 从两个方面改善了这个问题, 首先, 属性的差异输出至少 是机器可读的—这是对 1.7 版之前的属性显示的改进. 然后 Subversion 1.7 引入了新命令 svn patch, 专门用来处理 svn diff 的输出中带有的额外信息, 并把这些信息应用 到工作副本中. 对于属性来说, 使用 Subversion 1.7 及以后版本的 svn diff 生成的补丁, 如果其中包含了属性差异, 那么 svn patch 可以自动地把这些差异应用到工作副本. 关于 svn patch 的更多信息, 见 svn 参考手册—Subversion 命令行客户端svn patch.

[注意] 注意

svn diff 在报告属性的变化时有一个例外, 那就是 特殊的 svn:mergeinfo 属性—该属性用于跟踪合 并信息—的变化会以一种更适合人类阅读的方式呈现出来, 这对于那些 需要阅读合并信息的用户来说特别有帮助. 不过, 补丁程序 (包括 svn patch) 仍然会忽略与 svn:mergeinfo 相关的补丁. 这样做看起来好像是有问题的, 但事实上并非如此, 因为属性 svn:mergeinfosvn merge 单独 进行管理, 关于合并跟踪的更多信息, 见 第 4 章 分支与合并.

继承的属性

Subversion 1.8 引入了继承属性这个概念. 将一个属性设置成可继承的 并没有什么很特别的地方, 实际上, 所有版本化的属性都是可继承的! 1.8 前 的版本化属性和 1.8 后的版本化属性的主要区别是后者支持在一个目标路径 的 父路径 (parents) 上搜索 属性, 即使这些父路径在工作副本里不存在.

有些命令可以显示出一般的属性继承, 首先 svn proplistsvn propget 可以检索 URL 的或工作副本路径的父路径 上的所有属性, 方法是带上选项 --show-inherited-props. 读者可能会觉得这是选项 --recursive 的反面—选项 --recursive 递归到目标的子目录里, 而 --show-inherited-props 是向 看 目标的父目录. 命令 svnlook propgetsvnlook proplist 按照类似的方法使用选项 --show-inherited-props.

举个例子, 在工作副本的根目录递归地调用 propget , 发现子命令的目标路径及其中的一个子目录 site 都设置了属性 svn:auto-props:

$ svn pg svn:auto-props --verbose -R .
Properties on '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Properties on 'site':
  svn:auto-props
    *.html = svn:eol-style=native

如果我们把子目录 site 作为子命令的目标路径, 然后使用选项 --show-inherited-props, 我们将会看到属性 svn:auto-props 存在于目标路径 它的父路径上, 父路径的属性是 被继承的:

$ svn pg svn:auto-props --verbose --show-inherited-props site
Inherited properties on 'site',
from '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Properties on 'site':
  svn:auto-props
    *.html = svn:eol-style=native

在上一个例子里, 工作副本的根目录对应仓库的根目录, 但即使没有这种 对应, 属性也可以从工作副本的外面继承. 现在检出上一个例子的 site 目录, 使它成为工作副本的根目录:

$ svn co http://svn.example.com/repos site-wc
A    site-wc/publish
A    site-wc/publish/ch2.html
A    site-wc/publish/news.html
A    site-wc/publish/ch3.html
A    site-wc/publish/faq.html
A    site-wc/publish/index.html
A    site-wc/publish/ch1.html
 U   site-wc
Checked out revision 19.

$ cd site-wc

当我们在一条工作副本路径上检查继承的属性时将会看到, 一个属性继承 自工作副本里的父目录, 一个属性继承自仓库里的父路径, 该路径在工作副本 的根目录的 上层:

$ svn pg svn:auto-props --verbose --show-inherited-props publish
Inherited properties on 'publish',
from 'http://svn.example.com/repos':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Inherited properties on 'publish',
from '.':
  svn:auto-props
    *.html = svn:eol-style=native
[警告] 警告

用户只能从他拥有读权限的仓库路径上继承属性—见 “内建的认证与授权”一节“授权选项”一节. 如果用户对某条 父路径没有读权限, 看起来的效果就像是父路径上没有设置属性.

前面已经说过, svnlook proplistsvnlook propget 也支持选项 --show-inherited-props, 但它们不是以工作副本路径或 URL 作为目标路径, 而是以仓库的路径作为目标路径:

$ svnlook pg repos svn:auto-props /site/publish --show-inherited-props -v
Inherited properties on '/site/publish',
from '/':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Inherited properties on '/site/publish',
from '/site':
  svn:auto-props
    *.html = svn:eol-style=native

当工作副本被首次检出或者更新时, 从工作副本根目录上层继承而来的属性 会被缓存在工作副本的管理数据库里, 这样的话在查看继承的属性时就不用再 访问仓库了, 同时也允许那些不要求访问仓库的子命令 (例如 svn add ) 在保持 无连接 的同时, 仍然可以访问到从工作 副本之外的路径继承而来的属性. 但同时也意味着在最近一次更新之后, 来自工 作副本根目录上层的继承属性可能已经发生了变化, 使得本地缓存变成过时了的. 所以如果用户要求继承的属性始终是最新的, 最好更新一下工作副本或直接询问 仓库.

到这里读者可能会想 看起来挺有趣的, 但这有什么好处呢? 对于属性继承本身来说是没多大用处, 在 1.8 之前, Subversion 所有的保留属性 svn:* (还可能包括所有的用户自定 义属性) 都只能应用到它们所在的路径上, 至多再加上直接子路径 [14]. Subversion 使用继承属性完成另一些更有趣的事情, 比如说用 属性 svn:auto-props 设置自动属性, 用属性 svn:global-ignores 实现全局的忽略模式— 关于这些特殊属性的更多信息和使用方法, 见 “自动属性设置”一节“忽略未被版本控制的项”一节.

[提示] 提示

目前可继承的属性中起主要作用的是 svn:auto-props svn:global-ignores, 但这并不意味 着故事就此结束. 在 Subversion 未来的版本里, 我们期待继承属性能加 入更多的特性, 例如日志消息模版, 与此同时, 请尽情按照你自己的喜好 去使用继续属性. 用户想要应用到整个仓库的任意一段 版本化元数据 (或仓库中的某个较大的部分) 都可以轻易地存放到仓库的 根目录 (或适当的子目录) 的属性上. 我们相信某些用户和管理员使用 继承属性的方式可能连我们都未必想像得到.

自动属性设置

属性是 Subversion 最强大的特性之一, 它是本章和其他章节介绍的众多 Subversion 特性—文本差异比较, 合并支持, 关键字替换和换行符转换 等—的关键基础. 为了充分发挥属性的作用, 它们必须被设置到正确的 文件和目录上, 不幸的是, 这个步骤在日常工作中常常被人遗忘, 尤其是因为 即使属性设置不当通常也不会造成很明显的错误 (至少和文件添加失败比起 来, 不是很明显). 为了帮助用户更好地使用属性, Subversion 提供了几个 简单但很有用的特性.

每当用户使用 svn addsvn import 向仓库添加文件时, Subversion 自动地在文件上设置一些常见的 属性. 首先, 如果操作系统的文件系统支持可执行权限位并且文件具有可执行 权限, Subversion 就自动在文件上设置 svn:executable 属性 (关于这个属性的更多信息, 见 “文件的可执行性”一节).

然后, Subversion 会试图判断文件的 MIME 类型. 如果用户为 mime-types-files 设置了一个运行时配置参数, Subversion 就会尝试根据文件的后缀名为文件搜索一个对应的 MIME 类型映射, 若找到的话, 它就把文件的 svn:mime-type 属性设置成找到的 MIME 类型. 如果用户没有为 mime-types-files 设置运行时 配置参数, 或者根据后缀名没有找到对应的类型映射, Subversion 就使用启发 式的算法来判断文件的 MIME 类型. 取决于编译时的配置, Subversion 1.7 可以利用文件扫描函数库[15] 检测文件的类型. 如果前面的都失败了, Subversion 就 使用它非常基本的启发式算法来判断文件是否包含非文本数据, 如果是, 就自动 地把文件的 svn:mime-type 属性设置成 application/octet-stream (最一般的 MIME 类型, 表示 这是字节的集合). 当然, 如果 Subversion 的判断不正确, 又或者是用户想把 svn:mime-type 设置成更精确的值 —比如 image/png application/x-shockwave-flash—可以自由地修改或删除 属性 svn:mime-type (关于 Subversion 如何使用 MIME 类型的更多信息, 见本章后面的 “文件内容类型”一节).

[注意] 注意

有很多文件使用的是 UTF-16 编码, 虽然在语义上文件的内容是纯文本 的, 但是 UTF-16 使用的字节在 ASCII 字符的范围之外, 因此 Subversion 更倾向于把它们归类为二进制文件, 用户在给这些文件进行差异比较, 合并 和关键字替换时也会因此遇到一些小麻烦.

借助运行时配置系统 (见 “运行时配置区域”一节), Subversion 提供了一种更加灵活的自动属性设置功能, 它允许用户创建文件名 模式到属性名和值的映射. 再说一次, 这些映射会影响 svn addsvn import, 除了会 覆盖由 Subversion 判断出的默认 MIME 类型, 还可能添加额外的属性或自定义 属性. 例如, 用户想创建一个映射, 这个映射是说每次添加一个 JPEG 文件时 —文件的名字符合模式 *.jpg—Subversion 都应该自动地把这个文件的 svn:mime-type 属性设置为 image/jpeg. 又或者说匹配模式 *.cpp 的文件都应该把 svn:eol-style 设置成 native , 把 svn:keywords 设置成 Id . 关于运行时配置如何支持自动属性的更多细节, 见 “通用配置选项”一节.

虽然借助运行时配置系统来支持自动属性设置非常方便, 但 Subversion 管理员可能更希望看到这样一种情况: 当客户端在一个从特定服务器检出的 工作副本上工作时, 可以自动地顾及到某些属性定义. Subversion 1.8 及其 之后的客户端版本通过可继承属性 svn:auto-props 实现这个功能.

属性 svn:auto-props 可以像运行时配置系统那样, 自动地为新增的文件设置属性, 属性 svn:auto-props 的值应该和运行时配置选项 auto-props 的值相同 (也 就是任意数量的键值对, 格式是 FILE_PATTERN = PROPNAME=VALUE[;PROPNAME=VALUE ...]). 和运行时选项 auto-props 一样, 如果使用了选项 --no-auto-props, 属性 svn:auto-props 就会被忽略, 但是有所不同的是, 即使配置选项 enable-auto-props 被设置为 no, 属性 svn:auto-props 也不会被禁止.

举例来说, 你检出了主干的工作副本, 想在其中添加一个新文件 (假设 运行时配置系统禁止了自动属性):

$ svn st
?       calc/data.c

$ svn add calc/data.c
A         calc/data.c

$ svn proplist -v calc/data.c
Properties on 'calc/data.c':
  svn:eol-style
    native

可以看到, 当 data.c 被版本控制后, 文件自动 设置了属性 svn:eol-style. 因为运行时配置选项 auto-props 是禁止了的, 所以属性 svn:auto-props 肯定来自 data.c 的 父路径. 执行带上选项 --show-inherited-props 的命令 svn propget 可以看到, 事实的确是如我们所想的那样:

$ svn propget svn:auto-props --show-inherited-props -v calc
Inherited properties on 'calc',
from 'http://svn.example.com/repos':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

属性 svn:global-ignores 及其对应的运行时配置 选项 global-ignores 是一起起作用, 但属性 svn:auto-props 和运行时选项 auto-props 的关系就不这样, 如果运行时选项 auto-props 在一个模式上设置了一个自动属性, 而 属性 svn:auto-props 也在 同一个 模式上设置了自动属性, 那么属性的设置就会覆盖运行时配置选项的设置. 从一个路径继承而来的自动属性 [16]也只会覆盖从其他路径继承的 同一个 模式. 覆盖的先后顺序是:

  • svn:auto-props 上定义的, 针对某一模式的 自动属性会覆盖运行时配置选项 auto-props 上设置 的同一模式的自动属性.

  • 对于一个给定的模式而言, 如果它的自动属性继承自多个父路径的 svn:auto-props 属性, 那么在路径上最近的父路径 的自动属性会覆盖其中父路径.

  • 对一个给定的模式而言, 如果在路径的 svn:auto-props 属性上显式地设置了一个自动属性, 那它就会覆盖从其他路径 继承而来的相同模式上的自动属性.

举例来说, 假设你有一个如下所示的运行时配置:

[miscellany]
enable-auto-props = yes
[auto-props]
*.py  = svn:eol-style=CR
*.c   = svn:eol-style=CR
*.h   = svn:eol-style=CR
*.cpp = svn:eol-style=CR

你想添加 calc 目录中的三个文件:

$ svn st
?       calc/data-binding.cpp
?       calc/data.c
?       calc/editor.py

先看一下 calcsvn:auto-props 属性:

$ svn propget svn:auto-props -v --show-inherited-props calc
Inherited properties on 'calc',
from 'http://svn.example.com/repos':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:eol-style=native
    *.h = svn:eol-style=native

Inherited properties on 'calc',
from '.':
  svn:auto-props
    *.py = svn:eol-style=native
    *.c = svn:keywords=Author Date Id Rev URL

添加这三个文件, 然后检查它们的自动属性:

$ svn add calc --force
A         calc/data-binding.cpp
A         calc/data.c
A         calc/editor.py

文件 data-binding.cpp 只有一个匹配的模式, 也就是运行时配置选项里的 *.cpp = svn:eol-style=CR, 显然文件的属性 svn:eol-style 被设置为 CR :

$ svn proplist -v calc/data-binding.cpp
Properties on 'calc/data-binding.cpp':
  svn:eol-style
    CR

文件 editor.py 既匹配运行时配置选项里的一 条模式, 也匹配属性 svn:auto-props 里的模式, 根据前 面介绍的覆盖顺序, 显式设置在 calc 上的属性值 (*.py = svn:eol-style=native) 的优先级较高, 所以 属性 svn:eol-style 被设置为 native :

$ svn proplist -v calc/editor.py
Properties on 'calc/editor.py':
  svn:eol-style
    native

文件 data.c 同时匹配运行时配置选项和继承属性 svn:auto-props 的模式. 自动属性 svn:keywords 只被定义了一次, 在 calc 上定义, 所以 data.c 自动获取了该属性. calc 上的 svn:auto-props 没有 为 svn:eol-style 定义值, 所以最近的父路径 http://svn.example.com/repos 提供了这个值:

$ svn proplist -v calc/data.c
Properties on 'calc/data.c':
  svn:eol-style
    native
  svn:keywords
    Author Date Id Rev URL
[警告] 警告

自动属性的覆盖只发生在 相同的 模式上, 如果 新增的文件同时匹配多个模式, 那就无法确定最终应用的是哪一个自动属性. 比如说用户想把文件 foo.cpp 添加到目录 bar, 而 bar 的属性 svn:auto-props 的值是:

*.c*  = svn:eol-style=native
*.cpp = svn:eol-style=native;svn:keywords=Author Date Id Rev URL

因为 foo.cpp 同时匹配两个不同的模式, 所以 我们没办法事先确定属性 svn:keywords 是否被设置到 foo.cpp 上.

svn:auto-props 最后一个需要注意的地方是它 (以 及类似的 svn:global-ignores, 见 “忽略未被版本控制的项”一节) 只是向理解属性 的客户端工具提供了一个建议, 较老的客户端会忽略这些属性, 选项 --no-auto-props 会忽略它们, 用户可能会选择手动地修改 或删除自动属性—有很多方法可以旁路掉包含在 svn:auto-props 里的推荐属性. 因此, 管理员仍然需要使用钩子脚本验证文件和 目录上的属性是否符合管理员的策略, 并拒绝与策略不兼容的提交 (钩子脚本见 “忽略未被版本控制的项”一节).

Subversion 的保留属性

本节将对 Subversion 所有的保留属性做一个简单的总结, 包括版本化的 的属性 (和文件, 目录关联) 与非版本化的属性 (和版本号关联).

版本化的属性

这些是 Subversion 保留给自己用的版本化属性:

svn:auto-props

该属性包含了一系列的自动属性定义, 如果被设置在一个目录上, 那么自动属性定义会应用到目录内的所有文件, 见 “自动属性设置”一节.

svn:executable

如果该属性被设置到一个文件上, 那客户端就会给 Unix 工作副本里 的文件设置上可执行权限, 见 “文件的可执行性”一节.

svn:mime-type

如果属性出现在一个文件上, 那么属性值指出了文件的 MIME 类型, 当更新时, 属性可以帮助客户端判断是否可以安全地对文件进行基于行 的合并操作. 另外, 当用户通过网页浏览器获取文件时, 该属性还会影 响文件的具体行为. 更多的信息参考 “文件内容类型”一节.

svn:ignore

如果该属性出现在一个目录上, 属性值是一个未被版本化的文件 模式列表, 符合模式的文件会被 svn status 和 其他子命令忽略, 见 “忽略未被版本控制的项”一节.

svn:global-ignores

如果该属性出现在一个目录上, 属性值是一个未被版本化的文件 模式列表, 符合模式的文件会被 svn status 和 其他子命令忽略, 但是和 svn:ignore 不同的是, 这些模式会应用到目录内 所有的 子目录及其 子文件, 而不仅仅是目录的直接子文件, 见 “忽略未被版本控制的项”一节.

svn:keywords

如果该属性出现在一个文件上, 属性的值指出了客户端应该如何 扩展文件内的特定关键字, 见 “关键字替换”一节.

svn:eol-style

如果该属性出现在一个文件上, 则属性的值指出了客户端应该如何 处理工作副本和导出目录里的文件的行终止符, 见 “行结束标记”一节svn export.

svn:externals

如果该属性出现在一个目录上, 则属性的值是一个包含了多个路径 和 URL 的列表, 这些路径和 URL 都是客户端需要检出的内容, 见 “外部定义”一节.

svn:special

如果该属性出现在一个文件上, 则表示该文件不是一个普通的文件, 可能是一个符号链接或其他特殊的对象[17]

svn:needs-lock

如果该属性出现在一个文件上, 客户端就会把工作副本里的这个 文件设置成只读, 也就是提醒用户在编辑文件之前需要加锁, 见 “锁通信”一节.

svn:mergeinfo

Subversion 使用该属性跟踪合并信息, 更多的细节见 “合并信息和预览”一节, 除非你 真得 知道自己在做什么, 否则不要 编辑该属性.

未版本化的属性

下面是保留给 Subversion 使用的未版本化的 (或版本号) 属性, 它们中 的大部分都会出现在仓库的每个版本号上, 属性携带了关于修改的起因与 本质.

svn:author

如果设置了该属性, 则属性包含了创建此版本号用户名, 如果没有该属性, 那么版本号是匿名提交的.

svn:autoversioned

如果设置了该属性, 则说明版本号是通过自动版本化特性创建的, 见 “自动版本控制”一节.

svn:date

包含了版本号创建时的 UTC 时间, 使用 ISO 8601 格式, 时间来自 服务器 的机器时钟, 而不是客户 端的时钟.

svn:log

包含了描述版本号的日志消息.

Subversion 的某些辅助工具—svnrdumpsvnsync—也会使用未版本化的属性完成记帐工作, 这些属性只会出现在仓库的版本号 0 上, 关于 svnrdumpsvnsync 的更多信息, 见 第 5 章 仓库管理. 下面是由 svnrdumpsvnsync 创建并管理的属性.

svn:rdump-lock

svnrdump load 访问仓库临时施加互斥 性, 通常只有在 svnrdump load 活动时— 或者在 svnrdump 不能干净地与仓库断开连接时 —该属性才会被观察到 (只有当这个属性出现在版本号 0 上时, 它才是有意义的).

svn:sync-currently-copying

包含了源仓库中已经被 svnsync 镜像备份 的版本号 (只有当这个属性出现在版本号 0 上时, 它才是有意义的).

svn:sync-from-uuid

包含了由 svnsync 创建的镜像的源仓库的 UUID (只有当这个属性出现在版本号 0 上时, 它才是有意义的).

svn:sync-from-url

包含了由 svnsync 创建的镜像的源仓库目录 的 URL (只有当这个属性出现在版本号 0 上时, 它才是有意义的).

svn:sync-last-merged-rev

包含了最近一次被成功地镜像备份的源仓库的版本号 (只有当这 个属性出现在版本号 0 上时, 它才是有意义的).

svn:sync-lock

svnsync 的镜像操作临时添加仓库访问 的互斥性, 通常只有在 svnsync 活动时— 或者在 svnsync 不能干净地与仓库断开连接时, 只有当这个属性出现在版本号 0 上时, 它才是有意义的).



[12] 如果读者熟悉 XML, 就会发现这很像 XML Name 语法的 ASCII 子集.

[13] 修改提交日志的拼写错误, 语法问题和其他的一般性错误可能是 --revprop 最常见 的应用场景.

[14] 有一个例外是 svn:mergeinfo 属性, 它是可继承的—见 “合并信息和预览”一节

[15] 当前比较常用的函数库是 libmagic

[16] 用户只能从他拥有读权限 路径上继承属性, 所以说如果管理员在较高层的父路径上 (例如仓库的 根目录) 设置了属性 svn:auto-props, 他就应该 确保所有用户都能读取该路径或者期望的自动属性设置不会失效.

[17] 在写到这里时, 符号链接是已知的唯一一个 特殊 对象, 在以后 的版本里可能会出现更多种类的特殊对象.