﻿<chapter id="svn.intro">

  <title>介绍</title>

  <simplesect>
    <para>版本控制是管理信息变化的艺术。对于经常对软件改来改去的程序员来说，它早就是重要的工具。但是版本控制软件的价值已远远超出软件开发的领域。总是可以看到人们使用计算机管理易变的信息，这正是版本控制的生存之道，也是Subversion表演的舞台。</para>

    <para>本章综述了Subversion的基本情况&mdash;什么是Subversion？Subversion能做什么？从哪里能获得Subversion？</para>

  </simplesect>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.whatis">

    <title>Subversion是什么？</title>
      
    <para>Subversion是一个自由/开源的版本控制系统。也就是说，在Subversion管理下，文件和目录可以超越时空。Subversion将文件存放在中心<firstterm>版本库</firstterm>里。这个版本库很像一个普通的文件服务器，不同的是，它可以记录每一次文件和目录的修改情况。于是我们就可以籍此将数据回复到以前的版本，并可以查看数据的更改细节。正因为如此，许多人将版本控制系统当作一种神奇的<quote>时间机器</quote>。</para>
    
    <para>Subversion的版本库可以通过网络访问，从而使用户可以在不同的电脑上进行操作。从某种程度上来说，允许用户在各自的空间里修改和管理同一组数据可以促进团队协作。因为修改不再是单线进行（单线进行也就是必须一个一个进行），开发进度会进展迅速。此外，由于所有的工作都已版本化，也就不必担心由于错误的更改而影响软件质量&mdash;如果出现不正确的更改，只要撤销那一次更改操作即可。</para>

    <para>某些版本控制系统本身也是软件配置管理（SCM）系统，这种系统经过精巧的设计，专门用来管理源代码树，并且具备许多与软件开发有关的特性&mdash;比如，对编程语言的支持，或者提供程序构建工具。不过Subversion并不是这样的系统。它是一个通用系统，可以管理<emphasis>任何</emphasis>类型的文件集。对你来说，这些文件这可能是源程序&mdash;而对别人，则可能是一个货物清单或者是数字电影。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.history">

    <title>Subversion的历史</title>

    <para>早在2000年，CollabNet, Inc. (<ulink url="http://www.collab.net"/>)就开始寻找CVS替代产品的开发人员。CollabNet提供了一个名为CollabNet企业版（CEE）
      <footnote>
        <para>也有一个针对小团队的CollabNet团队版（CTE）。</para>
    </footnote>
    的协作软件套件。这个软件套件的一个组成部分就是版本控制系统。尽管CEE在最初采用了CVS作为其版本控制系统，但是CVS的局限性从一开始就很明显，CollabNet知道，迟早要找到一个更好的替代品。遗憾的是，CVS之所以成为开源世界<foreignphrase>事实上的</foreignphrase>标准，很大程度上是因为<emphasis>没有</emphasis>更好的替代品，至少是没有可以自由使用的替代品。所以CollabNet决定从头编写一个新的版本控制系统，这个系统保留CVS的基本思想，但是要修正其中的错误和不合理的特性。</para>

    <para>2000年2月，他们联系到<citetitle>Open Source Development with CVS</citetitle>(Coriolis, 1999)的作者Karl Fogel，并且询问他是否希望为这个新项目工作。巧合的是，当时Karl正在与朋友Jim Blandy讨论设计一个新的版本控制系统。1995年时，他们两人曾经开办了一个提供CVS支持的公司Cyclic Software，尽管他们最终卖掉了公司，但还是天天使用CVS进行日常工作。在使用CVS时的挫折促使Jim认真的思考如何管理版本化的数据，并且当时他不仅使用了<quote>Subversion</quote>这个名字，并且已经完成了Subversion版本库的最初设计。所以当CollabNet提出邀请的时候，Karl马上同意为这个项目工作，同时Jim也使他的雇主&mdash;Red Hat软件公司&mdash;允许他到这个项目工作，并且没有限定最终的期限。CollabNet雇佣了Karl和Ben Collins Sussman，详细设计工作从三月开始，在Behlendorf 、CollabNet、Jason Robbins和Greg Stein（当时是一个独立开发者，活跃在WebDAV/DeltaV系统规范制订工作中）恰到好处的激励下，Subversion很快吸引了许多活跃的开发者，结果使得许多具有CVS经验的人们很乐于为这个项目做些事情。</para>

    <para>最初，设计小组设定了一些简单的开发目标。他们不想在版本控制方法学中开垦处女地，他们只是希望修正CVS。他们决定Subversion应符合CVS的特性，并保留相同的开发模型，但不再重复CVS的一些显著缺陷。尽管Subversion并不需要成为CVS的完全替代品，但它应该与CVS保持足够的相似性，以使CVS用户可以轻松的转移到Subversion上。</para>

    <para>经过14个月的编码，2001年8月31日，Subversion能够<quote>自己管理自己</quote>了，开发者停止使用CVS保存Subversion的代码，而使用Subversion本身。</para>

    <para>当CollabNet启动了这个项目，并且一直提供了大量的工作支持（它为一些全职的Subversion开发者提供薪水），Subversion像其它许多开源项目一样，被松散的、透明的规则管理着，这样的规则激励着知识界的精英们。CollabNet的版权许可证完全符合Debian的自由软件方针。也就是说，任何人都可以根据自己的意愿自由的下载、修改和重新发布Subversion，不需要CollabNet或其他人的授权。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.features">

    <title>Subversion的特性</title>

    <para>Subversion将很多新特性引入版本控制领域。在讲解这些特性时，我们会经常性的与CVS进行对比，以说明Subversion比CVS高明在哪里。如果不熟悉CVS，了解所有Subversion的特性会有一定的困难。而如果根本就不熟悉版本控制，你就只有干瞪眼的份儿了。因此，最好首先阅读一下<xref linkend="svn.basic"/>，这一章简单介绍了一些版本控制的基本思想和概念。</para>

    <para>Subversion支持：</para>

    <variablelist>
      <varlistentry>
        <term>版本化的目录</term>
        <listitem>
          <para>CVS只能跟踪单个文件的变更历史，但是Subversion实现的<quote>虚拟</quote>版本化文件系统则可以跟踪目录树的变更。在Subversion中，文件<emphasis>和</emphasis>目录都是版本化的。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>真实的版本历史</term>
        <listitem>
          <para>由于只能跟踪单个文件的变更，CVS无法支持如文件拷贝和改名这些常见的操作&mdash;这些操作改变了目录的内容。同样，在CVS中，目录下的文件只要名字相同即拥有相同的历史，即使这些同名文件在历史上毫无关系。而在Subversion中，可以对文件或目录进行增加、拷贝和改名操作，也解决了同名而无关的文件之间的历史联系问题。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>原子提交</term>
        <listitem>
          <para>一系列相关的更改，要么全部提交到版本库，要么一个也不提交。这样用户就可以将相关的更改组成一个逻辑整体，防止出现部分修改而另一部分未修改的情况提交到版本库中。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>版本化的元数据</term>
        <listitem>
          <para>每一个文件和目录都有自己的一组属性&mdash;键和它们的值。可以根据需要建立并存储任何键/值对。和文件本身的内容一样，属性也在版本控制之下。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>可选的网络层</term>
        <listitem>
          <para>Subversion在版本库访问的实现上具有较高的抽象程度，利于人们实现新的网络访问机制。Subversion可以作为一个扩展模块嵌入到Apache之中。这种方式在稳定性和交互性方面有很大的优势，可以直接使用服务器的成熟技术&mdash;认证、授权和传输压缩等等。此外，Subversion自身也实现了一个轻型的，可独立运行的服务器软件。这个服务器使用了一个特定的协议，这个协议可以轻松的用SSH封装。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>一致的数据操作</term>
        <listitem>
          <para>Subversion用一个二进制差异算法描述文件的变化，对于文本（可读）和二进制（不可读）文件其操作方式是一致的。这两种类型的文件压缩存储在版本库中，而差异信息则在网络上双向传递。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>高效的分支和标签操作</term>
        <listitem>
          <para>在Subversion中，分支与标签操作的开销与工程的大小无关。Subversion的分支和标签操作用只是一种类似于硬链接的机制拷贝整个工程。因而这些操作通常只会花费很少且相对固定的时间。
          </para>
        </listitem>
      </varlistentry>
      
      <varlistentry>
        <term>可修改性</term>
        <listitem>
          <para>Subversion没有历史负担，它以一系列优质的共享C程序库的方式实现，具有定义良好的API。这使得Subversion非常容易维护，和其它语言的互操作性很强。</para>
        </listitem>
      </varlistentry>

    </variablelist>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.architecture">

    <title>Subversion的架构</title>

    <para><xref linkend="svn.intro.architecture.dia-1"/>给出了Subversion设计总体上的<quote>俯视图</quote>。</para>
    
    <figure id="svn.intro.architecture.dia-1">
      <title>Subversion的架构</title>
      <graphic fileref="images/ch01dia1.png"/>
    </figure>

    <para>图中的一端是保存所有在版本控制下数据的Subversion版本库，另一端是Subvesion的客户端程序，管理着所有在版本控制下数据的本地影射（称为<quote>工作拷贝</quote>），在这两极之间是各种各样的版本库访问（RA）层，某些使用电脑网络通过网络服务器访问版本库，某些则绕过网络服务器直接访问版本库。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.install">

    <title>安装Subversion</title>

    <para>Subversion是基于APR构建的。APR全称为Apache Portable Runtime library，是一个移植性很好的程序库。APR库提供了全部与操作系统相关的操作接口，如磁盘访问、内存管理等等，这使得Subversion自身能够在不加修改的情况下运行于不同的操作系统之上。Subversion对APR的依赖并不意味着必须使用Apache作为它的网络服务器程序，相反，Apache只是Subversion支持的网络服务器程序之一。APR是一个独立的程序库，任何应用程序都可以使用它（Apache也是基于它开发的）。这就是说，Subversion能够在所有可运行Apache服务器的操作系统上运转，如Windows、Linux、各种BSD、Mac OS X、Netware等等。</para>

    <para>最简单的安装Subversion的方法就是下载与你的操作系统对应的二进制程序包。在Subversion的网站（<ulink url="http://subversion.tigris.org"/>）上通常可以找到由志愿者提供下载的程序包。在这个网站上，会提供微软操作系统上的图形化应用程序安装包。而对于类Unix系统，则可以使用其自身的程序包系统（PRMs、DEBs、ports tree等等）来获取Subversion。</para>

    <para>此外，还可以通过编译源代码包直接生成Subversion程序。首先，从Subversion网站下载最新的源代码包，然后解压缩。最后，根据<filename>INSTALL</filename>文件的指示进行编译。需要注意的是，正式发布的源代码包中包含了构建命令行客户端工具所需的全部内容（如apr，apr-util和neno库），可以直接进行编译。但是一些可选的组件则依赖于其它一些程序库，如Berkeley DB和Apache httpd。因此，如果想要进行完整的编译，请根据<filename>INSTALL</filename>文件中的内容确认这些程序库是否可用。如果想为Subversion做一些工作，可以使用客户端程序取得最新的源代码，这部分内容参见<xref linkend="svn.developer.contrib.get-code"/>。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.components">

    <title>Subversion的组件</title>
    
    <para>安装好的Subversion有几个几个部分组成，本节将简单的介绍一下这些组件。下文的描述或许过于简略，不易理解，不过不用担心，本书后面章节中会用<emphasis>更多的</emphasis>内容来详细阐述这些组件。</para>

    <variablelist>
      <varlistentry>
        <term>svn</term>
        <listitem>
          <para>命令行客户端程序。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>svnversion</term>
        <listitem>
          <para>此工具用来显示工作副本的状态（用术语来说，就是当前项目的修订版本）。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>svnlook</term>
        <listitem>
          <para>查看Subversion版本库的工具。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>svnadmin</term>
        <listitem>
          <para>建立、调整和修复Subversion版本库的工具。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>svndumpfilter</term>
        <listitem>
          <para>过滤Subversion版本库转储数据流的工具。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>mod_dav_svn</term>
        <listitem>
          <para>Apache HTTP服务器的一个插件，使版本库可以通过网络访问。</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>svnserve</term>
        <listitem>
          <para>一个单独运行的服务器程序，可以作为守护进程或由SSH调用。这是另一种使版本库可以通过网络访问的方式。</para>
        </listitem>
      </varlistentry>
    </variablelist>

    <para>如果已经正确完成了Subversion的安装，我们就可以开始我们的学习之旅了。在后面的两章中，我们将讲解如何使用Subversion的客户端程序<command>svn</command>。</para>

  </sect1>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.intro.quickstart">

    <title>快速入门</title>
    
    <para>本书所采用的<quote>由上至下</quote>的方式讲述Subversion，某些读者可能不习惯以这样的方式学习一种新的技术。为此，本节对Subversion进行了一个简短的介绍，并为那些喜欢<quote>自下而上</quote>学习的读者提供一个实战的机会。下面的示例可以引导那些喜欢<quote>自下而上</quote>学习方式的读者走入Subversion的大门。同时，我们也会在相应的位置给出书中详细叙述部分的链接。</para>

    <para>如果读者还不熟悉版本控制，以及在Subversion和CVS中使用的<quote>拷贝-修改-合并</quote>模型这些基础的概念，那么建议在进一步学习之前，首先阅读<xref linkend="svn.basic"/>。</para>

    <note>
      <para>运行下面的例子需要首先正确安装Subversion客户端程序<command>svn</command>以及管理工具<command>svnadmin</command>，并且必须为1.2或更新版本的Subversion程序（可以运行<command>svn --version</command>来检查Subversion的版本）。</para>
    </note>

    <para>Subversion的所有版本化数据都储存在中心版本库中。因此首先，我们需要创建一个版本库：</para>

    <screen>
