﻿<chapter id="svn.tour">
  <title>指导教程</title>

  <simplesect>

    <para>现在，我们将要深入到Subversion到使用细节当中，完成本章，你将学会所有日常使用的Subversion命令，你将从一个初始化检出开始，做出修改并检查，你也将会学到如何将别人的修改取到工作拷贝，检查他们，并解决所有可能发生的冲突。</para>

    <para>这一章并不是Subversion命令的完全列表&mdash;而是你将会遇到的最常用任务的介绍，这一章假定你已经读过并且理解了<xref linkend="svn.basic"/>，而且熟悉Subversion的模型，如果想查看所有命令的参考，见<xref linkend="svn.ref"/>。</para>

  </simplesect>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.help">
    <title>帮助！</title>


    <para>在继续阅读之前，需要知道Subversion使用中最重要的命令：<command>svn help</command>，Subversion命令行工具是一个自文档的工具&mdash;在任何时候你可以运行<command>svn help
      &lt;subcommand&gt;</command>来查看子命令的语法、参数以及行为方式。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.import">
    <title>导入</title>

    <para>使用<command>svn import</command>来导入一个新项目到Subversion的版本库，这恐怕是使用Subversion必定要做的第一步操作，但不是经常发生的事情，详细介绍可以看本章后面的<xref
      linkend="svn.tour.other.import"/>。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.revs">
    <title>修订版本: 号码、关键字和日期，噢，我的天啊！</title>

    <para>在继续之前你一定要知道如何识别版本库的一个修订版本，像你在<xref linkend="svn.basic.in-action.revs"/>看到的，一个修订版本就是版本库的一个<quote>快照</quote>，当你的版本库持续扩大，你必须有手段来识别这些快照。</para>

    <para>你可以使用<option>--revision</option>（<option>-r</option>）参数来选择特定修订版本（<command>svn --revision REV</command>），你也可以指定在两个修订版本之间的一个范围 （<command>svn --revision REV1:REV2</command>）。你可以在Subversion中通过修订版本号、关键字或日期指定特定修订版本。</para>
    
    <!-- =============================================================== -->
    <sect2 id="svn.tour.revs.numbers">
      <title>修订版本号</title>
      
      <para>当你新建了一个Subversion版本库，从修订版本号0开始，每一次成功的提交加1，当你提交成功，Subversion告诉客户端这个新版本号：</para>
      
      <screen>
$ svn commit --message "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.
</screen>

      <para>如果你想在未来使用这个版本（我们将在此章的后面讲述我们这样做的方式和原因），你可以通过号码<quote>3</quote>指定。</para>

    </sect2>
    
    <!-- =============================================================== -->
    <sect2 id="svn.tour.revs.keywords">
      <title>修订版本关键字</title>
      
      <para>Subversion客户端可以理解一些<firstterm>修订版本关键字</firstterm>，这些关键字可以用来代替<option>--revision</option>的数字参数，这会被Subversion解释到特定版本：</para>

      <note>
        <para>工作拷贝中的每一个目录都有一个叫作<filename>.svn</filename>的管理目录，工作目录中的每一个文件，Subversion在管理区域为它保留了一个备份，这是上一个版本（叫做<quote>BASE</quote>版本）没有修改的（没有关键字变化，没有行结束符号转化，没有任何改动）拷贝，我们把这个文件当作<firstterm>原始拷贝</firstterm>或<firstterm>基准文件</firstterm>使用，它与版本库中的文件完全一样。</para> 
        </note>
      
      <variablelist>
        
        <varlistentry>
          <term>HEAD</term>
          <listitem>
            <para>版本库中最新的（或者是<quote>最年轻的</quote>）版本。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>BASE</term>
          <listitem>
            <para>工作拷贝中一个条目的修订版本号，如果这个版本在本地修改了，则<quote>BASE版本</quote>就是这个条目在本地未修改的版本。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>COMMITTED</term>
          <listitem>
            <para>在<literal>BASE</literal>版本之前（或在Base）一个项目最后修改的版本。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term>PREV</term>
          <listitem>
            <para>一个项目最后修改版本<emphasis>之前</emphasis>的那个版本（技术上为COMMITTED -1）。</para>
          </listitem>
        </varlistentry>
        
      </variablelist>

      <note>
        <para><literal>PREV</literal>、<literal>BASE</literal>、和<literal>COMMITTED</literal>指的都是本地路径而不是URL。</para>
      </note>
      
      <para>下面是一些关键字使用的例子，不要担心现在没有意义，我们将在本章的后面解释这些命令：</para>
      
      <screen>
$ svn diff --revision PREV:COMMITTED foo.c
# shows the last change committed to foo.c

$ svn log --revision HEAD
# shows log message for the latest repository commit

$ svn diff --revision HEAD
# compares your working file (with local changes) to the latest version
# in the repository

$ svn diff --revision BASE:HEAD foo.c
# compares your <quote>pristine</quote> foo.c (no local changes) with the 
# latest version in the repository

$ svn log --revision BASE:HEAD
# shows all commit logs since you last updated

$ svn update --revision PREV foo.c
# rewinds the last change on foo.c
# (foo.c's working revision is decreased)
</screen>
      
      <para>这些关键字允许你执行许多常用（而且有用）的操作，而不必去查询特定的修订版本号，或者记住本地拷贝的修订版本号。</para>
      
    </sect2>
    
    <!-- =============================================================== -->
    <sect2 id="svn.tour.revs.dates">
      <title>修订版本日期</title>
      
      <para>在任何你使用特定版本号和版本关键字的地方，你也可以在<quote>{}</quote>中使用日期，你也可通过日期或者版本号配合使用来访问一段时间的修改！</para>
      
      <para>如下是一些Subversion能够接受的日期格式，注意在日期中有空格时需要使用引号。</para>

      <screen>
$ svn checkout --revision {2002-02-17}
$ svn checkout --revision {15:30}
$ svn checkout --revision {15:30:00.200000}
$ svn checkout --revision {"2002-02-17 15:30"}
$ svn checkout --revision {"2002-02-17 15:30 +0230"}
$ svn checkout --revision {2002-02-17T15:30}
$ svn checkout --revision {2002-02-17T15:30Z}
$ svn checkout --revision {2002-02-17T15:30-04:00}
$ svn checkout --revision {20020217T1530}
$ svn checkout --revision {20020217T1530Z}
$ svn checkout --revision {20020217T1530-0500}
&hellip;
</screen>
      
      <para>当你指定一个日期，Subversion会在版本库找到接近这个日期的最新版本：</para>
        
      <screen>
