当前位置: 代码迷 >> CVS/SVN >> (转)svn树摩擦
  详细解决方案

(转)svn树摩擦

热度:10150   发布时间:2013-02-26 00:00:00.0
(转)svn树冲突
SVN 树冲突和目录丢失问题(1)
临下班了,一个老朋友 (之后用yzw代称) 在 MSN 上呼我。说他的 SVN 遇到问题了:

·             在执行分支合并时,一个目录发生了树冲突

·             直接在硬盘上将该目录删除

·             之后执行 svn update 该目录不能检出

·             不知道树冲突为何物,也不知道目录怎么变成了一团糟

好吧,谁让他公司的 SVN 是我给部署的呢?让他(yzw)执行 svn status 命令,看看显示什么信息,然后我在本地建立一个模型,争取重现并解决他的问题。

在已经一团糟的目录下,执行 svn 显示的信息如下:

$ svn st

M      .

!  +  C somedir

      >   local add, incoming add upon update

看来他遇到的树冲突,是因为在两个分支同时创建了一个同名目录 somedir,然后在合并更新时出现树冲突。

重现问题的过程
版本库准备
1.           创建svn 版本库于 /tmp/svnserver

2.           检出版本库到目录 ~/tmp/svntf 下 (windows用户需要知道的是:~ 代表我的主目录 /home/jiangxin 是也)

3.           创建 SVN 三个顶级目录:trunk tags branches

4.           创建分支 branches/0.x

5.           在 branches/0.x 分支下创建目录 somedir,并增加一个文件 somedir/branch.txt

6.           在主线 trunk 下也创建一个目录 somedir,并增加一个文件 somedir/trunk.txt

看看目前的版本情况

1.           主线下执行 svn info

2.                ~/tmp/svntf/trunk$ svn info

3.                路径: .

4.                URL: file:///tmp/svnserver/trunk

5.                版本库根: file:///tmp/svnserver

6.                版本库 UUID: c0e3cb0f-81b0-40e5-9c35-42972dbc50aa

7.                版本: 4

8.                节点种类: 目录

9.                调度: 正常

10.            最后修改的作者: jiangxin

11.            最后修改的版本: 4

最后修改的时间: 2010-04-22 19:51:55 +0800 (四, 2010-04-22)

12.        主线下目录下的文件

13.            ~/tmp/svntf/trunk$ svn ls -R

14.            somedir/

somedir/trunk.txt

15.        主线的更改历史

16.            ~/tmp/svntf/trunk$ svn log

17.            ------------------------------------------------------------------------

18.            r4 | jiangxin | 2010-04-22 19:51:55 +0800 (四, 2010-04-22) | 1 行

19.            

20.            to trunk, we add somedir/trunk.txt file.

21.            ------------------------------------------------------------------------

22.            r1 | jiangxin | 2010-04-22 19:48:57 +0800 (四, 2010-04-22) | 1 行

23.            

24.            initial

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

25.        分支的中的文件列表

26.            ~/tmp/svntf/trunk$ svn ls -R file:///tmp/svnserver/branches/0.x

27.            somedir/

somedir/branch.txt

28.        分支的更改历史

29.            ~/tmp/svntf/trunk$ svn log file:///tmp/svnserver/branches/0.x

30.            ------------------------------------------------------------------------

31.            r3 | jiangxin | 2010-04-22 19:51:21 +0800 (四, 2010-04-22) | 1 行

32.            

33.            to branch 0.x, we add somedir/branch.txt file.

34.            ------------------------------------------------------------------------

35.            r2 | jiangxin | 2010-04-22 19:50:27 +0800 (四, 2010-04-22) | 1 行

36.            

37.            trunk => branch/0.x

38.            ------------------------------------------------------------------------

39.            r1 | jiangxin | 2010-04-22 19:48:57 +0800 (四, 2010-04-22) | 1 行

40.            

41.            initial

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

备份当前的 .svn 目录
直接拷贝 .svn 到 .svn-orignal,以便在出现意外的时候,进行参照。

~/tmp/svntf/trunk$ cp -a .svn .svn-orignal

Tips: 如果不想每次在执行 svn status 时显示 .svn-orignal 目录,设置svn属性 svn:ignore 值为 “.svn*”。(问题优点复杂化了,算了)

合并分支改动,引发异常
1.           执行合并操作,引发异常

2.                ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x

3.                --- 正在合并 r2,经由 r4,到 “.”:

4.                   C somedir

5.                冲突概要:

  树冲突:1

6.           删除 somedir 目录

~/tmp/svntf/trunk$ rm -rf somedir/

7.           执行 svn status 查看当前状态

8.                ~/tmp/svntf/trunk$ svn st

9.                 M      .

