Subversion
Subversion版本控制系统
Redmine 和 subversion 版本库整合的问题
11月17日
有网友问:
请问,如果我的svn是在另外一台服务器,也通过HTTPS能访问了,在redmine还要做什么设置吗?我输入https的地址,但是redmine始终无法找到svn代码库
我先说一下在 Redmine 中通过 HTTP 协议访问远程 Subversion 版本库
我们最早研究的 Redmine,大概是 Redmine 0.8.5,通过 http 协议配置远程版本库,Subversion 的提交总是不能被 Redmine 获取。在排除了 Subversion 授权和认证的可能原因之后,我们发现问题出在 Redmine 调用 svn 命令被阻塞。
即:必须先在服务器以 Apache 的用户(www-data)访问 subversion(HTTP 协议),使用 SVN 命令行。在访问过程中,会出现交互性的提示:“是否在磁盘缓存明文口令之类的提示”,选择“是”之后, Redmine 就可以正常连接到 Subversion 服务器了。
通过研究代码,我们发现 Redmine 直接调用 Subversion 命令来获取 Subversion 的日志和提交之类,如果在命令行中传递参数:“–no-auth-cache” 等就可以避免因为 redmine 调用 svn 命令被阻塞。
Redmine 0.8.6 的改进
Redmine 0.8.6 改进了这个问题,和 0.8.5 的代码差异:
$ git diff 661bd08^ 661bd08 -- subversion_adapter.rb
diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb
index 1c85e84..b10100f 100644
--- a/lib/redmine/scm/adapters/subversion_adapter.rb
+++ b/lib/redmine/scm/adapters/subversion_adapter.rb
@@ -224,6 +224,7 @@ module Redmine
str = ''
str << " --username #{shell_quote(@login)}" unless @login.blank?
str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
+ str << " --no-auth-cache --non-interactive"^M
str
end
end
什么意思呢?因在 Redmine 在调用 SVN 命令的时候,是这么封装的:
# Get info about the svn repository
def info
cmd = "#{SVN_BIN} info --xml #{target('')}"
cmd << credentials_string
info = nil
shellout(cmd) do |io|
。。。
end
即 Redmine 0.8.6 在连接 subversion 的时候已经能够传递参数“–no-auth-cache –non-interactive” 了。
HTTPS 协议呢?
要知道 subversion 还有一个参数:
--trust-server-cert : 不提示的接受未知的 SSL 服务器证书
你的 Redmine 一定是在连接 HTTPS 协议的时候,因为证书问题被阻塞。你可以尝试更改 redmine 代码,并且要保证连接 SVN 的 URL 的主机名一定和证书中声明的一样。
为什么不用本地协议?
使用 HTTPS 协议除了有上面的麻烦之外,和 HTTP 协议一样,还需要配置专用帐号访问,此专用帐号还要拥有 Subversion 最高访问权限,否则只能有部分提交能够看到。
使用本地 file:// 协议是最理想的,无须认证信息。对于远程版本库,可以通过 svn 同步保持两者的一致。
关于限制低版本 Subversion 写操作问题的回复
10月20日
有网友提问:
我在百度文库中看到贵公司发表的一篇文章《开源版本控制SVN 树冲突、目录丢失问题及解决机制探讨》。在文中有提到一个事情是,可以在svn的hook中设置禁止低于某版本的客户端提交操作,不知道具体的脚本是如何实现,还请指教,谢谢。
另外关于svn mergeinfo的问题,对于刚刚创建完的mergeinfo信息是纯在的,但是在开发过一段时间后,这个信息就丢失了,导致在合并的时候出现找不到祖先的错误。(备份库中的mergeinfo信息还是存在的)。不知道这个问题的发生时什么原因呢?
我认为并非因为有人使用了低版本(<1.5)的 SVN 客户端导致了 svn:mergeinfo 属性的丢失。因为低版本的 SVN 客户端,不了解 svn:mergeinfo 属性,又怎么能够会删除该属性呢?
使用低版本 SVN 客户端的副作用主要是在执行 merge 的过程中,不能将合并操作记录在 svn:mergeinfo 中,而不是会删除 svn:mergeinfo 属性。
实际上找到罪魁祸首很容易,因为 svn:mergeinfo 是受版本控制的属性,可以通过 svn log 命令查到何时,何人,何故删除了 svn:mergeinfo 属性。只需要执行下面的命令:
$ svn log -v PATH/TO/MERGE/ROOT/DIR
提到的钩子,对于你的情况:
- 配置邮件通知,及时获取版本库变更通知。
修改 post-commit 和 post-revprop-change 钩子脚本,在有代码/版本控制的属性/非版本控制的属性被修改的时候,发送邮件通知 - 禁止低版本客户端对 SVN 版本库执行写操作
修改 start-commit 脚本。在 SVNBOOK 中有详细的说明。 - 你也可以参照我写的 pySvnManager 项目,不过其中的钩子脚本为了支持图形化配置,比较复杂。
http://pysvnmanager.svn.sourceforge.net/viewvc/pysvnmanager/trunk/pysvnmanager/hooks/init/hook1.5/start-commit?revision=176&view=markup
Subversion 镜像写代理的配置注意事项
9月25日
可以用 Subversion 镜像的方式实现容灾,一般无需配置复杂的写代理(Write-through proxy)
- 在镜像端提供相同授权的访问
- 禁止除了同步管理员帐号之外的写权限,通过 ReadonlySvnMirror 设定即可
但是分布式团队,需要同时在一个版本库工作:
- 为了提高读取速度,通过镜像实现各个异地团队拥有相同的本地读取速度
- 但只能在一个版本库写,因此只有有一个团队用本地的速度写版本库
实际上因为提交后还要进行异地版本库同步,因此写本地版本库速度也会有延迟。延迟出现在提交后的远程同步。 - 另外的团队要设置写代理,这样使用读取本地镜像时相同的 URL 实现版本库的写操作
但是版本库写代理的设置还是相当复杂,而且需要管理员大量的手工操作才能完成。注意事项有:
要为 HTTPS 虚拟机打开 SSLProxyEngine
如果要使用 https 协议代理提交,就需要打开 https 协议的 Apache 虚拟主机配置,启用 SSLProxyEngine 设置。
SSLEngine on SSLProxyEngine on
Subversion写代理的配置不能使用 SVNParentPath
SVNParentPath 是管理员最喜欢用的指令,只要设定为版本库的根,不必在为新建版本库设置 <Location> 配置小节。
但是对于 Subversion 写代理,必须使用 SVNPath 来版本库的实际部署路径,这是因为:
- 写代理需要配置 SVNMasterURI,但是 SVNMasterURI 只能设置一个 URL
- 即 SVNMasterURI 要可 SVNPath 配对使用,而不能和 SVNParentPath 一起使用。
- 因此需要为需要进行写代理的版本库逐一进行设置。
TODO:修改 mod_dav_svn 的代码,使得 SVNMasterURL 可以和 SVNParentPath 一起用,即 SVNMasterURI 是一个指向版本库根的 URL
基于 SVNParentPath 的版本库和单独配置的写代理版本库不能在有重叠的 location 中混搭
考虑是否可以使用如下有重叠的Location中进行混搭,既方便本地主版本库的维护,又方便同样域名和路径前缀的写代理版本库?
<Location /svn/> DAV svn SVNParentPath /opt/svn/svnroot ... </Location> <Location /svn/myrepos> SVNPath /opt/svn/svnroot/myrepos SVNMasterURI https://svn2.sun.moon.ossxp.com/svn/myrepos ... </Location>
不幸的是,上面的配置无法正确运行。虽然 Location 可以重叠,但是重叠的 Location 的配置会传递和覆盖。
上一个的 SVNParentPath 传递的下面不允许配置 SVNParentPath 的写代理的配置中,破坏了写代理的正确运行。反之亦然。
Subversion的镜像版本库要提供两套 URL!
如果镜像版本库只有一个 URL,那么向该 URL 写的时候,代理提交到主版本库。但是主版本库到镜像版本库同步时,又该如何呢?
因此需要提供两个 URL:
- 一个可以直接写(通过 ReadonlySvnMirror 钩子可以限制只有特定同步管理员可写)
- 另外一个不能直接写,而是通过代理。即用 SVNMasterURI 指令设置真正要写到的版本库地址
Subversion写代理的 URL 的路径部分必须和主版本库的路径相同!
即如果主版本库的 URL 是 https://svn.moon.ossxp.com/svn/myrepos 。镜像版本库的写代理的 URL 地址的主机名可以不同,但是路径必须相同,例如 https://svn2.sun.ossxp.com/svn/myrepos 。看到了么,一个是月亮 moon,一个是太阳 sun。
对于版本库同步用的 URL 地址则没有限制。

