使用API

使用Subversion库API开发应用看起来相当的直接,所有的公共头文件放在源文件的subversion/include目录,从源代码编译和安装Subversion本身,需要这些头文件拷贝到系统位置。这些头文件包括了所有用户可以访问的功能和类型。

你首先应该注意Subversion的数据类型和方法是命名空间保护的,每一个公共Subversion对象名以svn_开头,然后紧跟一个这个对象定义(如wcclientfs其他)所在的库的简短编码,然后是一个下划线(_)和后面的对象名称。半公开的方法(库使用,但是但库之外代码不可以使用并且只可以在库自己的目录看到)与这个命名模式不同,并不是库代码之后紧跟一个下划线,他们是用两个下划线(__)。给定源文件的私有方法没有特殊前缀,使用static声明。当然,一个编译器不会关心命名习惯,只是用来区分给定方法或数据类型。

Apache可移植运行库

伴随Subversion自己的数据类型,你会看到许多apr_开头的数据类型引用—来自Apache可移植运行库(APR)的对象。APR是Apache可移植运行库,源自为了服务器代码的多平台性,尝试将不同的操作系统特定字节与操作系统无关代码隔离。结果就提供了一个基础API的库,只有一些适度区别—或者是广泛的—来自各个操作系统。Apache HTTP服务器很明显是APR库的第一个用户,Subversion开发者立刻发现了使用APR库的价值。意味着Subversion没有操作系统特定的代码,也意味着Subversion客户端可以在Server存在的平台编译和运行。当前这个列表包括,各种类型的Unix、Win32、OS/2和Mac OS X。

除了提供了跨平台一致的系统调用, [37] APR给Subversion对多种数据类型有快速的访问,如动态数组和哈希表。Subversion在代码中广泛使用这些类型,但是或许大多数普遍深入的APR数据类型可以在所有的Subversion的API原型中发现,是apr_pool_t—APR内存池,Subversion使用内部缓冲池用来进行内存分配(除非外部库在API传递参数时需要一个不同的内存管理模式), [38] 而且一个人如果针对Subversion的API编码不需要做同样的事情,他们可以在需要时给API提供缓冲池,这意味着Subversion的API使用者也必须链接到APR,必须调用apr_initialize()来初始化APR字系统,然后必须得到一个缓冲池用来进行Subversion的API调用。详情见“使用内存池编程”一节

URL和路径需求

因为分布式版本控制操作是Subversion存在的重点,有意义来关注一下国际化(i18n)支持。毕竟,当“分布式”或许意味着“横跨办公室”,它也意味着“横跨全球”。为了更容易一点,Subversion的所有公共接口只接受路径参数,这些参数是传统的,使用UTF-8编码。这意味着,举个例子,任何新的使用libsvn_client接口客户端库,在把这些参数传递给Subversion库前,需要首先将路径从本地代码转化为UTF-8代码,然后将Subversion传递回来的路径转换为本地代码,很幸运,Subversion提供了一组任何程序可以使用的转化方法(见subversion/include/svn_utf.h)。

同样,Subversion的API需要所有的URL参数是正确的URI编码,所以,我们不会传递file:///home/username/My File.txt作为My File.txt的URL,而会传递file:///home/username/My%20File.txt。再次,Subversion提供了一些你可以使用的助手方法—svn_path_uri_encodesvn_path_uri_decode,分别用来URI的编码和解码。

使用C和C++以外的语言

除C语言以外,如果你对使用其他语言结合Subversion库感兴趣—如Python脚本或是Java应用—Subversion通过简单包裹生成器(SWIG)提供了最初的支持。Subversion的SWIG绑定位于subversion/bindings/swig,并且慢慢的走向成熟进入可用状态。这个绑定允许你直接调用Subversion的API方法,使用包裹器会把脚本数据类型转化为Subversion需要的C语言库类型。

通过语言绑定访问Subversion的API有一个明显的好处—简单性。通常来讲,Python和Perl之类的语言比C和C++更加的灵活和简单,这些语言的高级数据类型和上下文驱动类型更加易于处理用户提供的信息,就像你知道的,人们精于把程序搞坏,脚本语言可以更优雅的处理这些错误信息,当然,灵活性经常带来性能的损失,这就是为什么使用紧密优化的,C基础的接口和库组件,然后与一种高效的、灵活的绑定语言,是这样的吸引人。

让我们看看Subversion的Python SWIG绑定的实例,这个例子和前面的例子作同样的事,注意比较方法的长度和复杂性!

例 8.2. 使用Python处理版本库层

from svn import fs
import os.path

def crawl_filesystem_dir (root, directory, pool):
  """Recursively crawl DIRECTORY under ROOT in the filesystem, and return
  a list of all the paths at or below DIRECTORY.  Use POOL for all 
  allocations."""

  # Get the directory entries for DIRECTORY.
  entries = fs.dir_entries(root, directory, pool)

  # Initialize our returned list with the directory path itself.
  paths = [directory]

  # Loop over the entries
  names = entries.keys()
  for name in names:
    # Calculate the entry's full path.
    full_path = os.path.join(basepath, name)

    # If the entry is a directory, recurse.  The recursion will return
    # a list with the entry and all its children, which we will add to
    # our running list of paths.
    if fs.is_dir(fsroot, full_path, pool):
      subpaths = crawl_filesystem_dir(root, full_path, pool)
      paths.extend(subpaths)

    # Else, it is a file, so add the entry's full path to the FILES list.
    else:
      paths.append(full_path)

  return paths

前面C语言的实现确实有一点长,另外C语言的例行公事就是必须关注内存使用,并且需要使用自定义的数据类型来表示条目的哈希值和路径列表。Python有哈希(叫做“dictionaries”)并且列表示内置数据类型,并提供了许多操作这些类型的好方法。而且因为Python使用引用计数来进行垃圾收集,这种语言的用户不需要麻烦自己去分配和回收内存。

在本章的前面小节,我们提到libsvn_client接口,并且解释了它存在的唯一目的是为了简化编写Subversion客户端的过程,下面是一个如何同SWIG绑定访问库的简短例子,简单的几句Python代码,我们就可以检出一个完全功能的Subversion工作拷贝!

例 8.3. 一段检出工作拷贝的简单脚本

#!/usr/bin/env python
import sys
from svn import util, _util, _client

def usage():
  print "Usage: " + sys.argv[0] + " URL PATH\n"
  sys.exit(0)

def run(url, path):
  # Initialize APR and get a POOL.
  _util.apr_initialize()
  pool = util.svn_pool_create(None)

  # Checkout the HEAD of URL into PATH (silently)
  _client.svn_client_checkout(None, None, url, path, -1, 1, None, pool)

  # Cleanup our POOL, and shut down APR.
  util.svn_pool_destroy(pool)
  _util.apr_terminate()

if __name__ == '__main__':
  if len(sys.argv) != 3:
    usage()
  run(sys.argv[1], sys.argv[2])

非常不幸,Subversion的语言绑定缺乏对核心Subversion模块的关注,但是,使用Python、Perl和Java创建有功能的邦定取得了显著的成就。一旦你的SWIG接口文件正确的配置,对于SWIG支持的语言(我们当前包括的版本有C#、Guile、Java、MzScheme、OCaml、Perl、PHP、Python、Ruby和Tcl)的特定语言绑定的包裹器的生成理论上是非常琐碎的。但是,对复杂API还是需要一些额外的的补充,SWIG需要帮助归纳。对于SWIG的更多信息,见这个项目的网站http://www.swig.org/



[37] Subversion使用尽可能多ANSI系统调用和数据类型。

[38] Neon和Berkeley DB就是这种库的例子。