$ svnadmin create /path/to/repos
$ ls /path/to/repos
conf/  dav/  db/  format  hooks/  locks/  README.txt
</screen>

    <para>这个命令创建了一个新目录<filename>/path/to/repos</filename>，并在其中创建了一个Subversion版本库。这个目录里主要保存了一些数据库文件（还有其它一些文件），而不像CVS那样保存着版本化的文件。需要更多版本库创建和维护方面的内容，参见<xref linkend="svn.reposadmin"/>。</para>

    <para>在Subversion没有<quote>项目</quote>的概念。Subversion的版本库只是一个虚拟的版本化文件系统，可以存放你想要存放的任何文件。有些管理员喜欢为每个项目建立一个独立的版本库，而另外一些管理员则喜欢将多个项目存放到同一个版本库的不同目录里。这两种方式各有各的优点，关于这方面内容的叙述，参见<xref linkend="svn.reposadmin.projects.chooselayout"/>。不论是哪一种方式，版本库都只是负责管理文件和目录，而<quote>项目</quote>则是人为指定的概念。因此，尽管本书中遍布着项目这个词，但是请记住我们只不过是在谈论版本库中的某些特定目录（或者是一组目录）。</para>

    <para>在这个例子中，我们假定已经有一些需要导入到Subversion版本库的条目（一组文件和目录）。接下来，我们需要把这些条目整理到一个名为<filename>myproject</filename>的目录（或者其它任意目录）里。在这个目录下，创建三个顶级子目录：<filename>branches</filename>、<filename>tags</filename>和<filename>trunk</filename>，这样做的原因将在后文中阐述。之后，将所有需版本化的数据保存到<filename>trunk</filename>目录下，同时保持<filename>branches</filename>和<filename>tags</filename>目录为空：</para>

    <screen>