Subversion 管理后台升级
9月25日
Subversion 管理后台最近版本升级解决两个主要问题:Subversion版本库同步的同步锁清除,以及 IE8兼容性问题。
Subversion 版本库同步时同步锁的清除
Subversion版本库容灾备份,主版本库提交时,会临时在镜像版本库的 rev0 的版本属性中添加 svn:sync-lock 和 svn:sync-currently-copying 两个临时属性作为同步锁,避免针对一个版本库同时有多个同步进程。这么设计是因为 SVN 镜像可以是远程调用。
在特定情况下,会造成死锁:
- 同步开始后,突然的网络中断,导致同步失败,而同步的目标版本库(镜像版本库)的 rev0的锁尚未删除。
- 再次同步时,由于 rev0 的版本属性中的锁的存在,导致同步失败。
- Subversion 的主从备份失效。
死锁发生后,客户端的感受
- 客户端提交会发现速度很慢,会被阻塞10秒钟以上
- 实际上,提交已经完成,在进行主从同步时被阻塞
- Subversion的同步一旦发现被同步锁阻塞时,会持续尝试10次,每次间隔1秒钟
最新的同步机制,能够自动判断锁的有效性,避免主从同步失效
- 在同步发起时,会记录同步进程的 PID 到一个临时文件
- 当发现镜像中存在锁的时候,会检测本地锁文件,以及是否有对应的同步进程
- 如果发现锁是僵尸锁,则删除镜像版本库rev0 中的僵尸锁,继续执行版本库同步
- 如果同步过程有错误发生时,会通过钩子脚本(post-commit 或 post-revprop-change)将同步的错误会直接通过用户提交通知界面进行显示。
- 管理员也可以设置 debug 诊断标志,当 debug 打开,同步成功的信息也会以“错误通知”的方式显示在提交后的通知界面中。(同步成功的消息显示为控制台错误,这是 Subversion 的 post-commit 等脚本的设计机制决定的)

