﻿<chapter id="svn.basic">
  <title>基本概念</title>

  <simplesect>
    <para>本章主要为那些不熟悉版本控制技术的入门者提供一个简单扼要的、非系统的介绍。我们将从版本控制的基本概念开始，随后阐述Subversion的独特理念，并演示一些使用Subversion的例子。</para>
    
    <para>虽然我们在本章中以分享程序源代码作为例子，但是记住Subversion可以管理任何类型的文件集&mdash;它并非是程序员专用的。</para>
  </simplesect>
  
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.basic.repository">
    <title>版本库</title>  
    
    <para>Subversion是一个“中央集权式”的信息共享系统。版本库是Subversion的核心部分，是数据的中央仓库。版本库以文件和目录的典型结构形式&mdash;<firstterm>文件系统树</firstterm>来保存信息。此外，数个<firstterm>客户端</firstterm>（数量不限）连接到Subversion版本库，读取、修改这些文件。客户端之间通过修改数据实现与别人共享信息，通过读取数据实现获取别人共享的信息。<xref linkend="svn.basic.repository.dia-1"/>展示了这种系统的概貌：</para>

    <figure id="svn.basic.repository.dia-1">
      <title>一个典型的客户/服务器系统</title>
      <graphic fileref="images/ch02dia1.png"/>
    </figure>
    
    <para>这有什么意义吗？说了这么多，Subversion听起来和一般的文件服务器没什么不同。事实上，Subversion的版本库的确<emphasis>是</emphasis>一种文件服务器，但不是“一般”的文件服务器。Subversion版本库的特别之处在于，<emphasis>它会记录每一次改变</emphasis>，每一次文件和目录结构的改变，（比如增加、删除和重新布置文件和目录）它都不会放过。</para>

    <para>一般情况下，客户端从版本库中获取的数据是文件系统树中的最新数据。但是客户端也具备查看文件系统树<emphasis>以前</emphasis>任何一个状态的能力。举个例子，客户端有时会对一些历史性问题感兴趣，比如<quote>上星期三时的目录结构是什么样的？</quote>或者<quote>谁最后一个修改了这个文件，都修改了什么？</quote>为了回答类似的问题，<firstterm>版本控制系统</firstterm>产生了。它的主要设计目的即是记录和跟踪数据的变化。
    </para>
  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.basic.vsn-models">
    <title>版本模型</title>

    <para>
    版本控制系统的核心任务是提供协作编辑和数据共享，但是不同的系统使用不同的策略来达到目的。</para>
    
    <!-- =============================================================== -->
    <sect2 id="svn.basic.vsn-models.problem-sharing">
      <title>文件共享的问题</title>
      
      <para>
      所有的版本控制系统都需要解决这样一个基础问题：怎样让系统允许用户共享信息，而不会让他们因意外而互相干扰？版本库里意外覆盖别人的更改非常的容易。</para>

      <para>
      考虑<xref
        linkend="svn.basic.vsn-models.problem-sharing.dia-1"/>的情景，我们有两个共同工作者，Harry和Sally，他们想同时编辑版本库里的同一个文件，如果首先Harry保存它的修改，过了一会，Sally可能凑巧用自己的版本覆盖了这些文件，Harry的更改不会永远消失（因为系统记录了每次修改），Harry所有的修改<emphasis>不会</emphasis>出现在Sally的文件中，所以Harry的工作还是丢失了&mdash;至少是从最新的版本中丢失了&mdash;而且是意外的，这就是我们要明确避免的情况！</para>

      <figure id="svn.basic.vsn-models.problem-sharing.dia-1">
        <title>需要避免的问题</title>
        <graphic fileref="images/ch02dia2.png"/>
      </figure>

      </sect2>
    
    <sect2 id="svn.basic.vsn-models.lock-unlock">
      <title>锁定-修改-解锁 方案</title>
      
      <para>
      许多版本控制系统使用<firstterm>锁定-修改-解锁</firstterm>这种机制解决这种问题，在这样的模型里，在一个时间段里版本库的一个文件只允许被一个人修改。首先在修改之前，Harry要<quote>锁定</quote>住这个文件，锁定很像是从图书馆借一本书，如果Harry锁住这个文件，Sally不能做任何修改，如果Sally想请求得到一个锁，版本库会拒绝这个请求。在Harry结束编辑并且放开这个锁之前，她只可以阅读文件。Harry解锁后，就要换班了，Sally得到自己的轮换位置，锁定并且开始编辑这个文件。<xref
        linkend="svn.basic.vsn-models.lock-unlock.dia-1"/>描述了这样的解决方案。</para>
      
      <figure id="svn.basic.vsn-models.lock-unlock.dia-1">
        <title>锁定-修改-解锁 方案</title>
        <graphic fileref="images/ch02dia3.png"/>
      </figure>
      
      <para>锁定-修改-解锁模型有一点问题就是限制太多，经常会成为用户的障碍：</para>

      <itemizedlist>
        <listitem>
          <para><emphasis>锁定可能导致管理问题。</emphasis>有时候Harry会锁住文件然后忘了此事，这就是说Sally一直等待解锁来编辑这些文件，她在这里僵住了。然后Harry去旅行了，现在Sally只好去找管理员放开锁，这种情况会导致不必要的耽搁和时间浪费。</para>
        </listitem>
        
        <listitem>
          <para><emphasis>锁定可能导致不必要的线性化开发。</emphasis>如果Harry编辑一个文件的开始，Sally想编辑同一个文件的结尾，这种修改不会冲突，设想修改可以正确的合并到一起，他们可以轻松的并行工作而没有太多的坏处，没有必要让他们轮流工作。</para>
        </listitem>
    
        <listitem>
          <para><emphasis>锁定可能导致错误的安全状态。</emphasis>假设Harry锁定和编辑一个文件A，同时Sally锁定并编辑文件B，如果A和B互相依赖，这种变化是必须同时作的，这样A和B不能正确的工作了，锁定机制对防止此类问题将无能为力—从而产生了一种处于安全状态的假相。很容易想象Harry和Sally都以为自己锁住了文件，而且从一个安全，孤立的情况开始工作，因而没有尽早发现他们不匹配的修改。</para>
        </listitem>
      </itemizedlist>

      </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.basic.vsn-models.copy-merge">
      <title>拷贝-修改-合并 方案</title>
      
      <para>
      Subversion，CVS和一些版本控制系统使用<firstterm>拷贝-修改-合并</firstterm>模型，在这种模型里，每一个客户联系项目版本库建立一个个人<firstterm>工作拷贝</firstterm>&mdash;版本库中文件和目录的本地映射。用户并行工作，修改各自的工作拷贝，最终，各个私有的拷贝合并在一起，成为最终的版本，这种系统通常可以辅助合并操作，但是最终要靠人工去确定正误。</para>
      
      <para>
      这是一个例子，Harry和Sally为同一个项目各自建立了一个工作拷贝，工作是并行的，修改了同一个文件A，Sally首先保存修改到版本库，当Harry想去提交修改的时候，版本库提示文件A已经<firstterm>过期</firstterm>，换句话说，A在他上次更新之后已经更改了，所以当他通过客户端请求<firstterm>合并</firstterm>版本库和他的工作拷贝之后，碰巧Sally的修改和他的不冲突，所以一旦他把所有的修改集成到一起，他可以将工作拷贝保存到版本库，<xref
        linkend="svn.basic.vsn-models.copy-merge.dia-1"/>和<xref linkend="svn.basic.vsn-models.copy-merge.dia-2"/>展示了这一过程。</para>

      <figure id="svn.basic.vsn-models.copy-merge.dia-1">
        <title>拷贝-修改-合并 方案</title>
        <graphic fileref="images/ch02dia4.png"/>
      </figure>
      
      <figure id="svn.basic.vsn-models.copy-merge.dia-2">
        <title>拷贝-修改-合并 方案（续）</title>
        <graphic fileref="images/ch02dia5.png"/>
      </figure>

      <para>
      但是如果Sally和Harry的修改<emphasis>交迭</emphasis>了该怎么办？这种情况叫做<firstterm>冲突</firstterm>，这通常不是个大问题，当Harry告诉他的客户端去合并版本库的最新修改到自己的工作拷贝时，他的文件A就会处于冲突状态：他可以看到一对冲突的修改集，并手工的选择保留一组修改。需要注意的是软件不能自动的解决冲突，只有人可以理解并作出智能的选择，一旦Harry手工的解决了冲突&mdash;也许需要与Sally讨论&mdash;它可以安全的把合并的文件保存到版本库。</para>

      <para>
      拷贝-修改-合并模型感觉是有一点混乱，但在实践中，通常运行的很平稳，用户可以并行的工作，不必等待别人，当工作在同一个文件上时，也很少会有交迭发生，冲突并不频繁，处理冲突的时间远比等待解锁花费的时间少。</para>

      <para>
      最后，一切都要归结到一条重要的因素：用户交流。当用户交流贫乏，语法和语义的冲突就会增加，没有系统可以强制用户完美的交流，没有系统可以检测语义上的冲突，所以没有任何证据能够承诺锁定系统可以防止冲突，实践中，锁定除了约束了生产力，并没有做什么事。</para>
      
      <sidebar id="svn.basic.vsn-models.copy-merge.sb-1">
        <title>什么时候锁定是必需的</title>
        
        <para>锁定-修改-解锁模型被认为不利于协作，但有时候锁定会更好。</para>

        <para>拷贝-修改-合并模型假定文件是可以根据上下文合并的：就是版本库的文件主要是以行为基础的文本文件（例如程序源代码）。但对于二进制格式，例如艺术品或声音，在这种情况下，十分有必要让用户轮流修改文件，如果没有线性的访问，有些人的许多工作就最终要被放弃。</para>

        <para>尽管CVS和Subversion一直主要是拷贝-修改-合并系统，它们都意识到了需要锁定一些文件，并且提供这种锁定机制。见<xref linkend="svn.advanced.locking"/>。
        </para>

      </sidebar>


    </sect2>
    
  </sect1>
  
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.basic.in-action">
    <title>Subversion实战</title>
    
    <para>
    是时候从抽象转到具体了，在本小节，我们会展示一个Subversion真实使用的例子。</para>

    <!-- =============================================================== -->
    <sect2 id="svn.basic.in-action.wc">
      <title>工作拷贝</title>
      
      <para>
      你已经阅读过了关于工作拷贝的内容，现在我们要讲一讲客户端怎样建立和使用它。</para>
      
      <para>
      一个Subversion工作拷贝是你本地机器一个普通的目录，保存着一些文件，你可以任意的编辑文件，而且如果是源代码文件，你可以像平常一样编译，你的工作拷贝是你的私有工作区，在你明确的做了特定操作之前，Subversion不会把你的修改与其他人的合并，也不会把你的修改展示给别人，你甚至可以拥有同一个项目的多个工作拷贝。</para>

      <para>
      当你在工作拷贝作了一些修改并且确认它们工作正常之后，Subversion提供了一个命令可以<quote>发布</quote>你的修改给项目中的其他人（通过写到版本库），如果别人发布了各自的修改，Subversion提供了手段可以把这些修改与你的工作目录进行合并（通过读取版本库）。</para>

      <para>
      一个工作拷贝也包括一些由Subversion创建并维护的额外文件，用来协助执行这些命令。通常情况下，你的工作拷贝每一个文件夹有一个以<filename>.svn</filename>为名的文件夹，也被叫做工作拷贝<firstterm>管理目录</firstterm>，这个目录里的文件能够帮助Subversion识别哪一个文件做过修改，哪一个文件相对于别人的工作已经过期了。</para>
      
      <para>
      一个典型的Subversion的版本库经常包含许多项目的文件（或者说源代码），通常每一个项目都是版本库的子目录，在这种安排下，一个用户的工作拷贝往往对应版本库的的一个子目录。</para>
      
      <para>
      举一个例子，你的版本库包含两个软件项目，<literal>paint</literal>和<literal>calc</literal>。每个项目在它们各自的顶级子目录下，见<xref
        linkend="svn.basic.in-action.wc.dia-1"/>。</para>

      <figure id="svn.basic.in-action.wc.dia-1">
        <title>版本库的文件系统</title>
        <graphic fileref="images/ch02dia6.png"/>
      </figure>
      
      <para>
      为了得到一个工作拷贝，你必须<firstterm>检出</firstterm>（<firstterm>check
        out</firstterm>）版本库的一个子树，（术语<quote>check out</quote>听起来像是锁定或者保存资源，实际上不是，只是简单的得到一个项目的私有拷贝），举个例子，你检出 <filename>/calc</filename>，你可以得到这样的工作拷贝：</para>

