本文档描述了 Subversion 1.6.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。

稀疏目录

默认情况下,大多数 Subversion 对目录的操作都是递归的。例如,svn checkout 会创建一个工作副本,其中包含存储库中指定区域的所有文件和目录,并递归遍历存储库树,直到将整个结构复制到本地磁盘。Subversion 1.5 引入了一种称为 稀疏目录(或 浅层检出)的功能,允许您轻松检出一个工作副本(或工作副本的一部分),深度比完全递归更浅,并且可以随时自由地引入以前忽略的文件和子目录。

例如,假设我们有一个存储库,其文件和目录树的名称是人类家庭成员和宠物的名称。(这是一个奇怪的例子,请耐心听我解释。)常规的 svn checkout 操作将为我们提供整个树的工作副本

$ svn checkout file:///var/svn/repos mom
A    mom/son
A    mom/son/grandson
A    mom/daughter
A    mom/daughter/granddaughter1
A    mom/daughter/granddaughter1/bunny1.txt
A    mom/daughter/granddaughter1/bunny2.txt
A    mom/daughter/granddaughter2
A    mom/daughter/fishie.txt
A    mom/kitty1.txt
A    mom/doggie1.txt
Checked out revision 1.
$

现在,让我们再次检出相同的树,但这次我们将要求 Subversion 只为我们提供最顶层的目录,而不包含任何子目录

$ svn checkout file:///var/svn/repos mom-empty --depth empty
Checked out revision 1
$

请注意,我们在原始的 svn checkout 命令行中添加了一个新的 --depth 选项。此选项存在于许多 Subversion 的子命令中,与 --non-recursive (-N) 和 --recursive (-R) 选项类似。事实上,它将这两种旧选项组合起来,改进,取代并最终淘汰了这两种旧选项。首先,它扩展了用户可用的深度规范支持程度,添加了一些以前不支持(或支持不一致)的深度。以下是您可以为给定的 Subversion 操作请求的深度值

--depth empty

仅包含操作的直接目标,不包含其任何文件或目录子项。

--depth files

包含操作的直接目标及其所有直接文件子项。

--depth immediates

包含操作的直接目标及其所有直接文件或目录子项。目录子项本身将为空。

--depth infinity

包含直接目标、其文件和目录子项、子项的子项,以及所有子项,直到完全递归。

当然,仅仅将两个现有选项合并成一个并不能构成一项值得我们在书中专门写一节的新功能。幸运的是,还有更多内容。这个深度的概念不仅扩展到您使用 Subversion 客户端执行的操作,还作为工作副本公民 环境深度 的描述,它是工作副本为该项持久记录的深度。其关键优势在于这种持久性——它是一种 粘性。工作副本会记住您为其中的每一项选择的深度,直到您稍后更改该深度选择为止;默认情况下,Subversion 命令会对存在的工作副本公民进行操作,无论其选择的深度设置如何。

[Tip] 提示

您可以使用 svn info 命令检查工作副本的记录环境深度。如果环境深度不是无限递归,svn info 将显示一行描述该深度值的文本。

$ svn info mom-immediates | grep "^Depth:"
Depth: immediates
$

我们之前的示例演示了无限深度的检出(svn checkout 的默认值)和空深度的检出。现在让我们看看其他深度值的示例

$ svn checkout file:///var/svn/repos mom-files --depth files
A    mom-files/kitty1.txt
A    mom-files/doggie1.txt
Checked out revision 1.
$ svn checkout file:///var/svn/repos mom-immediates --depth immediates
A    mom-immediates/son
A    mom-immediates/daughter
A    mom-immediates/kitty1.txt
A    mom-immediates/doggie1.txt
Checked out revision 1.
$

如前所述,这些深度中的每一个都比仅包含目标多一些,但比完全递归少一些。

我们在此处以 svn checkout 为例,但您会发现 --depth 选项也存在于许多其他 Subversion 命令中。在这些其他命令中,深度规范是限制操作范围到某个深度的另一种方式,类似于旧的 --non-recursive (-N) 和 --recursive (-R) 选项的行为。这意味着当操作某个具有特定深度的工作副本时,如果请求操作的深度更浅,则操作将限制到该更浅的深度。事实上,我们可以做一个更普遍的陈述:给定一个具有任意(甚至混合)环境深度的工作副本,以及一个具有特定请求操作深度的 Subversion 命令,该命令将保持工作副本成员的环境深度,同时仍然将操作范围限制在请求的(或默认的)操作深度。

除了 --depth 选项之外,svn updatesvn switch 子命令还接受第二个与深度相关的选项:--set-depth。正是使用此选项,您可以更改工作副本项的粘性深度。让我们看看当我们使用 svn update --set-depth NEW-DEPTH TARGET 来逐步扩大空深度检出的深度时会发生什么

$ svn update --set-depth files mom-empty
A    mom-empty/kittie1.txt
A    mom-empty/doggie1.txt
Updated to revision 1.
$ svn update --set-depth immediates mom-empty
A    mom-empty/son
A    mom-empty/daughter
Updated to revision 1.
$ svn update --set-depth infinity mom-empty
A    mom-empty/son/grandson
A    mom-empty/daughter/granddaughter1
A    mom-empty/daughter/granddaughter1/bunny1.txt
A    mom-empty/daughter/granddaughter1/bunny2.txt
A    mom-empty/daughter/granddaughter2
A    mom-empty/daughter/fishie1.txt
Updated to revision 1.
$

当我们逐渐增加深度选择时,存储库向我们提供了更多树的一部分。

