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 diff

从行的级别上查看修改的内容

svn log

和版本号绑定的日志消息, 及其日期, 作者, 以及受影响的文件 路径.

svn cat

根据给定的版本号, 输出文件在该版本下的内容.

svn annotate

根据给定的版本号, 查看该版本下的文件的每一行的最后一 次修改信息.

svn list

根据给定的版本号, 列出仓库在该版本下的文件与目录清单.

查看历史修订的细节

我们已经介绍过 svn diff—按照标准差异格式 显示文件的变化, 前文我们是用它显示工作副本的本地修改.

实际上, svn diff 有三种用法:

  • 查看本地修改

  • 比较工作副本和仓库

  • 比较仓库的版本号

查看本地修改

如果不带选项地执行 svn diff, 命令就会拿文件的当前内容和存放在 .svn 中的原始 文件作对比:

$ svn diff
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

比较工作副本和仓库

如果带上选项 --revision (-r), 命令就把工作副本和仓库中指定的版本号作对比:

$ svn diff -r 3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

比较仓库的版本号

如果用选项 --revision (-r) 传递了一对用冒号隔开的版本号, 命令就会比较这两个版本号的差异.

$ svn diff -r 2:3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth open
$

如果要比较某个版本号与前一个版本号, 比较方便的做法是用选项 --change (-c):

$ svn diff -c 3 rules.txt
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth open
$

最后, 即使本地机器上没有工作副本, svn diff 也可以比较仓库的版本号, 方法是在命令行中指定 URL:

$ svn diff -c 5 http://svn.example.com/repos/example/trunk/text/rules.txt
…
$

生成历史修改列表

为了查看某个文件或目录的历史修改信息, 使用命令 svn log, 它显示的信息包括提交修改的作者, 版本号, 时间和日期, 以及日志消息 (如果有的话):

$ svn log
------------------------------------------------------------------------
r3 | sally | 2008-05-15 23:09:28 -0500 (Thu, 15 May 2008) | 1 line

Added include lines and corrected # of cheese slices.
------------------------------------------------------------------------
r2 | harry | 2008-05-14 18:43:15 -0500 (Wed, 14 May 2008) | 1 line

Added main() methods.
------------------------------------------------------------------------
r1 | sally | 2008-05-10 19:50:31 -0500 (Sat, 10 May 2008) | 1 line

Initial import
------------------------------------------------------------------------

注意, svn log 默认按照时间逆序来打印消息, 如果用户只想查看某段范围内的日志, 或者是单个版本号的日志, 又或者是想 改变打印顺序, 就带上选项 --revision (-r):

表 2.1. 常见的日志请求

命令 描述
svn log -r 5:19 按照时间顺序打印从版本号 5 到 19 的日志
svn log -r 19:5 按照时间逆序打印从版本号 5 到 19 的日志
svn log -r 8 显示版本号 8 的日志

也可以只查看单个文件或目录的日志, 例如:

$ svn log foo.c
…
$ svn log http://foo.com/svn/trunk/code/foo.c
…

上面的命令只会显示和 foo.c 相关的版本号的 日志.

如果用户想在日志消息中看到更多的细节, 就带上选项 --verbose (-v). 因为 Subversion 允许用户移动和复制文件或目录, 所以如果能在日志中看到文件路径的变化 就方便多了. 带上选项 --verbose (-r) 后, svn log 的输出中就会包含被修改的文件路径:

$ svn log -r 8 -v
------------------------------------------------------------------------
r8 | sally | 2008-05-21 13:19:25 -0500 (Wed, 21 May 2008) | 1 line
Changed paths:
   M /trunk/code/foo.c
   M /trunk/code/bar.h
   A /trunk/code/doc/README

Frozzled the sub-space winch.

------------------------------------------------------------------------

svn log 还支持选项 --quiet (-q), 它会阻止打印日志消息主体, 如果和 --verbose (-v) 一起使用, 那么 svn log 只会打印被修改的文件路径.

从 Subversion 1.7 开始, 用户还可以让 svn log 产生标准差异格式的输出, 就像 svn diff. 如果给 svn log 加上选项 --diff, 用户就可以在行的级别上看到本次修订的具体修改内容, 于是, 用户可以同 时从高层的语义修改和底层的基于行的变化来查看文件的修改历史.

从 Subversion 1.8 开始, svn log 支持选项 --search--search-and. 这两个 选项允许用户指定搜索模式字符串, 从而过滤 svn log 的输出: 只有当版本号的作者, 日期, 日志消息或被修改的文件路径与搜索 模式匹配时, 才会输出该日志.

浏览仓库

利用 svn catsvn list, 用户可以查看任意一个版本号下的文件和目录, 而无须修改工作副本, 实际上, 在使用这两个命令时甚至都不需要工作副本.

显示文件的内容

如果用户只想查看文件旧版本的内容, 可以用 svn cat:

$ svn cat -r 2 rules.txt
Be kind to others
Freedom = Chocolate Ice Cream
Everything in moderation
Chew with your mouth open
$

还可以把输出重定向到一个文件中:

$ svn cat -r 2 rules.txt > rules.txt.v2
$

显示每一行的修改属性

svn cat 比较类似的命令是 svn annotate 有点类似, 但是 svn annotate 的输出更丰富—除了文件的内容外, 还会输出每一行 最后一次被修改时的作者, 版本号及其日期 (可选).

如果参数是工作副本中的文件, svn annotate 会根据文件的当前内容输出每一行的属性:

