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