本文根据原文r33959,由Subversion中文站的中文化翻译小组翻译,欢迎各位参与翻译工作,加入地址:http://code.google.com/p/svncndoc/。参与翻译的志愿者包括rocksun、akeybupt2004、zhaozhi和jiaqifeng。
为了接管CVS的用户基础。确切的说,我们写了一个新的版本控制系统,它和CVS很相似,但是它修正了以前CVS所没有解决的许多问题。请看我们的首页。
不是,Subversion一款开源/免费的软件。CollabNet公司为几个全职的开发人员支付薪水,并且拥有相关代码的版权,但是这个版权是一个Apache/BSD-风格的许可证,它完全遵从于Debian Free Software Guidelines。换句话说,你可以随心所欲的去下载,修改,或者重新发布新的版本,而不需要经过CollabNet公司或者其他任何一个人的许可。
是的,绝对可以。它是一款已经准备好进入黄金时段的产品。
Subversion从2000年开始开发,在一年之后成为一个自足执行(self-hosting)的项目。在之后的一年,当我们将其称为"alpha"版本的时候,Subversion已经被很多的个人开发人员所使用,并确实发挥了真正的作用。在那之后,有两年多的时间被用来进行Bug追踪(bugfixing)和增强稳定性(stabilization),直到我们发布了1.0版本。大多数的其它项目可能都会把产品的“1.0”版本叫做早期版本,但是我们故意的尽可能长的推迟了这个版本的发布。我们意识到很多人在他们最初使用了Subversion之后一直在等待1.0版本的发布,并且对这个意义非凡的版本有着特别的期待,所以我们在这样一个版本上用了很多的时间而不至于让他们失望。
客户端和服务器被设计成只要他们相互之间没有超过一个主要的发行版本,就可以协调工作。例如任何一个1.x的客户端都可以和一个1.y的服务器协调工作。然而,如果客户端和服务器的版本不相匹配,某些特性可能不能使用。
客户端和服务器之间的兼容性策略记录在Hacker's Guide to Subversion的“兼容性”一节里面。
所有现代风格的操作系统,比如Unix, Win32, BeOS, OS/2, MacOS X。
Subversion是用ANSI C来写的,并且使用了APR,也就是Apache Portable Runtime库作为可移植层。Subversion的客户端将可以运行在任何APR运行的地方,这是最重要的一点。Subversion服务器(例如,版本库端)同样,唯一的例外是在Berkeley DB的Win9x(Win95/Win98/WinME)作为平台的主机上,因为Berkeley DB在Win9x平台上共享存储器的有段问题。FSFS版本库(从版本1.1开始引入)没有这种限制;然而由于Win9x对文件锁定的支持限制,他们仍然不能在Win9x的平台上工作。
重申一下,Subversion客户端可以运行在任何APR所运行的平台上。Subversion服务器也可以运行在任何APR平台所能运行的平台,但是不能在Win95/Win98/WinMe上管理版本库。
不是。"Subversion文件系统"不是一个可以装在操作系统上的内核级的文件系统,相反,它是Subversion版本库的接口,是一个版本文件系统,在某种意义上说,它可以存储一个文件目录结构(树)并且使这个目录结构从一个修订版本到下一个修订版本的过程中保留版本信息。访问这个版本库来编写程序和使用其他文件系统的API是很相似的,但是最大的不同在于,这个特殊的文件系统在你写入的时候不会丢失数据。目录结构(树)的一些旧的状态信息可以被很容易的恢复,就像恢复到最近的状态那样容易。
运行Subversion服务器要依赖各方面的多种因素,比如说一定数量的用户,经常性的提交,其它相关联的服务器,版本库的大小以及自定义版本库的负载等。当使用Apache时,有可能Apache本身在内存的使用情况将成为最大的制约因素。请参阅邮件列表上对这个问题的讨论,这里有针对这个问题的明确的答案。
需要记住的是,在同一台服务器上运行其它的应用程序,比如版本库的浏览器同样会使用内存,它是独立于Subversion本身。
通常,和CVS本版库相比,你可以期望需要更少的服务器内存。
不用。Subversion是一系列的库,它与一个命令行的客户端相互配合工作。有两类不同的Subversion的服务器进程,包括一个svnserve进程,这是一个小的类似CVS的pserver进程的独立运行程序,另一个是使用mod_dav_svn模块的Apachehttpd-2.0。svnserve进程可以说是一个常用协议,而mod_dav_svn进程使用了WebDAV作为它的网络协议。请参阅Subversion手册第六章以了解更多的内容。
一句话:不必。
详细的回答是:如果你仅仅是想去访问一个版本库,你只需要构建一个Subversion的客户端。如果你想拥有一个网络的版本库,那么你需要安装Apache2或者一个“svnserve”服务器。
关于安装一个可以访问的Subversion服务器的详细信息,请参阅:Subversion手册第六章。
不必,你可以用svnserve作为Subversion的服务器,它可以很好的工作。
在使用Apache server时,如果你想使用分布式创作和版本管理协议(WebDAV)和所有其他一些好的功能特性,那么是的,你需要Apache 2.0。但是不管怎样,当你继续在80端口运行Apache1.0的时候,你总是可以在另外一个不同的端口运行Apache2.0。不同版本的Apache可以在同一台机器上很好的和平相处。仅仅需要在改变httpd.conf文件中把Listen指令从80改成8080,或者其他你想要改成的端口。然后确保在你公布版本库的URL的时候加以说明:(例如,http://svn.mydomain.com:8080/repos/blah/trunk/)。
我们从来没有试图在SCM系统上推到重来,开辟出一个新的天地,也没有试图完全模仿每一个SCM系统的好的特性,我们只是要取代CVS。请参阅我们的第一个问题。
首先,请注意Subversion没有项目这个概念。版本库只是存储版本化的目录树 — 你可以将某个子目录当作项目,但是Subversion不会对其特殊对待。因此,对于一个项目的构成完全由用户自己解释。(与之类似的branches和tags的习惯是建立在复制之上,而不是建立在Subversion的概念之上。)
每当你提交变更时,版本库会在版本库目录树整体上增加一个修订版本,并将新的树赋予一个新的修订版本号,当然,大多数目录树和前一个修订版本完全一样,只是部分被更改。
新修订版本号码是一个顺序增加的标签,会附加给每个新增的树,而不是这个修订中的某个文件或目录,然而,通俗来说,会使用修订版本号码来引用修订中提交的变更;例如“r588中的变更”(“r588”是“修订版本588”的简写)的真实含义是“版本库目录树587和588的区别”,或者是另一个说法“将目录树587变成588的变更”。
因此,不断增加的修改版本号码会以一个整体标示版本库的进展;你通常不会使用修订版本号码来度量版本库中特定项目的进展,当然,修订版本号码不应该作为项目可见的发布号码,对此,你应该通过其他机制来区别发布,例如使用tags。
这个问题有些麻烦,因为好像每个人对变更集的定义多少有些不同,或者至少对版本控制系统的变更集特性多少有些不同的期望。
基于这个讨论的目的,这里对变更集有一个简单的定义:它是所有改变的唯一的名字的集合,这些改变可能包括对文件内容句法的编辑,目录结构的改变,或者是一些元数据的重新组合。更加通常的理解,一个变更集仅仅是一个你所能进行参阅的补丁的名字。
Subversion按照一阶对象的方式管理版本化目录树(版本库是一个目录树数组),而变更集则是被推生出来的东西(通过比较相邻的目录树)。Arch或Bitkeeper这类程序以相反方向创建:他们用一阶对象的方式管理变更集(版本库是一系列的补丁),而目录树则是由一系列的补丁组合而成。
从根本上来说,两者都不够好:争论至少可以追溯到三十年以前。对于不同类型的软件开发,各有利弊。现在我们不打算讨论这些,这里我将解释使用Subversion能怎么做。
用Subversion,用一个全局的版本编号N来命名版本库目录树:这是说版本库是经过第N次提交。它还包括一些不明显的变更集:如果你比较目录树N和目录树N-1,你可以精确得到提交的补丁。
因此,可以很容易的想到,版本N不仅仅是一个变更集。如果你用一个事件追踪去管理bug,你可以用版本号码去引用特殊的补丁,这些补丁很适合bug。例如,“这个事件被稳定在9238版本。”一些人可能运行‘svn log -r9238’来阅读对应Bug的补丁信息,运行‘svn diff -r9237:9238’来查看补丁本身。svn合并命令也是利用了版本号码。只要在合并参数中指明结果集,就可以把结果集从一个分支明确的合并到另一个分支:‘svn merge -r9237:9238 branchURL’将合并结果集#9238到你的工作拷贝。
似乎根据结果集构建和主要对象的构建一样复杂,但是已经比CVS方便许多了。
Subversion 1.1(和后续版本)已经可以把一个符号链置于版本控制之中,通过一个常用的svn add命令即可。
详细:版本库还没有一个关于符号链的内部概念,它存储一个附加'svn:special'属性的普通文件作为“符号链接”。svn客户端(在unix上)可以看到属性,并且在工作拷贝里转换成一个符号链。Win32没有符号链,所以一个win32的客户端不会做任何的转换:对象表现的像一个平常的文件。
可用的矢量Subversion logo在Subversion 版本库的www文件夹下可以得到。
特别的是,EPS 版本,和一个Adobe 插图文档也是可用的。
如果你在阅读这个常见问题回答之后,没有找到你问题的答案,那么你还有其他的几个资源可以利用:
我们的邮件列表为了保持在一个适度的标准,所以你最初的邮件可能会被延迟通过,直到维护人员有机会让它通过。一旦邮件允许通过,所有从相同的地址挂起的邮件将被自动支持,所以你应该不会再被拒绝。当然,如果你的发送地址改变了的话,你将不得不再次让维护人员检测通过。
使用Subversion客户端:
$ svn co http://svn.collab.net/repos/svn/trunk subversion
这将会从Subversion源文件目录里检出一个名叫subersion的目录到你的本机上。
请参阅 http://svn.collab.net/repos/svn/trunk/README;特别的,在第IV部分,即“快速指南”。
更详细的资料,请阅读Subversion手册第五章。
试一下cvs2svn转换工具,从http://cvs2svn.tigris.org/ (或者从这里 特性列表和相关文档)。cvs2svn是大多数人的选择,但是如果有别的原因使他不能满足你的要求,至少还有其他两种工具供你选择:
也可以参阅Subversion链接页面。
Subversion支持通过代理访问网络。首先,修改配置文件中的"servers"部分,并指定你的代理服务器。配置文件所在目录在不同的操作系统上可能不同,在Linux或 Unix系统中,通常是~/.subversion;在Windows系统中,通常是"%APPDATA%\Subversion"。(执行"echo %APPDATA%"显示目录的具体路径,注意这是一个隐藏目录。)
配置文件中的注释描述了配置文件书写的格式。如果配置文件还不存在,可以取得最新版的svn,并执行任何svn命令,这将创建配置文件模板及其相应的目录。
其次,请确认你的代理服务器支持所有Subversion必须的HTTP方法。有些代理服务器默认不支持以下命令:PROPFIND, REPORT, MERGE, MKACTIVITY, CHECKOUT。解决办法依赖于你使用的代理服务器软件,对于Squid,配置选项如下:
# TAG: extension_methods # Squid only knows about standardized HTTP request methods. # You can add up to 20 additional "extension" methods here. # #Default: # none extension_methods REPORT MERGE MKACTIVITY CHECKOUT
(Squid 2.4及其之后的版本支持PROPFIND.)
参见"Subversion用到的HTTP方法是有哪些?"来配置代理服务器来支持HTTP方法。
如果让代理服务器支持Subversion访问网络很困难甚至是不可能的话,而你只想checkout Subversion的源代码,你可以绕过代理服务器。一些代理服务器封锁了80端口,如果是这样的话,可以通过81端口访问svn.collab.net代码库服务器。执行:
svn checkout http://svn.collab.net:81/repos/svn/trunk subversion
可能代理服务器会放行。另一个办法是通过SSL来checkout,SSL被大部分代理服务器所支持:
svn checkout https://svn.collab.net/repos/svn/trunk subversion
当然,你的svn客户端应该支持ssl。在编译源代码的时候,在./configure时添加--with-ssl选项。执行svn --version可以知道你的svn是否支持'https'。
一个简单的选择是使用svnserve服务器来代替。请参阅SVN手册第6章中的详细内容。
然而,如果你的管理员不希望你运行Apache,这很可能是他们不想让你在3690端口运行一个客户服务器,则备选答案是假定你的管理员同意你使用现有的SSH设施。
如果你使用CVS,你可能已经使用SSH来登录CVS服务器,ra_svn Subversion的访问方法是和使用Subversion是相同的。仅仅是在你的版本库的URL上加上一个"svn+ssh"的前缀。
$ svn checkout svn+ssh://your.domain.com/full/path/to/repository
这将使你的SSH的程序在远程运行一个私有的'svnserve'进程,用你的用户ID访问版本库,并通过加密管道将数据传递回来。
然而,另外的一种可以用来替代的解决方案是将SSH端口转向连接到通过ra_dav保护的服务器上。你可以通过SSH连接到一个防火墙后的一个机器,这个机器可以访问Subversion服务器。注意,这个SSH服务器不需要与Subversion安装在同一个机器上,可以是,但不是必须是。
然后你可以创建一个本地端口来连接在你家里的Subversion版本库的HTTP服务器。你可以通过本地端口’连接‘Subversion版本库。然后,请求将被通过SSH服务器’通道‘发送到你的Subversion服务器。
一个示例:在你们公司位于10.1.1.50(叫它svn-server.example.com)的防火墙后面设置了一个Subversion ra_dav,你的公司允许SSH通过公共方式访问ssh-server.example.com,而在内部可以通过http://svn-server.example.com/repos/ours访问Subversion版本库。
实例:那么客户端通过端口转向连接ssh服务器,并通过端口转向检出
% ssh -L 8888:svn-server.example.com:80 me@ssh-server.example.com % svn checkout http://localhost:8888/repos/ours
请注意svn-server.example.com也可以让httpd实例被非信任用户运行在无特权的端口上,这允许Subversion不需要root访问权限。
Joe Orton注明
服务器对正在使用的MOVE和COPY请求头的主机名字是很敏感的,所以这个地方你必须要小心—工作正常可能需要配置"ServerAlias localhost"。
一些SSH端口转向的链接
这决定与你的项目的复杂度,如果你的项目是相关的,并且有可能要共享数据,那么最好的方式是通过子目录创建一个版本库。像下面这样子:
$ svnadmin create /repo/svn $ svn mkdir file:///repo/svn/projA $ svn mkdir file:///repo/svn/projB $ svn mkdir file:///repo/svn/projC
如果你的工程是完全不相关的,并且他们之间不可能共享数据,这样最好创建几个独立的完全不相关的版本库。
$ mkdir /repo/svn $ svnadmin create /repo/svn/projA $ svnadmin create /repo/svn/projB $ svnadmin create /repo/svn/projC
这两种方式之间不同之处在于: (由 Ben Collins-Sussman解释 <sussman@collab.net>):
如果你不在意某个版本库的完全历史,你可以只是在某个版本库为另一个项目创建一个新的目录,然后导入另一个项目。
如果你在意两边的历史,那么你可以使用'svnadmin dump'将一个版本库的内容转储出来,然后使用'svnadmin load'加载到另一个版本库。原来的修订版本号码会取消,但你还有历史。
Peter Davis <peter@pdavis.cx>也解释了一种方法,可以使用像CVS模块那样使用svn:
只要合并发生在不同的目录树,你可以使用svn版的CVS模块。
将svn:externals属性设置到从其他版本库检出的目录上,无论原来的目录是何时检出的。版本库还是分离的,但是从工作拷贝的角度看,他们好像合并了。如果你在导入的目录中提交,也将会影响外部版本库。
合并并不意味着完全的干净:导入只是影响了工作拷贝,所以你应该不能使用第一个版本库的URL访问从第二个导入的模块,他们都还是有各自的URL。
也可以看miscellaneous utilities,里面有一些在合并不同版本库时帮助选择和重排序修订版本的工具,特别如基本操作的perl脚本svn-merge-repos.pl和高级组织的python类SvnDumpTool。
如果你使用Berkeley DB作为版本库的后端(在Subversion 1.0和1.1这是默认值,此后不是默认值),我们建议你不要将版本库存放在远程文件系统(例如,NFS)。虽然Berkeley DB数据库和日志文件可以存放到远程文件系统,但是Berkeley DB的共享区域文件不可以放到远程文件系统上,所以版本库只有被一个文件系统客户访问时才能保证安全,而且那种情况下并不是所有的Subversion功能都在一个客户下工作正常。
如果你使用FSFS作为后端,那么将版本库存放到NFS服务器(应当是支持锁定的服务器)也应当没问题。
工作拷贝可以存放到NFS(一个常见的场景是你的主目录在NFS服务器上)上,在Linux NFS服务器上,因为在检出时Subversion内部使用的重命名的量比较大,一些用户报告应当关闭‘子目录检查(subtree checking)’应当关闭,如果关闭子目录检查的详细信息可以看NFS Howto Server Guide和exports(5)。
我们至少接到一份对于通过SMB访问造成工作拷贝楔住的报告,造成错误的服务器是版本非常老的Samba(2.2.7a),在新版本(3.0.6)Samba中并没有再出现这个问题。
在Berkeley DB环境中,版本库会在repos/db/子目录存放所有的数据,这个环境会包含一组表和一组日志文件(log.*)。Berkeley DB会记录对表所作的变更,这样在出现中断后能够恢复到一致的状态(more info)。
如果你放任不管(作为版本库管理员),日志文件会不断的生长,吞噬磁盘空间。在任何时刻,Berkeley DB只使用很小一部分的日志文件(可以看这个帖子以及相关的线索);其余的可以安全的删除。如果你永久保存所有的日志文件,理论上Berkeley DB能够根据这个文件回到出生的时刻。但是在实践中,如果你进行了备份,就没有必要在磁盘中浪费空间了。
可以使用svnadmin
查看哪些日志文件可以删除,你可以通过crob程序完成这个任务。
$ svnadmin list-unused-dblogs /repos /repos/db/log.000003 /repos/db/log.000004 [...] $ svnadmin list-unused-dblogs /repos | xargs rm # disk space reclaimed!
你也可以使用Berkeley DB的db_archive
命令:
$ db_archive -a -h /repos/db | xargs rm # disk space reclaimed!
还可以看svnadmin hotcopy
或hotbackup.py
。
注意:如果你正在使用Berkeley DB 4.2,Subversion创建的版本库会自动删除日志文件,如果你想关闭这个功能,可以在svnadmin create
命令中使用--bdb-log-keep
选项。可以参考Berkeley DB手册中的
DB_LOG_AUTOREMOVE
参数。
试图保证尽可能少的用户能够访问版本库,例如以某个用户运行apache或'svnserve -d',那么版本库由这个用户完全拥有。不允许任何其他的用户通过file:///的URL访问版本库,必须只让拥有版本库的用户运行'svnlook'和'svnadmin'。
如果你的客户端通过file:///或svn+ssh://访问,则无法避免多个用户的访问。在那种情况下,可以阅读第6章最后一小节,看一下最下面工具栏的“检查列表”,它总结了让这个场景更安全的步骤。
SELinux / Fedora Core 3+ / Red Hat Enterprise的用户注意:作为标准Unix许可的补充,在SELinux下,每个文件、目录和进程都有一个‘安全上下文’。当进程试图访问一个文件时,除了检查Unix访问许可,系统还会检查进程的安全上下文与文件的安全上下文是否兼容。
而Fedora Core 3,随SELinux一起安装,在默认情况下Apache会在一个限制的安全上下文中运行。为了在Apache下运行Subversion,你必须设置版本库的安全上下文使之支持Apache的访问(或者关闭对Apache的限制,如果你认为这是过分小心)。chcon命令可以用来设置文件的安全上下文(与chmod设置Unix访问许可类似)。例如,一个用户可以这样运行命令
$ chcon -R -h -t httpd_sys_content_t PATH_TO_REPOSITORY
设置安全上下文可以成功的访问版本库。
某些客户端操作是“只读的”,例如检出和更新。从访问控制的角度,Apache会这样处理。但是libsvn_fs(版本库文件系统API)在生成增量树的时候还是需要写临时数据,所以访问版本库的进程应该能够读写访问Berkeley DB的文件,才能完成功能。
尤其是版本库需要应答许多比较两个目录树的“只读”操作时,一个树可能是HEAD修订版本,而另一个则是临时的事务-目录树 -- 因此许多写权限。
这种限制只存在于Berkeley DB后端;FSFS后端并没有显示这种特性。
有某些特殊情况下,你可能希望完全的删除你曾经提交文件的所有信息(或许有人意外的提交了一份保密的文档)。但是这个操作不是很容易完成,因为Subversion有意被设计成决不丢失信息,修订版本是基于上一个版本所构建的不可改变(immutable)的文件树。从版本的历史记录里删除一个修订版本将会造成多米诺骨牌效应,造成后继版本的混乱,还有可能使得所有的工作拷贝无效。
项目有计划在未来实现一个svnadmin obliterate命令,能够完成永久删除信息的任务(可以看issue 516。)
现在,你只有求助于你版本库的svnadmin dump命令,将转储文件经过svndumpfilter过滤(排除错误的路径)传递给svnadmin load命令,请参阅Subversion手册第五章的相关详细信息。
日志信息作为每个修订版本的附加属性保存在版本库,默认情况下,日志信息属性(svn:log)不能在提交后修改。这是因为对于修订版本属性(svn:log是其中一个)的修改会导致以前的属性永久的消失,Subversion会防止这种意外情况发生。但是,还是有一些方法可以修改修订版本属性。
第一种方法是让版本库管理员允许修订版本属性修改,这可以通过创建"pre-revprop-change"(更多相关细节可以看Subversion手册的这个小节)。钩子"pre-revprop-change"可以在修改之前访问老的日志信息(例如,通过发送一个邮件),所以可以以某种方式保存它(例如,通过发送邮件)。一旦开启了修订版本属性修改,你可以通过svn propedit或svn propset的--revprop选项修改修订版本属性,就像下面这个:
$ svn propedit -r N --revprop svn:log URL $ svn propset -r N --revprop svn:log "new log message" URL
这里N是希望修改的日志信息的修订版本号码,而URL是版本库的位置。如果你从工作拷贝运行这个命令,你可以省略URL。
第二种方法是通过svnadmin setlog修改日志信息。这必须通过引用版本库的文件系统位置来执行,你不能使用这个命令远程修改版本库。
$ svnadmin setlog REPOS_PATH -r N FILE
这里REPOS_PATH是版本库的位置,而N是你希望修改日志信息的修订版本,而FILE是包含新日志信息的文件。如果"pre-revprop-change"钩子不存在(或者因为钩子脚本的原因你希望绕过),你还是可以通过--bypass-hooks选项实现。然而,如果你要使用这个选项,要非常小心。你会绕过诸如变更的邮件提醒,或者保存修订版本属性的备份系统。
首先,看一下Hacker's Guide to Subversion。
一旦你消化了这些内容,可以在dev邮件列表发送一个题目有[PATCH]的邮件,并在邮件中包含你的补丁(除非你的邮件客户端会完全处理它),不久之后就会有提交者捡起来,应用它(作出合适的格式化或内容变更),并将其检入。
基本的过程看起来如下:
$ svn co http://svn.collab.net/repos/svn/trunk subversion $ cd subversion/www [ make changes to faq.html ] $ svn diff faq.html > /tmp/foo $ Mail -s "[PATCH] FAQ updates" < /tmp/foo
当然,你的邮件应当包含补丁完成什么功能的解释,按照Hacker's Guide to Subversion所说的,但是你已经知道了,因为在你Hack之前已经完全阅读和理解,不是吗?:)
例如,假设你希望把/etc下的一些文件纳入版本控制:
# svn mkdir file:///root/svn-repository/etc \ -m "Make a directory in the repository to correspond to /etc" # cd /etc # svn checkout file:///root/svn-repository/etc . # svn add apache samba alsa X11 # svn commit -m "Initial version of my config files"
这里利用了svn checkout的一个非立即但明显的优势:你可以从版本库直接检出一个目录到已存在的目录,这里,我们首先在版本库创建一个目录,然后将其检出到已存在的目录/etc,将/etc目录变成工作拷贝。一旦完成,你可以使用普通的svn add命令选择文件和子目录进入版本库。
这是svn import的一个增强问题,它本应该能够在导入目录树时自动创建工作拷贝;可以看issue 1328。
Subversion的版本库数据库模式在开发中会偶尔改变,使用1.0以前的Subversion开发版本创建的老版本库在升级时需要如下操作。如果在发布X和Y之间的模式发生变更,则版本库管理员升级到Y的时候必须如下操作:
关于转储和加载的更多信息可以看Subversion手册的这个小节。
注意:大多数Subversion的升级不会需要转储和加载,如果某个版本需要,新版本的发布声明和CHANGES文件会显著说明这一点。如果你没有看到这个说明,那么应该是没有模式变更,也就没有转储/加载的必要。
TortoiseSVN有个在Windows下设置Subversion服务器的完美文档,可以看http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-serversetup.html#tsvn-serversetup-apache-5中的SSPI authentication小节。
配置中比较重要的一部分是这几行:
SSPIOfferBasic On
如果没有这行,支持SSPI的浏览器会提示用户输入凭证,但是不支持SSPI的客户端例如Subversion就不会提示。(当前版本的Neon - Subversion的HTTP库 - 只能处理基本的认证。)因为客户端永远不会被请求凭证,任何需要认证的操作都会失败。添加这一行告诉mod_auth_sspi对客户端使用基本认证,但使用Windows域控制器认证凭证。
如果可能,我们推荐你使用".svn",然而,如果你在Windows下使用ASP.NET,你可能需要按这里的介绍设置环境变量SVN_ASP_DOT_NET_HACK。
或者你可以使用一个完全自定义的管理区域名称,我们反对这样做,因为你的工作拷贝可能无法与你的Subversion客户端协调工作,然而,如果你喜欢,只需要将subversion/include/svn_wc.h的这一行从
#define SVN_WC_ADM_DIR_NAME ".svn"
修改为(例如)
#define SVN_WC_ADM_DIR_NAME "SVN"
然后重新编译客户端。
这个问题在两种情况下会发生。如果你的操作系统使用的文件系统对大小写不敏感,例如windows系统,当你在添加文件的时候,你可能会不小心的添加一个文件名大小写错误的文件。或者,你可能只是想改变版本库中已有文件的大小写。
如果你是在一个大小写敏感的文件系统上工作,那基本上就不会出现这样的问题。直接将文件改成新的名字。例如,
svn mv file.java File.java
但这样的方式在windows这类大小写不敏感的文件系统上是不能正常运作的。在windows下,你只能临时地将文件拷贝到其他地方,然后从Subversion中删除该文件,然后重新添加名字大小写正确的文件副本。或者更好的办法就是对Subversion的URL执行移动操作。推荐使用操作URL的方法,因为这样可以保持文件的历史日志,而且能够立即生效。
尽管如此,这两种方式都会导致windows下的工作拷贝出现一些问题,因为当试图更新文件名产生冲突的文件的时候,windows还是会不知所措。(你可能会得到一条消息如:svn: Failed to add file 'File.java': object of the same name already exists)。一个解决这个问题的办法就是删除工作拷贝然后重新检出。如果你不想这么做的话,那么你就必须执行上面提到的两个步骤来更新。
对于每个大小写错误的文件,执行下面的命令将会改变大小写:
svn mv svn://svnserver/path/to/file.java svn://svnserver/path/to/File.java
要更新工作拷贝,你先得转到相关的目录,然后执行:
svn update file.java svn update
第一步的更新会从你的工作拷贝中删除file.java,第二步更新会添加File.java,这样就会生成一个正确的工作拷贝。或者如果你有很多这种问题的文件,你可以按下面的方式来更新工作拷贝:
svn update * svn update
就像你看到的,添加一个大小写错误的文件在文件系统大小写不敏感的操作系统上,是需要一些小技巧来修正的。所以最好当你第一次添加文件的时候,一次性就添加对了。为了防止类似的问题再发生,你可以创建一个pre-commit的钩子脚本,在其中调用文件check-case-insensitive.pl。这个文件放在Subversion的源代码包里,在contrib/hook-scripts目录中
就像下面展示的,你可以不需要记住版本号就将一个分支合并到主线上面。反之亦然(在例子中并没有展示)。
下面这个例子假设在/home/repos存在一个版本库,你希望从中创建一个名叫bar的、包含你即将编辑的文件foo的分支。
为了跟踪分支合并的历史,版本库建立了tags/branch_traces/目录用来保存tags。
# setup branch and tags $ svn copy file:///home/repos/trunk \ file:///home/repos/branches/bar_branch \ -m "start of bar branch" $ svn copy file:///home/repos/branches/bar_branch \ file:///home/repos/tags/branch_traces/bar_last_merge \ -m "start" # checkout branch working copy $ svn checkout file:///home/repos/branches/bar_branch wc $ cd wc # edit foo.txt file and commit $ echo "some text" >>foo.txt $ svn commit -m "edited foo" # switch to trunk and merge changes from branch $ svn switch file:///home/repos/trunk $ svn merge file:///home/repos/tags/branch_traces/bar_last_merge \ file:///home/repos/branches/bar_branch # Now check the file content of 'foo.txt', it should contain the changes. # commit the merge $ svn commit -m "Merge change X from bar_branch." # finally, update the trace branch to reflect the new state of things $ svn delete -m "Remove old trace branch in preparation for refresh." \ file:///home/repos/tags/branch_traces/bar_last_merge $ svn copy file:///home/repos/branches/bar_branch \ file:///home/repos/tags/branch_traces/bar_last_merge \ -m "Reflect merge of change X."
Subversion每次都将整个版本库的版本号整个进行自增,所以它不能将关键词替换成你想要的数字 - 它可能不得不在每次更新或者提交时对工作拷贝中的每个文件进行搜索或者修改。
你想要的信息(你的工作拷贝的版本号)可以通过svnversion命令来获取;你可以根据指定的工作拷贝的路径提供其版本相关的信息(更多细节参考svnversion --help)。
你可以将其组合到你的构建或发布过程中,以获取需要存放到源代码的信息。例如,在一个基于GNU make的构建环境中,在你的Makefile中添加如下的信息:
## ## To use this, in yourfile.c do something like this: ## printf("this program was compiled from SVN revision %s\n",SVN_REV); ## SVNDEF := -D'SVN_REV="$(shell svnversion -n .)"' CFLAGS := $(SVNDEF) ... continue with your other flags ...
(注意这段代码在非GNU版本的make中无效,如果你的构建过程需要可移植,请不要使用它。)
或者使用如这些方法:
## ## on every build, record the working copy revision string ## svn_version.c: FORCE echo -n 'const char* svn_version(void) { const char* SVN_Version = "' \ > svn_version.c svnversion -n . >> svn_version.c echo '"; return SVN_Version; }' >> svn_version.c ## ## Then any executable that links in svn_version.o will be able ## to call the function svn_version() to get a string that ## describes exactly what revision was built. ##
Windows用户会希望使用SubWCRev.exe,TortoiseSVN的下载页面就有;可以用当前工作拷贝的修订版本替换给定文件中的所有$WCREV$标签。
没有。在CVS中没有对应的$Log$关键字,如果你希望将日志信息输入某个特定文件,你可以使用'svn log your-file-name'或'svn log url-to-your-file'。邮件列表中有几个解释$Log$缺点的例子:
“当你开始合并分支之间的变更时,$Log$会是一个完全的梦魇。你一定会在这里得到冲突 -- 因为关键字的本性 -- 很难简单的自动解决冲突。”
而且:
Subversion的日志信息是可以改变的,可以通过设置svn:log修订版本属性修改。所以在任意文件扩展$Log:$会造成数据的过时,更新操作可能需要访问所有出现$Log:$关键字的文件,即使这些文件没有其他的变更。
我对此并不在意,我还是期望使用它,你能实现吗?
不能,我们自己没有计划实现它,也不会接受实现这个特性的补丁。如果你希望发布包含某种变更日志的文件,你可以在你的构建系统中跳出这个限制。
答案是:不要将文件纳入版本控制,而是将文件的模板纳入版本控制,例如“file.tmpl”。
然后,在初始化的‘svn checkout’之后,让你的用户(或你的构建系统)执行通过普通的操作系统复制将文件修改为正确的文件名,文件未版本化,所以绝不会提交。并且如果你希望,你可以将文件加入到父目录的svn:ignore属性中,这样它就不会在‘svn status’命令中显示‘?’。
ssh拥有自己的密码短语和认证缓存模式,它的认证缓存是Subversion之外的,需要独立于Subversion设置。
OpenSSH使用ssh-keygen创建密钥、通过ssh-agent缓存密码短语、使用ssh-add将密码短语添加到代理缓存中。一个简化ssh-agent使用的脚本是keychain。在Windows下,PuTTY是一个流行的ssh客户端选择;将OpenSSH密钥导入到pageant并缓存密码短语可以看PuTTYgen。
设置ssh-agent已经超出了本文的范围,但是在Google中搜索“ssh-agent”可以快速得到答案,或者如果你没有耐心,可以是看这些:
http://mah.everybody.org/docs/ssh http://kimmo.suominen.com/docs/ssh/
注意:这里都假设你使用了OpenSSH。也有一些其他的ssh实现,大概它们也实现了这些功能,但是我们不知道具体的方法。
你已经对修改过各种诸如.bash_profile的登录文件,但是没有效果!那是因为当Subversion客户端调用ssh时会忽略那些文件。但是没有必要修改PATH;相反,你可以直接在svnserve命令中使用ssh的完全名,下面是一个例子:
对于每个需要通过svn+ssh访问的用户,生成一个新的Subversion使用的ssh公钥对—不是用来登陆的。让他们给密钥对不同的名称,例如~/.ssh/id_dsa.subversion。将密钥的公共部分添加到服务器主机的~/.ssh/authorized_keys中,在开始部分包含ssh-rsa或ssh-dss以及一些幻数的行中,如下:
之前 |
---|
ssh-dss AAAAB3Nblahblahblahblah |
之后 |
command="/opt/subversion/bin/svnserve -t" ssh-dss AAAAB3Nblahblahblahblah |
很明显,需要将/opt/subversion/bin/svnserve替换为你主机上的相应值。你也会希望在命令行指明Subversion版本库的完全路径(通过-r选项),节省用户使用时的输入。
command=魔法可以让远程主机的sshd调用svnserve,即使你的用户尝试运行其他命令,更多细节可以看sshd(8)的man页(AUTHORIZED_KEYS FILE FORMAT部分)。
现在当你的用户运行Subversion客户端时,请确定他们有一个SVN_SSH环境变量是“指向”他们密钥对的私有部分,通过下面的方法(Bourne Again shell):
SVN_SSH="ssh -i $HOME/.ssh/id_dsa.subversion" export SVN_SSH
这个主题的详细讨论可以看这个文件。
可以看对其他问题的回答中关于修改~/.ssh/authorized_keys的部分;先不管将svnserve设置到路径的问题。
Subversion缺省情况下不会修改文件的内容;你可以设置svn:eol-style或svn:keywords属性实现这个功能。这让Subversion比CVS的缺省行为模式更加安全,但是安全也带来了不便。
第一个问题的答案是:设置所有已经进入版本库的文件,将会有点困难。你所要做的是在每个文件(工作拷贝文件)上执行svn propset,然后执行svn commit,通过脚本应该可以帮助你做这件事。
但是对以后的文件呢?很不幸,没有在服务端自动设置提交文件属性的机制。这意味着你需要让你的用户记住在每个svn add的文件上添加属性,幸运的是,有一个客户端的工具可以帮助我们做这件事,可以阅读本书的auto-props特性。你需要保证所有的用户配置了合适的自动化属性。
你可以写一个pre-commit钩子脚本来拒绝忘记在新文件添加属性的提交(例子可以看http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/check-mime-type.pl)。然而这个方法有点过激了,例如某人忘记了设置svn:eol-style,那么在其他系统上用户很快就会注意到,一旦发现,可以很简单的修正:只要设置正确的值,并提交。
注意:许多用户期望得到服务器“自动广播”运行中设置的特性,例如自动化属性设置。对此已经有了一个特性请求(issue 1974),然而这个特性还存在争议,还没有去实现。
Subversion命令行客户端会调用通过环境变量SVN_EDITOR. 定义的编辑器,这个环境变量以及要编辑日志的临时文件会直接传递给操作系统。
因为是要将SVN_EDITOR的字符串传递给系统命令行,所以如果你不加引号,编辑器名称中的空格或路径中的空格会造成错误。
例如,在Windows下,如果你的编辑器在C:\Program Files\Posix Tools\bin\vi
,你需要这样设置环境变量:
set SVN_EDITOR="C:\Program Files\Posix Tools\bin\vi"
请注意没有必要在windows下回避引号,因为他们不是set
命令语法的一部分。
而在UNIX系统中,你需要遵从你shell的特定方法来设定变量,例如在bash shell中,应该这样:
SVN_EDITOR='"/usr/local/more editors/bin/xemacs"' export SVN_EDITOR
如果调用编辑器需要命令行选项,只要在SVN_EDITOR环境变量的编辑器名称后面输入选项,就像你平时调用它时那样。例如,如果你的编辑器需要-nx -r
选项,可以按照下面的方式设置:
在Windows下:
set SVN_EDITOR="C:\Program Files\Posix Tools\bin\vi" -nx -r
在UNIX/bash中:
SVN_EDITOR='"/usr/local/more editors/bin/xemacs" -nx -r' export SVN_EDITOR
请注意SVN_EDITOR是Subversion特定的设置编辑器的环境变量,Subversion也支持使用更普遍的EDITOR环境变量,但是如果你希望一些对于Subversion特殊的行为方式,最好还是使用SVN_EDITOR变量。
如果它是一个活动的版本库,那么对于这个问题最简单的答案是“你安装的Berkeley DB的版本”。但是,如果它是备份的或其它未知源得到的版本库,你对其的Berkeley DB版本一无所知,下面是你确定它的方法:
运行命令查看版本库中的高位计数文件db/log.*中位移12到16(10进制)的两个4字节的整数。这里是GNU od的例子:“od -j12 -N8 -tx4 log.<number>”。而这里是Mac OS X hexdump的例子:“hexdump -s12 -n8 -x log.<number>”。第一个整数一定是幻数0x00040988,用来注明这是Berkeley DB日志文件,第二个数字是日志格式版本 - 可以使用下面的表格和Berkeley DB的版本匹配:
日志格式版本 | Berkeley DB版本 |
---|---|
5 (0x00000005) | 4.0 |
7 (0x00000007) | 4.1 |
8 (0x00000008) | 4.2 |
10 (0x0000000a) | 4.3 |
11 (0x0000000b) | 4.4 |
12 (0x0000000c) | 4.5 |
13 (0x0000000d) | 4.6 |
这个早已经解决,可以通过为版本库添加post-commit钩子脚本简单实现,可以读一下手册第5章关于钩子脚本的内容,基本的思想是让“活动站点”变成一个普通的工作拷贝,让post-commit在工作拷贝中执行‘svn update’。
在实践中,有许多地方需要小心。执行提交的服务器程序(svnserve或apache)也是执行post-commit钩子脚本的程序,这意味着程序必须有正确的更新工作拷贝的访问许可。换句话说,运行svnserve或apache的用户应该拥有工作拷贝 -- 或至少工作拷贝设置了合适的访问许可。
如果服务器需要更新它并不拥有的工作拷贝(例如,用户joe的~/public_html/区域),一个技巧是创建一个+s的二进制程序运行更新,因为Unix不允许为脚本运行+s,编译一个小的C程序:
#include <stddef.h> #include <stdlib.h> #include <unistd.h> int main(void) { execl("/usr/local/bin/svn", "svn", "update", "/home/joe/public_html/", (const char *) NULL); return(EXIT_FAILURE); }
... 然后对二进制程序chmod +s,并确认它确实被用户‘joe’所有,然后在post-commit钩子中,添加一行运行二进程程序。
如果在让钩子运行时发生了问题,可以看“为什么我的钩子脚本都不能正常工作?”。
另外,你可能希望防止apache把工作拷贝中的.svn/目录暴露出去,在你的httpd.conf添加下面的内容:
# Disallow browsing of Subversion working copy administrative dirs. <DirectoryMatch "^/.*/\.svn/"> Order deny,allow Deny from all </DirectoryMatch>
Subversion不支持检出单个文件,它只支持检出目录结构。
然而,你可以使用‘svn export’来导出单个文件,这会获得文件的内容,只是不会创建版本化的工作拷贝。
你不需要检测,你也最好不要尝试去做这样的检测。
工作拷贝的基本设计有两个原则:(1) 你可以任意编辑文件,然后(2)使用Subversion客户端执行任何树目录的改动(增加、删除、移动、拷贝)。如果这两个原则能够很好的遵守,那么客户端就可以顺利的维护工作拷贝。如果重命名或者其他改动是在Subversion之外进行的,那么UI就会被破坏,工作拷贝也可能发生问题。客户端不能猜测究竟发生了什么事。
人们有时候会遇到这样的错误,因为他们想要把版本控制变得透明化。他们诱使用户使用一份工作拷贝,然后在稍候运行一个脚本,这个脚本会猜测用户做了什么样的操作,然后执行相应的客户端命令。不幸的是,这样的方式只能在短期内正常工作。‘svn status’会显示出缺失条目或者未被纳入版本控制的条目,这些条目在脚本运行的时候会自动的被‘svn rm’或者‘svn add’。但如果发生了文件移动或者拷贝,那么你就不那么幸运了。即使脚本有非常可靠的方法来检测这些动作,但‘svn mv’和‘svn cp’不能在这些操作已经完成的情况下运行。
总的来说,工作拷贝必须整个放置在Subversion的版本控制之下,Subversion本来就不是设计成对用户透明的。如果你想要获得这种透明性,那么你应该建立一个apache服务器,然后本书附录C使用的“SVNAutoversioning”特性。这将允许用户将版本库挂载成一个网络磁盘的形式,任何对这个磁盘的修改,都会自动提交到远程服务器上。
对于1.4.0及以后的版本,你可以查看这里。
对于1.4.0以前的版本,svnserve二进制本身并不可以被安装成一个windows服务,但有很多“service wrappers”可以完成一样的工作。例如:
在 TortoiseSVN手册还有一点内容关于怎么将svnserve安装成服务的形式。
有三步:
假设你在/svn/myrepos目录中放置了一个版本库,使用的是BDB,你想要将其转换成FSFS:
一旦一切工作正常后,删除旧的版本库即可。
如果要反过来操作,即从FSFS移植到BDB,只需要改变svnadmin create命令,将其指定为BDB即可。
当你第一次添加或者导入文件到Subversion中时,Subversion会检测该文件是否是二进制文件。目前,Subversion的策略是只检测文件的前1024个字节;如果每个字节都是0,或者超过15%都是非ASCII码可打印字符的话,那么Subversion就认定该文件是二进制文件。这种启发式方法将来可能会改进。
如果Subversion认定文件是二进制文件,那么这个文件就会自动添加svn:mime-type属性,并设置为“application/octet-stream”。(你随时可以使用auto-props特性来重写这样的行为,或者使用svn propset手动设置属性。)
Subversion对以下的文件做普通文本处理:
所有其他文件都将被视为二进制文件处理,这意味着Subversion:
在其他方面,Subversion将二进制文件和其他文本文件一样对待,例如,如果你设置了svn:keywords或者svn:eol-style属性,Subversion会在二进制文件中执行关键词替换或者行转换。
需要注意,不管是不是二进制文件,都不会影响版本库中用来存储文件变更的空间大小,也不会影响客户端和服务端之间的通讯量。出于存储和传输考虑,Subversion使用的是对二进制文件和普通文本文件一致处理的diffing方法;这和‘svn diff’使用的diffing方法完全不相关。
svn diff没有一个选项可以做到这一点,但是
svn log -vq -r10完全可以实现你的目的;
svn log -vq -r123:456 | egrep '^ {3}[ADMR] ' | cut -c6- | sort | uniq
你或许是想做这样一些事情
svn mv svn://server/trunk/stuff/* svn://server/trunk/some-other-dir
但是却失败了
svn: Path 'svn://server/trunk/stuff/*' does not exist in revision 123
... 或者还有其他一些看起来不可理解的错误信息。
简而言之,很不幸:Subversion没有内置这样的方法来进行这样一种操作;许多其他的命令,例如mv,不会接受任意数目的参数...在任何情况下,Subversion都不会扩展如shell所支持的“*”号通配符。
如果你刚好有一个工作拷贝包含了和目标目录一摸一样的源文件,那么你可以使用你的shell中的通配符特性来完成移动文件的目的,例如(在bash下):
for i in stuff/*; do svn mv $i some-other-dir; done svn ci -m "moved all the stuff into some other dir"
在任何情况下,你都可以将源文件的名字拼成一个列表,然后使用“svn mv”命令对列表中的每一项都执行同样的指令,就像下面所做的一样:
s=svn://server/trunk/stuff/ svn ls "$s" | \ while read f do svn mv "$s/$f" svn://server/trunk/some-other-dir -m "Moved just one file" done
但是需要注意的是,这样会造成每个文件都执行一次提交动作,这和上面的方法(使用工作拷贝)不同,上面的方法总共就会执行一次提交动作。
有一个程序叫“svnmucc”或者“mucc”可以解决你的这个问题,不过它依赖于你的Subversion版本号,它的源代码已经发布在Subversion中了(对于Subversion 1.4以及更早的版本,这个工具的源文件放置在/contrib/client-side/mucc/mucc.c,对于1.5及之后的版本,则在/contrib/client-side/svnmucc/svnmucc.c可以找到)。
注意:随着1.5版本的正式发布,Subversion事实上允许你一次性同时“cp”和“mv”多个文件。
人们通常希望使用Subversion去跟踪他们的变化的第三方代码,甚至从第三的代码进行升级,换句话说,他们想维护他们自己的一个分支,与此同时仍然希望从上游的代码并入新的发布。这通常被叫做供方分支(这个术语早于Subversion),Subversion维护这种分支的技术可以看这个描述。
如果供方代码是远程的主机上的Subversion版本库,你可以使用Piston来管理你的供方代码拷贝。
最后,如果使用svn_load_dirs.pl花太多时间的话,或者你正在寻找一个偷懒的方案,则可以参考Jon Stevens的渐进介绍如何使用Subversion供方分支。这种解决方案在你拷贝新的代码去覆盖旧代码的时候并不能利用Subversion后端的节省空间特性换;在这种解决方案中,每一份导入的供方代码都会是一个新的拷贝,相同的文件不会节省存储空间。
版本库使用的Berkeley DB数据库很容易受到破坏。如果进程退出时没有正常关闭数据库,那么数据库就处在非正常状态中。常见的原因可能是:
多数情况下,你可以执行"svnadmin recover",这个命令可以将版本库回退到上一个正常状态,详细解释请见bdb-recovery。注意,当频繁的checkout和update且磁盘空间用尽的时候,可能导致版本库处于无法恢复的状态(所以请多做备份)。
进程崩溃,被杀死以及磁盘空间用尽都是极端情况。访问权限问题可能是更常见的原因:当一个进程访问版本库且改变了版本库的所有者或访问权限,那么另一个访问这个版本库的进程可能被阻塞在访问权限上。
避免这种情况出现的最好办法是正确设置你的版本库的所有者和权限。查看更多信息。
你的版本库没有坏掉,数据也没有丢失。当你的进程直接访问版本库(mod_dav_svn、svnlook、svnadmin,或通过‘file://’)的时候,进程将直接通过Berkeley DB来访问版本库。Berkekey DB包含日志系统,也就是说所有的操作在执行前都被记录在日志中。当你的进程崩溃(Control-C,或段错误),遗留下文件锁,它记录了所有未完成操作的信息。此时所有试图访问数据库的进程将因为要访问文件锁而被挂起。要想解除文件锁,你可以调用Berkeley DB来完成所有未完成的操作或者回退到前一个正常状态。
警告:当一个进程正在访问版本库,而你又试图恢复版本库的时候,版本库可能会严重损坏。
在你恢复数据库之前,请确保没有其他进程在访问版本库(关闭Apache, 去掉'svn'的执行权限) 。确认你是以数据库所有者及管理员的用户进行操作的,而不是root用户。否则数据库将只能被root用户访问,而其他用户(你自己或Apache)将无法对数据库进行操作。同时,还要确保umask被正确设置,否则其他隶属与可以访问该数据库的组的用户可能无法对数据库进行操作。
执行:
svnadmin recover /path/to/repos
当命令执行完毕, 检查版本库db
目录的访问权限
有时"svnadmin recover"会失败,错误信息如下:
Repository lock acquired. Please wait; recovering the repository may take some time... svnadmin: DB_RUNRECOVERY: Fatal error, run database recovery svnadmin: bdb: Recovery function for LSN 175 7066018 failed on backward pass svnadmin: bdb: PANIC: No such file or directory svnadmin: bdb: PANIC: fatal region error detected; run recovery
或者:
Repository lock acquired. Please wait; recovering the repository may take some time... svn: DB_RUNRECOVERY: Fatal error, run database recovery svn: bdb: DB_ENV->log_flush: LSN of 115/802071 past current end-of-log of 115/731460 svn: bdb: Database environment corrupt; the wrong log files may have been removed or incompatible database files imported from another environment [...] svn: bdb: changes: unable to flush page: 0 svn: bdb: txn_checkpoint: failed to flush the buffer cache Invalid argument svn: bdb: PANIC: Invalid argument svn: bdb: PANIC: fatal region error detected; run recovery svn: bdb: PANIC: fatal region error detected; run recovery [...]
这种情况下可以试试Berkeley DB的工具db_recover (参见db_recover文档).这个命令通常在Berkeley DB安装目录的子目录"bin/"中, 例如你从源代码安装的Berkeley DB,命令可能是 /usr/local/BerkeleyDB.4.2/bin/db_recover;或在某些系统中预安装的Berkeley DB,可能是 /usr/bin/db_recover.如果你的系统中安装了多个版本的 Berkeley DB,请确保使用匹配版本库的Berkeley DB版本
执行db_recover,附带参数"-c" ("灾难恢复").你还可以附带 "-v"来看到执行的详细信息, "-h"指定被恢复的数据库。例如:
db_recover -c -v -h /path/to/repos/db
用版本库的拥有者的帐号执行这个命令,同时,请绝对确保没有其他进程在 访问版本库。(例如,关闭svnserve或)。
如果你通过http://来访问版本库,"Cannot allocate memory"应该出现在httpd服务器的日志中,像下面这样:
[Wed Apr 07 04:26:10 2004] [error] [client 212.151.130.227] (20014) Error string not specified yet: Berkeley DB error while opening 'strings' table for filesystem /usr/local/svn/repositories/svn/db: Cannot allocate memory [Wed Apr 07 04:26:10 2004] [error] [client 212.151.130.227] Could not fetch resource information. [500, #0] [Wed Apr 07 04:26:10 2004] [error] [client 212.151.130.227] Could not open the requested SVN filesystem [500, #160029] [Wed Apr 07 04:26:10 2004] [error] [client 212.151.130.227] (17) File exists: Could not open the requested SVN filesystem [500, #160029]
这通常表示Berkeley DB版本库用光了所有数据库锁(FSFS版本库不会出现这种情况)。通常的运行过程中不应该出现这种情况,如果确实出现了,解决办法是用本文中描述的方法来恢复数据库。如果这种情况时常发生,你很可能应该提高db/DB_CONFIG文件中锁设置(set_lk_max_locks、set_lk_max_lockers和set_lk_max_objects)的默认值。改变已存在的版本库的DB_CONFIG配置后,记得恢复数据库。
你的工作拷贝没有坏掉,数据也没有丢失。Subversion的工作拷贝是一个日志系统,所有的操作在执行之前都被记录在日志中。如果svc客户端程序被强制中止(段错误或杀进程,不包括Control-C),会有多个文件锁遗留下来,记录了未完成的操作。('svn status'命令会在被锁定的目录后显示'L'。)其他试图访问工作拷贝的进程看到这些锁后会返回操作失败。想解锁你的工作拷贝,需要让svn客户端完成未完的操作。执行:
svn cleanup working-copy
三种情况下会出现这个问题
提交失败导致了你的工作拷贝被破环。
在你提交时,新版本被添加到服务器上,然后你的客户端执行提交后的管理任务(包括刷新本地拷贝)。在第二个操作之前,你的提交处在一个“过期”的状态之中。导致这种情况发生的原因有,(极少数情况下)数据库一端出现了问题,(大多数情况下)网络在错误的时间中断了。
这时,很可能你的提交已经成功了。你可以执行"svn log -rHEAD"来查看提交是否真的成功了。如果成功了,执行“svn revert”,回退本地修改,然后执行“svn update”来取回你已经提交的修改。(注意,“svn update”能更新你的本地拷贝,而revert不能。)
版本混杂。
提交的时候,在客户端的工作拷贝中,只有此次提交涉及的节点会被更新,而不是所有节点。这意味着,你最后一次提交决定你的工作拷贝中了文件和目录的版本号,他们可能不是同一版本。对于像修改目录属性等特定操作,如果版本库中有更新的版本,你的提交会被拒绝,以防止数据丢失。详细请见版本混杂的局限在使用Subversion做版本控制中。
执行'svn update'可以解决这个问题。
你的版本可能确实是过时的 — 即在你提交的时候,有人基于你的上一次改动又作了更新。执行'svn update'来解决这个问题。
如果要往补丁里加入新文件,你可能得使用svn add命令,这样svn diff命令才会将新文件包含在补丁里面。如果你的补丁已经提交到代码库里头了,然后你运行svn update,这个时候你可能会收到错误提示:“svn:Failed to add file ‘my.new.file’。object of the same name already exists”(不能增加“my.new.file”,同名文件已经存在)。
之所以会产生这个错误是因为你的工作拷贝中已经有这个文件了。解决这个问题的步骤如下:
你可能需要把版本库中的新文件和你的本地的文件做一下比较。
Subversion使用了插件系统来访问版本库。现在支持三个插件:ra_local支持访问本地版本库,ra_dav支持通过WebDAV访问版本库,ra_svn支持通过svnserver服务器访问远程或本地的版本库。当你执行Subversion的操作时,会根据URL scheme来决定加载哪一个插件。以`file://'开头的URL会加载ra_local,`http://'开头的URL会加载ra_dav。
你看到的错误是说无法找到正确的插件。发生这个问题,通常是因为你将Subversion的库文件编译成动态库后,却没有执行'make install'。或者虽然你执行了'make install',但是操作系统还是找不到Subversion的动态库所在的路径。在Linux系统中,你可以把相应的路径加入到/etc/ld.so.conf中,然后运行ldconfig。如果你不想这么作,又或者你没有root权限,你可以在LD_LIBRARY_PATH中指定相应的路径。
参考这个FAQ条目。
可能你系统里的/usr/local/bin/apr-config和/usr/local/bin/apu-config太旧了。删了它们,更新apr/和apr-util/,使其和你编译的版本保持一致,再重新编译。
可能你需要最新版本的platform SDK,VC++ 6.0带的SDK太旧了。
像这样:
svn import file:///d:/some/path/to/repos/on/d/drive
更多细节请见Subversion书中的版本库URL。
VS.Net有一个ASP.Net子系统,它使用WebDAV来通过IIS来远程发布。这个子系统剔除了所有以"."开头的目录。在你试图远程发布你的工作目录的时候,".svn"将导致问题。错误信息类似"unable to read project information"。
要想绕过这个问题,设置环境变量SVN_ASP_DOT_NET_HACK为任意值 — 这将告诉Windows clients在你的工作拷贝中使用"_svn"作为目录名。参见Subversion 1.3发布说明的相关章节来取得更多信息,参见这个问题来用其他方法配置管理目录名。
例如,有用户反映,通过本地协议访问的,导入功能正常工作:
$ mkdir test $ touch test/testfile $ svn import test file:///var/svn/test -m "Initial import" Adding test/testfile Transmitting file data . Committed revision 1.但如果从远程访问的话就出错了:
$ svn import http://svn.sabi.net/test testfile -m "import" nicholas's password: xxxxxxx svn_error: #21110 : <Activity not found> The specified activity does not exist.
我们曾经遇到过这样的问题,当httpd进程对REPOS/dav/目录没有可写权限的时候会发生。你需要检查一下相关的权限问题,确保Apache能够往dav/目录中写入东西。(当然还有db/目录)。
你需要安装Windows XP的SP1补丁。你可以在下面这里找到所有相关的信息:
简而言之,这是为你好。
Subversion对保护你的数据非常重视,不只是你已经版本化的数据。你对已经版本控制的文件所做的修改,或者你即将添加到版本库中的文件,都必须小心对待。
使用svn revert命令需要非常明确地指定目标—即使目标是‘.’—就是其中一个方面。这个要求(同样对于--recursive (-R)标记,如果你真的需要递归执行某个操作,你也要这样做)是为了让你清楚的知道你要做的事情,因为一旦你执行了撤销工作拷贝修改的命令,所有本地的修改都会永远消失。
你的apr-util链接的是DB-3,而svn链接的是DB-4。不幸的是,DB符号并不是不相同的。当mod_dav_svn被加载入Apace的进程空间的时候,他会使用apr-util的DB-3库来解析符号。
解决办法就是确保apr-util是在DB-4下编译的。你可以在编译的时候指定特定的开关为apr-util的配置或者apache的配置:“--with-dbm=db4 --with-berkeley-db=/the/db/prefix”。
这确实不是Subversion的问题,但经常会影响Subversion用户。
随Red Hat 9还有Fedora发行的Berkeley DB库依赖于对NPTL的内核支持(本地Posix线程库)。
Red Hat提供的内核对这方面有内置的支持,但如果你是自己编译的内核,那么你可能不会有NPTL的支持。如果是这个原因的话,那么你可能会看到如下的错误:
svn: Berkeley DB error svn: Berkeley DB error while creating environment for filesystem tester/db: Function not implemented
这个问题可以按照下面的方法解决:
LD_ASSUME_KERNEL
环境变量是否已经设置为了2.2.5
,如果是的话,在重新启动Subversion(Apache)之前删除此环境变量。(通常只有当你在Red Hat 9上面运行 Wine或者Winex才需要设置这个环境变量)要使用NPTL版本的Berkeley DB,你还需要使用支持NPTL的glibc库,很可能是i686版本。参考 http://svn.haxx.se/users/archive-2004-03/0488.shtml。
如果你在Apache上开启了匿名用户的写权限的时候,Apache服务器就不会向svn客户端询问用户名,而是直接不经过验证就允许执行写操作。因为Subversion不知道谁做了这些操作,日志中这些操作信息就变成下面这样了:
$ svn log ------------------------------------------------------------------------ rev 24: (no author) | 2003-07-29 19:28:35 +0200 (Tue, 29 Jul 2003)
关于如何配置Apache的访问权限,参考Subversion书籍("Networking a Repository")。
这可能是由于一些监听文件系统变化的windows服务(如杀毒软件,索引服务,COM+事件通知服务)。这不属于Subversion的Bug,所以我们很难修正这个问题。关于这种情况的调查说明可以在这里找到。在7598版本中,对这个问题做了改进,对于大多数人来说,应该会降低这种情况出现的几率。如果你使用的是较早的版本,请更新到最新的发行版。
这通常是由于系统中某些资源不足的缘故造成的。你很可能需要配置一下系统,使其能够从源那里获取足够的资源,如硬盘还有网络中断。查阅你的系统说明手册,找到random(4)还有rndcontrol(8)这几节,看如何修改。
这意味着你的httpd.conf配置有问题,通常情况下,当你设置的Subversion虚拟目录同时存在两种寻址方式的时候会出现这样的错误。
例如,当你将版本库放到/www/foo目录下,但是你又同时设置了你的版本库的根目录为/www,那么你就麻烦了。当有人请求一个/www/foo/bar文件的时候,apache根本不会知道,对方真正想要寻找的文件,是在根目录里下的/foo/bar,还是通过调用mod_dav_svn模块从/www/foo版本库中去把/bar文件给取回来,通常Apache的处理行为是采取前者的方式,因此就会出现“永久转移”这样的错误了。
解决这个问题的办法就是确认你的版本库路径不会有重叠,或者存在其他网络共享可访问的路径里面。
出现这个问题还有一个可能的原因,就是在网站根目录存在一个和版本库的URL同名的文件(文件夹)。例如,假设你的WEB服务器的根目录设置在/var/www,你的Subversion版本库被放置在/home/svn/repo目录下,然后你在Apache下将该版本库的URL配置成http://localhost/myrepo。如果你这时又在/var/www下创建了一个myrepo的目录,那么同样会产生301的错误。
这个问题很可能是因为Apache服务器的一个已知的bug(存在于2.0.48或者更早期的版本),你可以在http://nagoya.apache.org/bugzilla/show_bug.cgi?id=25040找到相应的补丁。你也可以在http://subversion.tigris.org/issues/show_bug.cgi?id=1608 上看有没有和你说的情况类似的已反馈的bug。
在环境变量CFLAGS中加入-qlanglvl=extended作为配置参数,这会使到编译xls的时候更加灵活,这样应该不会再出现编译错误了。更详细的信息请参阅 http://svn.haxx.se/dev/archive-2004-01/0922.shtml 及相关的文章。
参考 issue 695. 当前的svn checkout -N命令的实现非常糟糕。它会造成工作拷贝某些条目缺失,但它本身又没有意识到已经出现了不完整性的问题。很显然,很多的CVS用户已经对这种使用方式习以为常了,但是Subversion的用户还并不习惯。目前还没有好的办法解决这个问题,只能让你自己改变操作的流程:先试着检出单独的子目录,然后再手动嵌入到你的工作拷贝中。
这个错误信息在这里有点误导人。通常这样的错误是因为Apache无法正确加载mod_dav_svn.so模块所依赖的模块。如果当前Apache是以服务的形式运行的,那么它的path环境变量和普通用户的并不相同。请确认libdb4*.dll、intl3_svn.dll、 libeay32.dll还有ssleay32.dll都可以在\Apache\bin或者\Apache\modules目录中找到。如果没有找到,你可以从Subversion的安装目录下拷贝一份。
如果这样还解决不了问题的话,那么你可以使用类似Dependency Walker这样的工具来查看mod_dav_svn.so库的依赖性,看是否还有尚未解决的依赖性问题存在。
这些钩子脚本应该会触发外部程序的,但是这个触发过程似乎并没有执行。
当Subversion调用一个钩子脚本时,它会将所有环境变量都清除干净,包括unix下的$Path和windows下的%Path%变量,因此,如果你的脚本要访问外部程序的话你就必须指定好外部程序的完整路径。
调试技巧:
如果你正在运行Linux或者Unix操作系统,试一下按照下面的步骤手动运行一下脚本:
注意到传递给env程序的第一个参数是一个横杠,这样可以保证环境变量为空。$ env - ./post-commit /var/lib/svn-repos 1234
当使用一个外部的diff命令时,Subversion会生成一个非常复杂的命令行。第一个参数就是具体的--diff-cmd,然后就是具体的--extensions (尽管使用空白的 --符号时会忽略扩展),或者如果没有指定--extensions或者--extensions为空的话,就加上‘-u’参数。第三和第四个参数,Subversion会传递一个“-L”还有第一个文件的标签(例如,“"project_issues.html (revision 11209)”)。第五个和第六个就是另一个“-L”和第二个文件的标签。第七和第八个参数分别是第一个和第二个文件的名称(例如,“.svn/text-base/project_issues.html.svn-base”和“.svn/tmp/project_issues.html.tmp”)。
如果你指定的diff命令不支持这些参数的话,你可能需要创建一个简单的封装脚本来忽略这些参数,然后将最后的你需要的文件的路径参数传递给diff命令。
警告:Subversion并不希望外部的diff工具会改变它接收到的文件,否则可能会破坏当前工作拷贝。
更多信息请参考issue #2044.
冷静一下,深呼吸。
在windows 2000及之后的版本上,svn 1.2之后的版本都使用标准的windows API来加密数据,所以只有用户自己能够解密出缓存的密码来。
在Mac OS X, svn 1.4以后的版本使用系统的keychain功能来加密以及存储svn的密码。
Subversion 1.6会为UNIX/Linux处理这个问题,对于GNOME Keyring和KWallet的支持已经实现,都可以方便的在磁盘上存储加密的密码。这些程序可以在运行中的或编译中添加。如果没有,客户端会使用明文缓存密码,但是它如果以明文存储我们会首先询问是否允许。
Subversion 1.5以及之前的版本在UNIX/Linux中,密码会以明文形式保存在~/.subversion/auth/中,请注意,虽然是明文存储(通常是~/.subversion/auth/),但是密码的访问许可是700,只有你可以阅读。
尽管如此,如果你还是担心的话,那么你可以将密码缓存的功能永久关闭。在svn 1.0的客户端中,你只需要在你的运行时配置文件设置‘store-auth-creds = no’。对于svn 1.1以及之后的版本来说,你可以使用粒度更细的设置‘store-passwords = no' (这样服务器的证书还是会缓存)。更多关于密码缓存的信息已经可以看“日构建”Subversion手册之中第六章的“客户端凭证缓存。
最后要说的是,我们知道CVS很多年来一直都是将缓存的密码存放在.cvspass文件中的。表面上来看存放在里面的数据是加密过的,但实际上,他们只是使用非常简单的算法来进行混淆,就像rot13一样。这些所谓的密码可以很轻易就被破解掉。这种混淆的唯一作用就是防止其他用户如管理员意外的看到密码。但是目前还没有人希望Subversion也这样做。如果你感兴趣的话,你可以为其写补丁然后发送到dev@list。
Berkeley DB 4.1版本相当的不稳定,而4.0和4.2都相对比较稳定。这条错误信息就是4.1版本有时候发生问题时的提示信息。
这个问题是由于使用Berkeley数据库做支撑的Subversion版本库中的其中一张表的某个数据库格式域(database format field)被破坏了。目前还不知道什么原因,这通常都是因为“copies”表出错,导致它从“btree”类型转换成“recno”类型。你可以按照下面列举的简单的恢复流程来处理。如果下面的步骤未能成功,你应该联系Subversion的用户邮件列表。
早期版本的APR在0.9版本分支上,如果使用Apache 2.0.x和Subversion 1.x,那么对于大文件是没有支持的(2Gb以上)。在APR 0.9.5及以后的版本还有Apache 2.0.50及以后的版本中已经考虑到这个问题了,并做了修正。但是这个修正并不是针对所有操作系统的,而是只针对Linux。
在Berkeley DB 4.3之前,svnadmin recover能够适当的将Berkeley DB的版本库进行升级。但是Berkeley DB 4.3版本中的一个行为的改变导致了现在的升级过程失败。
使用下面的流程来置换升级你目前基于Berkeley DB 4.3及之后版本之上的版本库:
现在版本库应该能够在Berkeley DB 4.3上正常使用了。
注意,这里假设版本库是运行在Apache 2.0.x之上。
APR 0.9.6 有个bug,当运行在Tiger上的时候会存在,当你尝试检出一个大于64KB的文件时会出现这个错误。这会导致检出失败,通常给出的错误信息都是不可预料的。下面是可能会出现的提示信息,你看到的可能不大一样。
svn: Invalid diff stream: [tgt] insn 1 starts beyond the target view position
svn: Unexpected end of svndiff input
svn: REPORT request failed on '/path/to/repository' svn: REPORT of '/path/to/repository/!svn/vcc/default': Chunk delimiter was invalid
同样,在Apache的error_log日志中也会记录相应的错误,如:
[error] Provider encountered an error while streaming a REPORT response. [500, #0] [error] A failure occurred while driving the update report editor [500, #190004]
为了确认这个bug是否存在—假设你可以访问版本库所在的机器—尝试使用File://的方式来检出,这样就可以绕过Apache直接通过文件系统来访问了。如果这样检出成功的话,那么问题就出在上面提到的bug上。
目前最好的解决方案就是升级到APR 1.2.0+。
或者你也可以从各自的源里重新编译Apache还有Subversion,在运行Apache配置前先设置好相应的环境变量。
setenv ac_cv_func_poll no
或者以Bourne shell 的语法,如下:
ac_cv_func_poll=no; export ac_cv_func_poll
如果你分别编译APR / APRUTIL(例如,你并不想使用Apache发行包中的某些部分),你必须在配置APR之前设置好环境变量,因为这就是问题所在。
如果你在编译Subversion主线源代码的最后一步出现了下面的错误:
/usr/local/apache2/lib/libaprutil-0.so.0: undefined reference to `db_create' /usr/local/apache2/lib/libaprutil-0.so.0: undefined reference to `db_strerror'
那很可能是由于你使用的是Debian系统,你需要升级你的‘libtool’。(我听说Debian的打包者不得不做了一些小的改动来修正一些错误。这可能会导致Subversion编译的时候出现问题。但这只是猜测,在写这个FAQ条目的时候我并没有时间去验证一下细节的东西。你可以查看http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=112617 还有相关的帖子得到更详细的讨论。)
不管怎么样,如果你在2005年11月15号的时候在Debian GNU/Linux系统上面运行最新的testing发行版,然后遇到这个问题,那么解决方法就是,将libtool 1.5.20的源代码使用标准的“./configure && make && sudo make install”重新编译,再对工作拷贝执行了一次“make clean”的清理,然后运行“./autogen.sh”, “./configure”,“make”,最后一切都正常了。
注意,有一个类似的反馈也已经发布在 http://svn.haxx.se/dev/archive-2003-01/1125.shtml,但是在上面却没有提到这个解决方案。
一句话概括:使用--listen-host=0.0.0.0参数调用svnserveShort。
再具体一点,FreeBSD的守护进程默认情况下只监听tcp6。上面这个选项用来启用tcp4的监听。
那是因为你要添加的目录已经包含了.svn的目录了 — 它已经是工作拷贝了 — 但那个目录是来自其他的版本库中而不是你当前正在访问的版本库。这种现象很可能就是因为你使用了操作系统的拷贝功能直接从另外一个工作拷贝上复制(没有使用svn copy)该目录到当前工作拷贝中。
比较快但是有些不雅的解决方案就是删除你之前复制的文件夹下所有的.svn目录,然后add命令就可以顺利执行了。如果你使用的是Unix,你可以使用下面的命令:
find dir -type d -name .svn -exec rm -rf {} \;
然而,如果工作拷贝来自同一个版本库,直接删除或移走工作拷贝,然后通过svn copy获得一个正确的拷贝也比较理想,可以节省版本库的空间。
如果来自不同的版本库,你应当问自己为什么要作这个拷贝;你应该确保通过添加目录,你不会在版本库中做出非预期的拷贝。
当编译APR时使用/dev/random设备,服务器没有办法获取足够的内存量的时候这个问题经常会出现。如果服务器上只有Subversion在使用APR,那么你可以安全的重新编译APR,编译的时候带上参数 --with-devrandom=/dev/urandom 。当然,如果有其他进程在使用APR的时候,那么你就不能这样来做了,否则会造成其他服务不安全。
这个错误可能是由于OpenSSL 0.9.8版本的问题。你可以下载早期不存在此问题的版本(或者可能的话,升级到更新的版本)。
svn: This client is too old to work with working copy '/path/to/your/working/copy'; please get a newer Subversion client这是因为Subversion工作拷贝的格式发生了不兼容的改动—新版本的Subclipse对你的工作拷贝做了升级,所以你的命令行客户端程序,因为是版本比较老的,因此无法读取其内容。(这个问题并不只是存在于Subclipse上,也有可能是当你同时使用另外一个1.4以后的新版本的时候,旧版本的程序也会出现类似的问题)你可以简单的把你的命令行客户端升级到1.4及之后的版本。 对于Subversion 1.5,提供了一个降级工作拷贝到较早版本的辅助脚本;见 这个FAQ。
有时候,工作拷贝中包含了一些未被纳入版本控制的文件,这个时候svn switch就会发生错误。switch的过程就会停止,这会导致工作拷贝一半被switch,另一半却保持原样。
不幸的是,如果这个时候你采取的措施不正确的话,那么你的工作拷贝将会不能使用。有时候,svn会提示用户执行清理(svn cleanup)。但清理命令又有可能造成错误。参考:issue #2505。
用户可以手动的删除那些造成问题的文件或目录,然后再执行清理命令,接着继续swich,这样便可以恢复。
注意,对从版本库中检出的原始副本进行switch通常不会发生错误。如果你正在开发过程中需要使用到svn switch命令,那么有3种方式可以使用:
# Check and delete svn unversioned files: svn status --no-ignore | grep '^[I?]' | sed 's/^[I?]//' svn status --no-ignore | grep '^[I?]' | sed 's/^[I?]//' | xargs rm -rf
在 issue 2505 中有更详细的例子。这个问题的产生是因为svn客户端为了保险起见,不会直接删除所有未被版本控制的文件。
下面还有两个更具体的例子描述了类似的问题。还有其他的svn switch错误在这里没有提及,你只有在从一个干净检出的工作拷贝中执行swich才能避免。
wc/$ svn switch $SVNROOT/$project/branches/$ticket-xxx svn: Won't delete locally modified directory '<dir>' svn: Left locally modified or unversioned files
删除所有未被版本控制的文件,然后继续切换过程,这样可以恢复。
wc/$ svn switch $SVNROOT/$project/branches/$ticket-xxx svn: Won't delete locally modified directory '<dir>' svn: Left locally modified or unversioned files
在这种情况下,删除未被版本控制的文件并不能恢复,执行cleanup的时候会出错,但是svn switch会提示你去执行svn cleanup。
wc/$ svn switch $SVNROOT/$project/branches/$ticket-xxx svn: Directory '<dir>/.svn' containing working copy admin area is missing wc/$ svn cleanup svn: '<dir>' is not a working copy directory wc/$ svn switch $SVNROOT/$project/branches/$ticket-xxx svn: Working copy '.' locked svn: run 'svn cleanup' to remove locks (type 'svn help cleanup' for details)
删除目录(还有其他所有未被版本控制的文件,这样可以避免转换命令不会再重复出现类似的错误。然后继续转换的过程,这样可以恢复。
TortoiseSVN的cleanup错误有一点不同,你可能会遇到下面的错误:
Subversion reported an error while doing a cleanup! <dir>/<anotherdir> is not a working copy directory
在这里讲到的每一个例子中,svn switch命令会失败,使到你的工作拷贝变成只有一半发生转换成功的,svn status会显示出所有发生转换的文件,用S标记出来(在根目录看到的情况可能有所不同),用!标记出发生问题的目录,~标记出出错的问题文件(可能还有L用来标记锁定的文件),就像下面列举的一样:
wc/$ svn status ! . ! <dir> S <switched_things> ~ <dir>/<thing_that_is_now_unversioned>
仔细研究了windows关于文件命名的API文档才发现造成这一问题的最常见原因。简单的说,当你使用unicode版本的windows和路径相关的函数时,你可以指定的路径长度比较长,而且提供了绝对路径定位,而不是相对路径定位。幸运的是Subversion使用的Apache Portable Runtime(APR)库透明的对这种绝对路径(如C:\WorkingCopy\file.txt)进行了转换,将其转换成符合windows API要求的格式(\\?\C:\WorkingCopy\file.txt),这种转换也可以反向。不幸的是,你只有在使用绝对路径的时候才享用了这些长路径的好处。
要查看路径长度是不是造成你所遇到的问题,你可以在Subversion命令行客户端下使用绝对路径代替相对路径(或者根本不提供路径参数)。换句话说,如果你原先是这么做的:
C:\> svn up WorkingCopy
或者这样做的:
C:\> cd C:\WorkingCopy C:\WorkingCopy> svn up
把它改成下面这样的方式
C:\> svn update C:\WorkingCopy
如果问题解决了,那么恭喜你——你已经成功突破了windows的路径长度限制,并且现在你已经知道怎么解决这个问题了。
为什么这个问题不会影响TortoiseSVN呢? 因为TortoiseSVN对SubversionAPI提供的永远都是绝对路径。
那为什么Subversion命令行客户端不是永远都把输入转换成绝对路径,然后使用绝对路径呢?Subversion的开发者在开发的时候基于一个原则,那就是出于对用户体验的考虑,在工具的输出中显示的路径必须匹配输入的路径的语法。如果输入的相对路径转换到绝对路径没有太多价值的话,那么这个转换出于复杂性考虑就去掉了。(换句话说,这是一个比较难的问题,但不是一个比较迫切的问题)。
有时候在次版本改动的发行包下,工作拷贝中的元信息格式也会发生不兼容的改变。例如,假设你使用Subversion1.4.4创建了一个工作拷贝,但有一天你升级到1.5.0版本。但后来你又想将其改成1.4.4,这个时候就出错了——提示信息就如你所描述的。
这是因为1.5.0将你的工作拷贝升级到可以支持新特性的格式(例如,修改列表,keep-local标志,variable-depth目录)尽管1.4.4并不知道任何这些特性,但它能够识别出来,工作拷贝里面使用的格式已经被升级到一个新的它不能支持的版本。
1.5.0升级工作拷贝是出于一个好的原因,那就是它知道1.4.4现在并不了解工作拷贝的任何元信息,因此如果让1.4.4的版本去干扰工作拷贝中的元数据,那么重要的信息可能会丢失,很有可能会造成破坏(例如你可以参考issue #2961)。
但是这种自动升级的行为在当你想要尝试一个新版本但是又不想永久安装时,挺让人讨厌的。出于这一点的考虑,我们发布了一个脚本,用来安全的对工作拷贝进行版本降级。你可以在下面的地址下载到:
http://svn.collab.net/repos/svn/trunk/tools/client-side/change-svn-wc-format.py
使用--help参数调用此脚本,可以查看怎么脚本的运行帮助。未来Subversion新版本发布时,我们会尽量更新这个FAQ条目,使他覆盖到更多的降级的场景。
Neon库,是用来作为Subversion服务器和HTTP客户端进行通讯的库,通常被编译成静态库。但是它后来被链接到不同的动态链接库中,这会导致在AMD 64位操作系统系统上面编译的过程出现错误,出现类似下面的信息:
subversion-1.4.6/neon/src/.libs/libneon.a(ne_request.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC /home/jrandom/subversion/subversion-1.4.6/neon/src/.libs/libneon.a: could not read symbols: Bad value
在开发者邮件列表中有一篇文章提到了这一点。
解决方法就是为Subversion的配置脚本提供一个“--enable-shared”的参数。
简而言之,这个错误代表了一类问题,也就是Apache认为Subversion的客户端不会再处理它建立的网络连接。取决于是否使用SSL,或者Apache何时决定中断连接,类似情形也会报告一些其他的错误信息,
Subversion客户端保证工作拷贝永远处于正常状态,一个方法是会临时保存所有检出文件的所有原始版本,直到获得给定目录的所有文件和子目录。一旦目录的数据被下载,客户端会整理目录,并将将文件的原始版本复制回工作区域,作为管理数据等等。在这个目录整理过程中,客户端会关注这些任务而不会去关心网络连接。有时候 —通常是版本化目录包含大量文件,或者非常大的文件—客户端会在整理目录(不管网络传输流)上花费大量时间,所以Apache会认为客户端已经永远离开了,所Apache关闭了网络传输流,然后客户端发现服务器放弃了连接,并报告了这样的错误。
一个办法是增加Apache一直等待客户端还在监听网络流的最长时间,你可以修改Apache的Timeout配置参数。你也应该注意一下你的数据集。如果你一个目录有大量的文件,就会更容易导致这个问题。如果你能将一组文件分配到一些目录中,将会对大家都有益。
如果将Subversion的测试数据保存在RAM磁盘上,那么测试的过程将会变得非常快。在Linux系统上,你可以直接将RAM磁盘挂载:
mount -t tmpfs tmpfs /path/to/src/subversion/tests/cmdline/svn-test-work -o uid=$USER,mode=770,size=32m
或者,如果要更长期的挂载,可以将下面这一行添加到你的/etc/fstab
文件中:
tmpfs /path/to/src/svn/subversion/tests/cmdline/svn-test-work tmpfs defaults,user,noauto,exec,size=32m
RAM磁盘空间至少为大约700MB。尽管如此,你还是可以通过清理测试目标来大大降低空间需求(在上面的配置例子中我们已经看到了)还有你的内存占用。清理意味着占用更多的IO资源,但是由于测试数据还是存在于内存中的,因此不会造成任何性能的下降。
make check CLEANUP=true
参考 http://svn.haxx.se/dev/archive-2003-02/0068.shtml 可以看到更多关于RAM磁盘使用的权威讨论。
在往unix-y系统上运行make install这一步之前,动态编译Subversion源代码实际上执行的是libtool-generated脚本,这个脚本会重新链接并且运行真正的二进制文件,就如下面显示的一样,这会让调试更加复杂:
subversion$ gdb subversion/svn/svn
... "/path/to/subversion/subversion/svn/svn": not in executable format: File format not recognized
如果编译的时候使用--disable-shared
参数来配置使用静态链接到二进制库,或者重新安装然后将调试器指向新安装的版本,这样可能能解决一部分问题,但是经常我们需要在源代码中临时直接的进行调试。
要做到这一点,在shell脚本中编辑上一次执行的语句,然后在你的调试器中运行真正的二进制库。在gdb中,就是将exec "$progdir/$progname"
替换exec gdb --args "$progdir/$progname"
。
这个小技巧在使用libtool-generated的shell脚本进行白盒测试的时候中非常有用。
默认情况下,gcc会经常优化那些私有变量及函数,还有相关的操作。这样会造成调试的时候跟进一段代码变得更复杂。
unix-y systems上的解决办法就是在make这一步的时候将这个优化过程关闭。
subversion$ make EXTRA_CFLAGS=-O0
(那是一个横杠加两个字母的O)。你也可以通过运行下面的配置使到此配置应用到以后所有的调试过程中:
subversion$ ./configure --enable-debug
对于产品的安装来说,要记住在从源码安装Subversion时撤销这个操作,你可以通过重新运行make或者configure并不附加任何参数来撤销上面的修改操作。
Subversion 客户端使用WebDAV/DeltaV 协议的一个子集来作为 mod_dav_svn server 模块的一部分。简而言之:
OPTIONS, PROPFIND, GET, REPORT, MKACTIVITY, PROPPATCH, PUT, CHECKOUT, MKCOL, MOVE, COPY, DELETE, LOCK, UNLOCK, MERGE
关于协议的细节问题可以在下面这个链接找到相应的文档:
http://svn.collab.net/repos/svn/trunk/notes/webdav-protocol参阅 Poul-Henning Kamp的关于freebsd-hackers的帖子:http://www.freebsd.org/doc/en_US.ISO8859-1/books/faq/misc.html#BIKESHED-PAINTING.
Jim Blandy设计了Subversion的名字和版本库的设计,把“Subversion”读作 “Subversion”。
在Subversion的源代码中有很多引用指向‘baton’对象。他们只是一些对象。有很多void *的数据结构作为某个函数的上下文。在另外一些API里,他们通常通过void *ctx或者void *userdata来调用的,就像Subversion的开发者通过“batons”数据结构来调用一样,但是更省资源。