• 1. SubversionSubversion基本概念 服务器配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 2. Subversion是什么?Subversion是一个自由/开源的版本控制系统。也就是说,在Subversion管理下,文件和目录可以超越时空。也就是Subversion允许你数据恢复到早期版本,或者是检查数据修改的历史。正因为如此,许多人将版本控制系统当作一种神奇的“时间机器”。
  • 3. Subversion的特性版本化的目录 真实的版本历史 原子提交 版本化的元数据 可选的网络层 一致的数据操作 高效的分支和标签操作 可修改性
  • 4. Subversion的架构
  • 5. Subversion的架构
  • 6. 版本库Subversion是一个“集中式”的信息共享系统。版本库是Subversion的核心部分,是数据的中央仓库。版本库以典型的文件和目录结构形式文件系统树来保存信息。任意数量的客户端连接到Subversion版本库,读取、修改这些文件。客户端通过写数据将信息分享给其他人,通过读取数据获取别人共享的信息。
  • 7. 版本模型需要避免的问题 锁定-修改-解锁 方案 拷贝-修改-合并 方案
  • 8. 版本模型需要避免的问题
  • 9. 版本模型 锁定-修改-解锁 方案
  • 10. 版本模型拷贝-修改-合并 方案
  • 11. 版本模型拷贝-修改-合并 方案(续)
  • 12. 工作拷贝一个Subversion工作拷贝是你本地机器上的一个普通目录,保存着一些文件,你可以任意的编辑文件,而且如果是源代码文件,你可以像平常一样编译,你的工作拷贝是你的私有工作区,在你明确的做了特定操作之前,Subversion不会把你的修改与其他人的合并,也不会把你的修改展示给别人,你甚至可以拥有同一个项目的多个工作拷贝。
  • 13. 工作拷贝当你在工作拷贝作了一些修改并且确认它们工作正常之后,Subversion提供了一个命令可以“发布”你的修改给项目中的其他人(通过写到版本库),如果别人发布了各自的修改,Subversion提供了手段可以把这些修改与你的工作目录进行合并(通过读取版本库)。
  • 14. 工作拷贝工作副本也包括一些由 Subversion 创建并维护的额外文件,用来协助执行命令。通常情况下,你的工作副本的每个文件夹都有一个以 .svn 为名的文件夹,也被叫做工作副本的管理目录,这个目录里的文件能够帮助 Subversion 识别哪些文件做过修改,哪些文件相对于别人的工作已经过期。
  • 15. 工作拷贝一个典型的Subversion的版本库经常包含许多项目的文件(或者说源代码),通常每一个项目都是版本库的子目录,在这种布局下,一个用户的工作拷贝往往对应版本库的的一个子目录。
  • 16. 修订版本一个svn commit操作可以作为一个原子事务操作发布任意数量文件和目录的修改,在你的工作拷贝里,你可以改变文件内容、删除、改名以及拷贝文件和目录,然后作为一个原子事务一起提交。 每当版本库接受了一个提交,文件系统进入了一个新的状态,叫做一次修订(revision),每一个修订版本被赋予一个独一无二的自然数,一个比一个大,初始修订号是0,只创建了一个空目录,没有任何内容。
  • 17. 工作拷贝怎样跟踪版本库对于工作拷贝的每一个文件,Subversion在管理区域.svn/记录两项关键的信息: 1、工作文件所作为基准的修订版本(叫做文件的工作修订版本) 2 、一个本地拷贝最后更新的时间戳。
  • 18. 工作拷贝怎样跟踪版本库给定这些信息,通过与版本库通讯,Subversion可以告诉我们工作文件是处于如下四种状态的那一种: 1、未修改且是当前的 文件在工作目录里没有修改,在工作修订版本之后没有修改提交到版本库。svn commit操作不做任何事情,svn update不做任何事情。 2 、本地已修改且是当前的 在工作目录已经修改,从基本修订版本之后没有修改提交到版本库。本地修改没有提交,因此svn commit会成功提交,svn update不做任何事情。
  • 19. 工作拷贝怎样跟踪版本库3、未修改且不是当前的了 这个文件在工作目录没有修改,但在版本库中已经修改了。这个文件最终将更新到最新版本,成为当时的公共修订版本。svn commit不做任何事情,svn update将会取得最新的版本到工作拷贝。 4、本地已修改且不是最新的 这个文件在工作目录和版本库都得到修改。一个svn commit将会失败,这个文件必须首先更新,svn update命令会合并公共和本地修改,如果Subversion不可以自动完成,将会让用户解决冲突。
  • 20. SubversionSubversion基本概念 服务器安装与配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 21. 概述Subversion的设计包括一个抽象的网络层,这意味着版本库可以通过各种服务器进程访问,而且客户端“版本库访问”的API允许程序员写出相关协议的插件,理论上讲,Subversion可以使用无限数量的网络协议实现。 目前实践中只有两种服务器: Apache svnserve
  • 22. ApacheApache是最流行的web服务器,通过使用mod_dav_svn模块,Apache可以访问版本库,并且可以使客户端使用HTTP的扩展协议WebDAV/DeltaV进行访问,因为Apache是一个非常易于扩展的web服务器,它提供了许多“易于获取的”特性,例如加密的SSL通讯,日志和与第三方工具的集成,以及内置的版本库web浏览功能。
  • 23. svnservesvnserve:一个更小,轻型的服务器程序,同客户 端使用自定义的协议。因为协议是为Subversion专门设计的,并且是有状态的(不像HTTP),它提供了更快的网络操作—但也有一些代价。它只理解 CRAM-MD5的认证,然而它非常易于配置,是开始使用Subversion的小团队的最佳选择。
  • 24. Subversion的安装安装步骤: 1、http://subversion.tigris.org/ Download subversion-1.5.1.tar.bz2 (若是在Windows下下载,可用rz命令将文件导入到Linux中) 2 、解压:#tar  jxvf  subversion-1.5.1.tar.bz2 3 、进入解压后的目录 #./configure --prefix=/usr/local/svn (--prefix=a: a为svn的安装目录,若configure未能通过,则还应选择性的安装 libapr libapr-util expat libneon 具体安装步骤见 附) #make #make install
  • 25. Subversion的安装4 、运行#/usr/local/svn  --version看是否安装成功,至少应具备以下几个模块: The following repository access (RA) modules are available: * ra_neon : Module for accessing a repository via WebDAV protocol using Neon.   - handles 'http' scheme * ra_svn : Module for accessing a repository using the svn network protocol.   - handles 'svn' scheme * ra_local : Module for accessing a repository on local disk.   - handles 'file' scheme
  • 26. Subversion的安装附: 1)安装libapr A.Go to http://apr.apache.org/ to download the lastest version of apr :apr-1.3.2.tar.bz2 B.  解压缩 :#tar jxvf apr-1.3.2.tar.bz2 C.编译安装 #./configure  -–prefix=/usr/local/apr #make     #make install 2)安装libapr-util A.Go to http://apr.apache.org/ to download the lastest version of apr-util:apr-util-1.3.2.tar.bz2 B.  解压缩     #tar  jxvf  apr-util-1.3.2.tar.bz2
  • 27. Subversion的安装 C.编译安装     #tar jxvf apr-util-1.3.2.tar.bz2 #./configure  --prefix=/usr/local/apr      #make     #make install 3)  安装expat (XML Parser) http://expat.sourceforge.net/ Download expat-2.0.1.tar.gz #tar zxvf expat-2.0.1.tar.gz #./configure --prefix=/usr/local/expat #make #make install
  • 28. Subversion的安装4) 安装libneon  (for http://) http://www.webdav.org/neon/ Download neon-0.28.2.tar.gz #tar zxvf neon-0.28.2.tar.gz #./configure  --prefix=/usr/local/neon #make #make install 安装完成后再回到安装步骤3,在#./configure命令中加上你所安装软件的相应参数,如下所示。然后操作步骤4. --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr --with-neon=/usr/local/neon
  • 29. 服务器配置内置的认证和授权 版本库的svnserve.conf文件是控制认证和授权政策的中央机构,这文件与其它配置文件格式相同:小节名称使用方括号标记([和]),注释以井号(#)开始,每一小节都有一些参数可以设置(variable = value)。
  • 30. 服务器配置创建一个用户文件和认证域 svnserve.conf文件的[general]部分包括所有你需要的变量,开始先定义一个保存用户名和密码的文件和一个认证域: [general] password-db = userfile realm = example realm realm是你定义的名称,这告诉客户端连接的“认证命名空间”,Subversion会在认证提示里显示,并且作为凭证缓存的关键字(还有服务器的主机名和端口),password-db参数指出了保存用户和密码列表文件,这个文件使用同样熟悉的格式。举个例子:
  • 31. 服务器配置举个例子: [users] harry = foopassword sally = barpassword password-db的值可以是用户文件的绝对或相对路径,对许多管理员来说,把文件保存在版本库conf/下的svnserve.conf旁边是一个简单的方法。另一方面,可能你的多个版本库使用同一个用户文件,此时,这个文件应该在更公开的地方,版本库分享用户文件时必须配置为相同的域,因为用户列表本质上定义了一个认证域,无论这个文件在哪里,必须设置好文件的读写权限,如果你知道运行svnserve的用户,限定这个用户对这个文件有读权限是必须的。
  • 32. 服务器配置设置访问控制 svnserve.conf有两个或多个参数需要设置:它们确定未认证(匿名)和认证用户可以做的事情,参数anon-access和auth-access可以设置为none、read或者write,设置为none会限制所有方式的访问,read允许只读访问,而write允许对版本库完全的读/写权限,例如: [general] password-db = userfile realm = example realm # anonymous users can only read the repository anon-access = read # authenticated users can both read and write auth-access = write
  • 33. 服务器配置基于路径的授权 访问文件authz的语法与svnserve.conf中配置文件非常相似,以(#)开头的行会被忽略,在它的简单形式里,每一小节命名一个版本库和一个里面的路径,认证用户名是在每个小节中的选项名,每个选项的值描述了用户访问版本库的级别:r(只读)或者rw(读写),如果用户没有提到,访问是不允许的。
  • 34. 服务器配置为简单学习subversion 1、在svnserve.conf文件中只需以默认方式设置password-db = passwd 2、在authz文件中,在[/svn]选项中设置为* = rw,在 [/svn:/trunk]选项中设置为* = rw 3、在passwd文件中,在[users]选项中设置密码,如:root = 123456
  • 35. SubversionSubversion基本概念 服务器安装与配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 36. 版本库的建立第一步:新建一个版本库: svnadmin create /usr/svn 存放Subversion版本库的文件夹路径,可以是你需要的其他文件夹路径 这个命令建立一个新的目录/usr/svn,包含了一个Subversion版本库。
  • 37. 版本库的建立第二步:建立一些需要导入版本库的文件和目录,应包含三个顶级子目录:branches,tags,trunk,建立起目录和文件的树形结构。例如: /home/kz/tmp/project/ branches/ /home/kz /tmp/project/tags/ /home/kz /tmp/project//trunk/ main.c agent.c prg ···
  • 38. 版本库的建立第三步:有了树形结构和数据,使用svn import导入数据到版本库: svn import /home/kz/tmp/project svn://202.115.17.56/usr/svn -m “initial import” 这时目录和数据被导入版本库
  • 39. 版本库的建立第四步:创建工作目录 你需要在本地建立一个工作目录,用来编辑提交你的工作成果。使用svn checkout命令来建立工作目录: svn checkout svn://202.115.17.56/usr/svn/trunk /home/kz/myproject
  • 40. 版本库的建立把trunk文件夹下的数据取道本地建立的myproject目录下建立工作目录(即myproject目录) 现在在myproject目录里有了一个版本库的个人拷贝,可以编辑里面的工作备份中的文件,并提交到版本库。
  • 41. SubversionSubversion基本概念 服务器安装与配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 42. 帮助help svn help 比如需要查checkout命令的帮助,例: svn help checkout 直接输入svn help可以查看svn常用命令。
  • 43. 导入import svn import 需要导入文件路径 版本库URL地址 如果需要添加日志注释则使用 –m参数后接日志字符串,例: svn import /home/kz/tmp/project svn://202.115.17.56/usr/svn -m "initial import"
  • 44. 初始化checkout svn checkout 版本库URL地址 需要建立工作目录的URL地址 在工作开始的时候建立,例: svn checkout svn://202.115.17.56/usr/svn/trunk /home/kz/myproject
  • 45. 更新工作目录update svn update 将本地工作目录同步到svn服务器的更新
  • 46. 添加文件addsvn add 需要添加的文件名 将工作目录下的文件添加到版本库中,例: svn add main.c
  • 47. 提交修改commitsvn commit 该命令将工作目录的修改提交到版本库中 参数:-m “提交的日志内容” -F 日志文件名 例:svn commit main.c -m "add main.c again"
  • 48. 删除文件deletesvn delete 需要删除的文件URL地址 将版本库中的文件删除,例: svn delete /home/kz/myproject/agent.c
  • 49. 复制文件copysvn copy 需要复制的原文件URL地址 复制到的目的文件URL地址 将版本库中的文件复制一个副本,例: svn copy svn://192.168.1.51/svn/ppcdn/trunk/ svn://192.168.1.51/svn/ppcdn/release 将trunk文件夹的内容复制到release文件夹
  • 50. 移动文件和修改文件名movesvn move 需要移动的文件URL地址 移动到的目的文件URL地址 如果两个URL在同一个文件夹下,即为修改文件名,例: svn move svn://192.168.1.51/svn/trunk/main.c svn://192.168.1.51/svn/release/main.c 将trunk目录下的main.c移动到release目录下 svn move svn://192.168.1.51/svn/trunk/main.c svn://192.168.1.51/svn/trunk/main.cpp 将trunk目录下的main.c更名为main.cpp
  • 51. 检查工作目录状态statussvn status或者 svn status 本地文件路径 不带路径的命令检查对工作目录的当前目录的修改,如果是在工作目录的顶级目录下,则返回所有修改 带路径的命令返回你制定目录的修改信息 参数:--verbose显示工作目录中所有项目,包括没有修改过的 --show-update将联系版本库为工作目录中已经过时的数据添加新信息 例:svn status --show-update --verbose
  • 52. 检查本地修改diffsvn diff 该命令比较工作目录与缓存在工作目录中的“原始”副本的区别 例:svn diff
  • 53. 比较工作目录与版本库diffsvn diff --revision 版本号 需要比较的文件名 该命令将文件和特定版本进行比较 例:svn diff --revision 2600 main.c
  • 54. 比较版本库与版本库diffsvn diff --revision 旧版本号:新版本号 需要比较的文件名 该命令将文件不同版本之间进行比较, 例: svn diff --revision 2599:2600 main.c
  • 55. 撤销修改revert svn revert 文件名 该命令插销以前的修改, 例:svn revert main.c
  • 56. 查看历史信息logsvn log或者 svn log 需要查看的文件名或者URL地址 该命令查看文件修改的日志信息 例: Svn log http://192.168.1.51/svn/ppcdn
  • 57. 列表文件夹listsvn list 需要列表的文件夹URL地址 该命令将文件夹的子目录和文件列出来,例: svn list http://192.168.1.51/svn/ppcdn
  • 58. SubversionSubversion基本概念 服务器安装与配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 59. 什么是分支?假设你的工作是维护本公司一个部门的手册文档,一天,另一个部门问你要相同的手册,但一些地方会有“区别”,因为他们有不同的需要。 显而易见的方法是:作一个版本的拷贝,然后分别维护两个版本,只要任何一个部门告诉要做一些小修改,你必须选择在对应的版本进行更改。
  • 60. 什么是分支?分支 开发的一条线独立于另一条线,如果回顾历史,可以发现两条线分享共同的历史,一个分支总是从一个备份开始的,从那里开始,发展自己独有的历史
  • 61. 创建分支版本库结构
  • 62. 创建分支使用svn copy命令给你的工程做个拷贝,Subversion不仅可以拷贝单个文件,也可以拷贝整个目录,作/calc/trunk的拷贝 在/calc/branches中建立分支,并命名为my-calc-branch 将拷贝置于/calc/branches/ my-calc-branch 操作如下: $ svn copy svn://svn.example.com/repos/calc/trunk \ svn://svn.example.com/repos/calc/branches/my-calc-branch \ -m "Creating a private branch of /calc/trunk." Committed revision 341.
  • 63. 创建分支创建分支后的版本库目录结构
  • 64. 在分支上工作现在你已经在项目里建立分支了,你可以取出一个新的工作拷贝来开始使用: $ svn checkout svn://svn.example.com/repos/calc/branches/my-calc-branch A my-calc-branch/Makefile A my-calc-branch/integer.c A my-calc-branch/button.c Checked out revision 341. 这一份工作拷贝没有什么特别的,它只是版本库另一个目录的一个镜像罢了
  • 65. 在分支上工作假如: 你修改了/calc/branches/my-calc-branch/button.c,生成修订版本342。 你修改了/calc/branches/my-calc-branch/integer.c,生成修订版本343。 其他用户修改了/calc/trunk/integer.c,生成了修订版本344。 当你提交修改时,该用户在更新时不会看到你的改变,他是/calc/trunk的工作拷贝。
  • 66. 在分支上工作integer.c的分支历史
  • 67. 分支的优缺点优点: 你和其他项目参与人员不会互相打扰 缺点: 有时候分离的太远 ,因为太多冲突,已 无法轻易合并你的分支和主干的修改。
  • 68. 什么是合并合并 当你在私有分支上作了很多修改后,将修改提交到主干线的过程,叫作合并。
  • 69. merge svn merge 功能:diff and apply,即首先两个版本库树比较,然后将区别应用到本地拷贝 这个命令包括三个参数: 初始的版本树(通常叫做比较的左边), 最终的版本树(通常叫做比较的右边), 一个接收区别的工作拷贝(通常叫做合并的目标)。
  • 70. merge一旦这三个参数指定以后,两个目录树将要做比较,比较结果将会作为本地修改应用到目标工作拷贝,当命令结束后,结果同你手工修改或者是使用svn add或svn delete没有什么区别,如果你喜欢这结果,你可以提交,如果不喜欢,你可以使用svn revert恢复修改。 svn merge的语法允许非常灵活的指定三个必要的参数,如下是一些例子:
  • 71. merge例1 使用URL@REV的形式直接列出了所有参数 $ svn merge svn://svn.example.com/repos/branch1@150 \ svn://svn.example.com/repos/branch2@212 \ my-working-copy 例2 比较同一个URL的不同版本 $ svn merge -r 100:200 svn://svn.example.com/repos/trunk my-working-copy 例3缺省工作拷贝,默认是当前目录 $ svn merge -r 100:200 svn://svn.ex ample.com/repos/trunk
  • 72. 合并的最佳实践手工跟踪合并 预览合并 合并冲突 关注还是忽视祖先 合并和移动
  • 73. 常见用例合并分支到另一分支 为了完成这个例子,我们将时间往前推进,假定已经过了几天,在主干和你的分支上都有许多更改,假定你完成了分支上的工作,已经完成了特性或bug修正,你想合并所有分支的修改到主干上,让别人也可以使用。 这种场景下如何使用svn merge?记住这个命令比较两个目录树,然后应用比较结果到工作拷贝,所以要接受这种变化,你需要主干的工作拷贝,我们假设你有一个最初的主干工作拷贝(完全更新),或者是你最近取出了/calc/trunk的一个干净的工作拷贝。
  • 74. 常见用例合并分支到另一分支 但是要哪两个树进行比较呢?乍一看,回答很明确,只要比较最新的主干与分支。但是你要意识到—这个想法是错误的,伤害了许多新用户!因为svn merge的操作很像svn diff,比较最新的主干和分支树不仅仅会描述你在分支上所作的修改,这样的比较会展示太多的不同,不仅包括分支上的增加,也包括了主干上的删除操作,而这些删除根本就没有在分支上发生过。
  • 75. 常见用例合并分支到另一分支 为了表示你的分支上的修改,你只需要比较分支的初始状态与最终状态,在你的分支上使用svn log命令,你可以看到你的分支在341版本建立,你的分支最终的状态用HEAD版本表示,这意味着你希望能够比较版本341和HEAD的分支目录,然后应用这些分支的修改到主干目录的工作拷贝。
  • 76. 常见用例合并分支到另一分支 最终的合并过程如下: $ cd calc/trunk $ svn update At revision 405. $ svn merge -r 341:405 svn://svn.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile
  • 77. 常见用例 $ svn status M integer.c M button.c M Makefile $ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk." Sending integer.c Sending button.c Sending Makefile Transmitting file data ... Committed revision 406.
  • 78. 常用分支模式发布分支 特性分支
  • 79. 标签 另一个常见的版本控制系统概念是标签(tag),一个标签只是一个项目某一时间的“快照”,在Subversion里这个概念无处不在—每一次提交的修订版本都是一个精确的快照。
  • 80. 标签简单标签的建立 svn copy再次登场,你希望建立一个/calc/trunk的一个快照,就像HEAD修订版本,建立这样一个拷贝: $ svn copy svn://svn.example.com/repos/calc/trunk \ svn://svn.example.com/repos/calc/tags/release-1.0 \ -m "Tagging the 1.0 release of the 'calc' project." Committed revision 351.
  • 81. 标签这个例子假定/calc/tags目录已经存在(如果不是,可以使用svn mkdir创建。),拷贝完成之后,一个表示当时HEAD版本的/calc/trunk目录的镜像已经永久的拷贝到release-1.0目录。
  • 82. SubversionSubversion基本概念 服务器安装与配置 版本库的建立 基本使用 分支与合并 冲突的解决
  • 83. 什么是冲突?用户hurry对版本库的文件A作了修改并提交到版本库,之后你也对文件A作了修改并准备提交。若你和hurry所作的修改发生交迭,这种情况叫作冲突。
  • 84. 解决冲突(合并别人的修改)可以使用svn status -u来预测冲突 $ svn update C bar.c Updated to revision 46. C表示冲突,说明服务器上的改动同你的改动冲突了,你需要自己手工去解决
  • 85. 解决冲突(合并别人的修改)当冲突发生了,有三件事可以帮助你注意到这种情况和解决问题: 1 Subversion在更新时打印C标记,并且标记这个文件已冲突 2 如果Subversion认为这个文件是可合并的,它会置入冲突标记—特殊的横线分开冲突的“两面”—在文件里可视化的描述重叠的部分 3 对于每一个冲突的文件,Subversion放置三个额外的未版本化文件到你的工作拷贝: filename.mine filename.rOLDREV filename.rNEWREV
  • 86. 解决冲突(合并别人的修改)如果你遇到冲突,三件事你可以选择: 1 “手动”合并冲突文本(检查和修改文件中的冲突标志) 2 用某一个临时文件覆盖你的工作文件 3 运行svn revert 来放弃所有的本地修改
  • 87. “手动”合并冲突文本 一个简单的例子 : 你和Sally,同时编辑了sandwich.txt。Sally提交了修改,当你准备更新你的工作拷贝,冲突发生了,我们不得不去修改sandwich.txt来解决这个问题
  • 88. “手动”合并冲突文本首先,看一下这个文件: $ cat sandwich.txt Top piece of bread Mayonnaise Lettuce Tomato Provolone <<<<<<< .mine Salami Mortadella Prosciutto ======= Sauerkraut Grilled Chicken >>>>>>> .r2 Creole Mustard Bottom piece of bread
  • 89. “手动”合并冲突文本小于号、等于号和大于号串是冲突标记,并不是冲突的数据,你一定要确定这些内容在下次提交之前得到删除,前两组标志中间的内容是你在冲突区所做的修改 ,后两组之间的是Sally提交的修改冲突 这时你需和Sally取得联系,并确定提交内容,一旦你们确认了提交内容后,修改文件并且删除冲突标志。 现在运行svn resolved $ svn resolved sandwich.txt $ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
  • 90. 复制文件到你的工作文件如果你只是希望取消你的修改,你可以仅仅拷贝Subversion为你生成的文件替换你的工作拷贝: $ svn update C sandwich.txt Updated to revision 2. $ ls sandwich.* sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1 $ cp sandwich.txt.r2 sandwich.txt $ svn resolved sandwich.txt
  • 91. 使用svn revert如果你得到冲突,经过检查你决定取消自己的修改并且重新编辑,你可以恢复你的修改: $ svn revert sandwich.txt Reverted 'sandwich.txt' $ ls sandwich.* sandwich.txt 注意,当你恢复一个冲突的文件时,不需要再运行svn resolved。