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

变更列表

开发人员经常发现自己同时处理同一部分源代码的多个不同更改。这并不一定是因为计划不周或某种数字虐待狂。软件工程师在处理某段附近的源代码时,经常会在视野范围内发现错误。或者,也许他正在进行一些大型更改,当他意识到他正在进行的解决方案最好提交为几个较小的逻辑单元时。通常,这些逻辑单元不会很好地包含在某个模块中,并与其他更改安全地隔离。这些单元可能会重叠,修改同一模块中的不同文件,甚至修改同一文件中的不同行。

开发人员可以采用各种工作方法来保持这些逻辑更改的组织。有些人使用同一存储库的不同工作副本,以保存每个正在进行的更改。其他人可能会选择在存储库中创建短暂的特性分支,并使用一个始终切换到指向该分支或其他分支的工作副本。还有一些人使用 diffpatch 工具来备份和恢复与每个更改关联的补丁文件中的未提交更改。每种方法都有其优点和缺点,在很大程度上,所做更改的详细信息会极大地影响用于区分这些更改的方法。

Subversion 提供了一个 变更列表 功能,为该方法添加了另一种方法。变更列表基本上是应用于工作副本文件的任意标签(目前每个文件最多一个),其明确目的是将多个文件关联在一起。许多 Google 软件产品的用户已经熟悉此概念。例如,Gmail 不提供传统的基于文件夹的电子邮件组织机制。在 Gmail 中,您将任意标签应用于电子邮件,如果多封电子邮件恰好共享特定标签,则可以认为它们属于同一组。然后,仅查看一组具有相似标签的电子邮件将成为一个简单的用户界面技巧。许多其他 Web 2.0 网站都有类似的机制——考虑 标签 用于网站,例如 YouTubeFlickr类别 应用于博客文章,等等。人们今天明白数据组织非常重要,但如何组织这些数据需要成为一个灵活的概念。旧的文件和文件夹范例对于某些应用程序来说过于僵化。

Subversion 的变更列表支持允许您通过将标签应用于要与该变更列表关联的文件来创建变更列表,删除这些标签,并将子命令的操作范围限制为仅包含特定标签的文件。在本节中,我们将详细了解如何执行这些操作。

创建和修改变更列表

您可以使用 svn changelist 命令创建、修改和删除变更列表。更准确地说,您使用此命令来设置或取消设置特定工作副本文件的变更列表关联。当您第一次使用该变更列表标记文件时,实际上就创建了一个变更列表;当您从具有该标签的最后一个文件中删除该标签时,它将被删除。让我们检查一个演示这些概念的使用场景。

哈里正在修复计算器应用程序的数学逻辑中的错误。他的工作导致他更改了几个文件

$ svn status
M       integer.c
M       mathops.c
$

在测试他的错误修复时,哈里注意到他的更改揭示了在 button.c 中发现的用户界面逻辑中一个与之相关的错误。哈里决定,他将继续修复该错误,并将其作为与他的数学修复程序独立的提交。现在,在一个只有少量文件和少量逻辑更改的小型工作副本中,哈里可能能够毫无问题地在他的脑海中将两个逻辑更改分组保持条理。但今天他将使用 Subversion 的变更列表功能,作为对本书作者的特殊帮助。

哈里首先创建一个变更列表,并将已经更改的两个文件与它关联。他通过使用 svn changelist 命令将相同的任意变更列表名称分配给这些文件来做到这一点

$ svn changelist math-fixes integer.c mathops.c
Path 'integer.c' is now a member of changelist 'math-fixes'.
Path 'mathops.c' is now a member of changelist 'math-fixes'.
$ svn status

--- Changelist 'math-fixes':
M       integer.c
M       mathops.c
$

如您所见,svn status 的输出反映了这个新的分组。

哈里现在开始修复次要的 UI 问题。由于他知道他将要更改哪个文件,因此他也将该路径分配给了一个变更列表。不幸的是,哈里不小心将第三个文件分配给了与前两个文件相同的变更列表

$ svn changelist math-fixes button.c
Path 'button.c' is now a member of changelist 'math-fixes'.
$ svn status

--- Changelist 'math-fixes':
        button.c
M       integer.c
M       mathops.c
$

幸运的是,哈里发现了他的错误。此时,他有两种选择。他可以从 button.c 中删除变更列表关联,然后分配不同的变更列表名称

$ svn changelist --remove button.c
Path 'button.c' is no longer a member of a changelist.
$ svn changelist ui-fix button.c
Path 'button.c' is now a member of changelist 'ui-fix'.
$

或者,他可以跳过删除操作,只需分配一个新的变更列表名称。在这种情况下,Subversion 将首先警告哈里,button.c 将从第一个变更列表中删除

$ svn changelist ui-fix button.c
svn: warning: Removing 'button.c' from changelist 'math-fixes'.
Path 'button.c' is now a member of changelist 'ui-fix'.
$ svn status

