本文档旨在描述 Apache™ Subversion® 的 1.7.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。
我们已经详细介绍了 Subversion 如何在其存储库中存储和检索文件的不同版本。整个章节都致力于 Subversion 提供的最基本的功能。如果版本控制支持到此为止,Subversion 从版本控制的角度来看仍然是完整的。
但它并没有止步于此。
除了对您的目录和文件进行版本控制外,Subversion 还提供了接口,用于在您每个版本控制的目录和文件上添加、修改和删除版本控制的元数据。我们将此元数据称为 属性,可以将其视为将属性名称映射到附加到工作副本中每个项目的任意值的双列表格。一般来说,属性的名称和值可以是您想要的任何值,但名称必须仅包含 ASCII 字符。这些属性最棒的一点是,它们也像文件的文本内容一样进行版本控制。您可以像修改、提交和恢复文件内容更改一样轻松地修改、提交和恢复属性更改。属性更改的发送和接收作为您典型的提交和更新操作的一部分进行——您不必更改基本流程来适应它们。
![]() |
注意 |
---|---|
Subversion 保留了名称以 |
属性在 Subversion 中的其他地方也出现。就像文件和目录可能具有附加到它们的任意属性名称和值一样,每个修订版作为一个整体也可能具有附加到它的任意属性。相同的约束适用——人类可读的名称和任何您想要的二进制值。主要区别在于修订版属性没有版本控制。换句话说,如果您更改或删除修订版属性的值,在 Subversion 功能范围内,无法恢复以前的值。
Subversion 对属性的使用没有特别的策略。它只要求您不要使用以 svn:
为前缀的属性名称,因为这是它为自身使用而保留的命名空间。实际上,Subversion 确实使用属性——包括版本化和非版本化属性。某些版本化属性在文件和目录中存在时具有特殊的含义或效果,或者它们包含有关其所在修订版的特定信息。某些修订版属性由 Subversion 的提交过程自动附加到修订版,并且它们携带有关修订版的信息。大多数这些属性在本节或其他章节中作为与之相关的更一般主题的一部分被提及。有关 Subversion 预定义属性的完整列表,请参见 名为“Subversion 属性”的部分 在 第 9 章,Subversion 完整参考 中。
![]() |
注意 |
---|---|
虽然 Subversion 会自动将属性( |
在本节中,我们将研究属性支持的效用——对 Subversion 用户和 Subversion 本身都有效用。您将了解与属性相关的 svn 子命令以及属性修改如何影响您的正常 Subversion 工作流程。
正如 Subversion 使用属性来存储有关其包含的文件、目录和修订版的额外信息一样,您也可能会发现属性具有类似的用途。您可能会发现,在您的版本化数据附近有一个地方来挂载有关该数据的自定义元数据会很有用。
假设您希望设计一个网站,该网站包含许多数字照片并以标题和日期戳显示它们。现在,您的照片集在不断变化,因此您希望尽可能多地自动化此网站。这些照片可能非常大,因此与这种类型的网站一样,您希望为您的网站访问者提供更小的缩略图图像。
现在,您可以使用传统文件来实现此功能。也就是说,您可以将 image123.jpg
和 image123-thumbnail.jpg
放在同一个目录中。或者,如果您想保持文件名相同,可以将缩略图放在不同的目录中,例如 thumbnails/image123.jpg
。您也可以以类似的方式存储标题和时间戳,同样与原始图像文件分开。但问题是,随着您在网站上添加的每张新照片,您的文件集合都会增加。
现在考虑将同一个网站部署在一个利用 Subversion 文件属性的方式中。想象一下,您有一个单一的图像文件 image123.jpg
,该文件上设置了名为 caption
、datestamp
甚至 thumbnail
的属性。现在您的工作副本目录看起来更加易于管理——事实上,对于普通浏览器来说,它看起来就像里面只有图像文件一样。但您的自动化脚本却知道得更多。它们知道可以使用 svn(或者更好的是,可以使用 Subversion 语言绑定——参见 名为“使用 API”的部分)来挖掘您的网站显示所需的其他信息,而无需读取索引文件或进行路径操作。
![]() |
注意 |
---|---|
虽然 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' $
对您可以用于属性的名称有一些限制。属性名称必须以字母、冒号 (:
) 或下划线 (_
) 开头;之后,您还可以使用数字、连字符 (-
) 和句点 (.
)。[13]
除了 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 propedit 或 svn 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)。例如,您可能希望替换现有修订版的提交日志消息。[14] 如果您当前的工作目录是存储库工作副本的一部分,您只需运行不带目标路径的 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 子命令,让我们看看属性修改如何影响通常的 Subversion 工作流程。正如我们之前提到的,文件和目录属性是版本化的,就像您的文件内容一样。因此,Subversion 提供了相同的机会将其他人的修改合并到您的修改中,无论是干净地还是有冲突地。
与文件内容一样,您的属性更改是本地修改,只有在您使用 svn commit 将它们提交到仓库时才会永久保存。您的属性更改也可以轻松撤销——svn revert 命令将恢复您的文件和目录到未编辑状态——内容、属性和所有内容。此外,您可以使用 svn status 和 svn 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 $
注意 status 子命令在第二列显示 M
而不是第一列。这是因为我们修改了 calc/button.c
的属性,但没有修改其文本内容。如果我们同时修改了这两者,我们也会在第一列看到 M
。(我们在 名为“查看更改概述”的部分 中介绍了 svn status)。
您可能还注意到 Subversion 当前显示属性差异的非标准方式。您仍然可以使用 svn diff 并将其输出重定向以创建可用的补丁文件。 patch 程序将忽略属性补丁——通常情况下,它会忽略它无法理解的任何噪音。不幸的是,这意味着要完全应用由 svn diff 使用 patch 生成的补丁,任何属性修改都需要手动应用。
Subversion 1.7 通过两种方式改进了这种情况。首先,它对属性差异的非标准显示至少是机器可读的——这比 1.7 之前版本中显示的属性有所改进。但 Subversion 1.7 还引入了 svn patch 子命令,专门用于处理 svn diff 输出可能携带的附加信息,并将这些更改应用于 Subversion 工作副本。与我们的主题特别相关的是,由 Subversion 1.7 或更高版本中的 svn diff 生成的补丁文件中存在的属性差异可以通过 svn patch 命令自动应用于工作副本。有关 svn patch 的更多信息,请参阅 svn patch,位于 第 9 章,Subversion 完整参考 中。
![]() |
注意 |
---|---|
关于属性更改如何由 svn diff 报告,有一个例外:对 Subversion 特殊的 |
属性是 Subversion 的一项强大功能,充当许多 Subversion 功能的关键组件,这些功能在本节和其他章节中讨论——文本差异和合并支持、关键字替换、换行符转换等等。但要充分利用属性,必须在正确的文件和目录上设置它们。不幸的是,这一步在日常工作中很容易被遗忘,尤其是在未设置属性通常不会导致明显的错误的情况下(至少与未将文件添加到版本控制相比)。为了帮助您的属性应用到需要它们的地方,Subversion 提供了几个简单但有用的功能。
每当您使用 svn add 或 svn import 命令将文件引入版本控制时,Subversion 会尝试通过自动设置一些常见的文件属性来提供帮助。首先,在文件系统支持执行权限位的操作系统上,Subversion 会自动在启用了执行位的已添加或导入的新文件上设置 svn:executable
属性。(有关此属性的更多信息,请参见本章后面的 “文件可执行性”部分。)
其次,Subversion 会尝试确定文件的 MIME 类型。如果您已配置 mime-types-files
运行时配置参数,Subversion 会尝试在该文件中为您的文件的扩展名查找 MIME 类型映射。如果找到这样的映射,它会将您的文件的 svn:mime-type
属性设置为它找到的 MIME 类型。如果没有配置映射文件,或者找不到您文件的扩展名的映射,Subversion 将退回到启发式算法来确定文件的 MIME 类型。根据其构建方式,Subversion 1.7 可以利用文件扫描库[15] 来根据文件内容检测文件的类型。如果所有其他方法都失败,Subversion 将使用其自己的非常基本的启发式方法来确定文件是否包含非文本内容。如果是,它会自动将该文件的 svn:mime-type
属性设置为 application/octet-stream
(通用 “这是一个字节集合” MIME 类型)。当然,如果 Subversion 猜测错误,或者您希望将 svn:mime-type
属性设置为更精确的值——也许是 image/png
或 application/x-shockwave-flash
——您始终可以删除或编辑该属性。(有关 Subversion 使用 MIME 类型的更多信息,请参见本章后面的 “文件内容类型”部分。)
![]() |
注意 |
---|---|
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 工具箱中最方便的与属性相关的工具。有关配置该支持的更多信息,请参见 名为“Config”的部分。
![]() |
注意 |
---|---|
Subversion 管理员经常询问是否可以在服务器端配置一组属性定义,所有连接的客户端在操作从该服务器检出的工作副本时会自动考虑这些定义。不幸的是,Subversion 不提供此功能。管理员可以使用钩子脚本验证添加到文件和目录中的属性以及修改的属性是否与管理员的首选策略匹配,拒绝以这种方式不符合要求的提交。(有关钩子脚本的更多信息,请参见 名为“实现存储库钩子”的部分。)但无法预先自动将这些首选项指示给 Subversion 客户端。 |