本文档仍在编写中,可能会发生较大变动,可能无法准确描述 Apache™ Subversion® 软件的任何发布版本。将此页面添加书签或推荐给其他人可能不是一个明智的选择。请访问 http://svnbooks.subversion.org.cn/ 以获取此书的稳定版本。

钉住版本和操作版本

我们经常在计算机上复制、移动、重命名和完全替换文件和目录。您的版本控制系统也不应该妨碍您对版本控制的文件和目录进行这些操作。Subversion 的文件管理支持非常灵活,它为版本控制的文件提供了几乎与您在操作未版本控制的文件时期望的一样多的灵活性。但是,这种灵活性意味着在您的存储库的整个生命周期中,给定版本控制的对象可能具有许多路径,并且给定路径可能代表几个完全不同的版本控制对象。这为您的路径和对象交互引入了某种复杂性。

Subversion 很聪明,它会注意到当一个对象的版本历史包含这样的 地址更改 时。例如,如果您请求上周重命名的特定文件的版本历史日志,Subversion 会很乐意提供所有这些日志——重命名本身发生的版本,以及重命名之前和之后相关版本的日志。因此,大多数情况下,您甚至不必考虑这些事情。但是偶尔,Subversion 需要您的帮助来消除歧义。

最简单的例子是当一个目录或文件从版本控制中删除,然后一个新的目录或文件使用相同的名称创建并添加到版本控制中。您删除的东西和您后来添加的东西不是同一个东西。它们只是碰巧具有相同的路径——例如 /trunk/object。那么,询问 Subversion 关于 /trunk/object 的历史意味着什么?您是在询问当前位于该位置的内容,还是您从该位置删除的旧内容?您是在询问对 所有 曾经存在于该路径上的对象的执行的操作吗?Subversion 需要一个提示,让它知道您真正想要什么。

由于移动的存在,版本控制对象的版本历史可能会比这更加复杂。例如,您可能有一个名为 concept 的目录,其中包含您一直在玩的一些新软件项目。但是,最终,该项目成熟到某种程度,这个想法似乎真的有了一些翅膀,因此您做出了不可想象的决定,决定给该项目起一个名字。[11] 假设您将软件命名为 Frabnaggilywort。此时,将目录重命名以反映项目的新名称是合理的,因此 concept 被重命名为 frabnaggilywort。生活继续,Frabnaggilywort 发布了 1.0 版本,并且每天都被成群结队希望改善生活的人下载和使用。

这是一个很棒的故事,但故事并没有到此结束。您是一位企业家,您已经有了另一个想法。因此,您创建了一个新的目录 concept,并且循环再次开始。事实上,循环在多年内多次开始,每次都从旧的 concept 目录开始,然后有时看到目录在想法治愈时被重命名,有时看到在您放弃想法时被删除。或者,为了变得更复杂,也许您将 concept 重命名为其他名称一段时间,但后来由于某种原因又将它重命名回 concept

在这样的情况下,尝试指示 Subversion 使用这些重用的路径有点像指示芝加哥西郊的司机沿罗斯福路向东行驶,然后左转进入梅恩街。在短短 20 分钟内,您可以在惠顿、格伦埃林和隆巴德穿过 梅恩街。而且,它们并不完全相同。我们的司机——以及我们的 Subversion——需要更多细节才能做正确的事情。

幸运的是,Subversion 允许您告诉它您到底指的是哪条梅恩街。所使用的机制称为 钉住版本,您将这些版本提供给 Subversion,其唯一目的是识别唯一的历史记录行。因为在任何给定时间(或更确切地说,在任何一个版本中)最多只有一个版本控制对象可以占用一个路径,所以路径和钉住版本的组合是唯一标识特定历史记录行所需的全部信息。钉住版本使用 at 语法 指定给 Subversion 命令行客户端,之所以这样命名是因为语法涉及将一个 at 符号 (@) 和钉住版本附加到与该版本关联的路径的末尾。

但是 --revision (-r) 在本书中我们已经讨论了很多呢?该版本(或版本集)称为 操作版本(或 操作版本范围)。一旦使用路径和钉住版本识别出特定历史记录行,Subversion 就会使用操作版本执行请求的操作。为了将这映射到我们芝加哥街区类比,如果我们被告知去惠顿的梅恩街 606 号北[12],我们可以将 梅恩街 视为我们的路径,将 惠顿 视为我们的钉住版本。这两部分信息确定了可以行驶的唯一路径(梅恩街向北或向南),并且它们使我们不会在错误的梅恩街上下行驶寻找目的地。现在我们添加 606 号北 作为我们的一种操作版本,并且我们知道 确切 的前往地点。

假设很久以前我们创建了我们的存储库,并在版本 1 中添加了第一个 concept 目录,以及该目录中的一个 IDEA 文件,其中讨论了这个概念。在添加和调整真实代码的几个版本之后,我们在版本 20 中将该目录重命名为 frabnaggilywort。到版本 27 时,我们有了一个新的概念,一个新的 concept 目录来保存它,以及一个新的 IDEA 文件来描述它。然后五年过去了,数千个版本过去了,就像任何好的爱情故事一样。

