2010-04-22

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
    ~/tmp/svntf/trunk$ svn info
    路径: .
    URL: file:///tmp/svnserver/trunk
    版本库根: file:///tmp/svnserver
    版本库 UUID: c0e3cb0f-81b0-40e5-9c35-42972dbc50aa
    版本: 4
    节点种类: 目录
    调度: 正常
    最后修改的作者: jiangxin
    最后修改的版本: 4
    最后修改的时间: 2010-04-22 19:51:55 +0800 (四, 2010-04-22)
  2. 主线下目录下的文件
    ~/tmp/svntf/trunk$ svn ls -R
    somedir/
    somedir/trunk.txt
  3. 主线的更改历史
    ~/tmp/svntf/trunk$ svn log
    ------------------------------------------------------------------------
    r4 | jiangxin | 2010-04-22 19:51:55 +0800 (四, 2010-04-22) | 1 行
    
    to trunk, we add somedir/trunk.txt file.
    ------------------------------------------------------------------------
    r1 | jiangxin | 2010-04-22 19:48:57 +0800 (四, 2010-04-22) | 1 行
    
    initial
    ------------------------------------------------------------------------
  4. 分支的中的文件列表
    ~/tmp/svntf/trunk$ svn ls -R file:///tmp/svnserver/branches/0.x
    somedir/
    somedir/branch.txt
  5. 分支的更改历史
    ~/tmp/svntf/trunk$ svn log file:///tmp/svnserver/branches/0.x
    ------------------------------------------------------------------------
    r3 | jiangxin | 2010-04-22 19:51:21 +0800 (四, 2010-04-22) | 1 行
    
    to branch 0.x, we add somedir/branch.txt file.
    ------------------------------------------------------------------------
    r2 | jiangxin | 2010-04-22 19:50:27 +0800 (四, 2010-04-22) | 1 行
    
    trunk => branch/0.x
    ------------------------------------------------------------------------
    r1 | jiangxin | 2010-04-22 19:48:57 +0800 (四, 2010-04-22) | 1 行
    
    initial
    ------------------------------------------------------------------------

备份当前的 .svn 目录

直接拷贝 .svn 到 .svn-orignal,以便在出现意外的时候,进行参照。
~/tmp/svntf/trunk$ cp -a .svn .svn-orignal
Tips: 如果不想每次在执行 svn status 时显示 .svn-orignal 目录,设置svn属性 svn:ignore 值为 ".svn*"。(问题优点复杂化了,算了)

合并分支改动,引发异常

  1. 执行合并操作,引发异常
    ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x
    --- 正在合并 r2,经由 r4,到 “.”:
       C somedir
    冲突概要:
      树冲突:1
  2. 删除 somedir 目录
    ~/tmp/svntf/trunk$ rm -rf somedir/
  3. 执行 svn status 查看当前状态
    ~/tmp/svntf/trunk$ svn st
     M      .
    ?       .svn-merge-conflict
    ?       .svn-orignal
    !     C somedir
          >   本地 增加,动作 增加,操作 合并
    
  4. 虽然结果和yzw很像了,但是还不是完全一致。
    • yzw 执行 svn status 显示的英文,最后一行翻译为中文是:
            >   本地 增加,动作 增加,操作 更新
    • 而我的结果是:
            >   本地 增加,动作 增加,操作 合并
    • 看来,还需要作些工作才能完全重现错误。
  5. 这时我又备份了一下 .svn 目录,将 .svn 复制到 .svn-merge-conflict,以便后面比较
  6. 执行 svn up 命令
    ~/tmp/svntf/trunk$ svn up
    跳过“somedir”
    D    somedir
    更新到版本 4。
    冲突概要:
      跳过的路径:1
    ~/tmp/svntf/trunk$ svn up
    版本 4。
  7. 以上执行了两次 svn up ,结果不一致,说明 svn 干了些什么。 实际上,通过比较 .svn 目录和之前备份的 .svn-merge-conflict 可以看出端倪:
    ~/tmp/svntf/trunk$ diff -r .svn-merge-conflict .svn
    diff -r .svn-merge-conflict/entries .svn/entries
    38,40d37
    < somedir
    < dir
    <
  8. 执行 svn 恢复操作,再执行 svn up,发现 somedir 没有了:
    ~/tmp/svntf/trunk$ svn revert -R .
    已恢复“somedir”
    ~/tmp/svntf/trunk$ svn up
    版本 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
    <
    
  9. 这时如果再执行从分支合并,合并的过程就很有意思了 居然没有发生冲突。yzw 这时后一定很欣喜,居然可以成功合并了。
    ~/tmp/svntf/trunk$ svn merge file:///tmp/svnserver/branches/0.x
    --- 正在合并 r2,经由 r4,到 “.”:
    A    somedir
    A    somedir/branch.txt
  10. 但是慢着,这个合并是假的。不信提交看一看
    ~/tmp/svntf/trunk$ svn st
     M      .
    ?       .svn-merge-conflict
    ?       .svn-orignal
    A  +    somedir
    A  +    somedir/branch.txt
    ~/tmp/svntf/trunk$ svn ci -m "merge from branch 0.x"
    正在发送       trunk
    增加           trunk/somedir
    svn: 提交失败(细节如下):
    svn: 目录 “/trunk/somedir” 已经过时
  11. 提交过时?很简单,SVN 的经典问题:服务器有更新的版本,更新之
    ~/tmp/svntf/trunk$ svn up
    版本 4。
  12. 执行 svn update 没有反应?带目录执行 svn update 试试,果然发生冲突了:
    ~/tmp/svntf/trunk$ svn up somedir
     C somedir
    版本 4。
    冲突概要:
     树冲突:1
  13. 查看冲突的状态,是不是和 yzw 的一致了
    ~/tmp/svntf/trunk$ svn st
     M      .
    ?       .svn-merge-conflict
    ?       .svn-orignal
    A  +  C somedir
          >   本地 增加,动作 增加,操作 更新
    A  +    somedir/branch.txt
  14. 不太一样?删掉 somedir 先。然后看状态:
    ~/tmp/svntf/trunk$ rm -rf somedir
    再看看状态?
    ~/tmp/svntf/trunk$ svn st
     M      .
    ?       .svn-merge-conflict
    ?       .svn-orignal
    !  +  C somedir
           >   本地 增加,动作 增加,操作 更新
饿了,吃饭先。回来继续...
注:在《SVN 树冲突和目录丢失问题(3)》中,我介绍了一个更为直接的重现 yzw 问题的方法。虽然 yzw 还是最有可能用本文的步骤遇到的问题,因为 分支和主线相应的提交都是 yzw 一个人完成的,没有他人参与。
blog comments powered by Disqus