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 支持的特性与选项非常丰富, 但是能够在日常工作中用到的却很 少. 本节将介绍日常工作中最常用到的 Subversion 操作.

典型的工作周期就像:

  1. 更新工作副本. 这会用到命令 svn update.

  2. 修改. 最常见的修改就是编辑已有文件的内容, 但有时还要添加, 删除, 复制和移动文件或目录 — 命令 svn add, svn delete, svn copysvn move 负责 处理工作副本的结构性调整.

  3. 审查修改. 用命令 svn statussvn diff 查看工作副本发生了哪些变化.

  4. 修正错误. 人无完人, 在审查修改时用户可 能会发现某些修改是不正确的. 有时候修正错误最简单的方式是撤消所有的 修改, 重新开始. 命令 svn revert 可以把文件或目 录恢复到修改前的样子.

  5. 解决冲突 (合并其他人的修改). 当一个用户 正在修改文件时, 其他人可能已经把自己的修改提交到了服务器上. 为了防止 在提交修改时, 由于工作副本过旧导致提交失败, 用户需要把其他人的修改 更新到本地, 用到的命令是 svn update. 如果命令 的执行结果有冲突产生, 用户需要用命令 svn resolve 解决冲突.

  6. 发布 (提交) 修改. 命令 svn commit 把工作副本的修改提交到仓库中, 如果修改 被接受, 其他用户就可以看到这些修改.

更新工作副本

如果某个项目正在被多个工作副本修改, 用户就需要更新自己本地的 工作副本, 以获取其他人提交的修改. 这些修改可能来自团队中的其他开发 人员, 也可能是自己在其他地方提交的修改. Subversion 不允许用户向过时 的文件或目录提交修改, 所以在开始修改前, 最好保证本地工作副本的内容 是最新的.

命令 svn update 把仓库上的最新数据同步到本地 的工作副本:

$ svn update
Updating '.':
U    foo.c
U    bar.c
Updated to revision 2.
$

从上面的例子可以看到, 在你最后一次更新了工作副本后, 有人修改了 foo.cbar.c, Subversion 把更新同步到本地工作副本.

通过命令 svn update, 服务器把修改应用到本地的 工作副本, 同时在被更新项目的旁边显示一个字母, 表示 Subversion 对文件 采取了什么操作. 为了明白这些字母的涵义, 查看 svn help update 的输出或参考 svn 参考手册—Subversion 命令行客户端svn update (up).

修改

现在用户可以开始工作, 修改工作副本里的资料. 工作副本支持的修改类型分 为两种: 文件修改 (file changes) 和 目录修改 (tree changes). 在修改文件时不需要告知 Subversion, 用户可以使用任意一种自己喜欢的工具来修改文件, 例如编辑 器, 字处理程序, 图形工具等. Subversion 可以自动检测到哪些文件发生了 变化, 处理二进制文件和处理文本文件一样简单高效. 目录修改涉及到目录结构 的变化, 例如添加和删除文件, 重命名文件和目录, 复制文件和目录. 目录修改 要使用 Subversion 的命令完成. 文件修改和目录修改只有在提交后才会更新 到仓库中.

下面是最常用到的 5 个改变目录结构的 Subversion 子命令:

svn add FOO

这个命令把文件, 目录或软链接 FOO 添加 到需要进行版本控制的名单中, 在下一次提交时, FOO 就会正式添加到仓库里. 如果 FOO 是一个目录, 那么目录内的所有内容都会 被添加到仓库中. 如果只想添加 FOO 它自己, 就带上选项 --depth=empty.

svn delete FOO

上面的命令从工作副本中删除文件, 目录或符号链接 FOO, 在下一次提交时, FOO 就会从仓库中删除. (当然, 没有什么东西可以从仓库中被完全地删除干净 —它们只是从版本号 HEAD 中删除, 用户可以 从更早的版本中看到被删除的文件). [6]

svn copy FOO BAR

FOO 复制出一个 BAR, 并把 BAR 添加到 需要进行版本控制的名单中. BAR 被提交到仓库 后, Subversion 会记录它是由 FOO 复制得到的. 除非带上选项 --parents, 否则 svn copy 不会创建父目录.

svn move FOO BAR

