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 就会要求你在合并了这些修改 之后才能提交. [8]
如果其他人把你正在编辑的文件移动到其他地方或删除了, 那这时候又会发生 什么事? 发生这种事的原因可能是同事之间沟通不及时, 一个人认为文件应该 被删除, 而另一个人还想接着修改该文件, 也可能是你的同事想重新规划目录 布局. 如果你正在编辑的文件已经移动到了其他位置, 那么这些修改可能需要 应用到移动后的文件中. 这种冲突的级别是在目录树结构上, 而不是在文件的 内容上, 称为 目录冲突 (tree conflicts).
和文件内容的冲突一样, 只有在目录冲突解决之后才能向仓库提交修改.
假设有一个软件项目的代码目录结构如下所示:
$ svn list -Rv svn://svn.example.com/trunk/ 13 harry Sep 06 10:34 ./ 13 harry 27 Sep 06 10:34 COPYING 13 harry 41 Sep 06 10:32 Makefile 13 harry 53 Sep 06 10:34 README 13 harry Sep 06 10:32 code/ 13 harry 54 Sep 06 10:32 code/bar.c 13 harry 130 Sep 06 10:32 code/foo.c $
后来, 在版本号 14, 你的同事 Harry 把 bar.c
重命名为 baz.c
, 但是你并不知情. 此时你正忙于
编写另外一套修改, 其中就牵涉到 bar.c
:
$ svn diff Index: code/foo.c =================================================================== --- code/foo.c (revision 13) +++ code/foo.c (working copy) @@ -3,5 +3,5 @@ int main(int argc, char *argv[]) { printf("I don't like being moved around!\n%s", bar()); - return 0; + return 1; } Index: code/bar.c =================================================================== --- code/bar.c (revision 13) +++ code/bar.c (working copy) @@ -1,4 +1,4 @@ const char *bar(void) { - return "Me neither!\n"; + return "Well, I do like being moved around!\n"; } $
提交失败时你开始意识到有人已经修改了 bar.c
:
$ svn commit -m "Small fixes" Sending code/bar.c Transmitting file data . svn: E155011: Commit failed (details follow): svn: E155011: File '/home/svn/project/code/bar.c' is out of date svn: E160013: File not found: transaction '14-e', path '/code/bar.c' $
此时应该执行 svn update, 命令不仅把 Harry 的修改同步到本地工作副本, 还产生了一个目录冲突:
$ svn update Updating '.': C code/bar.c A code/baz.c U Makefile Updated to revision 14. Summary of conflicts: Tree conflicts: 1 $
在上面的例子中, svn update 在第四列放置一个
大写字母 C
表示该条目有冲突.
svn status 可以显示冲突的其他细节:
$ svn status M code/foo.c A + C code/bar.c > local edit, incoming delete upon update Summary of conflicts: Tree conflicts: 1 $
注意 bar.c
如何又被自动地添加到工作副本中,
如果用户想保留 bar.c
, 就不需要再额外执行一次
svn add.
由于 Subversion 是用一个复制操作和一个删除操作实现移动, 而且在 更新时很难将这两个操作联系在一起, 所以 Subversion 的警告信息只是说 在本地被修改的文件已经在仓库中被删除了, 这个删除可能是移动操作的一 部分, 也可能就是一次单纯的删除操作. 准确地判断仓库在语义上发生了什 么变化显得尤为重要—只有这样才能让自己的修改适应项目的整体 轨迹. 为了弄清楚冲突发生的原因, 你可以阅读日志, 和同事沟通, 在行的 级别上查看修改等.
在这个例子里, Harry 的提交日志提供了所需要的信息.
$ svn log -r14 ^/trunk ------------------------------------------------------------------------ r14 | harry | 2011-09-06 10:38:17 -0400 (Tue, 06 Sep 2011) | 1 line Changed paths: M /Makefile D /code/bar.c A /code/baz.c (from /code/bar.c:13) Rename bar.c to baz.c, and adjust Makefile accordingly. ------------------------------------------------------------------------ $
svn info 显示了冲突条目的 URL. 左边 (left) 的 URL 显示了 冲突的本地端来源, 右边 (right) 的 URL 显示了冲突的服务器端来源, 这些 URL 指出了我们应该从哪个版本号开始搜索导致冲突的修改.
$ svn info code/bar.c Path: code/bar.c Name: bar.c URL: http://svn.example.com/svn/repo/trunk/code/bar.c … Tree conflict: local edit, incoming delete upon update Source left: (file) ^/trunk/code/bar.c@4 Source right: (none) ^/trunk/code/bar.c@5 $
bar.c
已经成为目录冲突的受害者, 在冲突解决
之前无法提交:
$ svn commit -m "Small fixes" svn: E155015: Commit failed (details follow): svn: E155015: Aborting commit: '/home/svn/project/code/bar.c' remains in confl ict $
为了解决这个冲突, 用户要么同意, 要么不同意 Harry 提交的重命名 修改.
如果用户同意重命名, 那么 bar.c
就成了多余的
了, 你可能想要删除 bar.c
并把目录冲突标记为已
解决, 但是请等一下, 文件上还有你的修改! 在删除
bar.c
之前你必须决定它上面的修改是否需要应用到
其他地方, 比如重命名后的文件 baz.c
. 不妨假设你
的修改需要 “跟随重命名” (follow the move), 但是 Subversion
还没有聪明到能够替你完成这件工作[9], 所以你必须手动地迁移修改.
在我们的例子里, 你完全可以手动地再修改一次
baz.c
—毕竟只修改了一行, 但是这种做法只适用
于修改很少的情况, 我们再介绍一种更具有通用性的方法. 先用
svn diff 创建一个补丁文件, 然后修改补丁文件的头
部信息, 使其指向重命名后的文件, 最后再应用修改后的补丁.
$ svn diff code/bar.c > PATCHFILE $ cat PATCHFILE Index: code/bar.c =================================================================== --- code/bar.c (revision 14) +++ code/bar.c (working copy) @@ -1,4 +1,4 @@ const char *bar(void) { - return "Me neither!\n"; + return "Well, I do like being moved around!\n"; } $ ### Edit PATCHFILE to refer to code/baz.c instead of code/bar.c $ cat PATCHFILE Index: code/baz.c =================================================================== --- code/baz.c (revision 14) +++ code/baz.c (working copy) @@ -1,4 +1,4 @@ const char *bar(void) { - return "Me neither!\n"; + return "Well, I do like being moved around!\n"; } $ svn patch PATCHFILE U code/baz.c $
现在 bar.c
上的修改已经成功地转移到了
baz.c
上, 用户现在可以删除
bar.c
并告诉 Subversion 把工作副本的当前内容
作为冲突解决的结果.
$ svn delete --force code/bar.c D code/bar.c $ svn resolve --accept=working code/bar.c Resolved conflicted state of 'code/bar.c' $ svn status M code/foo.c M code/baz.c $ svn diff Index: code/foo.c =================================================================== --- code/foo.c (revision 14) +++ code/foo.c (working copy) @@ -3,5 +3,5 @@ int main(int argc, char *argv[]) { printf("I don't like being moved around!\n%s", bar()); - return 0; + return 1; } Index: code/baz.c =================================================================== --- code/baz.c (revision 14) +++ code/baz.c (working copy) @@ -1,4 +1,4 @@ const char *bar(void) { - return "Me neither!\n"; + return "Well, I do like being moved around!\n"; } $
但是如果你不同意重命名, 那又该如何? 如果用户已经确定
baz.c
上的修改已经进行了保存或者可以丢弃, 那也可以
直接删除 baz.c
(别忘了撤消 Harry 对
Makefile
的修改). 因为
bar.c
已经准备好添加到仓库中, 所以接下来只需
要把冲突标记为已解决即可:
$ svn delete --force code/baz.c D code/baz.c $ svn resolve --accept=working code/bar.c Resolved conflicted state of 'code/bar.c' $ svn status M code/foo.c A + code/bar.c D code/baz.c M Makefile $ svn diff Index: code/foo.c =================================================================== --- code/foo.c (revision 14) +++ code/foo.c (working copy) @@ -3,5 +3,5 @@ int main(int argc, char *argv[]) { printf("I don't like being moved around!\n%s", bar()); - return 0; + return 1; } Index: code/bar.c =================================================================== --- code/bar.c (revision 14) +++ code/bar.c (working copy) @@ -1,4 +1,4 @@ const char *bar(void) { - return "Me neither!\n"; + return "Well, I do like being moved around!\n"; } Index: code/baz.c =================================================================== --- code/baz.c (revision 14) +++ code/baz.c (working copy) @@ -1,4 +0,0 @@ -const char *bar(void) -{ - return "Me neither!\n"; -} Index: Makefile =================================================================== --- Makefile (revision 14) +++ Makefile (working copy) @@ -1,2 +1,2 @@ foo: - $(CC) -o $@ code/foo.c code/baz.c + $(CC) -o $@ code/foo.c code/bar.c
恭喜, 你已经解决了你的第一个目录冲突! 现在你可以提交修改, 并告诉 Harry 由于他的修改, 你做了很多额外的工作.