--- Changelist 'ui-fix':
        button.c

--- Changelist 'math-fixes':
M       integer.c
M       mathops.c
$

哈里现在在他的工作副本中拥有两个不同的变更列表,而 svn status 将根据这些变更列表的确定结果对它的输出进行分组。请注意,即使哈里尚未修改 button.c,它仍然显示在 svn status 的输出中,因为它是有趣的,因为它具有变更列表分配。可以随时将变更列表添加到文件或从文件中删除,而与它们是否包含本地修改无关。

哈里现在修复了 button.c 中的用户界面问题。

$ svn status

--- Changelist 'ui-fix':
M       button.c

--- Changelist 'math-fixes':
M       integer.c
M       mathops.c
$

变更列表作为操作过滤器

在上一节中显示的 svn status 输出中,哈里看到的视觉分组很好,但并不完全有用。status 命令只是他可能希望在其工作副本上执行的许多操作之一。幸运的是,Subversion 的许多其他操作都了解如何通过使用 --changelist 选项在变更列表上进行操作。

当提供 --changelist 选项时,Subversion 命令将限制其操作范围,仅限于分配有特定变更列表名称的文件。如果哈里现在想要查看他对其 math-fixes 变更列表中的文件所做的实际更改,他 可以svn diff 命令行中显式列出构成该变更列表的文件。

$ svn diff integer.c mathops.c
Index: integer.c
===================================================================
--- integer.c	(revision 1157)
+++ integer.c	(working copy)
…
Index: mathops.c
===================================================================
--- mathops.c	(revision 1157)
+++ mathops.c	(working copy)
…
$

这对于几个文件来说还可以,但如果哈里的更改触及了 20 或 30 个文件怎么办?那将是一个令人厌烦的显式命名文件列表。现在他正在使用变更列表,因此哈里可以避免从现在开始显式列出其变更列表中的一组文件,而是只需提供变更列表名称

$ svn diff --changelist math-fixes
Index: integer.c
===================================================================
--- integer.c	(revision 1157)
+++ integer.c	(working copy)
…
Index: mathops.c
===================================================================
--- mathops.c	(revision 1157)
+++ mathops.c	(working copy)
…
$

到提交的时候,哈里可以再次使用 --changelist 选项来将提交的范围限制在特定变更列表中的文件。他可以通过执行以下操作来提交他的用户界面修复程序

$ svn commit -m "Fix a UI bug found while working on math logic." \
      --changelist ui-fix
Sending        button.c
Transmitting file data .
Committed revision 1158.
$

实际上,svn commit 命令提供了第二个与变更列表相关的选项:--keep-changelists。通常,在文件提交后,会从文件中删除变更列表分配。但如果提供了 --keep-changelists,Subversion 将保留已提交(现在未修改)文件的变更列表分配。无论如何,提交分配给一个变更列表的文件会保留其他变更列表不受影响。

$ svn status

--- Changelist 'math-fixes':
M       integer.c
M       mathops.c
$
[Note] 注意

--changelist 选项仅充当 Subversion 命令目标的过滤器,不会向操作添加目标。例如,在指定为 svn commit /path/to/dir 的提交操作中,目标是目录 /path/to/dir 及其子目录(到无限深度)。如果您随后向该命令添加一个变更列表说明符,则仅考虑目录 /path/to/dir 中以及其下分配了该变更列表名称的文件作为提交目标——提交将不包括位于其他位置的文件(例如在 /path/to/another-dir 中),无论其变更列表分配如何,即使它们是与操作目标位于同一个工作副本中。

甚至 svn changelist 命令也接受 --changelist 选项。这使您可以快速轻松地重命名或删除变更列表

$ svn changelist math-bugs --changelist math-fixes --depth infinity .
svn: warning: Removing 'integer.c' from changelist 'math-fixes'.
Path 'integer.c' is now a member of changelist 'math-bugs'.
svn: warning: Removing 'mathops.c' from changelist 'math-fixes'.
Path 'mathops.c' is now a member of changelist 'math-bugs'.
$ svn changelist --remove --changelist math-bugs --depth infinity .
Path 'integer.c' is no longer a member of a changelist.
Path 'mathops.c' is no longer a member of a changelist.
$

最后,您可以在单个命令行中指定 --changelist 选项的多个实例。这样做会将您正在执行的操作限制在从任何指定的更改集中找到的文件。

变更列表限制

Subversion 的变更列表功能是用于对工作副本文件进行分组的便捷工具,但它确实有一些限制。变更列表是特定工作副本的工件,这意味着变更列表分配不能传播到存储库或以其他方式与其他用户共享。变更列表只能分配给文件——Subversion 目前不支持将变更列表与目录一起使用。最后,您最多可以在给定工作副本文件上拥有一个变更列表分配。这就是博客文章类别和照片服务标签类比失败的地方——如果您发现自己需要将文件分配给多个变更列表,那么您就走运了。