<screen>
$ svn checkout http://svn.example.com/repos/calc
A    calc/Makefile
A    calc/integer.c
A    calc/button.c
Checked out revision 56.

$ ls -A calc
Makefile  integer.c  button.c  .svn/
</screen>

      <para>
      列表中的A表示Subversion增加了一些条目到工作拷贝，你现在有了一个<filename>/calc</filename>的个人拷贝，有一个附加的目录&mdash;<filename>.svn</filename>&mdash;保存着前面提及的Subversion需要的额外信息。</para>

      <sidebar id="svn.basic.in-action.wc.sb-1">
        <title>版本库的URL</title>

        <para>
        Subversion可以通过多种方式访问&mdash;本地磁盘访问，或各种各样不同的网络协议，但一个版本库地址永远都是一个URL，<xref linkend="svn.basic.in-action.wc.tbl-1"/>描述了不同的URL模式对应的访问方法。</para>

        <table id="svn.basic.in-action.wc.tbl-1">
          <title>版本库访问URL</title>
          <tgroup cols="2">
            <thead>
              <row>
                <entry>模式</entry>
                <entry>访问方法</entry>
              </row>
            </thead>
            <tbody>
              <row>
                <entry><literal>file:///</literal></entry>
                <entry>直接版本库访问（本地磁盘）。</entry>
              </row>
              <row>
                <entry><literal>http://</literal></entry>
                <entry>通过配置Subversion的Apache服务器的WebDAV协议。</entry>
              </row>
              <row>
                <entry><literal>https://</literal></entry>
                <entry>与<literal>http://</literal>相似，但是包括SSL加密。</entry>
              </row>
              <row>
                <entry><literal>svn://</literal></entry>
                <entry>通过<literal>svnserve</literal>服务自定义的协议。</entry>
              </row>
              <row>
                <entry><literal>svn+ssh://</literal></entry>
                <entry>与<literal>svn://</literal>相似，但通过SSH封装。</entry>
              </row>
            </tbody>
          </tgroup>
        </table>

        <para>关于Subversion解析URL的更多信息，见<xref linkend="svn.advanced.reposurls"/>。</para>

      </sidebar>
 
      <para>
      假定你修改了<filename>button.c</filename>，因为<filename>.svn</filename>目录记录着文件的修改日期和原始内容，Subversion可以告诉你已经修改了文件，然而，在你明确告诉它之前，Subversion不会将你的改变公开。将改变公开的操作被叫做提交（<firstterm>committing</firstterm>，或者是<firstterm>checking
        in</firstterm>）修改到版本库。</para>

      <para>
      发布你的修改给别人，你可以使用Subversion的提交（<command>commit</command>）命令：</para>

      <screen>
