属性
属性
我们已经详细讲述了Subversion存储和检索版本库中不同版本的文件和目录的细节,并且用了好几个章节来论述这个工具的基本功能。如果对于版本化的支持到此为止,从版本控制的角度来看Subversion已经完整了。
但不仅仅如此。
作为目录和文件版本化的补充,Subversion提供了对每一个版本化的目录和文件添加、修改和删除版本化的元数据的接口,我们用属性来表示这些元数据。我们可以认为它们是一个两列的表,附加到你的工作拷贝的每个条目上,映射属性名到任意的值。一般来说,属性的名称和值可以是你希望的任何值,限制就是名称必须是可读的文本,并且最好的一点是这些属性也是版本化的,就像你的文本文件内容,你可以像提交文本修改一样修改、提交和恢复属性修改,当你更新时也会接收到别人的属性修改—你不必为适应属性改变你的工作流程。
注意
Subversion自己保留了一组名称以svn:
开头的属性,现在已经有了一些在用的属性,所以在你根据需要创建自定义属性时,需要避免这些前缀开头的名称,否则,Subversion的新版本可能会采用同名的属性来满足新的特性,而其含义可能会完全不同。
Subversion的属性也可以在别的地方出现,就像文件和目录可能附加有任意的属性名和值,每个修订版本作为一个整体也可以附加任意的属性,也有同样的限制—可读的文本名称和任何你希望的二进制值,主要的区别是修订版本属性不是版本化的,换句话说,如果你修改,删除一个修订版本属性,在Subversion领域内没有办法恢复到以前的值。
Subversion不关心如何使用属性,但是要求你不要使用svn:
为前缀的属性名,这是Subversion自己使用的命名空间,Subversion使用了版本化的和未版本化的属性。文件和目录上的特定版本化属性都有特别的意义或效果,或者是提供了修订版本的一些信息。一些修订版本属性会在提交时自动附加到修订版本上,包含了修订版本的信息。大多数这些属性会作为普通的主题在后面提及,关于Subversion预定义的属性的详细列表可以看“Subversion属性”一节。
在本小节,我们将会检验这个工具—不仅是对Subversion的用户,也对Subversion本身—对于属性的支持。你会学到与属性相关的svn子命令,和属性怎样影响你的普通Subversion工作流,希望你会感到Subversion的属性可以提高你的版本控制体验。
为什么需要属性?
就像Subversion使用属性保存其包含的文件、目录和修订版本的附加信息,你也会发现属性有一些类似的使用,你会发现如果在数据附近有个地方保存自定义元数据会非常有用。
假设你希望设计一个存放许多数码照片的网站,会显示标题和缩略图。现在你的图片会经常修改,所以你希望能够让这个站点尽量自动处理这些事情,这些照片会很大,所以作为网站,你希望为访问者提供相似的缩略图。
现在,你可以利用这些功能使用传统文件。你可以有一个image123.jpg
和一个对应的image123-thumbnail.jpg
在同一个目录里,有时候你希望保持文件名相同,你可以使用不同的目录,如thumbnails/image123.jpg
。你可以用一种相似的样式来保存你的标题和时间戳,同原始图像文件分开。每个新图片的添加都会成倍的增加混乱,很快你的目录树会是一团糟。
现在考虑使用Subversion文件的属性的方式来管理这个站点,想象我们有一个单独的图像文件image123.jpg
,然后这个文件的属性集包括caption
、datestamp
甚至thumbnail
。现在你的工作拷贝目录看起来更容易管理—实际上,它看起来只有图像文件,但是你的自动化脚本知道得更多,它们知道可以用svn(更好的选择是使用Subversion的语言绑定—见“使用API”一节)来挖掘更多的站点显示需要的额外信息,而不必去阅读一个索引文件或者是玩一个路径处理的游戏。
自定义修订版本属性也经常被使用,一个常见的用法是一个包含问题跟踪ID的属性,可能是因为这个修改修正了这个ID的问题。另外一些人用属性来存放更容易记的修订版本名称—记住修订版本1935是一个完全测试的版本是很困难的,但是如果在修订版本上设置一个值为all passing
的test-results
属性,这就有了一个有用的信息。
可搜索性(或者,为什么不使用属性)
对于Subversion属性的所有功能—或者更准确的讲,对于属性的所有接口—都有一些主要的应用会削弱他们的应用。设置一个自定义属性后,很容易发现属性完全变成另外一会儿事。
为了定位一个自定义属性通常要线形访问版本库的所有修订版本,向每个修订版本询问,“你们有我找的属性吗?”尝试查找自定义版本化属性也是同样的痛苦,通常需要在整个工作拷贝递归调用svn propget。在你的情况下,可能不会比遍历所有修订版本差。但也在性能和成功可能性里留下了许多悬念,特别是当你需要从版本库的根开始搜索时。
因为这个原因,你会选择—特别是在修订版本属性用例—简单的添加你的元数据到修订版本日志信息,使用一些政策驱动(并且是编程强制的)且可以通过svn log快速解析的格式。如下的Subversion日志信息会很常见:
Issue(s): IZ2376, IZ1919 Reviewed by: sally This fixes a nasty segfault in the wort frabbing process …
但是现在依然有一些不幸,Subversion不支持日志信息模版机制,虽然这样对用户与日志嵌入的修订版本元数据保持一致有很大帮助。
操作属性
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。然而这两个命令的结果是相同的,前一个会允许他们查看修改以前的内容,可以帮助用户验证,实际上,作出他们所期望的修改,当修改未版本化修订版本属性时,这一点特别需要。另外,这个命令也可以通过文本编辑器或命令行轻松的修改多行属性。
属性和 Subversion 工作流程
现在你已经熟悉了所有与属性相关的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 update calc M calc/Makefile.in C calc/button.c Updated to revision 143. $
Subversion也会在冲突资源的同一个目录创建一个.prej
扩展名的文件,保存冲突的细节。你一定要检查这个文件的内容来决定如何解决冲突,在你解决冲突之前,你会在使用svn status时看到这个资源的输出的第二列是一个C
,提交本地修改的尝试会失败。
$ svn status calc C calc/button.c ? calc/button.c.prej $ cat calc/button.c.prej prop 'linecount': user set to '1256', but update set to '1301'. $
为了解决属性冲突,只需要确定冲突的属性保存了它们应该的值,然后使用svn resolved命令告诉Subversion你已经手工解决了问题。
你也许已经注意到了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工具箱中属性相关最垂手可得的工具,见“配置”一节来查看更多的配置支持。
[9] 如果你熟悉XML,其实这就是XML的"Name"语法的ASCII子集。
[10] 修正提交日志信息的拼写错误,文法错误和“简单的错误”是--revprop
选项最常见用例。