本文件旨在描述 Subversion 1.6.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/,并查阅适合您的 Subversion 版本的文档。

属性

我们已经详细介绍了 Subversion 如何在其存储库中存储和检索文件的各个版本。整个章节都致力于 Subversion 提供的最基本的功能。如果版本控制功能仅限于此,Subversion 从版本控制的角度来看仍然是完整的。

但它并不止于此。

除了对目录和文件进行版本控制外,Subversion 还提供了接口,用于在每个版本化的目录和文件中添加、修改和删除版本化的元数据。我们将此元数据称为 属性,可以将其视为一个双列表,它将属性名称映射到附加到工作副本中每个项目的任意值。一般来说,属性的名称和值可以是您想要的任何内容,但名称必须仅包含 ASCII 字符。最重要的是,这些属性也像文件的文本内容一样进行版本控制。您可以像修改文件内容一样轻松地修改、提交和还原属性更改。属性更改的发送和接收是在您典型的提交和更新操作中完成的,您无需更改基本流程来适应它们。

[Note] 注意

Subversion 保留了以 svn: 开头的属性名称集。虽然现在只有少数这样的属性在使用,但您应该避免为自己的需求创建名称以该前缀开头的自定义属性。否则,您就有可能在 Subversion 的未来版本中增加对由相同名称但可能具有完全不同解释的属性驱动的功能或行为的支持。

属性也在 Subversion 中的其他地方出现。就像文件和目录可能附加了任意的属性名称和值一样,每个修订版本身也可能附加了任意的属性。相同的约束适用 - 人类可读的名称和任何您想要的二进制值。主要区别在于修订版属性没有版本控制。换句话说,如果您更改或删除修订版属性的值,那么在 Subversion 功能的范围内就无法恢复以前的值。

Subversion 对属性的使用没有特定策略。它只要求您不要使用以 svn: 开头的属性名称,因为这是它为自身使用而保留的命名空间。事实上,Subversion 确实使用属性 - 版本化的和非版本化的两种。某些版本化的属性在文件和目录中找到时具有特殊含义或效果,或者它们包含关于发现它们的修订版的特定信息。某些修订版属性由 Subversion 的提交过程自动附加到修订版,它们包含有关修订版的信息。这些属性中的大多数在本章或其他章节中作为与它们相关的更一般主题的一部分被提及。有关 Subversion 预定义属性的完整列表,请参阅 “Subversion 属性”部分.

[Note] 注意

虽然 Subversion 会自动将属性 (svn:datesvn:authorsvn:log 等) 附加到修订版,但它 随后假定这些属性的存在,您或您用来与存储库交互的工具也不应该这样做。修订版属性可以通过编程方式或通过客户端(如果存储库挂钩允许)删除,而不会损害 Subversion 的功能。因此,在编写对 Subversion 存储库数据进行操作的脚本时,不要错误地假设任何特定修订版属性都存在于某个修订版上。

在本节中,我们将研究属性支持的实用性 - 对 Subversion 用户和 Subversion 本身都有用。您将了解与属性相关的 svn 子命令以及属性修改如何影响您的正常 Subversion 工作流程。

为什么要使用属性?

正如 Subversion 使用属性存储有关其包含的文件、目录和修订版的额外信息一样,您也可能会发现属性类似有用。您可能发现将自定义元数据挂在版本化数据的附近会很有用。

假设您想设计一个网站,该网站包含许多数字照片,并以标题和日期戳显示它们。现在,您的照片集一直在变化,因此您希望尽可能自动化该网站。这些照片可能很大,因此,就像这类网站的常见做法一样,您希望为您的网站访客提供更小的缩略图。

现在,您可以使用传统文件获得此功能。也就是说,您可以在目录中并排放置 image123.jpgimage123-thumbnail.jpg。或者,如果您想保持文件名相同,您可以在不同的目录中放置缩略图,例如 thumbnails/image123.jpg。您还可以以类似的方式存储标题和日期戳,同样与原始图像文件分开。但问题是,您的文件集合会随着添加到网站的每张新照片而增加。

现在,考虑使用 Subversion 文件属性以某种方式部署的相同网站。想象一下,有一个单一的图像文件 image123.jpg,在该文件上设置了名为 captiondatestamp 甚至 thumbnail 的属性。现在您的工作副本目录看起来更易于管理 - 事实上,对于随意浏览的人来说,它看起来就像其中只有图像文件。但您的自动化脚本却了解得更多。他们知道他们可以使用 svn(或者更好的是,他们可以使用 Subversion 语言绑定 - 请参阅 “使用 API”部分)来挖掘您的网站需要显示的额外信息,而无需读取索引文件或玩路径操作游戏。

[Note] 注意

虽然 Subversion 对您用于属性的名称和值施加的限制很少,但它并非为在一个给定文件或目录上最佳地承载大型属性值或大型属性集而设计的。Subversion 通常会同时将与单个项目关联的所有属性名称和值保存在内存中,当使用非常大的属性集时,这会导致性能下降或操作失败。

自定义修订版属性也经常使用。一种常见的用途是属性的值包含与修订版关联的问题跟踪器 ID,可能是因为该修订版中所做的更改修复了该 ID 跟踪器问题中记录的错误。其他用途包括在修订版上挂上更友好的名称 - 可能很难记住修订版 1935 是一个经过全面测试的修订版。但如果有一个名为 test-results 的属性在该修订版上,其值为 all passing,那么这是一个有意义的信息。Subversion 允许您通过 svn commit 命令的 --with-revprop 选项轻松完成此操作。

