posts - 262,  comments - 221,  trackbacks - 0

【17】解决冲突和比较差异


在团队合作的情况下,如果由于沟通不畅或者分工不明,有可能出现几个人同时修改一个文件的不同部分,甚至同时修改了同一部分的情况。当其中的一方先提交后他人再更新本地工作副本时,通常会出现冲突。这时SVN通常不知道该如何处理,而是把问题的解决方案选择留给程序员。本节我们将来学习如何解决冲突,还有就是避免冲突的几个基本原则。

为了模拟这个情况,我们必须在新建一个用户guest,该用户同样也从版本库checkout出一份ECoupon项目的拷贝到本地工作副本E:\Temp\ECoupon_tmp
C:\>svn checkout http://localhost:8000/svn/Workspace/Ericsson/ECoupon --username=guest --password=guest

SVN支持从命令行传递用户名和密码,否则将使用缓存的用户名和密码。现在我们来检查一下这个新的用户检出的项目的静态信息
C:\>svn info E:\Temp\ECoupon_tmp
Path: E:
\Temp\ECoupon_tmp
URL: http:
//localhost:8000/svn/Workspace/Ericsson/ECoupon
Repository Root: http:
//localhost:8000/svn/Workspace
Repository UUID: 06f3a259
-b3b5-ff4e-9a70-52e8834f1976
Revision: 
19
Node Kind: directory
Schedule: normal
Last Changed Author: qlinpen
Last Changed Rev: 
19
Last Changed 
Date2009-12-09 16:08:59 +0800 (星期三, 09 十二月 2009)

可以看出两个用户都共享了同一个版本库的路径,而且最后一次提交是由用户qlinpen执行的。假设现在程序员A和B同时对文件read-from-wc.txt进行修改。A在第一行添加了"add by user qlinpen",而B在第二行添加了"add by user guest"。

C:\>svn diff -r HEAD E:\Temp\ECoupon\read-from-wc.txt --username=qlinpen --password=test
Index: E:
/Temp/ECoupon/read-from-wc.txt
==============================================================
--- E:/Temp/ECoupon/read-from-wc.txt    (revision 22)
+++ E:/Temp/ECoupon/read-from-wc.txt    (working copy)
@@ 
-1 +1,2 @@
-modify by user qlinpen
\ No newline at end of file
+modify by user qlinpen
+add by user qlinpen
\ No newline at end of file

C:
\>svn diff -r HEAD E:\Temp\ECoupon_tmp\read-from-wc.txt --username=guest --password=guest
Index: E:
/Temp/ECoupon_tmp/read-from-wc.txt
=============================================================
--- E:/Temp/ECoupon_tmp/read-from-wc.txt        (revision 22)
+++ E:/Temp/ECoupon_tmp/read-from-wc.txt        (working copy)
@@ 
-1 +1,2 @@
-modify by user qlinpen
\ No newline at end of file
+modify by user qlinpen
+add by user guest
\ No newline at end of file

可以看到,用户qlinpen和gues都基于版本库检出的文件作出了改变。现在来模拟一下协同开发时遇到的冲突问题。

 Step 1: 用户qlinpen首先更新本地工作副本,然后提交自己的改变
C:\>svn update E:\Temp\ECoupon --username=qlinpen --password=test
At revision 
22.

C:
\>svn commit E:\Temp\ECoupon -"commit change by qlinpen" --username=qlinpen --password=test
Sending        E:
\Temp\ECoupon\read-from-wc.txt
Transmitting file data .
Committed revision 
23.

 Step 2: 用户guest在提交前检查版本库是否有更新(这是一个好习惯)
C:\>svn status E:\Temp\ECoupon_tmp --show-updates --username=guest --password=guest
M      
*       22   E:\Temp\ECoupon_tmp\read-from-wc.txt
Status against revision:     
23

C:
\>svn diff -r HEAD E:\Temp\ECoupon_tmp\read-from-wc.txt --username=guest --password=guest
Index: E:
/Temp/ECoupon_tmp/read-from-wc.txt
==============================================================
--- E:/Temp/ECoupon_tmp/read-from-wc.txt        (revision 23)
+++ E:/Temp/ECoupon_tmp/read-from-wc.txt        (working copy)
@@ 
-1,2 +1,2 @@
 modify by user qlinpen