10.            ?       .svn-merge-conflict

11.            ?       .svn-orignal

12.            !     C somedir

13.                  >   本地 增加,动作 增加,操作 合并

14.        虽然结果和yzw很像了,但是还不是完全一致。

o                yzw 执行 svn status 显示的英文,最后一行翻译为中文是:

      >   本地 增加,动作 增加,操作 更新

o                而我的结果是:

      >   本地 增加,动作 增加,操作 合并

o                看来,还需要作些工作才能完全重现错误。

15.        这时我又备份了一下 .svn 目录,将 .svn 复制到 .svn-merge-conflict,以便后面比较

16.        执行 svn up 命令

17.            ~/tmp/svntf/trunk$ svn up

18.            跳过“somedir”

19.            D    somedir

20.            更新到版本 4。

21.            冲突概要:

22.              跳过的路径:1

23.            ~/tmp/svntf/trunk$ svn up

版本 4。

24.        以上执行了两次 svn up ,结果不一致,说明 svn 干了些什么。
实际上,通过比较 .svn 目录和之前备份的 .svn-merge-conflict 可以看出端倪:

25.            ~/tmp/svntf/trunk$ diff -r .svn-merge-conflict .svn

26.            diff -r .svn-merge-conflict/entries .svn/entries

27.            38,40d37

28.            < somedir

29.            < dir

<

30.        执行 svn 恢复操作,再执行 svn up,发现 somedir 没有了:

31.            ~/tmp/svntf/trunk$ svn revert -R .

32.            已恢复“somedir”

33.            ~/tmp/svntf/trunk$ svn up

34.            版本 4。

执行 svn status,看到本地工作目录的冲突状态已经消失了,一切看起来正常了。

~/tmp/svntf/trunk$ svn st

?       .svn-merge-conflict

?       .svn-orignal

但是,慢着,此时 somedir 目录没有了!

~/tmp/svntf/trunk$ ls

实际上,这时版本库中还有 somedir 目录,我们可以用 svn ls 命令查看:

~/tmp/svntf/trunk$ svn ls -R

somedir/

somedir/trunk.txt

但是本地的确已经没有了。如果要刨根问底的话,可以比较一下 .svn 目录和我们之前创建的备份:

~/tmp/svntf/trunk$ diff -r .svn-orignal .svn

diff -r .svn-orignal/entries .svn/entries

38,40d37

< somedir

< dir

<

35.        这时如果再执行从分支合并,合并的过程就很有意思了
居然没有发生冲突。yzw 这时后一定很欣喜,居然可以成功合并了。

36.            ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x

37.            --- 正在合并 r2,经由 r4,到 “.”:

38.            A    somedir

A    somedir/branch.txt

39.        但是慢着,这个合并是假的。不信提交看一看

40.            ~/tmp/svntf/trunk$ svn st

41.             M      .

42.            ?       .svn-merge-conflict

43.            ?       .svn-orignal

44.            A  +    somedir

45.            A  +    somedir/branch.txt

46.            ~/tmp/svntf/trunk$ svn ci -m "merge from branch 0.x"

47.            正在发送       trunk

48.            增加           trunk/somedir

49.            svn: 提交失败(细节如下):

svn: 目录 “/trunk/somedir” 已经过时

50.        提交过时?很简单,SVN 的经典问题:服务器有更新的版本,更新之

51.            ~/tmp/svntf/trunk$ svn up

版本 4。

52.        执行 svn update 没有反应?带目录执行 svn update 试试,果然发生冲突了:

53.            ~/tmp/svntf/trunk$ svn up somedir

54.             C somedir

55.            版本 4。

56.            冲突概要:

树冲突:1

57.        查看冲突的状态,是不是和 yzw 的一致了

58.            ~/tmp/svntf/trunk$ svn st

59.             M      .

60.            ?       .svn-merge-conflict

61.            ?       .svn-orignal

62.            A  +  C somedir

63.                  >   本地 增加,动作 增加,操作 更新

A  +    somedir/branch.txt

64.        不太一样?删掉 somedir 先。然后看状态:

~/tmp/svntf/trunk$ rm -rf somedir

再看看状态?

~/tmp/svntf/trunk$ svn st

M      .

?       .svn-merge-conflict

?       .svn-orignal

!  +  C somedir

       >   本地 增加,动作 增加,操作 更新

SVN 树冲突和目录丢失问题(2)
前面的博文《SVN 树冲突和目录丢失问题(1)》,介绍了重现 update 导致树冲突的重现过程。那么应该如何解决树冲突,以及如何找回丢失目录呢?首先我们需要了解:

什么是树冲突?
首先关于树冲突的概念,最好的参考是:SVN BOOK的有关树冲突的章节。