同时,还为 subversion 钩子提供手工版本库同步的接口:
(其中 /opt/svn/svnroot 是版本库的根目录)
- 显示版本库的状态:
$ sudo python /etc/subversion/hooks/scripts/svn_mirror.py /opt/svn/svnroot/
- 手工对配置了容灾的版本库进行同步:
$ sudo python /etc/subversion/hooks/scripts/svn_mirror.py sync /opt/svn/svnroot/
IE8 兼容性问题
部分界面在 IE8 不能正常显示。当打开 IE8 浏览器的“兼容视图”,则能够正常显示。
最终定位到是元素显隐,IE8的支持和以前不同。原来的实现是通过设置 element.style.visibility 为 ‘hidden’ 或者 ‘visible’ 实现 DIV 的消隐。当对 div 重复进行隐藏后,再设置显示时, DIV 中的表单域却不能显示出来!
通过 google 查询了半天,也对子元素的 style.visibility 设置为 inherit 也没有什么改观,对IE 恨的牙痒痒的。IE 的调试工具也很匮乏,FireBug lite 让 Firebug 可以在 IE 里运行,但毕竟是 CSS 的问题,也帮不了大忙。
忽的灵机一动,既然框架中已经用了 prototype, 为什么不采用 prototype 本身已经封装好的元素消隐呢,于是利用 Prototype 重写 Javascript,解决这个棘手的问题。
主要的 Javascript 修改,类似于:
- $('path_list_box').style.visibility = 'hidden';
- $('path_list_box').style.position = 'absolute';
+ $('path_list_box').hide();
- $('path_input_box').style.visibility = 'visible';
- $('path_input_box').style.position = 'relative';
+ $('path_input_box').show();
关于 pySvnManager 的回复
9月15日
最近有网友问 pySvnManager 的问题,我把解答放到了这里
关于软件安装
问:
看到你开发的这个pysvnmanager很不错,比较适合于svn的 账户 权限管理,我的操作系统是centos,用easy_install安装的是0.4版本,不知道 哪里 有0.5版本下载呢?
答:
直接从 sourceforge.net 上下载源代码安装吧
安装出错
问:
我下载了源码,按照README中 的指导进行安装
$ cd config $ make $ python setup.py compile_catalog $ paster serve --reload develogment.ini 运行却报错如下: /usr/lib/python2.4/site-packages/PasteScript-1.7.3-py2.4.egg/paste/script/pluginlib.py:81: UserWarning: Unbuilt egg for pySvnManager [unknown version] (/usr/local/pysvnmanager) pkg_resources.require(plugin) Traceback (most recent call last): File "/usr/bin/paster", line 7, in ? .....
答复:
应该是依赖的 pylons 框 架未安装。
其实有源代码,你可以编译出 egg 包哇。
$ python setup.py bdist_egg
然后,就可以安装 dist 目录 下的 egg 包,安装 egg 包会自动安装依赖的 pylons 框架。
你看到 Readme 并执行的指 令是直接运行源码,为调试而用的。
关于认证
问:
还有一个问题想请交一下,我想从LDAP服务器同步帐号来做认证。现在单独从本地密码文件,或者单独LDAP服务登录都可以。但是两个一起用,好像不行。因为看到localconfig.py文件里面说可以二者都用。因为我想把root管理帐号认证放在本地,其他帐号放在LDAP服务器,不知道这样可不可以?谢谢!
答复:
同时用两种认证,就需要在 auth 数组同时加入两种认证方式,如下:
from pysvnmanager.model.auth.http import htpasswd_login
import ldap
from pysvnmanager.model.auth.ldap_login import ldap_login
auth = [htpasswd_login, ldap_login]
## LDAP login parameters
ldap_uri = 'ldap://localhost'
ldap_binddn = 'cn=ldapreader,dc=foo,dc=bar'
ldap_bindpw = 'guess'
ldap_base = 'dc=foo,dc=bar'关于 LDAP 用户组授权
问:
我还有一个问题关于LDAP账户同步的问题。我先在LDAP服务器上建立了一个svnusers用户组,把有权限使用svn的用户都加到这个组里面来;然后在apache里面读取svnusers组里面的memberUid来实现svn对特定组的账户读取。所以我就想问不知道能否在pysvnmanager里面实现这样的功能,也就是读取属于某一个组的用户帐号?
答复:
我的 LDAP 授权模型和你的不同,不是通过用户组而是为用户设置名为 authorizedService 多值属性进行授权。可以通过设置 ldap_filter 加以判断:
ldap_filter = '(&(uid=%(username)s)(authorizedService=svn)(ossxpConfirmed=TRUE))'
我的方案要新增 LDAP schema,可以配合我们公司的统一管理平台进行图形化的用户授权。
你的方案也很好,不过在认证过程增加了一次查询(用户组匹配),会影响执行效率。