在我们的示例中,我们只对工作副本的根目录执行了操作,改变了其环境深度值。但我们也可以独立更改工作副本中 任何 子目录的环境深度值。仔细使用此功能,我们可以只扩展工作副本树的某些部分,而完全保留其他部分(因此该功能名称中的“稀疏”部分)。以下是如何构建家庭树中一个分支的一部分、为另一个分支启用完全递归以及保持其他部分被修剪(从磁盘中移除)的示例。

$ rm -rf mom-empty
$ svn checkout file:///var/svn/repos mom-empty --depth empty
Checked out revision 1.
$ svn update --set-depth empty mom-empty/son
A    mom-empty/son
Updated to revision 1.
$ svn update --set-depth empty mom-empty/daughter
A    mom-empty/daughter
Updated to revision 1.
$ svn update --set-depth infinity mom-empty/daughter/granddaughter1
A    mom-empty/daughter/granddaughter1
A    mom-empty/daughter/granddaughter1/bunny1.txt
A    mom-empty/daughter/granddaughter1/bunny2.txt
Updated to revision 1.
$

幸运的是,在单个工作副本中具有复杂的集合环境深度不会使您与该工作副本的交互方式变得复杂。您仍然可以在工作副本中进行、回滚、显示和提交本地修改,而无需向相关的子命令提供任何新选项(包括 --depth--set-depth)。即使 svn update 在没有提供特定深度的情况下也能像其他地方一样工作——它会更新存在的工作副本目标,同时尊重其粘性深度。

您可能此时会想,“所以呢?我什么时候会使用这个?” 这种功能在特定存储库布局中非常有用,特别是在您有多个相关或相互依赖的项目或软件模块作为兄弟节点存在于单个存储库位置 (trunk/project1trunk/project2trunk/project3 等) 的情况下。在这种情况下,您可能只关心其中几个项目——可能是某个主要项目以及它所依赖的其他几个模块。您可以检出所有这些项目的单独工作副本,但这些工作副本是分开的,因此,同时对多个或所有工作副本执行操作可能会很麻烦。另一种选择是使用稀疏目录功能,构建一个只包含您关心的模块的工作副本。您可以从对项目共同父目录的空深度检出开始,然后只对您希望拥有的项进行无限深度的更新,就像我们在前面的示例中演示的那样。将其视为工作副本公民的 opt-in 系统。

浅层检出的原始实现(Subversion 1.5)很好,但不支持工作副本项的缩回。Subversion 1.6 解决了这个问题。例如,在无限深度工作副本中运行 svn update --set-depth empty 将会丢弃除了最顶层目录之外的所有内容。 [18] Subversion 1.6 还引入了另一个支持的 --set-depth 选项值:exclude。使用 svn update--set-depth exclude 会导致更新目标从工作副本中完全删除——目录目标甚至不会保留为存在但为空。当您想要保留工作副本中的内容多于您想要 保留的内容时,这一点特别有用。

考虑一个具有数百个子目录的目录,其中一个子目录您想从工作副本中省略。使用稀疏目录的“添加式”方法,您可以以空深度检出该目录,然后显式地扩大 (使用 svn update --set-depth infinity) 该目录中除您不关心的子目录之外的所有子目录。

$ svn checkout http://svn.example.com/repos/many-dirs --depth empty
…
$ svn update --set-depth infinity many-dirs/wanted-dir-1
…
$ svn update --set-depth infinity many-dirs/wanted-dir-2
…
$ svn update --set-depth infinity many-dirs/wanted-dir-3
…
### and so on, and so on, ...

这可能非常乏味,特别是因为您甚至没有这些目录的存根供您处理。这样的工作副本还具有另一个您可能不希望或不期望的特性:如果其他人在此顶层目录中创建任何新的子目录,您在更新工作副本时不会收到这些子目录。

使用 Subversion 1.6,您可以采用不同的方法。首先,您将以完整深度检出该目录。然后,您将对您不关心的子目录运行 svn update --set-depth exclude

$ svn checkout http://svn.example.com/repos/many-dirs
…
$ svn update --set-depth exclude many-dirs/unwanted-dir
D         many-dirs/unwanted-dir
$

这种方法会使您的工作副本与第一种方法中的内容相同,但任何出现在顶层目录中的新子目录也会在您更新工作副本时出现。这种方法的缺点是,您必须实际检出您甚至不想要的整个子目录,以便告诉 Subversion 您不想要它。如果该子目录太大而无法容纳在您的磁盘上(这可能是您不想要它在工作副本中的原因),那么您甚至可能无法做到这一点。

[Note] 注意

虽然从工作副本中排除现有项的功能挂在 svn update 命令上,但您可能已经注意到,svn update --set-depth exclude 的输出与正常更新操作的输出不同。这种输出泄露了一个事实,即在幕后,排除是一个完全的客户端操作,与典型的更新非常不同。

在这种情况下,您可以考虑一种折衷方案。首先,以 --depth immediates 检出顶层目录。然后,使用 svn update --set-depth exclude 排除您不想要的目录。最后,将所有剩余的项扩大到无限深度,这应该很容易做到,因为它们在您的 shell 中都是可寻址的。

$ svn checkout http://svn.example.com/repos/many-dirs --depth immediates
…
$ svn update --set-depth exclude many-dirs/unwanted-dir
D         many-dirs/unwanted-dir
$ svn update --set-depth infinity many-dirs/*
…
$

再次,您的工作副本将与前两种场景中的内容相同。但现在,只要在顶层目录中提交了新的文件或子目录,您在更新工作副本时就会收到它——以空深度形式。现在,您可以决定如何处理这些新出现的工作副本项:将它们扩展到无限深度,或者完全排除它们。



[18] 当然,安全地进行。与其他情况一样,Subversion 会将您修改过的或未版本化的文件保留在磁盘上。