需要调用svn去做一些操作时,有两种方式:调用svn.exe命令行和调用svn api接口。我不太喜欢调用命令行的方式,是因为它需要依赖一个外部的exe程序,同时,为了得到命令执行结果,还需要去捕捉命令行的输出控制台,然后去解析,使得不太可靠。因此,我选择了调用svn接口的方式,因为我使用的是c#,有一个现成的第三方包装的库SharpSvn可以调用。
SharpSvn主页:
http://sharpsvn.open.collab.net/
使用起来很简单,下面是一个例子:
{
using (SvnClient client = new SvnClient())
{
SvnInfoEventArgs serverInfo;
SvnInfoEventArgs clientInfo;
SvnUriTarget repos = new SvnUriTarget("http://svn.test.com/demo");
SvnPathTarget local = new SvnPathTarget(@"d:\Work\Code\demo");
client.GetInfo(repos, out serverInfo);
client.GetInfo(local, out clientInfo);
string path = @"d:\Work\Code\Demo";
client.CleanUp(path);
client.Revert(path);
client.Update(path);
Console.WriteLine(string.Format("serverInfo revision of {0} is {1}", repos, serverInfo.Revision));
Console.WriteLine(string.Format("clientInfo revision of {0} is {1}", local, clientInfo.Revision));
}
}
Subversion是一個文件版本管理工具, 廣泛的被大家採用來作為源代碼版本管理.
已有的工具不管是其自帶的命令行工具還是Windows UI的tortoiseSVN等還是很方便實用的, 但是如果想跟已有的系統整合的話,除了用其內建的Hook Script功能外,必然要使用SVN的API,這個API是用C寫的, 所以對於其他開發語言來說如java, C#等使用起來不方便.
於是就有了SVN Client的java實現,或者是用其他語言對C接口的DLL包裝了一層的代碼(參考SWIG),這樣我們就可以方便的使用其他語言來與SVN repository 進行溝通了
SharpSvn就是 .net平台的一個SVN API的實現, 其被廣泛採用到AnkhSVN 等工具中, 可以被用來擴展為自定義的訪問SVN的Client, 或者用來跟其他系統如Bug/CR追踪等進行整合.
下面是一位網友整理的一個簡單的指南,類似hello world, 告訴我們如何開始使用Sharp SVN
The SharpSvn library basically gives you a .NET interface to all the operations that you would normally perform through a tool like TortoiseSVN.
I found myself needing this exact library while writing a tool that changes files that have been checked out from SVN.
The problem with manipulating files that are under SVN is that you need to be careful about renaming files (and sometimes even deleting). If you don’t do it through the SVN api then you will end up with duplicates files/folders in SVN since SVN thinks that it’s a new file.
To solve this I finally got a chance to crack open the SharpSVN library which is used by my favourite AnkhSVN.
1. Download the latest library from http://sharpsvn.open.collab.net/. You have to pick between either 1.5 or 1.6. I went with 1.6 and didn’t run into any issues. I think this is based on the version of the SVN server that your connecting to.
2. In your Visual Studio project add a reference to the following assemblies.
- SharpSvn.dll
- SharpSvn.UI.dll (Only needed if you need the UI to prompt for login)
3. If like me your building on a 64 bit OS and you want your app to run on a 32 bit OS, make sure the project that references the SharpSvn.dll is set to Build for the x86 Platform. (Build –> Configuration Manager – Solution Platform)
4. Write your code using the SvnClient object. Here are some samples from the SharpSvn Wiki and some that I wrote.
ShowLog操作
using (SvnClient client = new SvnClient())
//client.Authentication.Clear();
client.Authentication.UserNamePasswordHandlers += new EventHandler<SharpSvn.Security.SvnUserNamePasswordEventArgs>(
delegate(Object s, SharpSvn.Security.SvnUserNamePasswordEventArgs ee)
{
ee.UserName = "你的帳號";
ee.Password = "你的密碼";
});
client.Authentication.SslServerTrustHandlers += new EventHandler<SharpSvn.Security.SvnSslServerTrustEventArgs>(
delegate(Object ssender, SharpSvn.Security.SvnSslServerTrustEventArgs se)
{
// Look at the rest of the arguments of E whether you wish to accept
// If accept:
se.AcceptedFailures = se.Failures;
se.Save = true; // Save acceptance to authentication store
});
this.txtLog.Text += DateTime.Now.ToLongTimeString() + "\r\n";
SvnLogArgs logArgs = new SvnLogArgs();
logArgs.Range = new SvnRevisionRange(long.Parse(this.txtRevisionFrom.Text), long.Parse(this.txtRevisionTo.Text));
logArgs.RetrieveAllProperties = true;
EventHandler<SvnLogEventArgs> logHandler = new EventHandler<SvnLogEventArgs>(delegate(object lo, SvnLogEventArgs le)
{
foreach (SvnChangeItem changeItem in le.ChangedPaths)
{
this.txtLog.Text += string.Format(
"{0} {1} {2} {3} {4}\r{5} {6}",
changeItem.Action,
changeItem.Path,
changeItem.CopyFromRevision,
changeItem.CopyFromPath,
le.Author,
le.LogMessage,
le.Revision);
}
});
client.Log(new Uri(<a href="https://url">https://url</a>), logArgs, logHandler);
this.txtLog.Text += DateTime.Now.ToLongTimeString() + "\r\n";
}
using (SvnClient client = new SvnClient())
//client.Authentication.Clear();
client.Authentication.UserNamePasswordHandlers += new EventHandler<SharpSvn.Security.SvnUserNamePasswordEventArgs>(
delegate(Object s, SharpSvn.Security.SvnUserNamePasswordEventArgs ee)
{
ee.UserName = "你的帳號";
ee.Password = "你的密碼";
});
client.Authentication.SslServerTrustHandlers+= new EventHandler<SharpSvn.Security.SvnSslServerTrustEventArgs>(
delegate(Object ssender, SharpSvn.Security.SvnSslServerTrustEventArgs se)
{
// Look at the rest of the arguments of E whether you wish to accept
// If accept:
se.AcceptedFailures = se.Failures;
se.Save = true; // Save acceptance to authentication store
});
client.CheckOut(
new Uri("https://yourSVN-Server:8443/svn/prd/UTL/trunk/ExcelPool"), //SVN repository url
@"c:\wc"); //local direcotory
}
Add new files to the working copy
using(SvnClient client = new SvnClient())
{
SvnAddArgs args = new SvnAddArgs();
// TODO: Set optional settings on args
client.Add(@"C:\file\in\workingcopy", args);
}
/***********************************************************/
//SVNSubversion 用户权限管理
//资料来源:网络、总结
//2010年7月20日
/***********************************************************/
基本的操作:
以我创建的Svn工程为例子来讲解SVN权限管理的配置
仓库创建路径:D:\SVNLibrary
>>>取消匿名登陆:
打开文件D:\SVNLibrary\conf\svnserve.conf
找到:###anon-access = read
将前面的注释去掉,并将read 改为:none
即使:anoe-access=none 表示匿名登陆下的用户权限为空。即:系统不支持匿名登陆
说明:
auth-access = write #通过验证的用户可以读和写
auno-access = read #匿名登陆下可以只读文件,即:文件修改后无法提交到服务器
password-db =password #用户保存文件的名称
authz-db =authz #权限管理文件 这个是非常重要的,如果我们要对整个工程的文件进行权限分配的时候,就必须将这个行文件前面注释掉,否咋即使我们在权限配置文件里面进行再多的配置都是无效的。这点我已经犯错了。
然后我们在authz 文件下面进行权限的分配
在权限分配的时候要注意的问题:
>>>对某个用户,如果只赋给他某个目录的权限,但对上级目录没有赋给,则他不能有上级目录的任何权限
例如某个用户有:/repository/project1的r权,而没有/repository的r权
>>>对于所有的目录,都优先处理设置在这个目录上的权限设置。
例如sai用户:
[/repository]
sai = rw
对于repository目录,他有rw的权限。
[/repository/project1]
sai = r
对于repository下的project1目录,他只有r权限。
则,这个saiy用户只有project1的r权。而repository下其他目录有rw权。
>>>权限分配,只可以分配到某个目录,而不能到某个文件
>>>如果某个目录上没有对某个用户设置权限,则一直向上级目录查找,看是否有权限
例如sai用户
[/repository]
sai=rw
[/repository/project1]
saiya=rw
则sai用户一样拥有/repository/project1的读写权限
>>>分配权限时,= 的左边为用户,不能想当然的以,号分开加入多个用户
>>>如果想设置某用户都没有rw的权限,只要 = 号的右边这空即可
User1 =
>>>如果某一个文件夹,对于人任何用户都没有权限
* =
其中*代表所有的人
SVN深入的部分
本章将详细介绍前一章所涉及的两个配置文件, svnserve.conf 和 authz.conf,通过对配置逐行的描述,来阐明其中的一些细节含义。
这 里首先要注意一点,任何配置文件的有效配置行,都不允许存在前置空格,否则程序会无法识别。也就是说,如果你直接从本文的纯文本格式中拷贝了相关的配置行 过去,需要手动将前置的4个空格全部删除。当然了,如果你觉得一下子要删除好多行的同样数目的前置空格是一件苦差使,那么也许 UltraEdit 的“Column Mode”编辑模式,可以给你很大帮助呢。
1 svnserve.conf
arm\conf\svnserve.conf 文件,是 svnserve.exe 这个服务器进程的配置文件,我们逐行解释如下。
首先,我们告诉 svnserve.exe,用户名与密码放在 passwd.conf 文件下。当然,你可以改成任意的有效文件名,比如默认的就是 passwd:
password-db = passwd.conf
接 下来这两行的意思,是说只允许经过验证的用户,方可访问代码库。 那么哪些是“经过验证的”用户呢?噢,当然,就是前面说那些在 passwd.conf 文件里面持有用户名密码的家伙。这两行的等号后面,目前只允许 read write none 三种值,你如果想实现一些特殊的值,比如说“read-once”之类的,建议你自己动手改源代码,反正它也是自由软件:
anon-access = none
auth-access = write
接下来就是最关键的一句呢,它告诉 svnserve.exe,项目目录访问权限的相关配置是放在 authz.conf 文件里:
authz-db = authz.conf
当 然,svn 1.3.2 引入本功能的时候,系统默认使用 authz 而不是 authz.conf 作为配置文件。不过由于鄙人是处女座的,有着强烈的完美主义情结,看着 svnserve.conf 有后缀而 passwd 和 authz 没有就是不爽,硬是要改了。
2 authz.conf 之用户分组
arm\conf\authz.conf 文件的配置段,可以分为两类,``[group]`` 是一类,里面放置着所有用户分组信息。其余以 [arm:/] 开头的是另外一类,每一段就是对应着项目的一个目录,其目录相关权限,就在此段内设置。
首先,我们将人员分组管理,以便以后由于人员变动而需要重新设置权限时候,尽量少改动东西。我们一共设置了5个用户分组,分组名称统一采用 g_ 前缀,以方便识别。当然了,分组成员之间采用逗号隔开:
[groups]
# 任何想要查看所有文档的非本部门人士
g_vip = morson
# 经理
g_manager = michael
# 北京办人员
g_beijing = scofield
# 上海办人员
g_shanghai = lincon
# 总部一般员工
g_headquarters = rory, linda
# 小秘,撰写文档
g_docs = linda
注 意到没有, linda 这个帐号同时存在“总部”和“文档员”两个分组里面,这可不是我老眼昏花写错了,是因为 svnserve.exe 允许我这样设置。它意味着,这个家伙所拥有的权限,将会比他的同事 rory 要多一些,这样的确很方便。具体多了哪些呢?请往下看!
3 authz.conf 之项目根目录
接着,我们对项目根目录做了限制,该目录只允许arm事业部的经理才能修改,其他人都只能眼巴巴的看着:
[arm:/]
@g_manager = rw
* = r
[arm:/] 表示这个目录结构的相对根节点,或者说是 arm 项目的根目录
这里的 @ 表示接下来的是一个组名,不是用户名。你当然也可以将 @g_manager=rw 这一行替换成 michael=rw ,而表达的意义完全一样。
* 表示“除了上面提到的那些人之外的其余所有人”,也就是“除了部门经理外的其他所有人”,当然也包括总经理那个怪老头
* = r 则表示“那些人只能读,不能写”
4 authz.conf 之项目子目录
然后,我们要给总部人员开放日志目录的读写权限:
[arm:/diary/headquarters]
@g_manager = rw
@g_headquarters = rw
@g_vip = r
* =
我 敢打赌,设计svn的家伙们,大部分都是在 unix/linux 平台下工作,所以他们总喜欢使用 / 来标识子目录,而完全忽视在 MS Windows 下是用 \ 来做同样的事情。所以这儿,为了表示 arm\diary\headquarters 这个目录,我们必须使用 [arm:/diary/headquarters] 这样的格式。
这里最后一行的 *= 表示,除了经理、总部人员、特别人士之外,任何人都被禁止访问本目录。这一行是否可以省略呢?
之所以这儿需要将 @g_vip=r 一句加上,就是因为存在上述这个解释。如果说你没有明确地给总经理授予读的权力,则他会和其他人一样,被 * 给排除在外。
如果众位看官中间,有谁玩过防火墙配置的话,可能会感觉上述的配置很熟悉。不过这里有一点与防火墙配置不一样,那就是各个配置行之间,没有 先后顺序 一说。也就是说,如果我将本段配置的 *= 这一行挪到最前面,完全不影响整个配置的最终效果。
请注意这儿,我们并没有给 arm\diary 目录设置权限,就直接跳到其子目录下进行设置了。我当然是故意这样的,因为我想在这儿引入“继承”的概念。
权限具备继承性 任何子目录,均可继承其父目录的所有权限,除非它自己被明确设置了其他的权限。也就是说,在 arm 目录设置权限后, arm\diary 目录没有进行设置,就意味着它的权限与 arm 目录一样,都是只有经理才有权读写,其他人只能干瞪眼。
【 * = 是否可以省略】【用例子引入覆盖】【单用户权限的继承问题】【父目录权限集成与全面覆盖问题】
现在来看看
好了,我们现在掌握了 “继承”的威力,它让我们节省了不少敲键盘的时间。可是现在又有一个问题了,
属性具备覆盖性质子目录若设置了属性,则完全覆盖父目录。
5 authz.conf 的其他注意点
父目录的 r 权限,对子目录 w 权限的影响
把 这个问题专门提出来,是因为在1.3.1及其以前的版本里面,有个bug,即为了子目录的写权限,项目首目录必须具备读权限。因此现在使用了1.3.2版 本,就方便了那些想在一个代码库存放多个相互独立的项目的管理员,来分配权限了。比如说央舜公司建立一个大的代码库用于存放所有员工日志,叫做 diary,而arm事业部只是其中一个部门,则可以这样做:
[diary:/]
@g_chief_manager = rw
[diary:/arm]
@g_arm_manager = rw
@g_arm = r
这 样,对于所有arm事业部的人员来说,就可以将 svn://192.168.0.1/diary/arm 这个URL当作根目录来进行日常操作,而完全不管它其实只是一个子目录,并且当有少数好奇心比较强的人想试着 checkout 一下 svn://192.168.0.1/diary 的时候,马上就会得到一个警告“Access deni”,哇,太酷了。
默认权限
如果说我对某个目录不设置任何权限,会怎样?马上动手做个试验,将:
[diary:/]
@g_chief_manager = rw
改成:
[diary:/]
# @g_chief_manager = rw
这样就相当于什么都没有设置。在我的 svn 1.3.2 版本上,此时是禁止任何访问。也就是说,如果你想要让某人访问某目录,你一定要显式指明这一点。这个策略,看起来与防火墙的策略是一致的。
只读权限带来的一个小副作用
若设置了:
[arm:/diary]
* = r
则svnserve认为,任何人,都不允许改动diary目录,包括删除和改名,和新增。
也就是说,如果你在项目初期创建目录时候,一不小心写错目录名称,比如因拼写错误写成 dairy,以后除非你改动 authz.conf 里面的这行设置,否则无法利用 svn mv 命令将错误的目录更正。
改进
1 对中文目录的支持
上 午上班的时候,Morson 来到 Michael 的桌子前面,说道:“你是否可以将我们的北京办、上海办目录,改成用中文的,看着那些拼音我觉得很难受?” Michael 心想,还好这两天刚了解了一些与 unicode 编码相关的知识,于是微笑地回答:“当然可以,你明天下午就可以看到中文目录名称了。”
使用 svn mv 指令,将原来的一些目录改名并 commit 入代码库,改名后的目录结构如下:
arm
├─工作日志
│ ├─ 总部人员
│ ├─北京办
│ └─上海办
├─公司公共文件参考目录
└─临时文件存放处
修改代码库的 authz.conf 文件,将相应目录逐一改名
使用 UltraEdit 将 authz.conf 文件转换成不带 BOM 的 UTF-8 格式
将 配置文件转换成 UTF-8 格式之后,Subversion 就能够正确识别中文字符了。但是这里需要注意一点,即必须保证 UTF-8 文件不包含 BOM 。BOM 是 Byte Order Mark 的缩写,指 UNICODE 文件头部用于指明高低字节排列顺序的几个字符,通常是 FFFE ,而将之用 UTF-8 编码之后,就是 EFBBBF 。由于 UTF-8 文件本身不存在字节序问题,所以对 UTF-16 等编码方式有重大意义的 BOM,对于 UTF-8 来说,只有一个作用——表明这个文件是 UTF-8 格式。由于 BOM 会给文本处理带来很多难题,所以现在很多软件都要求使用不带 BOM 的 UTF-8 文件,特别是一些处理文本的软件,如 PHP、 UNIX 脚本文件等,svn 也是如此。
目前常用的一些文本编辑工具中,MS Windows 自带的“记事本”里面,“另存为”菜单保存出来的 UTF-8 格式文件,会自动带上 BOM 。新版本 UltraEdit 提供了选项,允许用户选择是否需要 BOM,而老版本的不会添加 BOM。请各位查看一下自己常用的编辑器的说明文件,看看它是否支持这个功能。
利用 UltraEdit ,我们可以将 BOM 去掉。方法是,首先利用“UTF-8 TO ASCII”菜单将文件转换成本地编码,通常是GB2312码,然后再使用“ASCII TO UTF-8(UNICODE Editing)”来转换到 UTF-8 即可。