Subversion管理后台增加对SVN容灾的支持
8月29日
群英汇Subversion版本控制系统提供的基本服务,实际上已经包含了简单的容灾实现:
- 在本机使用 gistore 进行数据备份
- 在备机上用 git pull 进行每日一次的数据同步
这个方法对于大多数公司,对于非核心数据的容灾备份已经足够,但是对于代码,不同的公司有着不同的敏感度,可能每日一次的备份就不合适了。
最近在给一个客户进行SVN版本控制系统的实施时,就碰到了SVN服务器间建立镜像和实时备份的需要
- 两个异地的开发团队,每个团队工作在不同的本地版本库,可能会参考(读取)异地不同版本库的内容
- 异地团队中间通过无线网桥互联,互联网桥的通讯质量易受到环境、气候的影响
- 如果两个异地的SVN服务器能够互做镜像,可以实现双机热备
- 一个团队可以在本地的主版本库中读写,一旦有数据写入本地版本库,就会同步到无线网桥另外一端的版本库。反之亦然。
- 由于本地还提供无线网桥另外一端版本库的镜像,因此访问远程版本库可以改用本地镜像进行访问,实现本地读取速度。
如果需要写入,配置 SVN反向代理,实现透明的远程写入。
Subversion可以使用 svnsync 这一工具实现版本库的远程镜像,但是如果使用手工配置,对于客户数量众多的版本库,难以维护。加之很多版本库本身很大,从头建立同步可能会非常缓慢。
我们通过在SVN管理后台增加了一对儿插件,实现了对SVN容灾(镜像)的图形化配置界面。
阅读全部内容 »