/tmp/myproject/branches/
/tmp/myproject/tags/
/tmp/myproject/trunk/
                     foo.c
                     bar.c
                     Makefile
                     &hellip;
</screen>

    <para><filename>branches</filename>、<filename>tags</filename>和<filename>trunk</filename>这三个子目录不是Subversion必须的。但这样做是Subversion的习惯用法，我们还是遵守这个约定吧。</para>

    <para>准备好了数据之后，就可以使用<command>svn import</command>命令（参见<xref linkend="svn.tour.other.import"/>）将其导入到版本库中：
    </para>

    <screen>
$ svn import /tmp/myproject file:///path/to/repos/myproject -m "initial import"
Adding         /tmp/myproject/branches
Adding         /tmp/myproject/tags
Adding         /tmp/myproject/trunk
Adding         /tmp/myproject/trunk/foo.c
Adding         /tmp/myproject/trunk/bar.c
Adding         /tmp/myproject/trunk/Makefile
&hellip;
Committed revision 1.
$ 
</screen>

    <para>现在版本库中已经保存了目录中的数据。如前所述，直接查看版本库是看不到文件和目录的；它们存放在数据库之中。但是版本库的虚拟文件系统中则包含了一个名为<filename>myproject</filename>的顶级目录，其中依此保存了所有的数据。
   </para>

    <para>注意我们在一开始创建的那个<filename>/tmp/myproject</filename>目录并没有改变，Subversion并不在意它（事实上，完全可以删除这个目录）。要开始使用版本库数据，我们还要创建一个新的用于存储数据的<quote>工作拷贝</quote>，这是一个私有工作区。从Subversion版本库里<quote>检出</quote>一个<filename>myproject/trunk</filename>目录工作拷贝的操作如下：
   </para>

    <screen>
