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.

变更列表

对开发人员而言, 有时候可能会遇到这样一种情况: 在某些代码上完成 多个不同的修改. 这并非由于糟糕的工作计划, 因为开发人员常常在阅读某一部分的代码时, 发现另 一部分代码的问题, 又或许是开发人员把一个大修改拆分成几个逻辑性更强的小 修改, 而这几个小修改还没有全部完成. 很多时候, 这些小修改不能完全包含在一 个模块里, 修改之间也不能安全地隔开, 修改可能有重叠, 或修改了同一模块 的不同文件, 或修改了同一个文件的不同行.

开发人员可以采用不同的方法对这些在逻辑上分开的修改进行组织. 有的人 使用单独的工作副本保存未完成的修改, 其他人可能会创建短期的特性分支, 还 有的人会使用 diffpatch 来备份 与还原未提交的修改, 每一个修改都对应一个补丁文件. 每一种方法都有各自的 优缺点, 而且修改的细节会在很大程度上影响对修改进行区分的方法.

Subversion 提供了一种新方法: 变更列表 ( changelists). 变更列表基本上就是一些应用到工作副本文件上 的任意标签 (每个文件上最多只能有一个标签), 用来表示多个互相关联的文件的 共同目的, 经常使用谷歌软件的用户对此比较熟悉. 比如说 谷歌邮箱 并没有提供传统的基于文件夹 的邮件组织形式, 用户可以把任意的标签应用到邮件上, 如果有多个邮件的标签 相同, 就可以说它们是同一个组的, 查看具有类似标签的一组邮件变成了一个简单 的用户界面技巧. 很多 Web 2.0 网站也提供了类似的机制, 比如 YouTubeFlickr标签 (tag), 以及博文的 类别 (categories). 人们已经明白数据的 组织方式非常重要, 但是如何对数据进行组织应该是一个很灵活的概念. 旧的 文件与文件夹 范式对某些应用程序来说过于刻板.

Subversion 允许用户通过向文件打标签来创建变更列表, 如果一个文件被打 上标签, 说明该文件和这个变更列表是相关的, 用户还可以删除标签, 把命令的 操作限定到具有特定标签的文件上, 具体的细节将在本节进行介绍.

创建与修改变更列表

命令 svn changelist 用于创建, 修改和删除变更列表, 更准确地说这个命令可以设置或清除某个特定的工作副本文件上的变更列表关 联. 当用户第一次用某个变更列表为文件打标签时, 变更列表才被创建出来; 当用户把最后一个标签从文件上移除时, 对应的变更列表被删除. 下面用一个 例子来解释这些概念.

Harry 正在解决计算器程序中数字运算过程的几个问题, 他已经修改了几 个文件:

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

在测试的过程中, Harry 发现他的修改暴露了用户接口实现 button.c 里的一个问题, Harry 决定在另一个单独的提交中把 这个问题也解决掉. 在一个只包含了少量文件和修改的小工作副本里, Harry 可 以不依靠 Subversion 就可以对两个逻辑上不相关的修改进行组织, 但是今天 他想试用一下 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 着手修改用户接口的问题. 因为他知道将要修改哪个文件, 所 以他也向这个文件分配了一个变更列表, 不幸的是, 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
$

二是直接向 button.c 分配一个新的变更列表, 此时 Subversion 会先移除 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 仍然 会输出与它有关的信息, 这是因为 button.c 被分配了 一个变更列表. 任何时候都可以向文件添加或删除变更列表, 无论它们是否含有 本地修改.

接下来, Harry 解决了 button.c 的用户接口问题.

$ svn status

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

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

变更列表用作操作过滤器

我们在上一节看到的 svn status 对变更列表的分组 效果还不错, 但还不是很有用. 除了 svn status, 通过 选项 --changelist, 还有很多操作都会理解变更列表.

如果提供了选项 --changelist, Subversion 命令将会把 操作的作用域限定到具有特定变更列表的文件上. 假如说 Harry 想查看变更列表 math-fixes 里的文件的修改, 他可以在 svn diff 的后面显式地列出变更列表 math-fixes 的所有 文件.

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

准备提交时, 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
$
[注意] 注意

选项 --changelist 只是作为命令的操作目标的过滤 器, 它不会向命令添加更多的操作目标. 举个例子, 命令 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 目前还不支持在目录 上使用变更列表. 最后, 在工作副本的一个文件上最多只能分配一个变更列表, 如果用户发现自己需要在一个文件上分配多个变更列表, 那只能算你倒霉了.