2010-04-22

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 目录并提交后,执行合并,显示:“合并引发的树冲突”
    • 这时,如果执行 svn resolve --accept working somedir 命令解决冲突的话,
    • 就相当于说:“采用我增加的 somedir 目录,他增加的 somedir 目录不算数”
    • 然后提交即可
  • 树冲突发生后:
    • 还原冲突
    • 删除主线中的目录。潜台词是:我本地的修改不要了,而是使用远程(分支)添加的目录
    • 然后再执行从分支到主线的合并操作
    • 合并成功,提交。
    • 也就是说,采用的策略是:“采用他增加的 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. 还原
    ~/tmp/svntf/trunk$ svn revert -R .
    已恢复“.”
    已恢复“somedir”
    ~/tmp/svntf/trunk$ svn up
    版本 4。
    ~/tmp/svntf/trunk$ svn st
    ?       somedir
  2. 找回在 .svn/entries 中已经丢弃的 somedir我使用 svn up --set-depth infinity 但是并不能更新出来。如果先执行 --set-depth 为其他,在执行 --set-depth infinity 倒是能够找回 somedir。但是这个方法太过奇怪,好像是 SVN 的一个 BUG,通过奇怪的操作绕过去了一样。我用下面的命令找回丢失的 somedir 目录:
    ~/tmp/svntf/trunk$ rm -rf somedir/
    ~/tmp/svntf/trunk$ svn up
    版本 4。
    ~/tmp/svntf/trunk$ svn up somedir
    A    somedir
    A    somedir/trunk.txt
  3. 用 svn 命令删除 somedir目录,并提交
    ~/tmp/svntf/trunk$ svn rm somedir/
    D         somedir/trunk.txt
    D         somedir
    
    ~/tmp/svntf/trunk$ svn ci -m "丢弃我的改动"
    删除           trunk/somedir
    
    提交后的版本为 5。
  4. 然后将分支 0.x 合并到主线,因为本地目录已经不在,合并不会出现冲突了
    ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x
    --- 正在合并 r2,经由 r5,到 “.”:
    A    somedir
    A    somedir/branch.txt
    ~/tmp/svntf/trunk$ svn st
     M      .
    A  +    somedir
    A  +    somedir/branch.txt
    ~/tmp/svntf/trunk$ svn ci -m "删除我的增加的目录,分支 0.x 的增加是对的。"
    正在发送       trunk
    svn: 提交失败(细节如下):
    svn: 目录 “/trunk” 已经过时
    ~/tmp/svntf/trunk$ svn up
    版本 5。
    ~/tmp/svntf/trunk$ svn ci -m "删除我的增加的目录,分支 0.x 的增加是对的。"
    正在发送       trunk
    增加           trunk/somedir
    增加           trunk/somedir/branch.txt
    
    提交后的版本为 6。
    
    中间出现了一次过时问题,是因为目录属性修改,而目录的版本号尚未更新。这其实是 svn 混杂版本号造成的,已经超出本博文的内容。

总结

好了,通过这个例子,我介绍了增加同名目录引发的树冲突的解决方案。
  • 如何保留本地修改,取消远程修改
  • 如何取消本地修改,保留远程修改
  • 怎样的操作,会使 svn 进入一个无法解决的冲突合并状态,以及如何解决
  • 发现了一个 SVN 本地目录丢失无法通过  svn update 找回的问题,以及两种解决方法:
    • 一个很古怪:分别使用不同的 --set-depth 值执行多次 update,不过最终会成功
    • 另外一个却需要知道丢失的目录名称,如果不知道本地有哪个目录缺失,就很难操作了
最后,要向老杨的致谢,是他间接的为我们网站增加了两个博客。 老杨是我见过的最牛的程序员,但是他的 CVS 的死忠和对 SVN 的憎恨让我觉得异常诧异。可能他和Linus Torvalds 很想像,但又有不同,因为 Linus 对 CVS 和 SVN 同样憎恨。 我估计老杨一旦有时间(刚刚结婚,可能很难有时间),一旦掌握了 Git,他就会把 CVS (还有SVN?)扔到九霄云外。不过我估计他还得需要 SVN 和 GIT 接合使用,因为毕竟他现在做的是商业软件开发,还需要精细的代码授权(至少是对别人授权)。
后记: 睡醒一觉后,发现俺的闺女小雪已经学会了 one, two, three, four!真的不是吹的,2岁5个月的小孩,千字文已经可以从“天地玄黄背”到“如松之胜”,上个月开始莫名的喜欢英文歌,今天早晨居然会说 one, two, three, four!  “姑娘,别累坏了,不过要是你喜欢,...” 欣喜之余,想到给 yzw 的博文莫不如扩充到 one, two, three, four。于是就有了下面两个博文:
blog comments powered by Disqus