This text is a work in progress—highly subject to change—and may not accurately describe any released version of the Apache™ Subversion® software. Bookmarking or otherwise referring others to this page is probably not such a smart idea. Please visit http://www.svnbook.com/ for stable versions of this book.

稀疏目录

默认情况下, 大多数 Subversion 操作在处理目录时会采用递归的方式, 比如说, svn checkout 会检出仓库指定区域内的所有文件与目录. Subversion 1.5 引入了一个新特性: 稀疏目录 (sparse directories, 或 浅检出 (shallow checkouts)). 和完整的递归 操作相比, 新特性允许用户更加轻浅地检出工作副本—或工作副本的一部分, 以后仍然还能访问到原来未被检出的文件与子目录.

举个例子, 假设我们有一个仓库, 仓库中存放的是拥有宠物的家庭成员 (这个 例子确实有点奇怪), 普通的 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. 很多子命令都支持这个选项, 选项的意义类似于 --non-recursive (-N) 和 --recursive (-R). 实际上, Subversion 希望选项 --depth 最终能超过并替换掉这两个旧选项. 对 新手来说, --depth 拓宽了用户能够指定的操作深度, 增加了 一些原来不支持 (或支持地不一致) 的深度. 下面是用户可以使用的几种深度值:

--depth empty

只包含操作的直接目标, 不包括其中的文件或子目录.

--depth files

只包含操作的直接目标及其中的直接子文件.

--depth immediates

包括操作的目标自身, 及它的直接子文件与直接子目录, 子目录为空.

--depth infinity

包括目标自身, 及它的所有子文件与子目录, 子目录的子文件与 子目录, 等等.

当然, 如果仅仅是把两个选项合并成一个选项, 那就没必要花费整整一节的笔墨 介绍它, 幸运的是远不止选项合并这么简单. 深度的概念不仅延伸到 Subversion 客户端执行的操作, 同时还描述了工作副本的 周围深度 (ambient depth), 它是工作副本为项目记录的深度. 深度的关键之处在于它是 粘着 (sticky) 的, 工作副本记住了用户为每一个项目 指定的深度, 在用户显式地修改之前, 项目的深度不会发生变化. 默认情况下, 不管文件的深度设置是什么样的, Subversion 的命令只会操作工作副本中已有 的项目.

[提示] 提示

可以用命令 svn info 查看工作副本的周围深度, 如果周围深度是除了无限递归外的其他内容, svn info 就会显示一行描述深度值的信息:

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

前面的两个例子演示了深度值 infinity ( svn checkout 的默认行为) 和 empty 的效果, 现在看一下其他深度的例子:

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

empty 相比, 这些深度会得到更多的内容, 但和 infinity 相比, 会得到更少的内容.

我们已经介绍了 svn checkout 如何利用选项 --depth, 但读者会看到除了 checkout, 还有很多子命令也支持 --depth. 在这些命令中, 指定深度 将操作的作用域限制在某一层次上, 非常类似老选项 --non-recursive --recursive 的行为. 这就意味着当我们操作 一个处在某个深度上的工作副本时, 可以执行一个深度更浅的操作. 实际上, 我 们可以更一般地说: 对于一个给定的, 处于任意周围深度 (深度可以是混合的) 的工作副本, 和一个指定了操作深度 (或使用默认值) 的 Subversion 命令, 命令 将保持工作副本的周围深度不变, 同时将操作的作用域限制在所给定 (或默认的) 的深度上.

除了选项 --depth, 命令 svn updatesvn switch 还支持第二种与深度有关的选项 --set-depth, 它可以修改工作副本中项目的粘着深度. 现在看 一下如何使用 svn update --set-depth NEW-DEPTH TARGET, 把原来深度为 empty 的工作副本逐渐加深:

$ 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/project1 , trunk/project2, trunk/project3 等), 但是用户可能只关心其中的部分模块— 比如说项目的主要模块及其依赖模块. 用户可以分别检出他所关心的各个模块的 工作副本, 但是这些工作副本之间是互不相交的, 如果想同时对它们执行同一个操作 就会很麻烦, 必须多次切换目录. 另一种选择是利用稀疏目录特性, 检出一个只 包含了感兴趣的模块的工作副本. 首先为模块的公共父目录检出一个深度为 empty 的工作副本, 然后按照深度 infinity 更新感兴趣的模块目录, 就像我们在上一个例子中展示的那样. 可以把稀疏目录看成是工作副本中项目的选入系统.

浅检出的原始实现 (Subversion 1.5) 就已经很不错了, 但是它不能缩减 工作副本项目的深度, Subversion 1.6 解决了这个问题. 比如说在一个深度 原来是 infinity 的工作副本里执行 svn update --set-depth empty, 工作副本就会删除除了顶层目录 外的所有文件与目录 [26] Subversion 1.6 还 为选项 --set-depth 引入的一个新的值: exclude . 如果给命令 svn update 带上选项 --set-depth exclude 会造成被更新的目标从工作副本中完全 删除—如果目标是一个目录, 那么目录也会被完全删除, 而不是留下一个 空目录. 如果工作副本中用户想保留的东西要比不想保留的东西多, 那 --set-depth exclude 就能提供很大的方便.

考虑一个包含了几百个子目录的目录, 用户想要从工作副本中忽略其中一个 子目录, 如果是用 增量 的方法得到稀疏目录, 首先先检出一 个深度为 empty 的工作副本, 然后显式地把每一个子目录 的深度设置成 infinity (使用 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
$

和第一种方法相比, 使用第二种方法后在工作副本里留下的数据是相同的, 但是如果有新的子目录被提交到仓库中, 更新工作副本时仍然可以看到. 第二种 方法的缺点是一开始要检出后来不用的子目录, 如果子目录过于庞大, 大到磁盘 无法容纳 (可能这就是用户不想把它检出到工作副本里的原因).

[注意] 注意

虽然这个功能—从工作副本中排除已有的项目—由 svn update 完成, 但读者可能已经注意到 svn update --set-depth exclude 的输出和通常的 svn update 的不太一样. 排除是一个完全客户端的操作, 但命令的输出却不太符合这个事实.

如果出现这种情况, 你可能需要一个折衷的方法. 首先, 使用 --depth immediates 检出顶层目录, 然后用 svn update --set-depth exclude 排除不感兴趣的子目录, 最后, 把剩下的子目录的深度设置成 infinity, 因为子目录都已 经出现在本地了, 所以应该会容易一点.

$ 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/*
…
$

再说一次, 这种方法得到的工作副本里的数据和前两种方法完全相同, 当有 新的文件或目录提交到顶层目录时, 更新操作按深度 empty 把文件或目录更新到本地, 接下来你可以决定针对新出现的项目应该采取什么 操作: 是把深度扩展到 infinity, 还是把它排除.



[26] 删除操作是安全的, Subversion 会保留 修改过的或未被版本控制的项目.