-add by user qlinpen
\ No newline at end of file
+add by user guest
\ No newline at end of file

通过本地文件read-fom-wc.txt和版本库的最新版本同名文件(-r HEAD)比较,得知有人添加了"add by user qlinpen"。

 Step 3: 用户guest想得知这一行是谁加上去的,还有精确的时间
C:\>svn blame http://localhost:8000/svn/Workspace/Ericsson/ECoupon/read-from-wc.txt -v
    
23    qlinpen 2009-12-10 11:03:21 +0800 (星期四, 10 十二月 2009) modify by user qlinpen
    
23    qlinpen 2009-12-10 11:03:21 +0800 (星期四, 10 十二月 2009) add by user qlinpen

通过svn blame命令配合参数-v(--verbose),我们可以精确地知道文件每一行的修改时间,修改者,版本号。

 Step 4: 用户guest决定先提交自己的变更,再来更新本地的工作副本
C:\>svn commit E:\Temp\ECoupon_tmp -"commit change by guest" --username=guest --password=guest
Sending        E:
\Temp\ECoupon_tmp\read-from-wc.txt
svn: Commit failed (details follow):
svn: File 
or directory 'read-from-wc.txt' is out of date; try updating
svn: resource out of date; try updating

还记得前面我们说过的吗?当本地工作副本的版本低于版本库的时候,是不允许提交的,SVN会要求用户先更新本地的工作副本,再提交新的变更。

 Step 5: 用户guest根据建议选择了先update
C:\>svn update E:\Temp\ECoupon_tmp\ --username=guest --password=guest
Conflict discovered in 
'E:/Temp/ECoupon_tmp/read-from-wc.txt'.
Select: (p) postpone, (df) diff-full, (e) edit, (h) help for more options: p
C    E:
\Temp\ECoupon_tmp\read-from-wc.txt
Updated 
to revision 23.

很不幸,冲突出现了。为什么呢?因为大家同样都在第2行更改了文件,如果update成功那么用户qlinpen提交的内容将会覆盖用户guest的修改。所以冲突就出现了。这时SVN提供了几种建议,它们包括:

 A.(p) postone 延迟解决
 B.(df) diff-full 显示所有冲突的内容
 C.(e) edit 启动编辑器解决冲突
 D.(r) resolve 标识冲突已经解决
 E.(mf) mine-full 用我的版本覆盖他人的修改
 F.(tf) theirs-full 用他人的版本覆盖我的修改
 G.(l) lanuch 启动其他工具来解决冲突
 H.(h) help 启动帮助信息
 
 Step 6: 用户guest选择了p延迟解决。
C:\>dir E:\Temp\ECoupon_tmp\read*
 驱动器 E 中的卷是 Shared Folders
 卷的序列号是 
0000-0064

 E:
\Temp\ECoupon_tmp 的目录

2009-12-10  11:27               122 read-from-wc.txt
2009-12-10  11:27                41 read-from-wc.txt.mine
2009-12-10  11:27                22 read-from-wc.txt.r22
2009-12-10  11:27                43 read-from-wc.txt.r23

这时在E:\Temp\ECoupon_tmp目录下,read-from-wc.txt所在的位置会产生三个文件
 A.read-from-wc.txt.mine:这是我本地修改的副本
 B.read-from-wc.r22:这是read-from-wc.txt的BASE版本
 C.read-from-wc.r23:这是read-from-wc.txt的HEAD版本

我们分别打开这四个文件,观察其内容
C:\>type E:\Temp\ECoupon_tmp\read-from-wc.txt.r22
modify by user qlinpen

C:
\>type E:\Temp\ECoupon_tmp\read-from-wc.txt.r23
modify by user qlinpen
add by user qlinpen

C:
\>type E:\Temp\ECoupon_tmp\read-from-wc.txt.mine
modify by user qlinpen
add by user guest

C:
\>type E:\Temp\ECoupon_tmp\read-from-wc.txt
<<<<<<< .mine
modify by user qlinpen
add by user guest
=======
modify by user qlinpen
add by user qlinpen
>>>>>>> .r23

