本手册旨在描述 Subversion 1.4。如果您使用的是更新版本的 Subversion,我们强烈建议您访问 https://svnbook.subversion.org.cn/ 并参考与您的 Subversion 版本匹配的手册版本。
我们已经详细介绍了 Subversion 如何在存储库中存储和检索文件的不同版本。我们用整整一章来阐述这种 Subversion 工具提供的最基本的功能。如果版本控制功能到此为止,那么从版本控制的角度来看,Subversion 仍然是完整的。
但它并没有止步于此。
除了对目录和文件进行版本控制,Subversion 还提供了接口,用于添加、修改和删除版本化的元数据,这些元数据与您的工作副本中的每个版本化目录和文件相关联。我们将这些元数据称为 属性,可以将它们理解为双列表格,用于将属性名称映射到与工作副本中的每个项目关联的任意值。一般来说,属性的名称和值可以是您想要的任何内容,唯一的限制是名称必须是人类可读的文本。最棒的是,这些属性与文件的文本内容一样,也是版本化的。您可以像修改文件内容一样轻松地修改、提交和恢复属性更改。属性更改的发送和接收作为您典型的提交和更新操作的一部分进行,您无需更改基本流程来适应它们。
Subversion 将名称以 svn:
开头的属性集保留为自己使用。虽然目前只有少量这样的属性在使用,但您应该避免为自己的需求创建名称以该前缀开头的自定义属性。否则,您将冒着 Subversion 未来版本添加对以相同名称但可能具有完全不同解释的属性驱动的功能或行为的支持的风险。
属性在 Subversion 中也出现在其他地方。正如文件和目录可能具有附加到它们的任意属性名称和值一样,每个修订版作为一个整体也可能具有附加到它的任意属性。相同的限制适用——人类可读的名称和任何您想要的二进制值。主要区别在于修订版属性不是版本化的。换句话说,如果您更改或删除修订版属性的值,则在 Subversion 功能范围内无法恢复先前值。
Subversion 对属性的使用没有特别的策略。它只要求您不要使用以 svn:
为前缀的属性名称。这是它为自身使用而保留的命名空间。事实上,Subversion 确实使用了属性,包括版本化和非版本化属性。某些版本化属性在文件和目录中存在时具有特殊含义或效果,或者存储有关它们所在的修订版的特定信息。某些修订版属性由 Subversion 的提交过程自动附加到修订版,并包含有关修订版的信息。大多数这些属性在本手册或其他章节中作为与它们相关的更一般主题的一部分被提及。有关 Subversion 预定义属性的详尽列表,请参见 名为“Subversion 属性”的部分。
在本节中,我们将研究属性支持对 Subversion 用户和 Subversion 本身的有用性。您将了解与属性相关的 svn 子命令,以及属性修改如何影响您的正常 Subversion 工作流程。
正如 Subversion 使用属性来存储有关其包含的文件、目录和修订版的额外信息一样,您也可能会发现属性具有类似的用途。您可能会发现将自定义元数据挂在版本化数据旁边非常有用。
假设您希望设计一个网站来托管许多数字照片,并使用标题和日期戳来显示它们。现在,您的照片集一直在变化,因此您希望尽可能自动化该网站。这些照片可能非常大,因此与这类网站的常见做法一样,您希望向您的网站访问者提供较小的缩略图。
现在,您可以使用传统的文件来获得此功能。也就是说,您可以在目录中并排放置 image123.jpg
和 image123-thumbnail.jpg
。或者,如果您希望保持文件名相同,您可以在不同的目录中放置缩略图,例如 thumbnails/image123.jpg
。您还可以以类似的方式存储标题和日期戳,同样与原始图像文件分开。但这里的问题是,随着添加到网站的每张新照片,您的文件集合都会成倍增长。
现在考虑使用 Subversion 文件属性部署的相同网站。想象一下,您有一个图像文件 image123.jpg
,以及为该文件设置的属性,例如 caption
、datestamp
,甚至 thumbnail
。现在您的工作副本目录看起来更易于管理——实际上,对于普通浏览器来说,它看起来就像其中只有图像文件。但是您的自动化脚本知道更多。他们知道他们可以使用 svn(或者更好的是,他们可以使用 Subversion 语言绑定——参见 名为“使用 API”的部分)来挖掘您的网站显示所需的其他信息,而无需读取索引文件或执行路径操作游戏。
自定义修订版属性也经常被使用。一种常见的用法是,属性的值包含与修订版关联的问题跟踪器 ID,可能是因为该修订版中所做的更改修复了在该 ID 的跟踪器问题中提交的错误。其他用途包括在修订版上附加更友好的名称——可能很难记住修订版 1935 是一个完全测试的修订版。但是,如果该修订版上有一个名为 test-results
的属性,其值为 all passing
,那么这将是有意义的信息。
svn 命令提供了几种方法来添加或修改文件和目录属性。对于具有短而易于理解的值的属性,最简单的方法可能是使用 propset 子命令的命令行指定属性名称和值。
$ svn propset copyright '(c) 2006 Red-Bean Software' calc/button.c property 'copyright' set on 'calc/button.c' $
但是,我们一直在宣传 Subversion 为您的属性值提供的灵活性。如果您计划使用多行文本甚至二进制属性值,您可能不希望在命令行上提供该值。因此,propset 子命令接受一个 --file (-F)
选项,用于指定包含新属性值的名称。
$ svn propset license -F /path/to/LICENSE calc/button.c property 'license' set on 'calc/button.c' $
您为属性使用的名称存在一些限制。属性名称必须以字母、冒号 (:
) 或下划线 (_
) 开头;之后,您还可以使用数字、连字符 (-
) 和句点 (.
)。[9]
除了 propset 命令之外,svn 程序还提供 propedit 命令。此命令使用配置的编辑器程序(参见 名为“配置”的部分)来添加或修改属性。当您运行该命令时,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 允许您存储具有空值的属性,因此无法使用 propedit 或 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)。例如,您可能想要替换现有修订版的提交日志消息。[10] 如果您当前的工作目录是存储库工作副本的一部分,则只需运行 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 ___________________________________________________________________ Name: copyright + (c) 2006 Red-Bean Software $
注意 status 子命令如何在第二列而不是第一列中显示 M
。这是因为我们修改了 calc/button.c
的属性,但没有修改其文本内容。如果我们更改了这两个内容,我们也会在第一列中看到 M
(请参阅 名为“查看您的更改概述”的部分)。
您可能还注意到 Subversion 当前显示属性差异的非标准方式。您仍然可以运行 svn diff 并将输出重定向以创建可用的补丁文件。 patch 程序将忽略属性补丁——一般来说,它会忽略它无法理解的任何噪音。不幸的是,这意味着要完全应用由 svn diff 生成的补丁,任何属性修改都需要手动应用。
属性是 Subversion 的一个强大功能,充当本章和其他章节中讨论的许多 Subversion 功能的关键组件——文本差异和合并支持、关键字替换、换行符转换等。但是,要充分利用属性,必须在正确的文件和目录上设置它们。不幸的是,这一步很容易在日常工作中被遗忘,尤其是在没有设置属性通常不会导致明显的错误(至少与未能将文件添加到版本控制相比)。为了帮助将您的属性应用到需要它们的地方,Subversion 提供了几个简单但有用的功能。
无论何时您使用 svn add 或 svn import 命令将文件引入版本控制,Subversion 都会尝试通过自动设置一些常见的文件属性来提供帮助。首先,在文件系统支持执行权限位的操作系统上,Subversion 将自动在启用执行位的已添加或导入的新文件中设置 svn:executable
属性。(有关此属性的更多信息,请参阅 名为“文件可执行性”的部分)。其次,它运行一个非常基本的启发式方法来确定该文件是否包含人类可读的内容。如果不是,Subversion 将自动将该文件的 svn:mime-type
属性设置为 application/octet-stream
(通用“这是一组字节”MIME 类型)。当然,如果 Subversion 猜测错误,或者您希望将 svn:mime-type
属性设置为更精确的值——也许是 image/png
或 application/x-shockwave-flash
——您可以始终删除或编辑该属性。(有关 Subversion 使用 MIME 类型的更多信息,请参阅 名为“文件内容类型”的部分)。
Subversion 还通过其运行时配置系统(请参阅 名为“运行时配置区域”的部分)提供了一个更灵活的自动属性设置功能,允许您创建文件名模式到属性名称和值的映射。这些映射再次影响添加和导入,不仅可以覆盖 Subversion 在这些操作期间做出的默认 MIME 类型决策,还可以设置额外的 Subversion 或自定义属性。例如,您可以创建一个映射,说明无论何时您添加 JPEG 文件——其名称与模式 *.jpg
匹配——Subversion 应该自动将这些文件的 svn:mime-type
属性设置为 image/jpeg
。或者,也许任何与 *.cpp
匹配的文件都应该将 svn:eol-style
设置为 native
,并将 svn:keywords
设置为 Id
。自动属性支持可能是 Subversion 工具箱中最方便的与属性相关的工具。有关配置该支持的更多信息,请参阅 名为“Config”的部分。