·             平时我们说的冲突,是因为对同一文件的不同修改造成的冲突。

·             树冲突,指的是由于目录(文件)树的改变,造成内容修改修改不能匹配在同一对象(目录/文件)上

·             例如:由于在一个分支中修改的目录和文件,在另外的分支出现了改名的操作。

·             或者像 yzw 的例子,因为两个分支同时增加了一个同名的目录,导致了树冲突。

树冲突的解决,首先要说的是要最好使用 svn 1.6 的客户端。因为 svn 1.5 的客户端对树冲突的处理不好,甚至根本发现不了树冲突,很容易忽略潜在的冲突,出现意想不到的结果。

SVN BOOK 中举了一个本地修改,远程重命名的例子。

例如 svn 1.5 处理这种本地修改/远程文件改名的情况,可能会出现如下结果:

·             发现远程改名,则检查本地文件是否包含修改,若不包含修改直接删除。

·             结果因为远程改名的文件,本地包含修改,因此不删除,而是保留该文件,但是文件的状态变为未受版本控制状态。

·             本地会显示一个新增文件,实际上是本地修改文件的原始(修改前)版本重命名后的文件。

·             这时,如果不太细心(谁那有那么细心?),就会丢失本地的改动。

对于 svn 1.5 最困难的是发现冲突,当然解决冲突也要靠手工比较完成:将本地文件改动在重命名后文件中再次改动一次!

对于 svn1.6 来说,引入了树冲突的概念,看似复杂度增加了,实际上是解决了两个问题:

·             识别冲突的问题:
将本地修改的文件标记为树冲突

·             改动合并的问题:
将本地修改合并到远程改名后的文件中。因为远程改名后的文件来自于本地修改的原始文件

当然对于远程重命名的例子,还要你最终的决定权还是在于你自己:

·             是否接受远程的文件重命名操作?

·             你需要手动将树冲突的状态予以解决:本地文件删除即接受远程重命名;

·             或者将远程添加的文件删除即不允许重命名。

·             对于前一种状况(即接受远程重命名),本地改动 SVN 1. 6的客户端已经自动合并到远程改名后的文件中了。

那么对于 yzw 的情况呢?

本地和远程同时添加目录?

yzw 遇到的树冲突,是因为本地和远程同时添加了相同的目录。实际上,我们在上一个博客中已经看到了两种不同的解决方案影子:

·             本地添加 somedir 目录并提交后,执行合并,显示:“合并引发的树冲突”

o                这时,如果执行 svn resolve –accept working somedir 命令解决冲突的话,

o                就相当于说:“采用我增加的 somedir 目录,他增加的 somedir 目录不算数”

o                然后提交即可

·             树冲突发生后:

o                还原冲突

o                删除主线中的目录。潜台词是:我本地的修改不要了,而是使用远程(分支)添加的目录

o                然后再执行从分支到主线的合并操作

o                合并成功,提交。

o                也就是说,采用的策略是:“采用他增加的 somedir 目录,我增加的 somedir 目录不算数”

看看实际操作的例子:

接受我的修改,远程的修改不算数
·             合并引发树冲突状态

·                    ~/tmp/svntf/trunk$ svn st

·                     M      .

·                    ?       .svn-merge-conflict

·                    ?       .svn-update-conflict

·                    ?       .svn-orignal

·                          C somedir

·                          >   本地 增加,动作 增加,操作 合并

·             使用 svn resolve –accept working 解决冲突:
~/tmp/svntf/trunk$ svn resolve –accept working somedir
“somedir”的冲突状态已解决
~/tmp/svntf/trunk$ svn st
M .
? .svn-merge-conflict
? .svn-update-conflict
? .svn-orignal

·             显示的目录的属性有改变,是因为增加了一个 svn:merge-info 属性

·                    ~/tmp/svntf/trunk$ svn pl -v .

·                    “.” 上的属性:

·                      svn:mergeinfo

·                        /branches/0.x:2-4

·             提交合并的结果

·                    ~/tmp/svntf/trunk$ svn ci -m "忽略分支 0.x 的改动,我的增加是对的。"

·                    正在发送       trunk

·                    

·                    提交后的版本为 5。

·                    ~/tmp/svntf/trunk$ svn st

·                    ?       .svn-merge-conflict

·                    ?       .svn-update-conflict

·                    ?       .svn-orignal

·                    ~/tmp/svntf/trunk$ svn pl -v .

·                    “.” 上的属性:

·                      svn:mergeinfo

    /branches/0.x:2-4

接受远程的修改,我的修改不算数
上一个博文中,我几经尝试,进入到了由于更新引发的树冲突。但是在上一个博客示例最后的更新引发树冲突的状态,虽然也可以用 svn resolve 命令解决树冲突,但是无法提交。