可以看到从上到下,依次是原始版本,用户qlinpen修改后的版本,用户guest修改的版本(本地尚未提交的副本),合并后的版本。read-from-wc.txt文件是合并后的结果,但是里面出现的内容并不是我们想要的。里面多了一些<<,==,>>之类的符号,还有版本号。下面解析一下:

 A.从<<到==之间的部分是.mine的内容,即用户guest本地修改但尚未提交的内容
 B.从==到>>之间的部分是.r23的内容,即来自版本库由用户qlinpen提交的最新版本

SVN不知道如何合并,所以将它们都用冲突标记标记出来

 Step 7: 用户guest手工解决冲突

经过和用户qlinpen讨论,最终用户guest选择将两人的修改合并,合并后的结果如下:
C:\>type E:\Temp\ECoupon\read-from-wc.txt
modify by user qlinpen
add by user qlinpen

C:
\>type E:\Temp\ECoupon_tmp\read-from-wc.txt
modify by user qlinpen
add by user qlinpen
add by user guest

 Step 8: 用户guest通知SVN冲突已经解决

执行svn resolved PATH 来通知SVN冲突已经被解决,可以准备再次更新或提交了。需要注意的是:svn resolved命令只是简单地把文件的状态从锁定(L)改为正常,至于冲突是否真的解决,更新或提交后会不会引起别的问题它是不知道的。
C:\>svn status E:\Temp\ECoupon_tmp --show-updates
?                   E:
\Temp\ECoupon_tmp\read-from-wc.txt.r22
?                   E:
\Temp\ECoupon_tmp\read-from-wc.txt.r23
?                   E:
\Temp\ECoupon_tmp\read-from-wc.txt.mine
C              
23   E:\Temp\ECoupon_tmp\read-from-wc.txt
Status against revision:     
23

C:
\>svn resolved E:\Temp\ECoupon_tmp\read-from-wc.txt
Resolved conflicted state of 
'E:\Temp\ECoupon_tmp\read-from-wc.txt'

C:
\>svn status E:\Temp\ECoupon_tmp --show-updates
M              
23   E:\Temp\ECoupon_tmp\read-from-wc.txt
Status against revision:     
23

在没有执行svn resoved命令之前,文件read-from-wc.txt的状态是'C',代表冲突(Conflict)。执行svn resolved命令,SVN会做两件事:

 A.删除临时生成的几个文件:*.mine,*.rBASE,*.rHEAD
 B.去除锁定文件的锁,通知SVN冲突已经解决

在这里需要再次强调的是:svn resovle只负责清除冲突环境和通知SVN,是否真正解决冲突最终还是靠程序员自己。

 Step 9: 用户guest提交最终修复的版本
C:\>svn commit E:\Temp\ECoupon_tmp -"commit after conflict resolved by user guest" --username=guest --password=guest
Sending        E:
\Temp\ECoupon_tmp\read-from-wc.txt
Transmitting file data .
Committed revision 
24.

C:
\>svn update E:\Temp\ECoupon_tmp --username=guest --password=guest
At revision 
24.

 Step 10: 用户qlinpen更新本地工作副本
C:\>svn status E:\Temp\ECoupon --show-updates
       
*       23   E:\Temp\ECoupon\read-from-wc.txt
Status against revision:     
24

C:
\>svn update E:\Temp\ECoupon --username=qlinpen --password=test
U    E:
\Temp\ECoupon\read-from-wc.txt
Updated 
to revision 24.

至此冲突解决。

虽然SVN提供了比较差异和解决冲突的方法,但避免冲突的最好方法永远只有一个:明确的分工和良好的沟通,尽量避免多个人同时修改同一份文件。如果不能避免,那么最好指定一个人在提交前负责合并各人的更新,然后一次性提交。



-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要尽力打好一手烂牌。
posted on 2009-12-09 23:25 Paul Lin 阅读(585) 评论(0)  编辑  收藏 所属分类: 项目管理

只有注册用户登录后才能发表评论。


网站导航:
 
<2009年12月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿(21)

随笔分类

随笔档案

BlogJava热点博客

好友博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