SVN并不完全记录文件的属性

今天刚刚发现的,新建的一个Respontory,提交了一些文件进去,有的带有+x可执行属性,有的带有700非owner不可读属性,但是从仓库中再checkout之后,发现所有的文件属性都变成了755,再一查资料,原来svn并不记录和处理文件的这些属性,也没有什么svn chmod命令。

如果项目中确实对文件属性有要求,可以采用一个笨一点的替代的办法,就是自己写个脚本,比如叫svn_fa_modifier,把所有文件属性的设定都写在里面,每当checkout之后文件属性乱套的时候,就执行一下。(如果一直是在工作目录中checkin和update,文件属性应该不会乱)

至于svn_fa_modifier文件本身每次都要chmod +x么?其实也不用,可以用svn的propset命令来为其设定一个“可执行文件”属性,比如:

svn propset svn:executable ON svn_fa_modifier

这样svn_fa_modifier应该就永远都会带有+x属性了,不过propset中再也没有找到其他更改文件属性的资料。

参考一参考二参考三参考四(似乎svn中checkout出来的文件默认属性都是755?)。

PS: 顺便响应一下豆瓣的blog claim,doubanclaim8b95642ba743d85d,呵呵。

在linux挂载的fat32分区上无法使用svn的问题,以及keywords:Id

在ubuntu Linux下搭建了svn服务器,工作正常,svn的档案respository存放在挂载的一个fat32分区中,apache的用户www-data也加入了plugdev组,能够正常读写svn的档案。

svn server build on ubuntu, apache, work fine. and svn respository is on mounted fat32 partition, and have add apache’s user www-data to plugdev group, so apache can r/w svn respository.

但是当在fat32分区中使用svn客户端,checkout或者update文件的时候,却发生了错误:

but when use svn client on fat32 partition, failed:

fwolf@wf:/media/hdd6/temp$ svn co http://localhost/svn/svntest/trunk –username guest Authentication realm: Fwolf’s Subversion Repository Password for ‘guest’: svn: Can’t set file ‘trunk/.svn/entries’ read-only: Operation not permitted

原来,在Linux中,有些文件是不允许非所有者owner用户修改的(不知道是核心功能就禁止这样,还是一些软件按照这个规则来实现的?),即使当前用户有读、写权限。现在遇到的这个例子就说明svn客户端就是这样,所以我们必须把svn要操作的文件(.svn/entries什么的)的所有者owner改掉。

it’s because, in linux, some file are not write able to user who is not its owner, althought the operate user have write priv, this is a example. so to make svn run right, we must change owner of mounted fat32 partition.