$ svn log --revision {2002-11-28}
------------------------------------------------------------------------
r12 | ira | 2002-11-27 12:31:51 -0600 (Wed, 27 Nov 2002) | 6 lines
&hellip;
</screen>
        
      <sidebar>
        <title>Subversion会早一天吗？</title>
        
        <para>如果你只是指定了日期而没有时间（举个例子<literal>2002-11-27</literal>），你也许会以为Subversion会给你11-27号最后的版本，相反，你会得到一个26号版本，甚至更早。记住Subversion会根据你的日期找到<emphasis>最新的</emphasis>版本，如果你给一个日期，而没有给时间，像<literal>2002-11-27</literal>，Subversion会假定时间是00:00:00，所以在27号找不到任何版本。</para>

        <para>如果你希望查询包括27号，你既可以使用（<literal>{"2002-11-27
          23:59"}</literal>），或是直接使用（<literal>{2002-11-28}</literal>）。</para>
        
      </sidebar>
      
      <para>你可以使用时间段，Subversion会找到这段时间的所有版本：</para>
      
      <screen>
$ svn log --revision {2002-11-20}:{2002-11-29}
&hellip;
</screen>
        
      <para>我们也曾经指出，你可以混合日期和修订版本号：</para>
      
      <screen>
$ svn log --revision {2002-11-20}:4040
</screen>

      <para>用户一定要认识到这种精巧会成为处理日期的绊脚石，因为一个版本的时间戳是作为一个属性存储的&mdash;不是版本化的，而是可以编辑的属性&mdash;版本号的时间戳可以被修改，从而建立一个虚假的年代表，也可以被完全删除。这将大大破坏Subversion的这种时间&mdash;版本转化功能的表现。</para>
        
    </sect2>
      
  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.initial">
    <title>初始化的Checkout</title>

    <para>大多数时候，你会使用<firstterm>checkout</firstterm>从版本库取出一个新拷贝开始使用Subversion，这样会在本机创建一个项目的本地拷贝，这个拷贝包括版本库中的HEAD（最新的）版本：</para>
    

    <screen>
$ svn checkout http://svn.collab.net/repos/svn/trunk
A  trunk/subversion.dsw
A  trunk/svn_check.dsp
A  trunk/COMMITTERS
A  trunk/configure.in
A  trunk/IDEAS
&hellip;
Checked out revision 2499.
</screen>
    
    <sidebar>
      <title>名称中有什么？</title>

      <para>Subversion努力控制版本控制下数据的类型，文件的内容和属性值都是按照二进制数据存储和传递，并且<xref 
      linkend="svn.advanced.props.special.mime-type"/>给Subversion提示以说明对于特定文件<quote>文本化的</quote>操作是没有意义的，也有一些地方，Subversion对存放信息的有限制。
      </para>

      <para>Subversion内部使用二进制处理数据&mdash;例如，属性名称，路径名和日志信息&mdash;UTF-8编码的Unicode，这并不意味着与Subversion的交互必须完全使用UTF-8。作为一个惯例，Subversion的客户端能够透明的转化UTF-8和你所使用系统的编码，前提是可以进行有意义的转换（当然是大多数目前常见的编码）。
      </para>

      <para>此外，路径名称在WebDAV交换中会作为XML属性值，就像Subversion的管理文件。这意味着路径名称只能包含合法的XML（1.0）字符，Subversion也会禁止路径名称中出现TAB、CR或LF字符，所以它们才不会在区别程序或如<xref
        linkend="svn.ref.svn.c.log"/>和<xref
        linkend="svn.ref.svn.c.status"/>的输出命令中断掉。</para>

      <para>虽然看起来要记住很多事情，但在实践中这些限制很少会成为问题。只要你的本地设置兼容UTF-8，也不在路径名称中使用控制字符，与Subversion的通讯就不会有问题。命令行客户端会添加一些额外的帮助字节&mdash;自动将你输入的URL路径字符转化为<quote>合法正确的</quote>内部用版本。
      </para>

      <para>有经验的Subversion用户也发展出了一套关于布局版本库路径的最佳实践习惯。这些不像上面提到的有严格的语法要求，它们是用来组织经常执行的任务。URL中的<literal>/trunk</literal>部分就是这种习惯，我们会在<xref linkend="svn.branchmerge"/>进一步论述这个问题并提出相关建议。
     </para>

    </sidebar>

    <para>尽管上面的例子取出了trunk目录，你也完全可以通过输入特定URL取出任意深度的子目录：</para>
    
    <screen>
$ svn checkout http://svn.collab.net/repos/svn/trunk/doc/book/tools
A  tools/readme-dblite.html
A  tools/fo-stylesheet.xsl
A  tools/svnbook.el
A  tools/dtd
A  tools/dtd/dblite.dtd
&hellip;
Checked out revision 2499.
</screen>
    
    <para>因为Subversion使用<quote>拷贝-修改-合并</quote>模型而不是<quote>锁定-修改-解锁</quote>模型（见<xref
      linkend="svn.basic"/>），你可以开始修改工作拷贝中的目录和文件，你的工作拷贝和你的系统中的其它文件和目录完全一样，你可以编辑并改变它，移动它，也可以完全的删掉它，把它忘了。</para>

      <note>
        <para>因为你的工作拷贝<quote>同你的系统上的文件和目录没有什么区别</quote>，如果你希望重新规划工作拷贝，你必须要让Subversion知道，当你希望拷贝或者移动工作拷贝的一个项目时，你应该使用<command>svn copy</command>或者 <command>svn
          move</command>而不要使用操作系统的命令，我们会在以后的章节详细介绍。</para>
      </note>

    <para> 除非你准备好了提交一个新文件或目录，或改变了已存在的，否则没有必要通知Subversion你做了什么。</para>

    <sidebar>
      <title><filename>.svn</filename>目录包含什么？</title>

      <para>
   工作拷贝中的任何一个目录包括一个名为<filename>.svn</filename>管理区域，通常列表操作不显示这个目录，但它仍然是一个非常重要的目录，无论你做什么？不要删除或是更改这个管理区域的任何东西，Subversion使用它来管理工作拷贝。</para>

    </sidebar>

    <para>因为你可以使用版本库的URL作为唯一参数取出一个工作拷贝，你也可以在版本库URL之后指定一个目录，这样会将你的工作目录放到你的新目录，举个例子：</para>
    
    <screen>
$ svn checkout http://svn.collab.net/repos/svn/trunk subv
A  subv/subversion.dsw
A  subv/svn_check.dsp
A  subv/COMMITTERS
A  subv/configure.in
A  subv/IDEAS
&hellip;
Checked out revision 2499.
</screen>
    
    <para>这样将把你的工作拷贝放到<literal>subv</literal>而不是和前面那样放到<literal>trunk</literal>。</para>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.cycle">
    <title>基本的工作周期</title>

    <para>Subversion有许多特性、选项和华而不实的高级功能，但日常的工作中你只使用其中的一小部分，有一些只在特殊情况才会使用，在这一节里，我们会介绍许多你在日常工作中常见的命令。</para>
    
    <para>典型的工作周期是这样的：</para>

    <itemizedlist>
      <listitem>
        <para>更新你的工作拷贝</para>
        <itemizedlist>
          <listitem>
            <para><command>svn update</command></para>
          </listitem>
        </itemizedlist>
        
      </listitem>
      
      <listitem>
        <para>做出修改</para>
        <itemizedlist>
          <listitem>
            <para><command>svn add</command></para>
          </listitem>
          <listitem>
            <para><command>svn delete</command></para>
          </listitem>
          <listitem>
            <para><command>svn copy</command></para>
          </listitem>
          <listitem>
            <para><command>svn move</command></para>
          </listitem>
        </itemizedlist>
      </listitem>

      <listitem>
        <para>检验修改</para>
        <itemizedlist>
          <listitem>
            <para><command>svn status</command></para>
          </listitem>
          <listitem>
            <para><command>svn diff</command></para>
          </listitem>
          <listitem>
            <para><command>svn revert</command></para>
          </listitem>
        </itemizedlist>
      </listitem>

      <listitem>
        <para>合并别人的修改到工作拷贝</para>
        <itemizedlist>
          <listitem>
            <para><command>svn update</command></para>
          </listitem>
          <listitem>
            <para><command>svn resolved</command></para>
          </listitem>
        </itemizedlist>
      </listitem>

      <listitem>
        <para>提交你的修改</para>
        <itemizedlist>
          <listitem>
            <para><command>svn commit</command></para>
          </listitem>
        </itemizedlist>
      </listitem>
    </itemizedlist>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.cycle.update">
      <title>更新你的工作拷贝</title>

      <para>
      当你在一个团队的项目里工作时，你希望更新你的工作拷贝得到所有其他人这段时间作出的修改，使用<command>svn update</command>让你的工作拷贝与最新的版本同步。</para>
      
      <screen>
$ svn update
U  foo.c
U  bar.c
Updated to revision 2.
</screen>
      
      <para>这种情况下，其他人在你上次更新之后提交了对<filename>foo.c</filename>和<filename>bar.c</filename>的修改，因此Subversion更新你的工作拷贝来引入这些更改。</para>
      
      <para>让我们认真检查<command>svn update</command>的输出，当服务器发送修改到你的工作拷贝，一个字母显示在每一个项目之前，来让你知道Subversion对你的工作拷贝做了什么操作：</para>

      <variablelist>

        <varlistentry>
          <term><computeroutput>U      foo</computeroutput></term>
          <listitem>
            <para>文件<filename>foo</filename>更新了（从服务器收到修改）。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><computeroutput>A      foo</computeroutput></term>
          <listitem>
            <para>文件或目录<filename>foo</filename>被添加到工作拷贝。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><computeroutput>D      foo</computeroutput></term>
          <listitem>
            <para>文件或目录<filename>foo</filename>在工作拷贝被删除了。</para>
          </listitem>
        </varlistentry>
        
        <varlistentry>
          <term><computeroutput>R      foo</computeroutput></term>
          <listitem>
            <para>文件或目录<filename>foo</filename>在工作拷贝已经被替换了，这是说，<filename>foo</filename>被删除，而一个新的同样名字的项目添加进来，它们具有同样的名字，但是版本库会把它们看作具备不同历史的不同对象。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><computeroutput>G      foo</computeroutput></term>
          <listitem>
            <para>文件<filename>foo</filename>接收到版本库的更改，你的本地版本也已经修改，但改变没有互相影响，Subversion成功的将版本库和本地文件合并，没有发生任何问题。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><computeroutput>C      foo</computeroutput></term>
          <listitem>
            <para>文件<filename>foo</filename>的修改与服务器冲突，服务器的修改与你的修改交迭在一起，不要恐慌，这种冲突需要人（你）来解决，我们在后面的章节讨论这种情况。</para>
          </listitem>
        </varlistentry>

      </variablelist>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.cycle.edit">
      <title>修改你的工作拷贝</title>
      
      <para>现在你可以开始工作并且修改你的工作拷贝了，你很容易决定作出一个修改（或者是一组），像写一个新的特性，修正一个错误等等。这时可以使用的Subversion命令包括<command>svn add</command>、
        <command>svn delete</command>、<command>svn copy</command>和<command>svn move</command>。如果你只是修改版本库中已经存在的文件，在你提交之前，不必使用上面的任何一个命令。你可以对工作备份作的修改包括：</para>
      
      <variablelist>
        
        <varlistentry>
          <term>文件修改</term>
          <listitem>
            <para>这是最简单的一种修改，你不必告诉Subversion你想修改哪一个文件，只需要去修改，然后Subversion会自动地探测到哪些文件已经更改了。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term>目录树修改</term>
          <listitem>
            <para>你可以<quote>标记</quote>目录或者文件为预定要删除、增加、复制或者移动，也许这些改动在你的工作拷贝马上发生，而版本库只在你提交的时候才发生改变。</para>
          </listitem>
        </varlistentry>

      </variablelist>
      
      <para>修改文件，可以使用文本编辑器、字处理软件、图形程序或任何你常用的工具，Subverion处理二进制文件像同文本文件一样&mdash;效率也一样。</para>
      
      <para>这些是常用的可以修改目录树结构的子命令（我们会在后面包括<command>svn import</command>和<command>svn mkdir</command>）。</para>

      <warning>
        <para>你可以使用任何你喜欢的工具编辑文件，但你不可以在修改目录结构时不通知Subversion，需要使用<command>svn copy</command>、<command>svn
          delete</command>和<command>svn move</command>命令修改工作拷贝的结构，使用<command>svn add</command>增加版本控制的新文件或目录。</para> </warning>
      
      <variablelist>
        
        <varlistentry>
          <term><command>svn add foo</command></term>
          <listitem>
            <para>预定将文件、目录或者符号链<filename>foo</filename>添加到版本库，当你下次提交后，<filename>foo</filename>会成为其父目录的一个子对象。注意，如果<filename>foo</filename>是目录，所有foo中的内容也会预定添加进去，如果你只想添加<filename>foo</filename>本身，使用<option>--non-recursive</option>（<option>-N</option>）参数。</para>

          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn delete foo</command></term>
          <listitem>
            <para>预定将文件、目录或者符号链<filename>foo</filename>从版本库中删除掉，如果foo是文件，它马上从工作拷贝中删除，如果是目录，不会被删除，但是Subversion准备好删除了，当你提交你的修改，<filename>foo</filename>就会在你的工作拷贝和版本库中被删除。<footnote><para>当然没有任何东西是在版本库里被删除了&mdash;只是在版本库的<literal>HEAD</literal>里消失了，你可以通过检出（或者更新你的工作拷贝）你做出删除操作的前一个修订版本来找回所有的东西。</para></footnote></para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn copy foo bar</command></term>
          <listitem>
            <para>建立一个新的项目<filename>bar</filename>作为<filename>foo</filename>的复制品，当在下次提交时会将<filename>bar</filename>添加到版本库，这种拷贝历史会记录下来（按照来自<filename>foo</filename>的方式记录），<command>svn copy</command>并不建立中介目录。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn move foo bar</command></term>
          <listitem>
            <para>这个命令与与运行<command>svn copy foo bar; svn delete foo</command>完全相同，<filename>bar</filename>作为<filename>foo</filename>的拷贝准备添加，<filename>foo</filename>已经预定要被删除，<command>svn move</command>不建立中介的目录。</para>
          </listitem>
        </varlistentry>

      </variablelist>

      <sidebar>
        <title>不通过工作拷贝修改版本库</title>

        <!-- ### pll - Fri 07 Feb 2003 12:55:07                            -->
        <!-- I find this sidebar rather confusing here, since you really   -->
        <!-- haven't gone through explaining the svn command line syntax   -->
        <!-- anywhere.  Maybe a short section explaining that subversion   -->
        <!-- can be used both in networked and non-networked environments, -->
        <!-- and which types of commands need URLs, etc.  This should      -->
        <!-- probably be in chapter 2 (around the "Subversion in action"   -->
        <!-- though, so that this sidebar makes more sense.                -->
        
        <para>本章的前面曾经说过，为了使版本库反映你的改动，你应该提交所有改动。这并不完全正确&mdash;有一些方式<emphasis>是</emphasis>可以直接操作版本库的，当然只有子命令直接操作URL而不是本地拷贝路径时才可以实现，通常<command>svn mkdir</command>、<command>svn
          copy</command>、<command>svn move</command>、和
          <command>svn delete</command>可以使用URL工作。</para>

        <para>指定URL的操作方式有一些区别，因为在使用工作拷贝的运作方式时，工作拷贝成为一个<quote>集结地</quote>，可以在提交之前整理组织所要做的修改，直接对URL操作就没有这种奢侈，所以当你直接操作URL的时候，所有以上的动作代表一个立即的提交。</para>

      </sidebar>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.cycle.examine">
      <title>检查你的修改</title>
      
      <para>当你完成修改，你需要提交他们到版本库，但是在此之前，检查一下做过什么修改是个好主意，通过提交前的检查，你可以整理一份精确的日志信息，你也可以发现你不小心修改的文件，给了你一次恢复修改的机会。此外，这是一个审查和仔细察看修改的好机会，你可通过命令<command>svn
        status</command>、<command>svn diff</command>和<command>svn revert</command>精确地察看所做的修改。你可以使用前两个命令察看工作拷贝中的修改，使用第三个来撤销部分（或全部)的修改。</para>
      
      <!-- pll - Fri 07 Feb 2003 12:55:07                                   -->
      <!-- I find this following paragraph a little unclear.  Mostly for    -->
      <!-- the same reasons as I found the sidebar above unclear.  There    -->
      <!-- hasn't been much discussion of the Subversion architecture, and  -->
      <!-- therefore, nothing has been clearly stated wrt the fact that the -->
      <!-- repository may not be local.                                     -->
      
      <para>Subversion已经被优化来帮助你完成这个任务，可以在不与版本库通讯的情况下做许多事情，详细来说，对于每一个文件，你的的工作拷贝在<filename>.svn</filename>包含了一个<quote>原始的</quote>拷贝，所以Subversion可以快速的告诉你那些文件修改了，甚至允许你在不与版本库通讯的情况下恢复修改。</para>
      
      <sect3 id="svn.tour.cycle.examine.status">
        <title><command>svn status</command></title>
        
        <para>相对于其他命令，你会更多地使用这个<command>svn status</command>命令。</para>
        
        <sidebar>
          <title>CVS用户：控制另类的更新！</title>
          
          <para>你也许使用<command>cvs update</command>来看你做了哪些修改，<command>svn status</command>会给你所有你做的改变&mdash;而不需要访问版本库，并且不会在不知情的情况下与其他用户作的更改比较。</para>
        
          <para>在Subversion，<command>update</command>只是做这件事&mdash;将工作拷贝更新到版本库的最新版本，你可以消除使用<command>update</command>察看本地修改的习惯。</para>

        </sidebar>
        
        <para>如果你在工作拷贝的顶级目录运行不带参数的<command>svn status</command>命令，它会检测你做的所有的文件或目录的修改，以下的例子是来展示<command>svn
          status</command>可能返回的状态码（注意，<literal>#</literal>之后的不是<command>svn status</command>打印的）。</para>
      
        <screen>
  L     some_dir            # svn已经在.svn目录锁定了some_dir 
M       bar.c               # bar.c的内容已经在本地修改过了
 M      baz.c               # baz.c属性有修改，但没有内容修改
X       3rd_party           # 这个目录是外部定义的一部分
?       foo.o               # svn并没有管理foo.o
!       some_dir            # svn管理这个，但它可能丢失或者不完
~       qux                 # 作为file/dir/link进行了版本控制，但类型已经改变
I       .screenrc           # svn不管理这个，配置确定要忽略它
A  +    moved_dir           # 包含历史的添加，历史记录了它的来历
M  +    moved_dir/README    # 包含历史的添加，并有了本地修改
D       stuff/fish.c        # 这个文件预定要删除
A       stuff/loot/bloo.h   # 这个文件预定要添加
C       stuff/loot/lump.c   # 这个文件在更新时发生冲突
                              
 C      stuff/loot/glub.c   # 文件在更新时发生属性冲突
R       xyz.c               # 这个文件预定要被替换
    S   stuff/squawk        # 这个文件已经跳转到了分支
     K  dog.jpg             # 文件在本地锁定；有锁定令牌
     O  cat.jpg             # 文件在版本库被其他用户锁定
     B  bird.jpg            # 文件本地锁定，但锁定发生错误
     T  fish.jpg            # 文件本地锁定，但锁定丢失
        
</screen>
      
        <para>在这种格式下，<command>svn status</command>打印五列字符，紧跟一些空格，接着是文件或者目录名。第一列告诉一个文件的状态或它的内容，返回代码解释如下：</para>


        <variablelist>

          <varlistentry>
            <term><computeroutput>A      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>预定加入到版本库。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>C      item</computeroutput></term>
            <listitem>
              <para>文件<filename>item</filename>发生冲突，在从服务器更新时与本地版本发生交迭，在你提交到版本库前，必须手工的解决冲突。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>D      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>预定从版本库中删除。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>M      item</computeroutput></term>
            <listitem>
              <para>文件<filename>item</filename>的内容被修改了。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>R      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>预定将要替换版本库中的<filename>item</filename>，这意味着这个对象首先要被删除，另外一个同名的对象将要被添加，所有的操作发生在一个修订版本。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>X      item</computeroutput></term>
            <listitem>
              <para>目录没有版本化，但是与Subversion的外部定义关联，关于外部定义，可以看<xref
                linkend="svn.advanced.externals"/>。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>?      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>不在版本控制之下，你可以通过使用<command>svn
                status</command>的<option>--quiet</option>（<option>-q</option>）参数或父目录的<literal>svn:ignore</literal>属性忽略这个问题，关于忽略文件的使用，见<xref linkend="svn.advanced.props.special.ignore" />。</para>

            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>!      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>在版本控制之下，但是已经丢失或者不完整，这可能因为使用非Subversion命令删除造成的，如果是一个目录，有可能是检出或是更新时的中断造成的，使用<command>svn update</command>可以重新从版本库获得文件或者目录，也可以使用<command>svn revert file</command>恢复原来的文件。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>~      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>在版本库已经存在，但你的工作拷贝中的是另一个。举一个例子，你删除了一个版本库的文件，新建了一个在原来的位置，而且整个过程中没有使用<command>svn delete</command>或是<command>svn add</command>。</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term><computeroutput>I      item</computeroutput></term>
            <listitem>
              <para>文件、目录或是符号链<filename>item</filename>不在版本控制下，Subversion已经配置好了会在<command>svn add</command>、<command>svn import</command>和<command>svn status</command>命令忽略这个文件，关于忽略文件，见<xref
                linkend="svn.advanced.props.special.ignore"/>。注意，这个符号只会在使用<command>svn
                status</command>的参数<option>--no-ignore</option>时才会出现&mdash;否则这个文件会被忽略且不会显示！</para>
            </listitem>
          </varlistentry>

        </variablelist>

        <para>第二列说明文件或目录的属性的状态（更多细节可以看<xref
          linkend="svn.advanced.props"/>），如果一个<computeroutput>M</computeroutput>出现在第二列，说明属性被修改了，否则显示空白。</para>
        
        <para>第三列只显示空白或者<computeroutput>L</computeroutput>，<computeroutput>L</computeroutput>表示Subversion已经锁定了这个目录的工作区域<filename>.svn</filename>，当你的<command>svn
          commit</command>正在运行的时候&mdash;也许正在输入log信息，运行<command>svn
          status</command>你可以看到<computeroutput>L</computeroutput>标记，如果这时候Subversion并没有运行，可以推测Subversion发生中断并且已经锁定，你必须运行<command>svn cleanup</command>来清除锁定（本节后面将有更多论述）。</para>
        
        <para>第四列只会显示空白或<computeroutput>+</computeroutput>，<computeroutput>+</computeroutput>的意思是一个有附加历史信息的文件或目录预定添加或者修改到版本库，通常出现在<command>svn move</command>或是<command>svn copy</command>时，如果是看到<computeroutput>A&nbsp;&nbsp;+</computeroutput>就是说要包含历史的增加，它可以是一个文件或是拷贝的根目录。<computeroutput>+</computeroutput>表示它是即将包含历史增加到版本库的目录的一部分，也就是说他的父目录要拷贝，它只是跟着一起的。 <computeroutput>M&nbsp;&nbsp;+</computeroutput>表示将要包含历史的增加，并且已经更改了。当你提交时，首先会随父目录进行包含历史的增加，然后本地的修改提交到更改后的版本。</para>

        <para>第五列只显示空白或是<computeroutput>S</computeroutput>，表示这个目录或文件已经转到了一个分支下了（使用<command>svn
          switch</command>）。</para>
        
        <para>第六列显示了锁定的信息，将会在<xref
          linkend="svn.advanced.locking"/>详细说明。（这与第三列中的<computeroutput>L</computeroutput>并不是同一个东西，见<xref linkend="svn.advanced.locking.meanings"/>。）
        </para>
          
        <para>如果你传递一个路径给<command>svn
          status</command>，它只给你这个项目的信息：</para>
        
        <screen>
$ svn status stuff/fish.c
D      stuff/fish.c
</screen>
        
        <para><command>svn status</command>也有一个<option>--verbose</option>（<option>-v</option>）选项，它可以显示工作拷贝中的<emphasis>所有</emphasis>项目，即使没有改变过：</para>
           
        <screen>
$ svn status --verbose
M               44        23    sally     README
                44        30    sally     INSTALL
M               44        20    harry     bar.c
                44        18    ira       stuff
                44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
                44        21    sally     stuff/things
A                0         ?     ?        stuff/things/bloo.h
                44        36    harry     stuff/things/gloo.c
</screen>
           
        <para>这是<command>svn status</command>的<quote>加长形式</quote>，第一列保持相同，第二列显示一个工作版本号，第三和第四列显示最后一次修改的版本号和修改人。</para>

        <para>上面所有的<command>svn
          status</command>调用并没有联系版本库，只是与<filename>.svn</filename>中的元数据进行比较的结果，最后，是<option>--show-updates</option>（<option>-u</option>）参数，它将会联系版本库为已经过时的数据添加新信息：</para>
      
        <screen>
$ svn status --show-updates --verbose
M      *        44        23    sally     README
M               44        20    harry     bar.c
       *        44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
A                0         ?     ?        stuff/things/bloo.h
Status against revision:   46
</screen>
           
        <para>注意这两个星号：如果你现在执行<command>svn update</command>，你的<filename>README</filename>和<filename>trout.c</filename>会被更新，这告诉你许多有用的信息&mdash;你可以在提交之前，需要使用更新操作得到文件<filename>README</filename>的更新，或者说文件已经过时，版本库会拒绝了你的提交。（后面还有更多关于此主题）。</para>

      </sect3>

      <sect3 id="svn.tour.cycle.examine.diff">
        <title><command>svn diff</command></title>
        
        <para>另一种检查修改的方式是<command>svn diff</command>命令，你可以通过不带参数的<command>svn diff</command><emphasis>精确的</emphasis>找出你所做的修改，这会输出统一区别格式：<footnote><para>Subversion使用内置区别引擎，缺省情况下输出为统一区别格式。如果你期望不同的输出格式，你可以使用<option>--diff-cmd</option>指定外置的区别程序，并且通过<option>--extensions</option>传递其他参数，举个例子，察看本地文件<filename>foo.c</filename>的区别，同时忽略空格修改，你可以运行<command>svn diff --diff-cmd /usr/bin/diff
          --extensions '-bc' foo.c</command>。</para>
          </footnote>
        </para>
        
        <screen>
$ svn diff
Index: bar.c
===================================================================
--- bar.c	(revision 3)
+++ bar.c	(working copy)
@@ -1,7 +1,12 @@
+#include &lt;sys/types.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;unistd.h&gt;
+
+#include &lt;stdio.h&gt;

 int main(void) {
-  printf("Sixty-four slices of American Cheese...\n");
+  printf("Sixty-five slices of American Cheese...\n");
 return 0;
 }

Index: README
===================================================================
--- README	(revision 3)
+++ README	(working copy)
@@ -193,3 +193,4 @@ 
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c	(revision 1)
+++ stuff/fish.c	(working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h	(revision 8)
+++ stuff/things/bloo.h	(working copy)
+Here is a new file to describe
+things about bloo.
</screen>
        
        <para><command>svn diff</command>命令通过比较你的文件和<filename>.svn</filename>的<quote>原始</quote>文件来输出信息，预定要增加的文件会显示所有增加的文本，要删除的文件会显示所有要删除的文本。</para>

        <para>输出的格式为<firstterm>统一区别格式</firstterm>（unified diff format），删除的行前面加一个<literal>-</literal>，添加的行前面有一个<literal>+</literal>，<command>svn diff</command>命令也打印文件名和<command>打补丁</command>需要的信息，所以你可以通过重定向一个区别文件来生成<quote>补丁</quote>：</para>

        <screen>
$ svn diff &gt; patchfile
</screen>

        <para>举个例子，你可以把补丁文件发送邮件到其他开发者，在提交之前审核和测试。</para>

      </sect3>

      <sect3 id="svn.tour.cycle.examine.revert">
        <title><command>svn revert</command></title>

        <para>假设你通过上面的diff输出发现你不小心用编辑器在<filename>README</filename>中输入了一些字符。
    </para>
      
        <para>这是使用<command>svn
          revert</command>的好机会。</para>
           
        <screen>
$ svn revert README
Reverted 'README'
</screen>
        
        <para>Subversion把文件恢复到未修改的状态，叫做<filename>.svn</filename>目录的<quote>原始</quote>拷贝，应该知道<command>svn revert</command>可以恢复任何预定要做的操作，举个例子，你不再想添加一个文件：</para>

        <screen>
$ svn status foo
?      foo

$ svn add foo
A         foo

$ svn revert foo
Reverted 'foo'

$ svn status foo
?      foo
</screen>

        <note>
          <para><command>svn revert</command>
            <replaceable>ITEM</replaceable>的效果与删除<replaceable>ITEM</replaceable>然后执行<command>svn update -r
            BASE</command> <replaceable>ITEM</replaceable>完全一样，但是，如果你使用<command>svn revert</command>它不必通知版本库就可以恢复文件。</para>
        </note>

        <para>或许你不小心删除了一个文件：</para>

        <screen>
$ svn status README 
       README

$ svn delete README 
D         README

$ svn revert README
Reverted 'README'

$ svn status README
       README
</screen>

      </sect3>

      <sidebar>
          <title>看！没有网络！</title>

        <para>这三个命令（<command>svn
          status</command>、<command>svn diff</command>和
          <command>svn revert</command>）都可以在没有网络的情况下工作，这让你在没有网络连接时的管理修改过程更加简单，像在飞机上旅行，乘坐火车往返或是在海滩上奋力工作时。</para>

        <para>Subversion通过在<filename>.svn</filename>管理区域使用原始的版本缓存来做到这一点，这使得恢复本地版本而<emphasis>不必访问网络</emphasis>，这个缓存（叫做<quote>text-base</quote>）也允许Subversion可以根据原始版本生成一个压缩的增量（<quote>区别</quote>）
提交&mdash;即使你有个非常快的网络，有这样一个缓存有极大的好处，非常的快，只向服务器提交修改的部分，这一点乍一看好像并不重要，但当你要提交一个400M大小的文件的修改时，你就会明白！</para>
    </sidebar>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.cycle.resolve">
      <title>解决冲突（合并别人的修改）</title>
           
      <para>我们可以使用<command>svn status -u</command>来预测冲突，当你运行<command>svn
        update</command>一些有趣的事情发生了：</para>
      
      <screen>
$ svn update
U  INSTALL
G  README
C  bar.c
Updated to revision 46.
</screen>
           
      <para><computeroutput>U</computeroutput>和<computeroutput>G</computeroutput>没必要关心，文件干净的接受了版本库的变化，文件标示为<computeroutput>U</computeroutput>表明本地没有修改，文件已经根据版本库更新。<computeroutput>G</computeroutput>标示合并，标示本地已经修改过，与版本库没有重迭的地方，已经合并。</para>
           
      <para>但是<computeroutput>C</computeroutput>表示冲突，说明服务器上的改动同你的改动冲突了，你需要自己手工去解决。</para>
           
      <para>当冲突发生了，有三件事可以帮助你注意到这种情况和解决问题：</para>
           
      <itemizedlist>

        <listitem>
          <para>Subversion打印<computeroutput>C</computeroutput>标记，并且标记这个文件已冲突。</para>
        </listitem>

        <listitem>
          <para>如果Subversion认为这个文件是可合并的，它会置入<firstterm>冲突标记</firstterm>&mdash;特殊的横线分开冲突的<quote>两面</quote>&mdash;在文件里可视化的描述重叠的部分（Subversion使用<literal>svn:mime-type</literal>属性来决定一个文件是否可以使用上下文的，以行为基础合并，更多信息可以看<xref linkend="svn.advanced.props.special.mime-type"/>）。</para>
        </listitem>

        <listitem>
          <para>对于每一个冲突的文件，Subversion放置三个额外的未版本化文件到你的工作拷贝：</para>

          <variablelist>
            
            <varlistentry>
              <term><filename>filename.mine</filename></term>
              <listitem>
                <para>你更新前的文件，没有冲突标志，只是你最新更改的内容。（如果Subversion认为这个文件不可以合并，<filename>.mine</filename>文件不会创建，因为它和工作文件相同。）</para>
              </listitem>
            </varlistentry>
            
            <varlistentry>
              <term><filename>filename.rOLDREV</filename></term>
              <listitem>
                <para>这是你的做更新操作以前的<literal>BASE</literal>版本文件，就是你在上次更新之后未作更改的版本。</para>
              </listitem>
            </varlistentry>

            <varlistentry>
              <term><filename>filename.rNEWREV</filename></term>
              <listitem>
                <para>这是你的Subversion客户端从服务器刚刚收到的版本，这个文件对应版本库的<literal>HEAD</literal>版本。</para>
              </listitem>
            </varlistentry>

          </variablelist>
          
          <para>这里<literal>OLDREV</literal>是你的<filename>.svn</filename>目录中的修订版本号，<literal>NEWREV</literal>是版本库中<literal>HEAD</literal>的版本号。</para>
        </listitem>
        
      </itemizedlist>
      
      <para>举一个例子，Sally修改了<filename>sandwich.txt</filename>，Harry刚刚改变了他的本地拷贝中的这个文件并且提交到服务器，Sally在提交之前更新它的工作拷贝得到了冲突：</para>
      
      <screen>
$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2
</screen>
      
      <para>在这种情况下，Subversion<emphasis>不</emphasis>会允许你提交<filename>sandwich.txt</filename>，直到你的三个临时文件被删掉。</para>

      <screen>
$ svn commit --message "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
</screen>

      <para>如果你遇到冲突，三件事你可以选择：</para>

        <itemizedlist>

          <listitem>
            <para><quote>手动</quote>合并冲突文本（检查和修改文件中的冲突标志）。</para>
          </listitem>

          <listitem>
            <para>用某一个临时文件覆盖你的工作文件。</para>
          </listitem>

          <listitem>
            <para>运行<command>svn revert &lt;filename&gt;</command>来放弃所有的修改。</para>
          </listitem>

        </itemizedlist>

      <para>一旦你解决了冲突，你需要通过命令<command>svn resolved</command>让Subversion知道，这样就会删除三个临时文件，Subversion就不会认为这个文件是在冲突状态了。<footnote><para>你也可以手工的删除这三个临时文件，但是当Subversion会给你做时你会自己去做吗？我们是这样想的。</para>
        </footnote></para>

      <screen>
$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'
</screen>

      <sect3 id="svn.tour.cycle.resolve.byhand">
        <title>手工合并冲突</title>

        <para>第一次尝试解决冲突让人感觉很害怕，但经过一点训练，它简单的像是骑着车子下坡。</para>

        <para>这里一个简单的例子，由于不良的交流，你和同事Sally，同时编辑了<filename>sandwich.txt</filename>。Sally提交了修改，当你准备更新你的版本，冲突发生了，我们不得不去修改<filename>sandwich.txt</filename>来解决这个问题。首先，看一下这个文件：</para>

        <screen>
$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
&lt;&lt;&lt;&lt;&lt;&lt;&lt; .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
&gt;&gt;&gt;&gt;&gt;&gt;&gt; .r2
Creole Mustard
Bottom piece of bread
</screen>

        <para>小于号、等于号和大于号串是冲突标记，并不是冲突的数据，你一定要确定这些内容在下次提交之前得到删除，前两组标志中间的内容是你在冲突区所做的修改：</para>

        <screen>
&lt;&lt;&lt;&lt;&lt;&lt;&lt; .mine
Salami
Mortadella
Prosciutto
=======
</screen>

        <para>后两组之间的是Sally提交的修改冲突：</para>

        <screen>
=======
Sauerkraut
Grilled Chicken
&gt;&gt;&gt;&gt;&gt;&gt;&gt; .r2
</screen>

        <para>通常你并不希望只是删除冲突标志和Sally的修改&mdash;当她收到三明治时，会非常的吃惊。所以你应该走到她的办公室或是拿起电话告诉Sally，你没办法从从意大利熟食店得到想要的泡菜。<footnote><para>如果你向他们询问，他们非常有理由把你带到城外的铁轨上。</para></footnote>一旦你们确认了提交内容后，修改文件并且删除冲突标志。</para>

        <screen>
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread
</screen>

        <para>现在运行<command>svn resolved</command>，你已经准备好提交了：</para>

        <screen>
$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
</screen>

        <para>记住，如果你修改冲突时感到混乱，你可以参考subversion生成的三个文件&mdash;包括你未作更新的文件。你也可以使用第三方的合并工具检验这三个文件。</para>

      </sect3>

      <sect3 id="svn.tour.cycle.resolve.copyover">
        <title>拷贝覆盖你的工作文件</title>

        <para>如果你只是希望取消你的修改，你可以仅仅拷贝Subversion为你生成的文件替换你的工作拷贝：</para>

        <screen>
$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt
</screen>

      </sect3>

      <sect3 id="svn.tour.cycle.resolve.revert">
        <title>下注：使用<command>svn revert</command></title>

        <para>如果你得到冲突，经过检查你决定取消自己的修改并且重新编辑，你可以恢复你的修改：</para>

        <screen>
$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt
</screen>

        <para>注意，当你恢复一个冲突的文件时，不需要再运行<command>svn resolved</command>。</para>

      </sect3>

      <para>现在我们准备好提交修改了，注意<command>svn resolved</command>不像我们本章学过的其他命令一样需要参数，在任何你认为解决了冲突的时候，只需要小心运行<command>svn resolved</command>，&mdash;一旦删除了临时文件，Subversion会让你提交这文件，即使文件中还存在冲突标记。</para>
      
    </sect2>
    
    <sect2 id="svn.tour.cycle.commit">
      <title>提交你得修改</title>
      
      <para>最后！你的修改结束了，你合并了服务器上所有的修改，你准备好提交修改到版本库。</para>
      
      <para><command>svn commit</command>命令发送所有的修改到版本库，当你提交修改时，你需要提供一些描述修改的<firstterm>日志信息</firstterm>，你的信息会附到这个修订版本上，如果信息很简短，你可以在命令行中使用<option>--message</option>（<option>-m</option>）选项：</para>
           
      <screen>
$ svn commit --message "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.
</screen>
           
      <para>然而，如果你把写日志信息当作工作的一部分，你也许会希望通过告诉Subversion一个文件名得到日志信息，使用<option>--file</option>选项：</para>

      <screen>
$ svn commit --file logmsg 
Sending        sandwich.txt
Transmitting file data .
Committed revision 4.
</screen>

      <para>如果你没有指定<option>--message</option>或者<option>--file</option>选项，Subversion会自动地启动你最喜欢的编辑器（见<xref linkend="svn.advanced.confarea.opts.config"/>的<literal>editor-cmd</literal>部分）来编辑日志信息。</para>
        
        
      <tip>
        <para>如果你使用编辑器撰写日志信息时希望取消提交，你可以直接关掉编辑器，不要保存，如果你已经做过保存，只要简单的删掉所有的文本并再次保存。</para>

        <screen>
$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$
</screen>
      </tip>

      <para>版本库不知道也不关心你的修改作为一个整体是否有意义，它只检查是否有其他人修改了同一个文件，如果别人<emphasis>已经</emphasis>这样做了，你的整个提交会失败，并且提示你一个或多个文件已经过时了：</para>

<screen>
$ svn commit --message "Add another rule"
Sending        rules.txt
svn: Commit failed (details follow):
svn: Out of date: 'rules.txt' in transaction 'g'
</screen>

      <para>此刻，你需要运行<command>svn
        update</command>来处理所有的合并和冲突，然后再尝试提交。</para>

           
      <para>我们已经覆盖了Subversion基本的工作周期，还有许多其它特性可以管理你得版本库和工作拷贝，但是只使用前面介绍的命令你就可以很轻松的工作了。</para>

    </sect2>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.history">
    <title>检验历史</title>

    <para>我们曾经说过，版本库就像是一台时间机器，它记录了所有提交的修改，允许你检查文件或目录以及相关元数据的历史。通过一个Subversion命令你可以根据时间或修订号取出一个过去的版本（或者恢复现在的工作拷贝），然而，有时候我们只是想<emphasis>看看</emphasis>历史而不想<emphasis>回到</emphasis>历史。</para>
           
    <para>有许多命令可以为你提供版本库历史：</para>

      <variablelist>

        <varlistentry>
          <term><command>svn log</command></term>
          <listitem>
            <para>展示给你主要信息：每个版本附加在版本上的作者与日期信息和所有路径修改。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn diff</command></term>
          <listitem>
            <para>展示一个文件改变的详细情况。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn cat</command></term>
          <listitem>
            <para>取得在特定版本的某一个文件显示在当前屏幕。</para>
          </listitem>
        </varlistentry>

        <varlistentry>
          <term><command>svn list</command></term>
          <listitem>
            <para>显示一个目录在某一版本存在的文件。</para>
          </listitem>
        </varlistentry>

      </variablelist>


    <!-- =============================================================== -->
    <sect2 id="svn.tour.history.log">
      <title><command>svn log</command></title>

      <para>找出一个文件或目录的历史信息，使用<command>svn log</command>命令，<command>svn log</command>将会提供你一条记录，包括：谁对文件或目录作了修改、哪个修订版本作了修改、修订版本的日期和时间、还有如果你当时提供了日志信息，也会显示。</para>
           
      <screen>
$ svn log
------------------------------------------------------------------------
r3 | sally | Mon, 15 Jul 2002 18:03:46 -0500 | 1 line

Added include lines and corrected # of cheese slices.
------------------------------------------------------------------------
r2 | harry | Mon, 15 Jul 2002 17:47:57 -0500 | 1 line

Added main() methods.
------------------------------------------------------------------------
r1 | sally | Mon, 15 Jul 2002 17:40:08 -0500 | 1 line

Initial import
------------------------------------------------------------------------
</screen>
           
      <para>注意日志信息缺省根据<emphasis>时间逆序排列</emphasis>，如果希望察看特定顺序的一段修订版本或者单一版本，使用<option>--revision</option> （<option>-r</option>）选项：</para>
           
      <screen>
$ svn log --revision 5:19    # shows logs 5 through 19 in chronological order

$ svn log -r 19:5            # shows logs 5 through 19 in reverse order

$ svn log -r 8               # shows log for revision 8
</screen>
        
      <para>你也可以检查单个文件或目录的日志历史，举个例子：</para>
           
      <screen>
$ svn log foo.c
&hellip;
$ svn log http://foo.com/svn/trunk/code/foo.c
&hellip;
</screen>
           
      <para>这样<emphasis>只会</emphasis>显示这个工作文件（或者URL）做过修订的版本的日志信息。</para>
           
      <para>如果你希望得到目录和文件更多的信息，你可以对<command>svn log</command>命令使用<option>--verbose</option> （<option>-v</option>）开关，因为Subversion允许移动和复制文件和目录，所以跟踪路径修改非常重要，在详细模式下，<command>svn
        log</command> 输出中会包括一个路径修改的历史：</para>

      <screen>
$ svn log -r 8 -v
------------------------------------------------------------------------
r8 | sally | 2002-07-14 08:15:29 -0500 | 1 line
Changed paths:
M /trunk/code/foo.c
M /trunk/code/bar.h
A /trunk/code/doc/README

Frozzled the sub-space winch.

------------------------------------------------------------------------
</screen>
      <para>
      <command>svn log</command>也有一个<option>--quiet</option>
        (<option>-q</option>)选项，会禁止日志信息的主要部分，当与<option>--verbose</option>结合使用，仅会显示修改的文件名。</para>
        
      <sidebar>
        <title>为什么<command>svn log</command>给我一个空的回应？</title>
        
        <para>当使用Subversion一些时间后，许多用户会遇到这种情况：</para>

        <screen>
$ svn log -r 2
------------------------------------------------------------------------
$
</screen>

        <para>乍一看，好像是一个错误，但是想一下修订版本号是作用在版本库整体之上的，如果你没有提供路径，<command>svn log</command>会使用当前目录作为默认的目标，所以，作为结果，如果你对一个本身和子目录在指定版本到现在没有做过修改的目录运行这个命令，你会得到空的日志。如果你希望察看某个版本做的修改的日志，只需要直接告诉<command>svn log</command>使用版本库顶级的目录作为参数，例如<command>svn log -r 2
          http://svn.collab.net/repos/svn</command>。</para>

      </sidebar>
           
    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.history.diff">
      <title><command>svn diff</command></title>

      <para>我们已经看过<command>svn diff</command>&mdash;使用标准区别文件格式显示区别，它在提交前用来显示本地工作拷贝与版本库的区别。</para>
      
      <para>事实上，<command>svn diff</command>有<emphasis>三</emphasis>种不同的用法：</para>

      <itemizedlist>

        <listitem>
          <para>检查本地修改</para>
        </listitem>

        <listitem>
          <para>比较工作拷贝与版本库</para>
        </listitem>

        <listitem>
          <para>比较版本库和版本库</para>
        </listitem>

      </itemizedlist>
      
      <sect3 id="svn.tour.history.diff.local">
        <title>比较本地修改</title>

        <para>像我们看到的，不使用任何参数调用时，<command>svn diff</command>将会比较你的工作文件与缓存在<filename>.svn</filename>的<quote>原始</quote>拷贝：</para>
        
        <screen>
$ svn diff
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$
</screen>
        
      </sect3>

      <sect3 id="svn.tour.history.diff.wcrepos">
        <title>比较工作拷贝和版本库</title>

        <para>如果传递一个<option>--revision</option>（<option>-r</option>）参数，你的工作拷贝会与指定的版本比较。</para>
          
        <screen>
$ svn diff --revision 3 rules.txt 
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$
</screen>
          
      </sect3>

      <sect3 id="svn.tour.history.diff.reposrepos">
        <title>比较版本库与版本库</title>
        
        <para>如果通过<option>--revision</option>
          （<option>-r</option>）传递两个版本号，通过冒号分开，这两个版本会进行比较。</para>
          
        <screen>
$ svn diff --revision 2:3 rules.txt 
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth open
$
</screen>
          
        <para>你不仅可以用<command>svn diff</command>比较你工作拷贝中的文件，你甚至可以通过提供一个URL参数来比较版本库中两个文件的的区别，通常在本地机器没有工作拷贝时非常有用：</para>
           
        <screen>
$ svn diff --revision 4:5 http://svn.red-bean.com/repos/example/trunk/text/rules.txt
&hellip;
$
</screen>

      </sect3>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.history.cat">
      <title><command>svn cat</command></title>

      <para>如果你只是希望检查一个过去的版本而不希望察看它们的区别，使用<command>svn cat</command>：</para>

      <screen>
$ svn cat --revision 2 rules.txt 
Be kind to others
Freedom = Chocolate Ice Cream
Everything in moderation
Chew with your mouth open
$
</screen>

      <para>你可以重定向输出到一个文件：</para>

      <screen>
$ svn cat --revision 2 rules.txt &gt; rules.txt.v2
$
</screen>

      <para>你一定疑惑为什么不只是使用<command>svn update --revision</command> ，将文件更新到旧的文件，我们有使用<command>svn cat</command>的原因。</para>

      <para>首先，你或许希望使用外置的比较工具（或许是一个图形化的工具，或者你的格式无法用标准区别格式察看）察看这两个版本的区别，这种情况下，你需要得到一个旧的版本的拷贝，所以重定向到一个文件，并且在你的比较工具中指定这两个版本来察看区别。</para>

      <para>有时候察看整个文件比只看区别要容易。</para>

      </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.history.list">
      <title><command>svn list</command></title>
      
      <para><command>svn list</command>可以在不下载文件到本地目录的情况下来察看目录中的文件：</para>
      
      <screen>
$ svn list http://svn.collab.net/repos/svn
README
branches/
clients/
tags/
trunk/
</screen>

      <para>如果你希望察看详细信息，你可以使用<option>--verbose</option> （<option>-v</option>）参数：</para>

      <screen>
$ svn list --verbose http://svn.collab.net/repos/svn
   2755 harry          1331 Jul 28 02:07 README
   2773 sally               Jul 29 15:07 branches/
   2769 sally               Jul 29 12:07 clients/
   2698 harry               Jul 24 18:07 tags/
   2785 sally               Jul 29 19:07 trunk/
</screen>

      <para>这些列告诉你文件和目录最后修改的修订版本、做出修改的用户、如果是文件还会有文件的大小，最后是修改日期和项目的名字。</para>
 
    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.history.finalword">
      <title>关于历史的最后一个词</title>

      <para>除了以上的命令，你可以使用带参数<option>--revision</option>的<command>svn update</command>和<command>svn
        checkout</command>来使整个工作拷贝<quote>回到过去</quote><footnote><para>看到了吧？我们说过Subversion是一个时间机器。</para></footnote>：</para>

      <screen>
$ svn checkout --revision 1729 # Checks out a new working copy at r1729
&hellip;
$ svn update --revision 1729 # Updates an existing working copy to r1729
&hellip;
</screen>

    </sect2>

  </sect1>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.other">
    <title>其他有用的命令</title>

      <para>不象这章前面讨论的那些经常用到的命令，这些命令只是偶尔被用到。</para>

    <!-- =============================================================== -->
    <sect2 id="svn.tour.other.cleanup">
      <title><command>svn cleanup</command></title>

      <para>当Subversion改变你的工作拷贝（或是<filename>.svn</filename>中的任何信息），它会尽可能的小心，在修改任何事情之前，它把意图写到日志文件中去，然后执行log文件中的命令，并且执行过程中在工作拷贝的相关部分保存一个锁&mdash; 防止Subversion客户端在变更过程中访问工作拷贝。然后删掉日志文件，这与记帐试的文件系统架构类似。如果Subversion的操作中断了（举个例子：进程被杀死了，机器死掉了），日志文件会保存在硬盘上，通过重新执行日志文件，Subversion可以完成上一次开始的操作，你的工作拷贝可以回到一致的状态。</para>

      <para>这就是<command>svn cleanup</command>所作的：它查找工作拷贝中的所有遗留的日志文件，删除进程中工作拷贝的锁。如果Subversion告诉你工作拷贝中的一部分已经<quote>锁定</quote>了，你就需要运行这个命令了。同样，<command>svn status</command>将会使用<literal>L</literal> 显示锁定的项目：</para>

      <screen>
$ svn status
  L    somedir
M      somedir/foo.c 

$ svn cleanup
$ svn status
M      somedir/foo.c
</screen>

    </sect2>


    <!-- =============================================================== -->
    <sect2 id="svn.tour.other.import">
      <title><command>svn import</command></title>

      <para><command>svn import</command>命令是拷贝用户的一个未被版本化的目录树到版本库最快的方法，如果需要，它也要建立一些中介文件。</para>

      <screen>
$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/some/project \
             -m "Initial import"
Adding         mytree/foo.c
Adding         mytree/bar.c
Adding         mytree/subdir
Adding         mytree/subdir/quux.h

Committed revision 1.
</screen>

      <para>在上一个例子里，将会拷贝目录<filename>mytree</filename>到版本库的<filename>some/project</filename>下：</para>

      <screen>
$ svn list file:///usr/local/svn/newrepos/some/project
bar.c
foo.c
subdir/
</screen>

      <para>注意，在导入之后，原来的目录树并<emphasis>没有</emphasis>转化成工作拷贝，为了开始工作，你还是需要运行<command>svn
        checkout</command>导出一个工作拷贝。</para>
        
        <para>不要将工作拷贝中的锁与并行版本控制的<quote>锁定-修改-解锁</quote>模型中用户使用的锁混淆；为了清楚的认识可以看一下<xref linkend="svn.advanced.locking.meanings"/>。</para>

    </sect2>

  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.tour.summary">
    <title>摘要</title>
        
      <para>我们已经覆盖了大多数Subversion的客户端命令，引人注目的例外是处理分支与合并（见<xref 
      linkend="svn.branchmerge"/>）以及属性（见<xref 
      linkend="svn.advanced.props"/>）的命令，然而你也许会希望跳到<xref 
      linkend="svn.ref"/>来察看所有不同的命令&mdash;怎样利用它们使你的工作更容易。</para>
      
 
  </sect1>

</chapter>

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