总是报错

~/tmp/svntf/trunk$ svn st

M      .

A  +  C somedir

      >   本地 增加,动作 增加,操作 更新

A  +    somedir/branch.txt

~/tmp/svntf/trunk$ ls somedir

branch.txt

~/tmp/svntf/trunk$ svn resolve --accept working somedir

“somedir”的冲突状态已解决

~/tmp/svntf/trunk$ svn st

M      .

A  +    somedir

A  +    somedir/branch.txt

~/tmp/svntf/trunk$ svn ci -m "删除我的增加的目录,分支 0.x 的增加是对的。"

增加           somedir

svn: 提交失败(细节如下):

svn: 目录 “/trunk/somedir” 已经过时

无论你如何执行 svn update,解决过时错误,也仍然不能提交。会陷入一个无穷无尽的冲突解决==>过时冲突的循环中。

一个撤销本地修改,接受远程分支修订的解决的办法是:

1.           还原

2.                ~/tmp/svntf/trunk$ svn revert -R .

3.                已恢复“.”

4.                已恢复“somedir”

5.                ~/tmp/svntf/trunk$ svn up

6.                版本 4。

7.                ~/tmp/svntf/trunk$ svn st

?       somedir

8.           找回在 .svn/entries 中已经丢弃的 somedir我使用 svn up –set-depth infinity 但是并不能更新出来。如果先执行 –set-depth 为其他,在执行 –set-depth infinity 倒是能够找回 somedir。但是这个方法太过奇怪,好像是 SVN 的一个 BUG,通过奇怪的操作绕过去了一样。我用下面的命令找回丢失的 somedir 目录:

9.                ~/tmp/svntf/trunk$ rm -rf somedir/

10.            ~/tmp/svntf/trunk$ svn up

11.            版本 4。

12.            ~/tmp/svntf/trunk$ svn up somedir

13.            A    somedir

A    somedir/trunk.txt

14.        用 svn 命令删除 somedir目录,并提交

15.            ~/tmp/svntf/trunk$ svn rm somedir/

16.            D         somedir/trunk.txt

17.            D         somedir

18.            

19.            ~/tmp/svntf/trunk$ svn ci -m "丢弃我的改动"

20.            删除           trunk/somedir

21.            

提交后的版本为 5。

22.        然后将分支 0.x 合并到主线,因为本地目录已经不在,合并不会出现冲突了

23.            ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x

24.            --- 正在合并 r2,经由 r5,到 “.”:

25.            A    somedir

26.            A    somedir/branch.txt

27.            ~/tmp/svntf/trunk$ svn st

28.             M      .

29.            A  +    somedir

30.            A  +    somedir/branch.txt

31.            ~/tmp/svntf/trunk$ svn ci -m "删除我的增加的目录,分支 0.x 的增加是对的。"

32.            正在发送       trunk

33.            svn: 提交失败(细节如下):

34.            svn: 目录 “/trunk” 已经过时

35.            ~/tmp/svntf/trunk$ svn up

36.            版本 5。

37.            ~/tmp/svntf/trunk$ svn ci -m "删除我的增加的目录,分支 0.x 的增加是对的。"

38.            正在发送       trunk

39.            增加           trunk/somedir

40.            增加           trunk/somedir/branch.txt

41.            

42.            提交后的版本为 6。

中间出现了一次过时问题,是因为目录属性修改,而目录的版本号尚未更新。这其实是 svn 混杂版本号造成的,已经超出本博文的内容。

总结
好了,通过这个例子,我介绍了增加同名目录引发的树冲突的解决方案。

·             如何保留本地修改,取消远程修改

·             如何取消本地修改,保留远程修改

·             怎样的操作,会使 svn 进入一个无法解决的冲突合并状态,以及如何解决

·             发现了一个 SVN 本地目录丢失无法通过  svn update 找回的问题,以及两种解决方法:

o                一个很古怪:分别使用不同的 –set-depth 值执行多次 update,不过最终会成功

o                另外一个却需要知道丢失的目录名称,如果不知道本地有哪个目录缺失,就很难操作了

老杨是我见过的最牛的程序员,但是他的 CVS 的死忠和对 SVN 的憎恨让我觉得异常诧异。可能他和Linus Torvalds 很想像,但又有不同,因为 Linus 对 CVS 和 SVN 同样憎恨。

我估计老杨一旦有时间,一旦掌握了 Git,他就会把 CVS (还有SVN?)扔到九霄云外。不过我估计他还得需要 SVN 和 GIT 接合使用,因为毕竟他现在做的是商业软件开发,还需要精细的代码授权(至少是对别人授权)。
1 楼 keke8614 2011-09-15  
废话真多,无语了
  相关解决方案