$ 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 命令。该命令使用配置的编辑器程序(请参阅 “Config”部分)来添加或修改属性。当您运行该命令时,svn 在一个临时文件中调用您的编辑器程序,该文件包含属性的当前值(或者为空,如果您要添加新属性)。然后,您只需在编辑器程序中修改该值,直到它代表您希望存储在属性中的新值,保存临时文件,然后退出编辑器程序。如果 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

甚至有一个 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 完全删除属性。 例如,此命令将 产生预期的效果

$ 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(包括存储库的根 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'
$

请注意,修改这些未版本化属性的能力必须由存储库管理员显式添加(参见 名为“提交日志消息更正”的部分)。 这是因为属性没有版本化,因此如果修改不当,就会有丢失信息的风险。 存储库管理员可以设置方法来防止这种损失,默认情况下,禁用对未版本化属性的修改。

[Tip] 提示

用户应尽可能使用 svn propedit 而不是 svn propset。 虽然命令的最终结果相同,但前者将允许他们看到要更改的属性的当前值,这有助于他们验证他们是否确实正在进行他们认为正在进行的更改。 当修改未版本化的修订属性时尤其如此。 此外,在文本编辑器中修改多行属性值比在命令行中要容易得多。

属性和 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
___________________________________________________________________
Name: copyright
   + (c) 2006 Red-Bean Software

$

注意 status 子命令如何在第二列而不是第一列中显示 M。 这是因为我们修改了 calc/button.c 上的属性,但没有修改其文本内容。 如果我们两者都更改了,我们也会在第一列中看到 M。(我们在 名为“查看更改概述”的部分中介绍了 svn status)。

您可能还注意到 Subversion 目前显示属性差异的非标准方式。 您仍然可以使用 svn diff 并将其输出重定向以创建可用的补丁文件。 patch 程序将忽略属性补丁——一般来说,它会忽略它无法理解的任何噪音。 这确实意味着,要完全应用由 svn diff 生成的补丁,任何属性修改都需要手动应用。

自动设置属性

属性是 Subversion 的一项强大功能,它充当本章和其他章节中讨论的许多 Subversion 功能的关键组成部分——文本差异和合并支持、关键字替换、换行符转换等。 但是要充分利用属性,必须将它们设置在正确的文件和目录上。 不幸的是,这一步很容易在日常工作中被遗忘,尤其是在没有设置属性通常不会导致明显的错误(至少与没有将文件添加到版本控制中相比)的情况下。 为了帮助您的属性应用到需要它们的地方,Subversion 提供了一些简单但有用的功能。

无论何时使用 svn addsvn import 命令将文件引入版本控制,Subversion 都会尝试通过自动设置一些常见的文件属性来提供帮助。 首先,在文件系统支持执行权限位的操作系统上,Subversion 将自动在已启用执行位的已添加或导入的新文件上设置 svn:executable 属性。(有关此属性的更多信息,请参见本章后面 名为“文件可执行性”的部分)。

其次,Subversion 会尝试确定文件的 MIME 类型。 如果您已配置了 mime-types-files 运行时配置参数,Subversion 将尝试在该文件中为您的文件的扩展名找到 MIME 类型映射。 如果找到这样的映射,它将把文件的 svn:mime-type 属性设置为它找到的 MIME 类型。 如果没有配置映射文件,或者找不到文件的扩展名的映射,Subversion 将回退到它自己的非常基本的启发式方法来确定文件是否包含非文本内容。 如果是,它会自动将该文件的 svn:mime-type 属性设置为 application/octet-stream(通用 这是一个字节集合 MIME 类型)。 当然,如果 Subversion 猜测错误,或者您希望将 svn:mime-type 属性设置为更精确的值(可能是 image/pngapplication/x-shockwave-flash),您可以随时删除或编辑该属性。(有关 Subversion 使用 MIME 类型的更多信息,请参见本章后面 名为“文件内容类型”的部分)。

[Note] 注意

UTF-16 通常用于编码语义内容本质上是文本的但编码本身大量使用超出典型 ASCII 字符字节范围的字节的文件。 因此,Subversion 往往会将此类文件归类为二进制文件,这令那些希望对这些文件进行基于行的差异和合并、关键字替换和其他操作的用户感到沮丧。

Subversion 还通过其运行时配置系统(参见 名为“运行时配置区域”的部分)提供了一种更灵活的自动属性设置功能,它允许您创建文件名模式到属性名称和值的映射。 这些映射再次影响添加和导入,不仅可以覆盖 Subversion 在这些操作期间做出的默认 MIME 类型决定,还可以设置额外的 Subversion 或自定义属性。 例如,您可能会创建一个映射,它说无论何时您添加 JPEG 文件(其名称与模式 *.jpg 匹配),Subversion 都会自动将这些文件的 svn:mime-type 属性设置为 image/jpeg。 或者,也许任何与 *.cpp 匹配的文件都应该将 svn:eol-style 设置为 native,并将 svn:keywords 设置为 Id。 自动属性支持可能是 Subversion 工具箱中最方便的与属性相关的工具。 有关配置该支持的更多信息,请参见 名为“配置”的部分

[Note] 注意

存储库管理员经常询问是否可以在服务器端配置一组属性定义,所有连接的客户端在操作从该服务器检出的工作副本时会自动考虑这些定义。 不幸的是,Subversion 不提供此功能。 管理员可以使用钩子脚本来验证添加到文件和目录中的属性以及修改后的属性是否符合管理员的首选策略,拒绝以这种方式不符合的提交。(有关钩子脚本的更多信息,请参见 名为“实现存储库钩子”的部分)。 但是,无法事先自动将这些首选项告知 Subversion 客户端。



[12] 如果您熟悉 XML,这几乎是 XML Name 语法的 ASCII 子集。

[13] 修复提交日志消息中的拼写错误、语法错误和 错误 可能是 --revprop 选项最常见的用例。