这条命令等价于 svn copy FOO BAR; svn delete FOO , 也就是从 FOO 复制出一个 BAR, 然后再删除 FOO. 除非带上选项 --parents, 否则 svn move 不会创建父目录.

svn mkdir FOO

该命令等价于 mkdir FOO; svn add FOO, 也就是创建一个新目录 FOO, 并把它添加到仓库中.

审查修改

工作副本修改完成后, 就要把它们都提交到仓库中, 不过在提交之前, 应该查看 一下自己到底修改了哪些东西. 通过检查修改, 用户可以写出更准确的 提交日志 (log message, 和修改一起存放到仓库中的一段文本, 该文本以人类可读的形式描述了本次 修改的相关信息). 在审查修改时, 用户可能会发现自己无意中修改了一个不相 关的文件, 因此在提交之前需要撤消它的修改. 用户可以使用命令 svn status 查看修改的整体概述, 用命令 svn diff 查看修改的细节.

查看修改的整体概述

为了看到修改的整体概述, 使用命令 svn status, 它可能是用户最常用到的一个 Subversion 命令.

[提示] 提示

因为 cvs status 的输出非常庞杂, 而 cvs update 不仅执行更新操作, 还会报告本 地的修改情况, 所以大多数 CVS 用户更喜欢用 cvs update 来查看修改情况. 对 Subversion 来 说, 更新和状态报告这两个功能是完全分离的, 更多的细节在 “状态与更新的区别”一节.

如果在工作副本的根目录不加任何参数地执行 svn status, Subversion 就会检查并报告所有 文件和目录的修改.

$ svn status
?       scratch.c
A       stuff/loot
A       stuff/loot/new.c
D       stuff/old.c
M       bar.c
$

在默认的输出模式下, svn status 先打印 7 列 字符, 然后是几个空白字符, 最后是文件或目录名. 第一列字符报告文件或 目录的状态, 其中最常的几种字符或状态是:

? item

文件, 目录或符号链接 item 不在版本 控制的名单中.

A item

文件, 目录或符号链接 item 是新增的, 在下一次提交时就会加入到仓库中.

C item

文件 item 有未解决的冲突, 意思是说从 服务器收到的更新和该文件的本地修改有所重叠, Subversion 在处理 这些重叠的修改时发生了冲突. 用户必须解决掉冲突后才能向仓库 提交修改.

D item

文件, 目录或符号链接 item 已被删除, 在下一次提交时就会从仓库中删除 item.

M item

文件 item 的内容被修改.

如果给 svn status 传递一个路径名, 那么命 令只会输出和该路径相关的状态信息:

$ svn status stuff/fish.c
D       stuff/fish.c

svn status 支持选项 --verbose (-v), 带上该选项后, 命令会输出当前目录中每一项的 状态, 即使是未被修改的项目:

$ svn status -v
M               44        23    sally     README
                44        30    sally     INSTALL
M               44        20    harry     bar.c
                44        18    ira       stuff
                44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
                44        21    sally     stuff/things
A                0         ?     ?        stuff/things/bloo.h
                44        36    harry     stuff/things/gloo.c

这是 svn status长格式 (long form) 输出. 第一列字符的含义不变, 第二列显示该项在工作副本 中的版本号, 第三和第四列显示该项最后一次被修改的版本号和作者.

前面执行的几次 svn status 都不需要和仓库 通信 — 它们只是根据工作副本管理区里的数据和文件当前的内容 来报告各个文件的状态. 有时候用户可能想知道在上一次更新之后, 哪些 文件在仓库中又被更新了, 为此, 可以给 svn status 带上选项 --show-updates (-u), 这样 Subversion 就会和仓库通信, 输出工作副本中已过时的项目:

$ svn status -u -v
M      *        44        23    sally     README
M               44        20    harry     bar.c
       *        44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
A                0         ?     ?        stuff/things/bloo.h
Status against revision:   46

注意带有星号的那 2 行, 如果此时执行 svn update, 就会从仓库收到 READMEtrout.c 的更新. 除此之外我们还可以知道, 在本地被修改的文件当中, 至少有一个在仓库中 也被更新了 (文件 README), 所以用户必须在提交前把 仓库的更新同步到本地, 否则仓库将会拒绝针对已过时文件的提交, 关于这点我们会在后面介绍更多的细节.