$ svn annotate rules.txt
     1      harry Be kind to others
     3      sally Freedom = Responsibility
     1      harry Everything in moderation
     -          - Chew with your mouth closed
     -          - Listen when others are speaking

在上面的例子里, 某些行的属性没有打印出来, 原因是这几行在工作 副本中被修改了. 利用这个特点, 我们也可以通过 svn annotate 判断出文件的哪些行被修改了. 用户可以用版本号关键词 BASE (见 “版本号关键字”一节) 查看文件的未修改版本的输出:

$ svn annotate rules.txt@BASE
     1      harry Be kind to others
     3      sally Freedom = Responsibility
     1      harry Everything in moderation
     1      harry Chew with your mouth open

选项 --verbose (-v) 使得 svn annotate 在输出中增加每一行的版本号的 提交日期 (这会显著增加输出内容的宽度, 所以我们不在这里展示添加 了选项 --verbose 后的运行效果).

svn cat 一样, svn annotate 也能针对文件的旧版本进行操作, 这个功能有时候会很有 帮助—如果用户已经找到了文件中某一行的最后一次修改的版本号, 他可能还想知道在此之前是谁最后一次修改了这一行:

$ svn blame rules.txt -r 2
     1      harry Be kind to others
     1      harry Freedom = Chocolate Ice Cream
     1      harry Everything in moderation
     1      harry Chew with your mouth open

svn cat 不同的是, svn annotate 的正常运行要求文件必须是人类可读的, 以行为单位的文本 文件, 如果 Subversion 认为文件不是人类可读的 (根据文件的 svn:mime-type 属性—见 “文件内容类型”一节), svn annotate 就输出一条错误消息:

$ svn annotate images/logo.png
Skipping binary file (use --force to treat as text): 'images/logo.png'
$

就像错误消息中提示的那样, 用户可以通过增加选项 --force 来禁止 Subversion 去检查文件是否是人类 可读的. 如果用户强制要求 svn annotate 去读取 非人类可读的文件, 命令就会输出一堆混乱的信息:

$ svn annotate images/logo.png --force
     6      harry \211PNG
     6      harry ^Z
     6      harry 
     7      harry \274\361\MI\300\365\353^X\300…
[提示] 提示

根据执行命令时的心情, 用户可能不会使用标准形式 (svn annotate), 转而使用 svn blame svn praise, 后面两种形式是 标准形式的别名, 它们是完全等价的.

和许多获取信息的命令一样, svn annotate 也接受仓库的 URL 作为参数, 这样即使没有工作副本, 用户也可以照常 执行命令.

列出被版本控制的文件

命令 svn list 可以列出仓库目录中的文件, 而 不用把它们下载到本地:

$ svn list http://svn.example.com/repo/project
README
branches/
tags/
trunk/

如果想得到更详细的信息, 添加选项 --verbose (-v):

$ svn list -v http://svn.example.com/repo/project
  23351 sally                 Feb 05 13:26 ./
  20620 harry            1084 Jul 13  2006 README
  23339 harry                 Feb 04 01:40 branches/
  23198 harry                 Jan 23 17:17 tags/
  23351 sally                 Feb 05 13:26 trunk/

从左到右分别表示文件或目录最后一次被修改时的版本号, 作者, 文件大小 (仅针对文件), 日期以及文件或目录的名字.

[警告] 警告

不带参数的 svn list 默认使用当前工作 目录对应的仓库 URL 作为参数, 而非本地的工作副本目录. 毕竟, 如果 用户想要列出本地目录中的文件, 应该使用 Shell 命令 ls (或其他非 Unix 系统的等价命令).

获取老的仓库快照

用户可以用带有选项 --revision (-r) 的 svn update 命令, 把整个工作副本回退到旧版本: [7]

# Make the current directory look like it did in r1729.
$ svn update -r 1729
Updating '.':
…
$
[提示] 提示

许多 Subversion 新手会尝试利用上面的 svn update 例子来 撤消 (undo) 已经提交的修改, 但是这不可能 实现, 因为如果被修改的文件的版本号比较旧, 那就无法成功提交. 关于如何 撤消 提交请参考 “恢复已删除的文件”一节

如果用户想以较老的快照为基础创建一个新的工作副本, 只要稍微修改 一下 svn checkout 的命令行即可; 对于 svn update, 可以给它添加一个选项 --revision (-r). 由于 “限定版本号与实施版本号”一节 介绍的原因, 用户可能想 把目标版本号作为 Subversion 扩展 URL 语法的一部分.

# Checkout the trunk from r1729.
$ svn checkout http://svn.example.com/svn/repo/trunk@1729 trunk-1729
…
# Checkout the current trunk as it looked in r1729.
$ svn checkout http://svn.example.com/svn/repo/trunk -r 1729 trunk-1729
…
$

如果用户想构建一个发布版, 其中包含了所有的被版本控制的文件和 目录, 命令 svn export 可以完成这项工作. svn export 在本地创建一份仓库的完整或部分副本, 但是没有 .svn 目录. 命令的基本语法和 svn checkout 相同:

# Export the trunk from the latest revision.
$ svn export http://svn.example.com/svn/repo/trunk trunk-export
…
# Export the trunk from r1729.
$ svn export http://svn.example.com/svn/repo/trunk@1729 trunk-1729
…
# Export the current trunk as it looked in r1729. 
$ svn export http://svn.example.com/svn/repo/trunk -r 1729 trunk-1729
…
$


[7] 看到了吗? 我们早就告诉过你 Subversion 是一台时间机器.