$ svn checkout file:///path/to/repos/myproject/trunk myproject
A  myproject/foo.c
A  myproject/bar.c
A  myproject/Makefile
&hellip;
Checked out revision 1.
</screen>
    <para>现在，在<filename>myproject</filename>目录下生成了一个版本库数据的独立拷贝。我们可以在这个工作拷贝中编辑文件，并将修改提交到版本库中。</para>

    <itemizedlist>
      <listitem>
        <para>进入工作拷贝目录，编辑某个文件的内容。</para>
      </listitem>
      <listitem>
        <para>运行<command>svn diff</command>以标准差别格式查看修改的内容。</para>
      </listitem>
      <listitem>
        <para>运行<command>svn commit</command>将更改提交到版本库中。</para>
      </listitem>
      <listitem>
        <para>运行<command>svn update</command><quote>更新</quote>工作拷贝。</para>
      </listitem>
    </itemizedlist>

    <para>完整的工作拷贝操作指南，请参见<xref linkend="svn.tour"/>。</para>

    <para>现在，Subversion版本库可以通过网络方式访问。参考<xref linkend="svn.serverconfig"/>，了解不同服务器软件的使用以及配置方法。</para>

  </sect1>


</chapter>

<!--
local variables: 
sgml-parent-document: ("book.xml" "chapter")
end:
-->