除了我们介绍的例子, svn status 还可以显示更 丰富的信息, 关于 svn status 更详细的介绍, 查看 svn help status 的输出或阅读 svn 参考手册—Subversion 命令行客户端svn status (stat, st)

查看修改的细节

查看修改的另一个命令是 svn diff, 它会输出文件 内容的变化. 如果在工作副本的根目录不加任何参数地执行 svn diff, Subversion 就会输出工作副本中人类 可读的文件的变化. 文件的变化以 标准差异 (unified diff) 格式进行输出, 这种格式把文件 内容的变化描述成 (hunk) 或 片断 (snippet), 其中每一行文本都加上一个单字符前缀: 空格表示该行没有 变化; 负号 (-) 表示该行被删除; 正号 (+) 表示该行是新增的. 在 svn diff 的语境中, 这些冠以正负号的行显示了修改 前的行和修改后的行分别是什么样子的.

这是一个执行 svn diff 的例子:

$ svn diff
Index: bar.c
===================================================================
--- bar.c	(revision 3)
+++ bar.c	(working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>

 int main(void) {
-  printf("Sixty-four slices of American Cheese...\n");
+  printf("Sixty-five slices of American Cheese...\n");
 return 0;
 }

Index: README
===================================================================
--- README	(revision 3)
+++ README	(working copy)
@@ -193,3 +193,4 @@
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c	(revision 1)
+++ stuff/fish.c	(working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h	(revision 8)
+++ stuff/things/bloo.h	(working copy)
+Here is a new file to describe
+things about bloo.

svn diff 在比较了工作副本中的文件和基文本后 再输出它们之间的差异. 在命令的输出中, 新增的文件其每一行都被冠 以正号; 被删除的文件其每一行都被冠以负号. svn diff 的输出格式和程序 patch 以及 Subversion 1.7 引入的子命令 svn patch 兼容. 处理补丁的命令 (例如 patchsvn patch) 可以读取并应用 补丁文件 (patch files, 简称 补丁). 利用补丁, 用户就可以在不提交的情况下, 把工作副本的修改分享给其他 人, 创建补丁的方式是把 svn diff 的输出重定向到 补丁文件里:

$ svn diff > patchfile
$

Subversion 默认使用它自己内部的差异比较程序来生成标准差异格式 的输出. 如果用户想要其他格式的差异输出, 就用选项 --diff-cmd 指定一个外部的差异比较程序, 如果需要 的话, 还可以用选项 --extensions 向差异比较程序 传递其他额外的参数. 例如, 用户想用 GNU 的程序 diff 对文件 foo.c 进行差异比较, 还要求 diff 在比较时忽略大小写, 按照上下文差异格式来 产生输出:

$ svn diff --diff-cmd /usr/bin/diff -x "-i" foo.c
…
$

修正错误

假设用户在查看 svn diff 的输出时发现针对某 一文件的修改都是错误的, 也许这个文件就不应该被修改, 也许重新开始 修改文件会更加容易. 为了撤消现在的修改, 用户可以再次编辑文件, 手动 地复原成原来的样子, 又或者是从其他地方找到一个原始文件, 把改错的 文件覆盖掉, 还可以用 svn patch --reverse-diffpatch -R 逆向应用补丁, 除此之外可能还有 其他办法.

幸运的是 Subversion 提供了一种简便的方法来撤消工作副本中的 修改, 用到的命令是 svn revert:

$ svn status README
M       README
$ svn revert README
Reverted 'README'
$ svn status README
$

在上面的例子里, Subversion 利用缓存在基文本中的内容, 把文件 回滚到修改前的原始状态. 需要注意的是, svn revert 会撤消 任何 一个未提交的修改, 例如用户可能不 想往仓库中添加新文件:

$ svn status new-file.txt
?       new-file.txt
$ svn add new-file.txt
A         new-file.txt
$ svn revert new-file.txt
Reverted 'new-file.txt'
$ svn status new-file.txt
?       new-file.txt
$

或者是用户错误地删除了一个本不该删除的文件:

$ svn status README
$ svn delete README
D         README
$ svn revert README
Reverted 'README'
$ svn status README
$

svn revert 提供了一个很好的补救机会, 否则的话, 用户就得花费大量的时间, 自己一点一点地手工撤消修改, 又或 者采用一个更麻烦的做法, 直接删除工作副本, 然后重新从服务器上检出一个 干净的工作副本.

解决冲突

我们已经看过 svn status -u 如何预测是否 有冲突, 但是解决冲突仍然需要由用户自己来完成. 冲突可以在用户把 仓库中的修改合并到本地工作副本的任何时候发生, 到目前为止用户已经 知道的命令中, svn update 就有可能产生冲突— 该命令的唯一功能就是把仓库中的更新合并到本地工作副本. 那么当发生冲突 时 Subversion 如何通知用户, 以及用户应该如何处理它们?

假设用户在执行 svn update 后看到了如下 输出:

$ svn update
Updating '.':
U    INSTALL
G    README
Conflict discovered in 'bar.c'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options:
        

不用担心左边有 U (Updated, 更新) 或 G (merGed, 合并) 的文件, 这表示 它们成功地吸收了来自仓库的更新. U 表示该文件不包含本地修改, 只是用仓库中的修改更新了文件内容. G 表示该文件含有本地修改, 但是这 些修改和来自仓库的修改没有冲突.

再下来几行就比较有趣了. 首先, Subversion 报告说在把仓库的修改 合并到文件 bar.c 时, 发现其中一些修改和本地未 提交的修改产生了冲突. 原因可能是其他人和用户都修改了同一行, 无论是 因为什么, Subversion 在发现冲突时会马上把文件置成冲突状态, 然后询问 用户他想怎么办. 用户可以从 Subversion 给出的几个选项中选择一个, 如果想看完整的选项列表, 就输入 s:

…
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: s

  (e)  - change merged file in an editor  [edit]
  (df) - show all changes made to merged file
  (r)  - accept merged version of file

  (dc) - show all conflicts (ignoring merged version)
  (mc) - accept my version for all conflicts (same)  [mine-conflict]
  (tc) - accept their version for all conflicts (same)  [theirs-conflict]

  (mf) - accept my version of entire file (even non-conflicts)  [mine-full]
  (tf) - accept their version of entire file (same)  [theirs-full]

  (m)  - use internal merge tool to resolve conflict
  (l)  - launch external tool to resolve conflict  [launch]
  (p)  - mark the conflict to be resolved later  [postpone]
  (q)  - postpone all remaining conflicts
  (s)  - show this list (also 'h', '?')
Words in square brackets are the corresponding --accept option arguments.

Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options:

先简单地介绍一下每一个选项.

(e) edit [edit]

使用环境变量 EDITOR 定义的编辑器打开 发生冲突的文件.

(df) diff-full

按照标准差异格式显示基础修订版和冲突的文件之间的差异.

(r) resolved

编辑完成后, 告诉 svn 用户已经解决了冲突, 现在应该接受文件的当前内容.

(dc) display-conflict

显示冲突的区域, 忽略合并成功的修改.

(mc) mine-conflict [mine-conflict]

丢弃从服务器收到的, 与本地冲突的所有修改, 但是接受不会产生 冲突的修改.

(tc) theirs-conflict [theirs-conflict]

丢弃与服务器产生冲突的所有本地修改, 但是保留不会产生冲突 的本地修改.

(mf) mine-full [mine-full]

丢弃从服务器收到的该文件的所有修改, 但是保留该文件的 本地修改.

(tf) theirs-full [theirs-full]

丢弃该文件的所有本地修改, 只使用从服务器收到的修改.

(m) merge

打开一个内部文件合并工具来解决冲突, 该选项从 Subversion 1.8 开始支持.

(l) launch

打开一个外部程序来解决冲突, 在第一次使用该选项之前需要完成 一些准备工作.

(p) postpone [postpone]

让文件停留在冲突状态, 在更新完成后再解决冲突.

(s) show all

显示所有的, 可以用在交互式的冲突解决中的命令.

我们将对以上命令进行更为详细的说明, 说明中将按照功能对命令进行 分组.

交互式地查看冲突差异

在决定如何交互地解决冲突之前, 有必要看一下冲突的内容, 其中有两 个命令可以帮到我们. 第一个是 df:

…
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: df
--- .svn/text-base/sandwich.txt.svn-base      Tue Dec 11 21:33:57 2007
+++ .svn/tmp/tempfile.32.tmp     Tue Dec 11 21:34:33 2007
@@ -1 +1,5 @@
-Just buy a sandwich.
+<<<<<<< .mine
+Go pick up a cheesesteak.
+=======
+Bring me a taco!
+>>>>>>> .r32
…

差异内容的第一行显示了工作副本之前的内容 (版本号 BASE), 下一行是用户的修改, 最后一行是从服务器 收到的修改 (通常 是版本号 HEAD).

第二个命令和第一个比较类似, 但是 dc 只会显示冲突区域, 而不是文件的所有修改. 另外, 该命令显示冲突区域 的格式也稍有不同, 这种格式允许用户更方便地比较文件在三种状态下的 内容: 原始状态; 带有用户的本地修改, 忽略服务器的冲突修改; 带有服 务器的修改, 忽略用户的本地修改.

审查完这些命令提供的信息之后, 用户就可以采取下一步动作.

交互式地解决冲突差异

交互式地解决冲突的主要方法是使用一个内部文件合并工具, 该工具 询问用户如何处理每一个冲突修改, 而且允许用户有选择地合并和编辑修改. 除此之外还有其他几种方式用于交互式地解决冲突—其中两种允许用 户使用外部编辑器, 有选择地合并和编辑修改, 另外几种允许用户简单地选择 文件版本. 内部合并工具集合了所有解决冲突的方式.

看完引起冲突的修改后, 接下来就要解决这些冲突. 我们要介绍的第 一个命令是 m (merge), 从 Subversion 1.8 开 始支持, 该命令允许用户从众多选项中选择一个来解决冲突:

Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: m
Merging 'Makefile'.
Conflicting section found during merge:
(1) their version (at line 24)                  |(2) your version (at line 24)
------------------------------------------------+------------------------------------------------
top_builddir = /bar                             |top_builddir = /foo
------------------------------------------------+------------------------------------------------
Select: (1) use their version, (2) use your version,
        (12) their version first, then yours,
        (21) your version first, then theirs,
        (e1) edit their version and use the result,
        (e2) edit your version and use the result,
        (eb) edit both versions and use the result,
        (p) postpone this conflicting section leaving conflict markers,
        (a) abort file merge and return to main menu:

从上面可以看到, 使用内部文件合并工具时, 用户可以循环遍历文件中 的每一个冲突区域, 对每一个冲突区域用户都可以选择一个不同的选项, 或者 推迟解决该冲突.

如果用户想用一个外部编辑器来选择本地修改的某些组合, 此时可用 用命令 e (edit) 来手动地编辑带有冲突标记的 文件, 该命令会打开一个文本编辑器 (参考 “使用外部编辑器”一节). 文件编辑完毕后, 如果用户感到满意, 就要用命令 r (resolved) 告诉 Subversion 文件的冲突已经解决了.

不管别人怎么说, 使用文本编辑器编辑文件来解决冲突是一种比较低 级的方法 (见 “手动地解决冲突”一节), 因此, Subversion 提供了一个命令 l (launch) 来打开 精美的图形化合并工具 (见 “外部合并工具”一节).

还有两个稍微折衷一点的选项, 命令 mc (mine-conflict) 和 tc (theirs-conflict) 分别告诉 Subversion 选择用户的本地修改或从服务器收到的修改作为冲突 获胜的一方. 但是和 mine-full 以及 theirs-full 不同的是, 这两个命令会保留不产生冲突的 本地修改和从服务器收到的修改.

最后, 如果用户决定只想使用本地修改, 或者是只使用从服务器收到 的修改, 可以分别选择 mf (mine-full) 与 tf (theirs-full).

推迟解决冲突

这节的标题看起来好像是在讲如何避免夫妻之间爆发冲突, 可实际上 本节还是在介绍和 Subversion 相关的内容. 如果用户在更新时遇到了冲 突, 但是还没有准备好立即解决, 这时可以选择 p (postpone) 来推迟解决. 如果用户早就准备好不想交互式地解决冲突, 可以给 svn update 增加一个参数 --non-interactive, 此时发生冲突的文件会被自动 标记为 C.

从 Subversion 1.8 开始, 内部的文件合并工具允许用户推迟解决 某些特定的冲突, 但仍然可以解决其他冲突. 于是, 用户可以以冲突 区域为单位 (而不仅仅是以文件为单位) 来决定哪些冲突可以推迟解决.

C (Conflicted) 表示来自服务器的修改和用户的本地修改有所重叠, 用户在更新完成后必须 手动加以选择. 如果用户推迟解决冲突, svn 通常 会从三个方面帮助用户解决冲突:

  • 如果在更新过程中产生了冲突, Subversion 就会为含有冲突的 文件打印一个字符 C, 并记住 该文件处于冲突状态.

  • 如果 Subversion 认为文件是支持合并的, 它就会把 冲突标记 (conflict markers)—一段给冲突划分 边界的特殊文本—插入到文本中来显式地指出重叠区域 (Subversion 使用属性 svn:mime-type 来判断 一个文件是否支持基于行的合并, 见 “文件内容类型”一节).

  • 对每一个产生冲突的文件, Subversion 都会在工作副本中生成 三个额外的文件, 这些文件不在版本控制的名单中:

    filename.mine

    该文件的内容和用户执行更新操作前的文件内容相同, 它 包含了当时所有的本地修改 (如果 Subversion 认为该文件不 支持合并就不会创建 .mine).

    filename.rOLDREV

    该文件的内容和版本号 BASE 对应 的文件内容相同, 也就是在执行更新操作前工作副本中未修改 的版本, OLDREV 是基础版本号.

    filename.rNEWREV

    该文件的内容和从服务器收到的版本相同, NEWREV 等于更新到的版本号 (如果没有额外指定的话, 就是 HEAD).

例如, Sally 修改了文件 sandwich.txt, 但 是还没有提交. 同时, Harry 提交了同一文件的修改. 在提交前 Sally 执行了更新操作, 结果产生了冲突, 她选择推迟解决冲突:

$ svn update
Updating '.':
Conflict discovered in 'sandwich.txt'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: p
C    sandwich.txt
Updated to revision 2.
Summary of conflicts:
  Text conflicts: 1
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

此时, 直到这三个临时文件被删除之前, Subversion 不会允许 Sally 提交 sandwich.txt:

$ svn commit -m "Add a few more things"
svn: E155015: Commit failed (details follow):
svn: E155015: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict

如果用户选择推迟解决冲突, 只有在冲突解决之后, Subversion 才 会重新允许用户提交修改, 其中要用到的命令是 svn resolve. 该命令接受一个 --accept 选项, 它指明了用户想要如何解决冲突. 在 Subversion 1.8 以前, --accept 是命令 svn resolve 的必填选项, 但是现在它是可选的. 如果不带 --accept 地执行 svn resolve, Subversion 就会进入交互式地冲突 解决步骤, 这部分内容我们已经在上一节— “交互式地解决冲突差异”一节—介绍过了. 下面我们会介绍如何使用选项 --accept.

选项 --accept 指示 Subversion 使用预先定义 好的几种方法之一来解决冲突. 如果用户想要用上一次检出时的版本,就 写成 --accept=base; 如果用户只想保留自己的修改, 就写成 --accept=mine-full; 如果用户只想保留从 服务器收到的更新, 就写成 --accept=theirs-full. 除了刚才介绍的几个, 还有其他一些选项值, 参考 svn 参考手册—Subversion 命令行客户端--accept ACTION.

如果用户想要自己选择哪些修改进入最终版本, 那就自己手动编辑 文件, 修改冲突区域 (带有冲突标记的区域), 然后使用选项 --accept=working 告诉 Subversion 把文件的 当前内容作为冲突解决后的状态.

svn resolve 删除三个临时文件, 将用户指定的 文件版本作为冲突解决后的最终版. 命令执行成功后 Subversion 不再认为 文件处于冲突状态:

$ svn resolve --accept working sandwich.txt
Resolved conflicted state of 'sandwich.txt'

手动地解决冲突

第一次尝试手动解决冲突会让不少人感到紧张, 但只要多练几次, 就会像 骑自行车一样简单.

这里有一个例子. 由于沟通上的误会, 你和你的同事, Sally, 同时修改 了 sandwich.txt, Sally 先提交了修改, 结果当你 更新工作副本时发生了冲突, 现在你需要手动编辑文件来解决冲突. 首先先 看一下发生冲突后的文件内容:

$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread

分别由小于号, 等号和大于号组成的行是冲突标记, 它们不是冲突数据 的一部分, 用户通常只需要确保在提交前把它们都删除掉即可. 前两个标记之 间的文本是用户的本地修改.

<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======

后两个标记之间的内容是 Sally 提交的修改:

=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2

通常情况下你不能直接删除冲突标记和 Sally 的修改— 否则的话当她收到三明治时就会感到一头雾水, 此时你应该向她说明意大 利熟食店不出售泡洋白菜丝. 假设 sandwich.txt 修改完毕后的内容是:

Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread

使用命令 svn resolve 移除文件的冲突状态后, 接下来就可以提交修改了:

$ svn resolve --accept working sandwich.txt
Resolved conflicted state of 'sandwich.txt'
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."

通常情况下, 如果用户还没有编辑好文件就不要用 svn resolve 告诉 Subversion 你已经解决好了 冲突, 因为临时文件一旦被删除, 即使文件中还含有冲突标记, Subversion 依然会允许用户提交修改.

如果用户在编辑含有冲突的文件时感到困惑, 应该看一下 Subversion 创建的那三个临时文件, 甚至可以用第三方的交互式文件合并工具来查看 它们.

只使用从服务器收到的更新

如果在更新时产生了冲突, 而你想要完全丢弃自己的修改, 就执行 svn resolve --accept theirs-full CONFLICTED-PATH , 此时 Subversion 就会丢弃用户的本地修改, 并 删除临时文件:

$ svn update
Updating '.':
Conflict discovered in 'sandwich.txt'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
        (mc) my side of conflict, (tc) their side of conflict,
        (s) show all options: p
C    sandwich.txt
Updated to revision 2.
Summary of conflicts:
  Text conflicts: 1
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ svn resolve --accept theirs-full sandwich.txt
Resolved conflicted state of 'sandwich.txt'
$

使用 svn revert

如果用户决定丢弃当前的所有修改 (无论是在冲突后, 还是在任何时候), 就 用 svn revert:

$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt
$

注意, 含有冲突的文件被回滚后不需要再对它使用 svn resolve.

提交修改

终于, 所有的编辑都完成了, 从服务收到的更新也已合并完成, 现在你 已经准备好向仓库提交修改.

svn commit 把本地的所有修改发往仓库. 提交时 用户需要输入一段日志来描述本次修改, 日志被附加到新的版本号上. 如果 日志比较简短, 可以用选项 --message (-m) 直接在命令行上输入日志:

$ svn commit -m "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

如果用户已经事先把日志写到了某个文本文件中, 希望 Subversion 在 提交时直接从该文件中读取日志, 这可以通过选项 --file (-F) 实现:

$ svn commit -F logmsg
Sending        sandwich.txt
Transmitting file data .
Committed revision 4.

如果用户在提交时没有指定选项 --message (-m) 或 --file (-F), Subversion 就会自动打开用户指定的编辑器 (见 “通用配置选项”一节editor-cmd) 来编写日志.

[提示] 提示

如果用户在编写日志时突然又不想提交了, 那就不保存地退出编辑 器; 如果已经保存过, 那就删除全部的提交日志, 再保存一遍, 然后 退出编辑器:

$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
(a)bort, (c)ontinue, (e)dit
a
$

仓库不知道也不关心用户的提交是否有意义, 它只能确保没有人趁你不 注意时修改了同一文件. 如果确实有人这么做了, 整个提交就会失败, 并打印 一条错误消息说其中某些文件过时了:

$ svn commit -m "Add another rule"
Sending        rules.txt
Transmitting file data .
svn: E155011: Commit failed (details follow):
svn: E155011: File '/home/sally/svn-work/sandwich.txt' is out of date
…

(错误消息的具体内容取决于网络协议和服务器, 但是基本内容都是类似 的.)

此时用户需要执行 svn update, 解决可能 的冲突, 然后再次尝试提交.

本节介绍的内容覆盖了 Subversion 的基本工作周期. 为了方便用户 使用仓库和工作副本, Subversion 还提供了很多特性, 但是在大部分情况下, Subversion 的日常使用只会用到我们目前所介绍的这些命令. 下面我们还将 会介绍几个较常用到的命令.



[6] 如果你希望在 HEAD 中重新看到被删除的文件, 参考 “恢复已删除的文件”一节.