本文档旨在描述 Subversion 1.4。如果您正在运行更新版本的 Subversion,我们强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的书籍版本。
我们经常在电脑上复制、移动、重命名和完全替换文件和目录。您的版本控制系统也不应该妨碍您对版本控制的文件和目录执行这些操作。Subversion 的文件管理支持非常灵活,为版本控制的文件提供了几乎与您在操作未版本控制的文件时一样的灵活性。但是,这种灵活性意味着在您的仓库的生命周期中,给定的版本控制对象可能有多条路径,而给定的路径可能代表多个完全不同的版本控制对象。这给您与这些路径和对象的交互带来了一定程度的复杂性。
Subversion 很聪明地注意到当一个对象的版本历史包含这样的“地址更改”时。例如,如果您请求上周重命名的特定文件的修订历史记录,Subversion 很乐意提供所有这些日志——重命名本身发生的修订,以及重命名前后相关修订的日志。因此,大多数情况下,您甚至不必考虑这些事情。但是,偶尔,Subversion 需要您的帮助来消除歧义。
最简单的例子是,当一个目录或文件从版本控制中删除,然后一个新的目录或文件使用相同的名称创建并添加到版本控制中。显然,您删除的和后来添加的东西不是同一个东西。它们只是碰巧具有相同的路径,例如 /trunk/object
。那么,询问 Subversion 关于 /trunk/object
的历史意味着什么?您是在询问当前位于该位置的东西,还是询问您从该位置删除的旧东西?您是在询问对所有曾经位于该路径的对象执行的操作吗?显然,Subversion 需要一个提示来了解您真正想要什么。
而且由于移动,版本控制对象的版本历史可能会变得比这复杂得多。例如,您可能有一个名为 concept
的目录,其中包含您一直在玩弄的某个新生软件项目。但是,最终,该项目成熟到足以让人觉得这个想法实际上有一些意义,所以您做出了令人难以置信的决定,决定给该项目起个名字。 [18] 假设您将软件命名为 Frabnaggilywort。此时,将目录重命名以反映项目的新名称是有意义的,因此 concept
被重命名为 frabnaggilywort
。生活继续,Frabnaggilywort 发布了 1.0 版本,并且每天都被成群结队想要改善生活的人们下载和使用。
这是一个很棒的故事,但故事并没有结束。作为企业家,您已经在脑子里想出了另一个想法。因此,您创建了一个新目录 concept
,循环再次开始。事实上,循环在数年中多次重复,每次都从旧的 concept
目录开始,然后有时看到该目录在想法成熟时被重命名,有时看到该目录在您放弃想法时被删除。或者,为了变得更糟,也许您将 concept
重命名为其他东西一段时间,但后来出于某种原因将该东西重新命名为 concept
。
在这样的情况下,试图指示 Subversion 使用这些重复使用的路径就像指示芝加哥西郊的驾车者沿着罗斯福路向东行驶,然后左转进入梅恩街一样。在短短 20 分钟内,您就可以穿越惠顿、格伦埃林和隆巴德的“梅恩街”。它们并不是同一条街。我们的驾车者——以及我们的 Subversion——需要更多细节才能做正确的事情。
在 1.1 版中,Subversion 引入了一种方法,让您可以告诉它您到底指的是哪条梅恩街。它被称为标注修订版,它是一个提供给 Subversion 的修订版,其唯一目的是识别一条独特的历史线。因为在任何给定时间——或者更准确地说,在任何一个修订版中——最多只有一个版本控制对象可以占用一个路径,所以路径和标注修订版的组合是引用特定历史线的全部必要信息。标注修订版使用at 语法指定给 Subversion 命令行客户端,之所以这样命名是因为该语法涉及在与修订版关联的路径的末尾附加一个“at 符号” (@
) 和标注修订版。
但是,我们在这本书中谈论了这么多关于 --revision (-r)
的内容呢?那个修订版(或修订版集)被称为操作修订版(或操作修订版范围)。一旦使用路径和标注修订版识别了特定的历史线,Subversion 就会使用操作修订版执行请求的操作。为了将此映射到我们的芝加哥地区街道类比,如果我们被告知去惠顿梅恩街 606 号, [19] 我们可以将“梅恩街”视为我们的路径,并将“惠顿”视为我们的标注修订版。这两部分信息标识了一个可以行驶的唯一路径(在梅恩街上向北或向南行驶),并将防止我们为了到达目的地而在错误的梅恩街上上下行驶。现在,我们将“606 N.”作为我们的操作修订版,这样我们就 完全 知道该去哪里了。
假设很久以前我们创建了仓库,并在修订版 1 中添加了第一个 concept
目录,以及该目录中关于该概念的 IDEA
文件。在添加和调整实际代码的几个修订版之后,我们在修订版 20 中将该目录重命名为 frabnaggilywort
。在修订版 27 中,我们有了新的概念,新的 concept
目录来存放它,以及新的 IDEA
文件来描述它。然后,五年和两万个修订版像任何好的爱情故事一样飞逝而过。
现在,几年过去了,我们想知道 IDEA
文件在修订版 1 中是什么样子。但是,Subversion 需要知道我们是在询问当前文件在修订版 1 中的样子,还是在询问修订版 1 中位于 concepts/IDEA
的任何文件的內容。当然,这两个问题的答案不同,由于标注修订版的存在,您可以询问这两个问题中的任何一个。要找出当前 IDEA
文件在那个旧修订版中的样子,您需要运行
$ svn cat -r 1 concept/IDEA svn: Unable to find repository location for 'concept/IDEA' in revision 1
当然,在这个例子中,当前的 IDEA
文件在修订版 1 中还不存在,因此 Subversion 会报错。上面的命令是更长表示法的简写,后者明确列出了标注修订版。扩展的表示法是
$ svn cat -r 1 concept/IDEA@BASE svn: Unable to find repository location for 'concept/IDEA' in revision 1
执行后,它将产生预期结果。
有洞察力的读者可能此时会想知道,标注修订版语法是否会对工作副本路径或 URL 造成问题,而这些路径或 URL 实际上包含 at 符号。毕竟,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 的软件的文件。事实上,我们可以使用明确的标注修订版和明确的操作修订版的组合来验证这一点。我们知道,在 HEAD
中,Frabnaggilywort 项目位于 frabnaggilywort
目录中。因此,我们指定我们想要查看在修订版 1 中,HEAD
中识别为路径 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 需要清除歧义的额外提示。