本文档旨在描述 Subversion 1.2。如果您正在运行较新版本的 Subversion,我们强烈建议您访问 https://svnbooks.subversion.org.cn/ 并查阅适合您 Subversion 版本的版本。

钉子修订和操作修订

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

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

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

由于移动操作,版本控制资源历史记录甚至可能比这更加复杂。例如,您可能有一个名为 concept 的目录,其中包含您一直在玩弄的某个新软件项目。但是,最终,该项目成熟到足以让这个想法真正具备了一些潜力,因此您做出了不可思议的决定,决定为该项目起个名字。[37] 假设您将软件命名为 Frabnaggilywort。此时,将目录重命名为反映项目的新名称是有意义的,因此将 concept 重命名为 frabnaggilywort。生活继续,Frabnaggilywort 发布了 1.0 版本,并且每天被成群结队想要改善生活的人下载和使用。

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

当出现这些情况时,尝试指示 Subversion 使用这些重复使用的路径就像指示芝加哥西郊的驾驶员沿罗斯福路向东行驶,然后左转进入梅恩大街一样。仅仅 20 分钟,您就可以在惠顿、格伦艾林和隆巴德穿过“梅恩大街”。而且,它们不是同一条街。我们的驾驶员——以及我们的 Subversion——需要更多信息才能做正确的事情。

在 1.1 版中,Subversion 引入了一种方法,可以让您准确地告诉它您指的是哪条梅恩大街。它被称为 钉子修订,它是提供给 Subversion 的一个修订,其唯一目的是标识唯一的历史记录行。由于最多只有一个版本控制资源可以在任何给定时间(或更准确地说,在任何一个修订中)占据一个路径,因此路径和钉子修订的组合是引用特定历史记录行所需的全部内容。钉子修订是使用 at 语法指定给 Subversion 命令行客户端的,之所以这样称呼是因为该语法涉及将“at 符号” (@) 和钉子修订追加到与该修订相关的路径的末尾。

但是我们在这本书中经常提到的 --revision (-r) 呢?该修订(或一组修订)被称为 操作修订(或 操作修订范围)。一旦使用路径和钉子修订标识了特定的历史记录行,Subversion 就会使用操作修订执行请求的操作。为了将此映射到我们芝加哥郊区街道的类比,如果我们被告知前往惠顿的梅恩大街 606 号北侧,[38] 我们可以将“梅恩大街”视为我们的路径,将“惠顿”视为我们的钉子修订。这两部分信息标识了一个独特的路径,可以沿该路径行驶(在梅恩大街上向北或向南),并且可以防止我们在错误的梅恩大街上上下行驶来寻找目的地。现在,我们将“606 号北侧”作为操作修订,这样我们就知道要 确切 地去哪里。

假设很久以前我们创建了存储库,并在修订版 1 中添加了第一个 concept 目录,以及该目录中关于该概念的 IDEA 文件。在添加和调整实际代码的几个修订版本之后,我们在修订版 20 中将该目录重命名为 frabnaggilywort。到修订版 27 时,我们有了一个新概念,一个新的 concept 目录来保存它,以及一个新的 IDEA 文件来描述它。然后,五年和两万个修订版本飞逝而过,就像它们在任何好的爱情故事中都会发生一样。

现在,几年之后,我们想知道 IDEA 文件在修订版 1 中是什么样子的。但是 Subversion 需要知道我们是询问 当前 文件在修订版 1 中是什么样子,还是询问在修订版 1 中位于 concepts/IDEA 的任何文件的內容?这些问题肯定会有不同的答案,并且由于钉子修订,您可以提出这两个问题中的任何一个。要了解当前的 IDEA 文件在那个旧的修订版中是什么样子的,您需要运行

$ svn cat -r 1 concept/IDEA 
subversion/libsvn_client/ra.c:775: (apr_err=20014)
svn: Unable to find repository location for 'concept/IDEA' in revision 1

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

$ svn cat -r 1 concept/IDEA@BASE
subversion/libsvn_client/ra.c:775: (apr_err=20014)
svn: Unable to find repository location for 'concept/IDEA' in revision 1

并且在执行时,它具有预期的结果。当应用于工作副本路径时,钉子修订通常默认为 BASE(当前存在于工作副本中的修订),而当应用于 URL 时,则默认为 HEAD

那么,让我们来问另一个问题——在修订版 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.

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

$ 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.

钉子修订和操作修订也不必如此简单。例如,假设 frabnaggilywort 已从 HEAD 中删除,但我们知道它存在于修订版 20 中,并且我们想要查看其 IDEA 文件在修订版 4 和 10 之间的差异。我们可以将钉子修订 20 与在修订版 20 中将保存 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.

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



[37] 你不应该给它起名字。一旦你给它起名字,你就会开始对它产生依恋。” — 大眼仔

[38] 惠顿伊利诺伊州梅恩大街 606 号北侧是惠顿历史中心的所在地。明白了——“历史中心”?这似乎很合适……。

TortoiseSVN 官方中文版 1.14.7 发布