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

变更列表

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

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

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

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

创建和修改变更列表

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

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

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

在测试他的错误修复时,Harry 注意到他的更改暴露了 button.c 中的用户界面逻辑中一个相关的错误。Harry 决定他将继续修复该错误,将其作为与他的数学修复分开提交。现在,在一个只有少数文件和少量逻辑更改的小型工作副本中,Harry 可能可以毫无问题地将他的两个逻辑更改分组在脑海中进行组织。但今天,他将使用 Subversion 的变更列表功能,作为对本书作者的特殊帮助。

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

$ svn changelist math-fixes integer.c mathops.c
A [math-fixes] integer.c
A [math-fixes] mathops.c
$ svn status

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

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

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

$ svn changelist math-fixes button.c
A [math-fixes] button.c
$ svn status

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

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

$ svn changelist --remove button.c
D [math-fixes] button.c
$ svn changelist ui-fix button.c
A [ui-fix] button.c
$

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

$ svn changelist ui-fix button.c
D [math-fixes] button.c
A [ui-fix] button.c
$ svn status

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

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

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

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

$ svn status

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

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

变更列表作为操作过滤器

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

当提供 --changelist 选项时,Subversion 命令将限制其操作范围,仅限于分配了特定变更列表名称的文件。如果 Harry 现在想查看他对 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)
…
$

对于几个文件来说,这还可以,但如果 Harry 的更改影响了 20 或 30 个文件呢?这将是一个令人讨厌的长长的显式命名文件列表。现在他正在使用变更列表,因此 Harry 可以从现在开始避免显式列出变更列表中的文件集,而是只提供变更列表名称。

$ 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)
…
$

当要提交时,Harry 可以再次使用 --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 .
D [math-fixes] integer.c
A [math-bugs] integer.c
D [math-fixes] mathops.c
A [math-bugs] mathops.c
$ svn changelist --remove --changelist math-bugs --depth infinity .
D [math-bugs] integer.c
D [math-bugs] mathops.c
$

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

变更集限制

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