fwolf@wf:/media/hdd6/temp$ sudo chown fwolf svntest Password: chown: changing ownership of `svntest’: Operation not permitted

然而挂载的fat32分区还无法修改owner,那就只能通过修改/etc/fstab文件来实现了:

it seems that we cannot change owner of mounted fat32 partition, so we can only modify fstab file. open /etc/fstab, change the line

/dev/hdd5 /media/hdd5 vfat defaults,utf8,umask=007,gid=46 0 1

to / 改为

/dev/hdd5 /media/hdd5 vfat defaults,utf8,umask=007,gid=46,uid=1000 0 1

vfat是分区格式(fat32),utf8是分区的字符集iocharset,umask是挂载时分配的权限,gid 46时组“plugdev”,uid 1000就是我使用的用户fwolf。

vfat is partition fstype, utf8 is iocharset, umask is privilege not give when mount, gid 46 is group “plugdev”, uid 1000 is me — fwolf.

现在重新mount这个分区,所有分区上的文件的owner就是用户fwolf了,这也是我日常使用的用户,现在再来使用svn客户端就一切正常了。

then remount the partition, the all file on this partition is owned by fwolf, and user fwolf use svn client finely.

不过这样虽然解决了问题,但是如果多用户同时使用一台主机的同样一个fat32分区,还是无法解决,多用户的话还是把分区格式转换成ext3吧,大不了以后不用的时候再转过去。

参考: ref1, ref2-1, ref2-2

$Id$的自动修改

和windows下的客户端类似,我以前介绍过,在Linux下编辑/etc/subversion/config,添加:

[miscellany] enable-auto-props = yes [auto-props] *.html = svn:keywords=Id

就可以自动替换所有.html文件里的$Id$了。

update @ 2006-5-24 后来发现这种把仓库存在fat32分区上的方式,虽然能够正常的checkout,但是checkin/commit的时候,会出现svn无法chmod的错误,所以仍然需要把svn的仓库存放在ext3分区上,并且把目录owner设为www-data。

利用SVN更新网站

如果你有一个假设在公网上的SVN服务器,而你的网站所在的主机允许你使用SVN客户端,并且开放了php的exec函数,那么你有福了,你可以利用SVN作为中转,更新你的网站程序。

首先,也是前提,就是你的网站程序是用SVN管理的,那么只要你的网站主机能够访问SVN,就能够使用SVN的update功能来更新程序。

准备工作一:将网站程序加上svn的控制标记,由于SVN的控制信息都存在程序所在目录的.svn子目录中,所以需要找一个空目录,并且将网站现有程序checkout到这个目录里面来,注意是checkout而不是export,因为接下来要将最新的网站程序连同他里面包含的很多个.svn目录一同上传到服务器上去。为了不让.svn目录泄露机密,要在.htaccess文件或者是httpd.conf中设定如下规则,禁止对.svn目录的访问。

	<directory ~ "\.svn">
		Order Allow,Deny
		Deny from all
	</directory>

准备工作二:作一段小程序,调用服务器上的svn命令行命令,update网站程序,下面是一个写好了的简单例子。

	//setup update target path
	$target_ar['fwolf'] = 'd:\fwolf';
 
	//setup commandline
	$svn_cmd1 = 'd:\server\svn\bin\svn.exe update ';
	$svn_cmd2 = ' --username updatebot --password xxxxxx --no-auth-cache';
 
	//output html string
	$html = '';	
 
	//recieve get parameter
	$target = isset($_GET['target']) ? $_GET['target'] : '';
	if (empty($target) || !isset($target_ar[$target]))
	{
		$html = 'Target does not correct.';
	}
	else
	{
		//execute svn update command
		$cmd = $svn_cmd1 . $target_ar[$target] . $svn_cmd2;
		$ar = array();
		$status = 0;
		exec($cmd, $ar, $status);
		for ($i=0; $i < count($ar); $i++)
			$ar[$i] = htmlspecialchars($ar[$i]);
		$html .= 'Status: ' . $status . "<br />\r\n";
		$html .= implode('<br />' . "\r\n", $ar);
	}
	echo $html;

把这个程序放到服务器上能够访问到的地方,也可以在此基础上加上一些访问限制,那么只要访问这个程序/页面,服务器就会自动更新你的网站啦。

	Status: 0
	D H:\cvswork\svntest\update_from_svn.php
	Updated to revision 44.

这样,在利用SVN很好的管理网站程序的基础上,还实现了服务器程序的很方便的更新,一举两得!尤其是在程序上传不是十分方便的场合,用起来就更舒服了,我就是在更换了一个劣质防火墙,ftp无法正常使用的情况下想出这个怪招儿的:-)。

Update @ 2007-07-31

如果在windows主机上使用本方法,而svn服务器是采用了ssl的https://…地址,那么会遇到一点小麻烦,就是在执行svn update的时候,由于使用的是web的用户,在出现确认证书的提示信息时,用户是无法输入的:

Error validating server certificate for 'https://20070731.fwolf.com':
 - The certificate is not issued by a trusted authority. Use the
   fingerprint to validate the certificate manually!
Certificate information:
 - Hostname: 20070731.fwolf.com
 - Valid: from Jul 31 06:49:53 2007 GMT until Jul 28 06:49:53 2017 GMT
 - Issuer: Fwolf, US
 - Fingerprint: 38:43:0b:29:75:1t:ba:d8:29:8f:94:9a:10:42:a0:fe:ae:93:4d:91
(R)eject, accept (t)emporarily or accept (p)ermanently?

这时就只能用变通的方法了,首先在dos方式中使用svn up,svn会自动缓存身份验证以及ssl确认信息,这些信息保存在C:\Documents and Settings\Administrator\Application Data\Subversion目录下,然后把这个目录整体拷贝到C:\Documents and Settings\Default User\Application Data\Subversion就可以了,测试环境windows2003,并且试过复制到All Users的对应目录无效。

另外修改配置文件servers,添加ssl-trust-default-ca = yes的方式在windows下好像没有作用。

Update @ 2009-04-07

Git(ssh证书登录)也可以这样用,略有不同,我使用的客户端是msysgit

  • Admin用户使用桌面操作(Git Bash)时,.ssh目录位于C:\Documents and Settings\Administrator\下。
  • 直接使用C:\Program Files\Git\bin\ssh.exe当ssh客户端时,.ssh目录要放到C:\Program Files\Git\下,并且似乎忽略.ssh/config文件,私钥文件只认identity id_rsa id_dsa三个。
  • 系统服务比如Apache运行时,使用上面一条的设置,很是诡异。

说公钥文件只认固定的三个,是从这里猜到的:

	Program Files/Git$ grep identity share -R
	share/git-gui/lib/sshkey.tcl:   foreach name {~/.ssh/id_dsa.pub ~/.ssh/id_rsa.pub ~/.ssh/identity.pub} {

好像是写死了,并且我用config定义其他私钥它也不认。第一条中使用Git的时候没事,很正常。

同时,为了安全起见,apache里还要增加如下设置:

	<Directory ~ "\.(git|svn)">
		Order allow,deny
		Deny from all
		Satisfy All
	</Directory>

subversion也需要设置“set LANG=en_US”

在服务器上下载了一个svn(zip格式,解压就可以的),原本是想利用svn作为中转/记录,实现网站远程上传和更新,但是解压之后,运行命令行命令svn.exe,却遇到了像下面这样的乱码:

D:\server\svn\bin>svn ?\232?\175?\183?\228?\189?\191?\231?\148?\168?\226?\128?\156svn help?\226?\128?\ 157?\228?\187?\165?\229?\190?\151?\229?\136?\176?\231?\148?\168?\230?\179?\149?\ 229?\146?\140?\229?\184?\174?\229?\138?\169?\227?\128?\130

这实在是让我纳闷了半天,按理说svn解压缩就行,连install脚本、程序都没有,怎么一运行就乱码呢?并且敲几个其他的svn命令观察,发现乱码还不一样,应该是svn正常的执行了,而输出内容不正常而已。

同样也是偶然的,发现了解决方法。记得在Sybase Open Client 和 GTK+有点小冲突,当时问题出在了环境变量LANG上,这个svn原来也使用了这个环境变量,当环境变量LANG设置为en_US以后,svn的输出信息就正常了。

C:\>set lang=en_us C:\>svn Type ‘svn help’ for usage.

现在把这个LANG设置到windows系统变量中,问题就解决了。

Update @ 2008-04-16

近日在用php实现svn的hooks脚本时发现,虽然使用的系统ubuntu的语言是en_US.UTF-8,但在php中仍然要将LANG设置为en_US才行(LANG设置为en_US.UTF-8就画蛇添足了),不过直接在php中更改环境变量是不行的:

putenv('LANG="en_US"'); 

这样只会影响php的环境,而php调用shell_exec()的时候,传递给命令的还是sh的默认环境,所以要在每次调用的时候强制指定环境变量。除了LANG以外,还要将LC_ALL赋值为en_US.UTF-8,这样才完全没有乱码,完整的结果如下:

$svn_log = shell_exec("env LANG=en_US LC_ALL=en_US.UTF-8 svnlook log $repos -r $rev");

参考:subversion的svn命令行显示乱码的解决

Apache自动添加地址末尾的斜线不能用于SVN

一般来说,如果在Apache服务器上建立了一个别名work,然后使用hostname/work来访问的话,会出现404找不到文件的错误,这是因为Apache把work当作一个文件来解析了,一般我们解决这个问题的办法是通过mod_rewrite,自动把后面遗漏掉的/加上,比如:

#自动添加地址末尾的斜线 RewriteEngine on RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.+[^/])$ $1/ [R]

另一方面,由于SVN也借助了Apache的DAV方式来进行工作,所以一般使用SVN时会建立一个别名svn,然后使用hostname/svn来进行访问。

上面两个问题加在一起,问题就随之而产生了,在使用TortoiseSVN导入import内容的时候,服务器提示302 Found,但不工作。302 Found意味着要访问的目标已经移动,用户请求将被导向到新的位置。但是似乎TortoiseSVN无法正确处理这样的情况,所以就停留在这里,不工作了。所以,需要修改设置,使得在访问svn目录下的内容的时候,不启用自动添加地址末尾斜线的功能,因此在上面的代码中,在最后的处理RewriteRule之前,加上一句:

RewriteCond %{REQUEST_URI} !/svn*

这样既不影响自动添加地址末尾斜线功能的使用,也不会干扰SVN的正常工作。

另外,Apache 2中有一个mod_dir,并且一般默认都是开启的,已经能够提供自动添加URL末尾斜线的功能了,官方说明如下:

A “trailing slash” redirect is issued when the server receives a request for a URL http://servername/foo/dirname where dirname is a directory. Directories require a trailing slash, so mod_dir issues a redirect to http://servername/foo/dirname/.

所以在一般情况下,上面使用mod_rewrite来添加斜线的设定就不需要了,一个例外的情况就是当你使用了反向代理的时候。

比如设定domain1/dir反向代理到domain2/dir,那么mod_dir就无法正确添加domain1/dir后面的斜线,所以仍然需要使用上面的mod_rewrite功能。

也许,在设定上述反向代理的同时,在domain1上设定alias dir,这样mod_dir就能够正确识别并添加domain1/dir后面的斜线了?我觉得应该是这样的,因为设定了Alias以后,mod_dir就能够识别出domain1/dir是一个目录,从而进行正确的处理了。

经过初步实践,我的设想是正确的,只需把在domain1上设定一个Alias dir,目标随意,就能够让mod_dir正确进行处理了。

为什么要尽量使用mod_dir而不是RewriteRule呢?因为我认为不需要解析httpd.conf的mod_dir应该效率会高些。:-)

ChangeLog的写法

从CVS到SVN,软件开发过程中维护一个ChangeLog文件是个很好的习惯。

ChangLog可以放在程序的根目录,也可以放在文档中。

ChangeLog的文件名多为:
ChangeLog
ChangeLog.txt
CHANGELOG

在内容中,一个版本为一段,段首标明版本信息,每条变更记录占一行,行首为变更类别,后面写变更信息,最后可以用括号标上作者。

变更类别我大概见过两种方式,方式一是:
* 代表修改
+ 代表新增(功能…etc)
– 代表删除

方式二是在TortoiseSVN中见到的,个人觉得更准确:
NEW 代表新增
CHG 代表变动
ENH 代表增强、改进
BUG 代表修复错误
DEV ??

更详细的可参见TortoiseSVN的ChangeLog.txt