$ svn commit button.c
Sending        button.c
Transmitting file data .
Committed revision 57.
</screen>

      <para>
      这时你对<filename>button.c</filename>的修改已经提交到了版本库，如果其他人取出了<filename>/calc</filename>的一个工作拷贝，他们会看到这个文件最新的版本。</para>

      <para>
      假设你有个合作者，Sally，她和你同时取出了<filename>/calc</filename>的一个工作拷贝，你提交了你对<filename>button.c</filename>的修改，Sally的工作拷贝并没有改变，Subversion只在用户要求的时候才改变工作拷贝。</para>

      <para>
      要使项目最新，Sally可以要求Subversion<firstterm>更新</firstterm>她的工作备份，通过使用更新（<command>update</command>）命令，将结合你和所有其他人在她上次更新之后的改变到她的工作拷贝。</para>

      <screen>
$ pwd
/home/sally/calc

$ ls -A 
.svn/ Makefile integer.c button.c

$ svn update
U    button.c
Updated to revision 57.
</screen>

      <para>
      <command>svn update</command>命令的输出表明Subversion更新了<filename>button.c</filename>的内容，注意，Sally不必指定要更新的文件，subversion利用<filename>.svn</filename>以及版本库的进一步信息决定哪些文件需要更新。</para>
      
    </sect2>
    
    
    <!-- =============================================================== -->
    <sect2 id="svn.basic.in-action.revs">
      <title>修订版本</title>

      <para>
      一个<command>svn commit</command>操作可以作为一个原子事务操作发布任意数量文件和目录的修改，在你的工作拷贝里，你可以改变文件内容、删除、改名和拷贝文件和目录，然后作为一个整体提交。</para>

      <para>
       在版本库中，每一次提交被当作一次原子事务操作：要么所有的改变发生，要么都不发生，Subversion努力保持原子性以应对程序错误、系统错误、网络问题和其他用户行为。</para>

      <para>
      每当版本库接受了一个提交，文件系统进入了一个新的状态，叫做一次修订（<firstterm>revision</firstterm>），每一个修订版本被赋予一个独一无二的自然数，一个比一个大，初始修订号是0，只创建了一个空目录，没有任何内容。</para>
      
      <para>
      <xref linkend="svn.basic.in-action.revs.dia-1"/>可以更形象的描述版本库，想象有一组修订号，从0开始，从左到右，每一个修订号有一个目录树挂在它下面，每一个树好像是一次提交后的版本库<quote>快照</quote>。</para>
      
      <figure id="svn.basic.in-action.revs.dia-1">
        <title>版本库</title>
        <graphic fileref="images/ch02dia7.png"/>
      </figure>

      <sidebar>
        <title>全局修订号</title>
         
        <para>
        不像其他版本控制系统，Subversion的修订号是针对整个<emphasis>目录树</emphasis>的，而不是单个文件。每一个修订号代表了一次提交后版本库整个目录树的特定状态，另一种理解是修订号N代表版本库已经经过了N次提交。当Subversion用户讨论<quote><filename>foo.c</filename>的修订号5</quote>时，他们的实际意思是<quote>在修订号5时的<filename>foo.c</filename></quote>。需要注意的是，修订号N和M并<emphasis>不</emphasis>表示一个文件需要不同。因为CVS使用每一个文件一个修订号的策略，CVS用户可能希望察看<xref linkend="svn.forcvs"/>来得到更多细节。</para>
      </sidebar>

      <para>
      需要特别注意的是，工作拷贝并不一定对应版本库中的单个修订版本，他们可能包含多个修订版本的文件。举个例子，你从版本库检出一个工作拷贝，最近的修订号是4：</para>

      <screen>