爱上Git——《Git培训讲义》摘录
8月17日
群英汇的开源版本控制系统服务和咨询主要是基于Subversion,群英汇的开源项目管理系统平台主要集成的也是Subversion。但是我们公司(群英汇)内部的开发一直在使用Git,所以实际上,我们在Git上也有着丰富的使用和管理经验。
最近一个客户选择了我们提供Git的培训和部署服务,于是从上周开始着手准备《Git培训讲义》。
这个客户是有着实际的需求,才不得不从 Subversion 迁移到 Git,虽然只是公司的部分项目组。
《Git培训讲义》中开头有一章,我给它命名为“爱上Git”,抓眼球是其次,主要是先让用户能够立刻“爱上Git”。
阅读全部内容 »
pySvnManager 0.5 升级指引
8月9日
对于不能提供 SSH 远程登录服务器的用户,可以参照本文进行 pySvnManager 的升级。关于 pySvnManager 的新功能,参照:《pySvnManager新功能:LDAP用户同步》
升级的步骤简单的说就是:备份 -> 软件升级 -> 重启服务
阅读全部内容 »

pySvnManager 新功能:LDAP用户同步
8月8日
pySvnManager 升级到 0.5 版本,引入了数据库支持,数据库主要用于保存和 LDAP 用户帐号同步的用户帐号信息。
数据库采用 sqlite 数据库(如果需要也可以使用其它类型数据库),无须手动创建数据库,在 pySvnManager 运行时,会根据需要自动创建数据库。
在没有使用 LDAP 用户信息之前,pySvnManager 是读取 SVN 授权文件,从该授权文件中获取用户名单并显示。
可以看出这种实现的问题是:
- 在权限检查的用户列表中,只能看到用户登录ID,而看不到用户 ID 对应的用户名
- 显示的用户帐号很少(只有在 SVN 授权文件中引用的用户帐号才能显示)
- 为新用户授权,如果用户不在授权文件中,需要手工输入用户名,容易出错。
pySvnManager 0.5 增加了内置数据库和LDAP用户同步功能之后,再看 pySvnManager 中显示的用户列表:
你会发现新版本的用户列表(已经完成和LDAP同步):
- 除了显示用户ID外,还显示用户名
- 显示的用户多了很多,除了在 SVN 授权文件中引用到的用户外,LDAP 中授权的 SVN 用户也显示出来了
- 在为新用户授权时,不必手工输入用户,只需要进行一次 LDAP 用户同步,用户帐号信息自动显示出来。
那么如何进行LADP 同步呢?
我们把 LDAP 用户同步的按钮放在“角色管理”界面,只要点击其中的“和LDAP用户同步”图标按钮,自动完成同步。
其它改进还包括:更好的 IE6 支持,UI 的重新设计等等。我们会陆续协助客户完成软件升级。
用户手册也已经更新,参见: 《pySvnManager 用户手册》
关于如何升级到新版本的 pySvnManager,参照《pySvnManager 0.5 升级指引》







最新评论