现在,几年后,我们想知道 IDEA 文件在版本 1 中是什么样的。但是 Subversion 需要知道我们是在询问 当前 文件在版本 1 中的样子,还是我们是在请求在版本 1 中位于 concept/IDEA 的任何文件的內容。当然,这些问题的答案不同,由于钉住版本的存在,您可以提出这些问题。要找出当前的 IDEA 文件在那个旧版本中是什么样的,您可以运行

$ svn cat -r 1 concept/IDEA 
svn: E195012: Unable to find repository location for 'concept/IDEA' in revision 1

当然,在这个例子中,当前的 IDEA 文件在版本 1 中还不存在,因此 Subversion 会报错。前面的命令是更长表示法的简写形式,该表示法明确列出了钉住版本。扩展表示法是

$ svn cat -r 1 concept/IDEA@BASE
svn: E195012: Unable to find repository location for 'concept/IDEA' in revision 1

并且在执行时,它将产生预期的结果。

细心的读者可能想知道钉住版本语法是否会对工作副本路径或实际上包含 at 符号的 URL 造成问题。毕竟,svn 如何知道 news@11 是我树中目录的名称,还是仅仅是 版本 11 的 news 的语法?值得庆幸的是,虽然 svn 始终会假设后者,但有一个简单的解决方法。您只需要在路径的末尾附加一个 at 符号,例如 news@11@svn 只关心参数中的最后一个 at 符号,并且在该 at 符号之后省略文字钉住版本规范符并不被认为是非法的。此解决方法甚至适用于以 at 符号结尾的路径——您将使用 filename@@ 来谈论名为 filename@ 的文件。

那么,让我们问另一个问题——在版本 1 中,位于地址 concepts/IDEA 的任何文件当时的内容是什么?我们将使用显式钉住版本来帮助我们。

$ svn cat concept/IDEA@1
The idea behind this project is to come up with a piece of software
that can frab a naggily wort.  Frabbing naggily worts is tricky
business, and doing it incorrectly can have serious ramifications, so
we need to employ over-the-top input validation and data verification
mechanisms.

请注意,我们这次没有提供操作版本。这是因为,当没有指定操作版本时,Subversion 会假设默认的操作版本,它与钉住版本相同。

正如您所见,我们操作的输出似乎是正确的。文本中甚至提到了 frabbing naggily worts,因此这几乎可以肯定是描述现在称为 Frabnaggilywort 的软件的文件。事实上,我们可以使用显式 peg 修订版和显式操作修订版的组合来验证这一点。我们知道,在 HEAD 中,Frabnaggilywort 项目位于 frabnaggilywort 目录中。因此,我们指定我们要查看在 HEAD 中标识为路径 frabnaggilywort/IDEA 的历史记录行在修订版 1 中是什么样子。

$ svn cat -r 1 frabnaggilywort/IDEA@HEAD
The idea behind this project is to come up with a piece of software
that can frab a naggily wort.  Frabbing naggily worts is tricky
business, and doing it incorrectly can have serious ramifications, so
we need to employ over-the-top input validation and data verification
mechanisms.

并且 peg 和操作修订版也不必如此简单。例如,假设 frabnaggilywort 已从 HEAD 中删除,但我们知道它存在于修订版 20 中,并且我们想要查看其 IDEA 文件在修订版 4 和 10 之间的差异。我们可以使用修订版 20 中的 peg 修订版以及将包含 Frabnaggilywort 的 IDEA 文件的 URL,然后使用 4 和 10 作为我们的操作修订版范围。

$ svn diff -r 4:10 http://svn.red-bean.com/projects/frabnaggilywort/IDEA@20
Index: frabnaggilywort/IDEA
===================================================================
--- frabnaggilywort/IDEA	(revision 4)
+++ frabnaggilywort/IDEA	(revision 10)
@@ -1,5 +1,5 @@
-The idea behind this project is to come up with a piece of software
-that can frab a naggily wort.  Frabbing naggily worts is tricky
-business, and doing it incorrectly can have serious ramifications, so
-we need to employ over-the-top input validation and data verification
-mechanisms.
+The idea behind this project is to come up with a piece of
+client-server software that can remotely frab a naggily wort.
+Frabbing naggily worts is tricky business, and doing it incorrectly
+can have serious ramifications, so we need to employ over-the-top
+input validation and data verification mechanisms.

幸运的是,大多数人不会遇到如此复杂的情况。但当您遇到这种情况时,请记住 peg 修订版是 Subversion 需要清除歧义的额外提示。



[11] 你不应该给它命名。一旦你命名它,你就会开始对它产生感情。—迈克·瓦佐斯基

[12] 伊利诺伊州惠顿市北梅因街 606 号是惠顿 历史中心的所在地。这似乎很合适……

TortoiseSVN 官方中文版 1.14.7 发布