本文档旨在描述 Apache™ Subversion® 的 1.7.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 作为示例,但您会发现许多其他 Subversion 命令中也存在 --depth 选项。在这些其他命令中,深度规范是一种将操作范围限制到某个深度的途径,就像旧的 --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
Updating 'mom-empty':
A    mom-empty/kittie1.txt
A    mom-empty/doggie1.txt
Updated to revision 1.
$ svn update --set-depth immediates mom-empty
Updating 'mom-empty':
A    mom-empty/son
A    mom-empty/daughter
Updated to revision 1.
$ svn update --set-depth infinity mom-empty
Updating '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
Updating 'mom-empty/son':
A    mom-empty/son
Updated to revision 1.
$ svn update --set-depth empty mom-empty/daughter
Updating 'mom-empty/daughter':
A    mom-empty/daughter
Updated to revision 1.
$ svn update --set-depth infinity mom-empty/daughter/granddaughter1
Updating '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等)的情况下。在这种情况下,您可能只关心其中几个项目——也许是主要项目和几个它依赖的其他模块。您可以检出所有这些项目的单个工作副本,但这些工作副本是分离的,因此,同时对多个或所有工作副本执行操作可能会很麻烦。另一种方法是使用稀疏目录功能,构建一个只包含您关心的模块的单个工作副本。您将从对项目的公共父目录进行空深度检出开始,然后仅对您希望拥有的项目进行无限深度更新,就像我们在前面的示例中演示的那样。把它想象成工作副本公民的加入系统。

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

考虑一个包含数百个子目录的目录,其中一个您希望从工作副本中省略。使用稀疏目录的 添加式 方法,您可以以空深度检出目录,然后显式地伸缩(使用 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/*
…
$

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



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

TortoiseSVN 官方中文版 1.14.7 发布