8.外部定义

Posted on Posted in 4.高级主题

外部定义

有时候创建一个由多个不同检出得到的工作拷贝是非常有用的,举个例子,你或许希望不同的子目录来自不同的版本库位置,或者是不同的版本库。你可以手工设置这样一个工作拷贝—使用svn checkout来创建这种你需要的嵌套的工作拷贝结构。但是如果这个结构对所有的用户是很重要的,每个用户需要执行同样的检出操作。

很幸运,Subversion提供了外部定义的支持,一个外部定义是一个本地路经到URL的影射—也有可能一个特定的修订版本—一些版本化的资源。在Subversion你可以使用svn:externals属性来定义外部定义,你可以用svn propsetsvn propedit(见“操作属性”一节)创建和修改这个属性。它可以设置到任何版本化的路经,它的值是一个多行的子目录,可选的修订版本标记和完全有效的Subversion版本库URL的列表(相对于设置属性的版本化目录)。

$ svn propget svn:externals calc
third-party/sounds             http://sounds.red-bean.com/repos
third-party/skins              http://skins.red-bean.com/repositories/skinproj
third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker

     

svn:externals的方便之处是这个属性设置到版本化的路径后,任何人可以从那个目录取出一个工作拷贝,同样得到外部定义的好处。换句话说,一旦一个人努力来定义这些嵌套的工作拷贝检出,其他任何人不需要再麻烦了—Subversion会在原先的工作拷贝检出之后,也会检出外部工作拷贝。

警告

外部定义的相对目标子目录不需要存在于你的或其它用户的系统中—Subversion会在检出工作拷贝时创建这些文件。实际上,你一定不要使用外部定义来产生已经在版本控制的路径。

注意前一个外部定义实例,当有人取出了一个calc目录的工作拷贝,Subversion会继续来取出外部定义的项目。

$ svn checkout http://svn.example.com/repos/calc
A  calc
A  calc/Makefile
A  calc/integer.c
A  calc/button.c
Checked out revision 148.
Fetching external item into calc/third-party/sounds
A  calc/third-party/sounds/ding.ogg
A  calc/third-party/sounds/dong.ogg
A  calc/third-party/sounds/clang.ogg
…
A  calc/third-party/sounds/bang.ogg
A  calc/third-party/sounds/twang.ogg
Checked out revision 14.
Fetching external item into calc/third-party/skins
…

     

如果你希望修改外部定义,你可以使用普通的属性修改子命令,当你提交一个svn:externals属性修改后,当你运行svn update时,Subversion会根据修改的外部定义同步检出的项目,同样的事情也会发生在别人更新他们的工作拷贝接受你的外部定义修改时。

提示

因为svn:externals的值是多行的,所以我们强烈建议使用svn propedit,而不是使用svn propset

提示

你一定要要考虑在所有的外部定义中使用明确的修订版本,这样做意味着你已经决定了何时拖出外部信息不同的快照,和精确的拖出哪个快照。除了不会受到第三方版本库的意外修改的影响以外,当你的工作拷贝回溯到以前的版本库时,使用明确的修订版本号会让外部定义回到以前的那个修订版本,也意味着外部定义的工作拷贝更新会匹配以前修订版本的样子。对于软件项目,这可能是编译复杂代码基的老快照成功和失败的区别。

svn status命令也认识外部定义,会为外部定义的子目录显示X状态码,然后迭代这些子目录来显示外部项目的子目录状态信息。

Subversion目前对外部定义的支持可能会引起误导,首先,一个外部定义只可以指向目录,而不是文件。第二,外部定义不可以指向相对路径(如../../skins/myskin)。第三,通过外部定义创建的工作拷贝与主工作拷贝没有连接起来(与设置svn:externals属性的工作拷贝的版本库),所以Subversion会以不关联的工作拷贝操作。所以举个例子,如果你希望提交一个或多个外部定义的拷贝,你必须在这些工作拷贝显示的运行svn commit—对主工作拷贝的提交不会迭代到外部定义的部分。

另外,因为定义本身使用绝对路径,移动和拷贝路径他们附着的路径不会影响他们作为外部的检出(尽管相对的本地目标子目录会这样,当然,根据重命名的目录改变)。在特定情形下这看起来有些迷惑—甚至让人沮丧。举个例子,你的顶级目录叫作my-project,你在它的子目录(my-project/some-dir)创建了一个外部定义,而这个外部定义指向的是另一个子目录(my-project/external-dir)的最新版本。

$ svn checkout http://svn.example.com/projects .
A    my-project
A    my-project/some-dir
A    my-project/external-dir
…
Fetching external item into 'my-project/some-dir/subdir'
Checked out external at revision 11.
Checked out revision 11.
$ svn propget svn:externals my-project/some-dir
subdir http://svn.example.com/projects/my-project/external-dir
$

     

现在你使用svn move将目录my-project改名,此刻,你的外部定义还是指向my-project目录,即使这个目录已经不存在了。

$ svn move -q my-project renamed-project
$ svn commit -m "Rename my-project to renamed-project."
Deleting       my-project
Adding         my-renamed-project
Committed revision 12.
$ svn update
Fetching external item into 'renamed-project/some-dir/subdir'
svn: Target path does not exist
$

     

当然,如果版本库存在多种URL模式时,使用绝对URL来引用外部定义会导致问题。例如,如果你的Subversion服务器已经配置为任何用户可以使用http://或https://检出,但是只能通过https://提交,你现在有了一个很有趣的问题。如果你的外部定义使用http://形式,则你不能从这个工作拷贝提交任何内容。另一方面,如果他们使用https://方式的URL,任何因为不支持https://的客户使用http://检出的工作拷贝不能得到外部项目。也需要意识到,如果你需要重定位你的工作拷贝(使用svn switch –relocate),外部定义不会重新定位。

最后,你或许经常希望svn子命令不会识别或其它作为外部定义处理的结果的外部工作拷贝上的操作,在这种情况下,你可以对子命令使用–ignore-externals选项。