calc/Makefile:4
     integer.c:4
     button.c:4
</screen>

      <para>
      此刻，工作目录与版本库的修订版本4完全对应，然而，你修改了<filename>button.c</filename>并且提交之后，假设没有别的提交出现，你的提交会在版本库建立修订版本5，你的工作拷贝会是这个样子的：</para>

      <screen>
calc/Makefile:4
     integer.c:4
     button.c:5
</screen>

      <para>
      假设此刻，Sally提交了对<filename>integer.c</filename>的修改，建立修订版本6，如果你使用<command>svn update</command>来更新你的工作拷贝，你会看到：</para>

      <screen>
calc/Makefile:6
     integer.c:6
     button.c:6
</screen>

      <para>
      Sally对<filename>integer.c</filename>的改变会出现在你的工作拷贝，你对<filename>button.c</filename>的改变还在，在这个例子里，<filename>Makefile</filename>在4、5、6修订版本都是一样的，但是Subversion会把他的<filename>Makefile</filename>的修订号设为6来表明它是最新的，所以你在工作拷贝顶级目录作一次干净的更新，会使得所有内容对应版本库的同一修订版本。</para>

    </sect2>
    
    
    <!-- =============================================================== -->
    <sect2 id="svn.basic.in-action.track-repos">
      <title>工作拷贝怎样追踪版本库</title>
      
      <para>
      对于工作拷贝的每一个文件，Subversion在管理区域<filename>.svn/</filename>记录两项关键的信息：</para>
      
      
      <itemizedlist>
        <listitem>
          <para>
          工作文件所作为基准的修订版本（叫做文件的<firstterm>工作修订版本</firstterm>）和</para>
        </listitem>
        
        <listitem>
          <para>一个本地拷贝最后更新的时间戳。</para>
        </listitem>
      </itemizedlist>

      <para>给定这些信息，通过与版本库通讯，Subversion可以告诉我们工作文件是处与如下四种状态的那一种：</para>

      <variablelist>
        <varlistentry>
          <term>未修改且是当前的</term> 

          <listitem>
            <para>文件在工作目录里没有修改，在工作修订版本之后没有修改提交到版本库。<command>svn
              commit</command>操作不做任何事情，<command>svn update</command>不做任何事情。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>本地已修改且是当前的</term>

          <listitem>
            <para>在工作目录已经修改，从基本修订版本之后没有修改提交到版本库。本地修改没有提交，因此<command>svn commit</command>会成功的提交，<command>svn update</command>不做任何事情。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>未修改且不是当前的了</term> 

          <listitem>
            <para>
            这个文件在工作目录没有修改，但在版本库中已经修改了。这个文件最终将更新到最新版本，成为当时的公共修订版本。<command>svn
              commit</command>不做任何事情，<command>svn update</command>将会取得最新的版本到工作拷贝。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>本地已修改且不是最新的</term>

          <listitem>
            <para>
            这个文件在工作目录和版本库都得到修改。一个<command>svn
              commit</command>将会失败，这个文件必须首先更新，<command>svn update</command>命令会合并公共和本地修改，如果Subversion不可以自动完成，将会让用户解决冲突。</para>
          </listitem>
        </varlistentry>
      </variablelist>
      
      
      <para>
      这看起来需要记录很多事情，但是<command>svn status</command>命令可以告诉你工作拷贝中文件的状态，关于此命令更多的信息，请看<xref linkend="svn.tour.cycle.examine.status" />。</para>
     
    </sect2>
    
    <!-- =============================================================== -->
    <sect2 id="svn.basic.in-action.mixedrevs">
      <title>混合修订版本的工作拷贝</title>

      <para>作为一个普遍原理，Subversion努力做到尽可能的灵活，一个特殊的灵活特性就是让工作拷贝包含不同工作修订版本号的文件和目录，不幸的是，这个灵活性会让许多新用户感到迷惑。如果上一个混合修订版本的例子让你感到困惑，这里是一个为何有这种特性和如何利用这个特性的基础介绍。</para>

      <sect3 id="svn.basic.in-action.mixedrevs.update-commit">
        <title>更新和提交是分开的</title>

        <para>Subversion有一个基本原则就是一个<quote>push</quote>动作不会导致<quote>pull</quote>，或者相反的过程，因为你准备好了提交你的修改并不意味着你已经准备好了从其他人那里接受修改。如果你的新的修改还在进行，<command>svn update</command>将会优雅的合并版本库的修改到你的工作拷贝，而不会强迫将修改发布。
       </para>

        <para>这个规则的主要副作用就是工作拷贝需要记录额外的信息来追踪混合修订版本，并且也需要能容忍这种混合，当目录本身也是版本化的时候情况更加复杂。
       </para>

        <para>举个例子，假定你有一个工作拷贝，修订版本号是10。你修改了<filename>foo.html</filename>，然后执行<command>svn commit</command>，在版本库里创建了修订版本15。当成功提交之后，许多用户希望工作拷贝完全变成修订版本15，但是事实并非如此。修订版本从10到15会发生任何修改，可是客户端在运行<command>svn
          update</command>之前不知道版本库发生了怎样的改变，<command>svn commit</command>不会拖出任何新的修改。另一方面，如果<command>svn commit</command>会自动下载最新的修改，可以使得整个工作拷贝成为修订版本15&mdash;但是，那样我们会打破<quote>push</quote>和<quote>pull</quote>完全分开的原则。因此，Subversion客户端最安全的方式是标记一个文件&mdash;<filename>foo.html</filename>&mdash;为修订版本15，工作拷贝余下的部分还是修订版本10。只有运行<command>svn update</command>才会下载最新的修改，整个工作拷贝被标记为修订版本15。
          </para>

        </sect3>

        <sect3 id="svn.basic.in-action.mixedrevs.normal">
          <title>混合修订版本非常正常</title>
          
          <para>事实上，<emphasis>每次</emphasis>运行<command>svn commit</command>，你的工作拷贝都会进入混合多个修订版本的状态，刚刚提交的文件会比其他文件有更高的修订版本号。经过多次提交（之间没有更新），你的工作拷贝会完全是混合的修订版本。即使只有你一个人使用版本库，你依然会见到这个现象。为了检验混合工作修订版本，可以使用<command>svn status --verbose</command>命令（详细信息见<xref linkend="svn.tour.cycle.examine.status"/>）。
          </para>

          <para>通常，新用户对于工作拷贝的混合修订版本一无所知，这会让人糊涂，因为许多客户端命令对于所检验条目的修订版本很敏感。例如<command>svn log</command>命令显示一个文件或目录的历史修改信息（见<xref linkend="svn.tour.history.log"/>），当用户对一个工作拷贝对象调用这个命令，他们希望看到这个对象的整个历史信息。但是如果这个对象的修订版本已经相当老了（通常因为很长时间没有运行<command>svn update</command>），此时会显示比这个对象<emphasis>更老的</emphasis>历史。
         </para>
        </sect3>

        <sect3 id="svn.basic.in-action.mixedrevs.useful">
          <title>混合修订版本很有用</title>

          <para>如果你的项目十分复杂，有时候你会发现强制工作拷贝的一部分<quote>回溯</quote>到过去非常有用，你将在第三章学习到如何这样做。或许你很希望测试某一子目录下某一子模块的早期版本，又或是要测试一个bug什么时候发生，这是版本控制系统像<quote>时间机器</quote>的一个方面&mdash;这个特性允许工作拷贝的任何一个部分在历史中前进或后退。</para>

        </sect3>

        <sect3 id="svn.basic.in-action.mixedrevs.limits">
          <title>混合修订版本也有限制</title>

          <para>无论你如何在工作拷贝中利用混合修订版本，这种灵活性还是有限制的。
          </para>
          
          <para>首先，你不可以提交一个不是完全最新的文件或目录，如果有个新的版本存在于版本库，你的删除操作会被拒绝，这防止你不小心破坏你没有见到的东西。</para>

          <para>第二，如果目录已经不是最新的了，你不能提交一个目录的元数据更改。你将会在第6章学习附加<quote>属性</quote>，一个目录的工作修订版本定义了许多条目和属性，因而对一个过期的版本提交属性会破坏一些你没有见到的属性。</para>

        </sect3>

    </sect2>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.basic.summary">
    <title>摘要</title>
    
    <para>我们在这一章里学习了许多Subversion的基本概念：</para>

    <itemizedlist>
      <listitem>
        <para>我们介绍了中央版本库的概念、客户工作拷贝和版本修订树。</para>
      </listitem>

      <listitem>
        <para>
        我们介绍了两个协作者如何使用Subversion发布和获得对方的修改，使用<quote>拷贝-修改-合并</quote>模型。</para>
      </listitem>

      <listitem>
        <para>我们讨论了一些Subversion跟踪和管理工作拷贝信息的方式。</para>
      </listitem>

    </itemizedlist>
    
    <para>
    现在，你一定对Subversion在多数情形下的工作方式有了很好的认识，有了这些知识的武装，你一定已经准备好跳到下一章去了，一个关于Subversion命令与特性的详细教程。</para>
      
  </sect1>

</chapter>

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